:root {
  /* ENTRY #74 — ONE CANDIDATE PALETTE. The same six people walk the whole demo
     (x1riqt parse → nifhr board → compare → ghnij → the profile drawer). Their
     colour was previously authored in THREE disagreeing tables, so Marta went
     teal→blue and Ahmed pink→rust mid-walkthrough. These tokens are the ONLY
     source of truth — never hardcode a candidate hex again. */
  --cand-marta: #1D4ED8;
  --cand-ahmed: #C2410C;
  --cand-priya: #14B8A6;
  --cand-clare: #8B5CF6;
  --cand-jonas: #0EA5E9;
  --cand-daniel: #22C55E;

  /* ⚠️ ENTRY #274 - THE SELECTION PANEL'S OWN PALETTE, AND IT IS A FIX AS WELL AS AN ADDITION.
     collaborator-invite.html's Act 3 roster paints its five reviewers with the CANDIDATE classes
     (`.avatar.priya` on Mia Chen, `.avatar.orange` on Tom Becker, and so on) because six colour
     classes existed and six people needed colouring. That is the exact thing #74 outlawed one line
     above: a class named after Priya holding Mia's colour lies about what it produces.
     These tokens carry the SAME hexes that screen already paints, so nobody changes colour between
     the two screens, but here the colour is named after the person who owns it. Two entities sharing
     a hex is fine and is recorded rather than hidden; a token that names the wrong owner is not.
     ⚠️ NEVER point a collaborator at a `--cand-*` token, and never the reverse. */
  --collab-mia: #14B8A6;
  --collab-tom: #C2410C;
  --collab-sofia: #8B5CF6;
  --collab-raj: #0EA5E9;
  --collab-hannah: #22C55E;
  /* ENTRY #284. Derek Smith is the collaborator the whole film is ABOUT, and he was the one
     member of the selection panel with no token, so he inherited the bare `.avatar` fallback and
     rendered as --cand-marta's blue. Same hex, correctly named: the #274 note above already
     allows two entities to share a value, what it forbids is a token that names the wrong owner. */
  --collab-derek: #1D4ED8;

  --brand: #7C3AED;
  /* ⚠️ ENTRY #374: WAS `#4C1D95`, AND THIS FILE WAS THE ONE OUT OF STEP, NOT THE FILMS.
     Read from the bytes at all nine declaration sites: `jobseeker-paycheck:43`,
     `jobseeker-notifications:36`, `production-ready-interviews:70`, `references-mobile:26`,
     `scheduling-mobile:84`, `reference-check:13`, `screening-mobile:24` and
     `screening-questionnaire:29` ALL say `#5B21B6`. Only this file said `#4C1D95`.
     ⚠️ EIGHT AGAINST ONE IS THE WHOLE ARGUMENT, and it is why the fix runs in this direction:
     changing the majority would have been eight files edited to make the odd one out win. The
     films do not link this stylesheet, so nothing was ever holding the two values equal and the
     drift could not have been caught by any guard that exists.
     ⚠️ THIS MOVES PIXELS AND IS NOT A TIDY-UP: 22 rules in this file read `var(--brand-dark)`. */
  --brand-dark: #5B21B6;
  --brand-hover: #6D28D9;
  --brand-tint: #EDE9FE;
  --bg-app: #F9FAFB;
  --bg-row-highlight: #F9F7FF;
  --bg-subtle: #F3F4F6;
  --surface: #FFFFFF;
  --border: #E5E7EB;
  --border-strong: #D1D5DB;
  --text-primary: #111827;
  --text-strong: #374151;
  --text-muted: #4B5563;
  --text-secondary: #6B7280;
  --text-tertiary: #9CA3AF;
  --text-inverse: #FFFFFF;
  --success: #10B981;
  --success-bg: #D1FAE5;
  --success-text: #059669;
  --warning-bg: #FEF3C7;
  --warning-text: #B45309;
  --info-bg: #DBEAFE;
  --info-text: #1D4ED8;
  --danger: #EF4444;
  --danger-bg: #FEE2E2;
  --danger-text: #B91C1C;

  /* ENTRY #111 (2026-07-18, user call). ONE MODAL/SPOTLIGHT DIM.
     The user put x1riqt's welcome modal beside rj0wj's "Start here" spotlight and the two greys
     visibly disagreed. They did: x1riqt was pure black at .35, both rj0wj rings were dark navy
     #111827 at .34, and all three were hardcoded literals in three separate rules. This token is
     now the only source of truth, per the entry #74 law: the token, never the hex.
     ⚠️ rj0wj's dim is NOT an overlay element. It is the 9999px spread of `.coach-ring`'s
     box-shadow (the documented box-shadow trap), so the token is consumed inside a `box-shadow`
     there and as a `background` here. A colour token works unchanged in both. */
  --scrim-dim: rgba(0, 0, 0, 0.35);

  /* ENTRY #89. ONE APPLIED-COLUMN SURFACE. The lilac worn by nifhr's curated-move source and
     target columns was authored as the same two-layer stack hardcoded in THREE rules, so x1riqt's
     Applied column could not reuse it without a fourth copy. These tokens are the only source of
     truth. Never hardcode the stack again.
     ⚠️ Only the LAST layer of a `background` shorthand may carry a colour, so `var(--brand-tint)`
     here resolves to background-COLOR, not a fourth image: the computed backgroundImage of any
     user is "linear-gradient(...), none" with backgroundColor rgb(237, 233, 254). That is the
     shape the guards assert. Do not "fix" the trailing `none`. */
  --col-applied-tint-top: rgba(249, 247, 255, 0.98);
  --col-applied-tint-bottom: rgba(242, 235, 255, 0.98);
  --col-applied-surface:
    linear-gradient(180deg, var(--col-applied-tint-top), var(--col-applied-tint-bottom)),
    var(--brand-tint);

  /* ENTRY #89. The compare modal's preference traffic light. Pastel tints on purpose: they alias
     the existing bg/text token families, so the dark label text stays legible and the row reads as
     the same design system rather than three saturated signal colours. */
  --rank-most: var(--success-bg);
  --rank-mid: var(--warning-bg);
  --rank-least: var(--danger-bg);
  --shadow-float: 0 8px 24px rgba(0, 0, 0, 0.08);
  --shadow-popover: 0 20px 48px rgba(0, 0, 0, 0.25);
  --shadow-brand: 0 4px 14px rgba(124, 58, 237, 0.25);
  /* ⚠️ ENTRY #276, USER CALL ON AN ANNOTATED FRAME: *"Give me a drop shadow on the phone"*. The
     hero stage already carried `--shadow-popover` and it RENDERS (measured
     `drop-shadow(rgba(0, 0, 0, 0.25) 0px 20px 48px)` on the live element), it simply does not read
     as depth under a 741px phone on a near-white app body: a 48px blur at 0.25 is tuned for a
     popover a fraction of that size.
     ⚠️ A FOURTH TOKEN RATHER THAN A SECOND `drop-shadow()`, AND THAT IS #272za's MEASUREMENT, NOT
     TASTE. Every `drop-shadow` is computed from the element's ALPHA, so each one re-rasterises the
     whole 390x844 phone INCLUDING its shadow-DOM subtree; #272za cut three stacked filters to one
     after per-rAF sampling found a ~110ms window with NO PAINTED FRAME AT ALL at the phone's
     entrance. A heavier single token costs exactly one pass, the same as the token it replaces.
     ⚠️ Not reused anywhere else on purpose: this is the one element in the app rendered at phone
     scale over an undimmed surface. */
  --shadow-hero: 0 32px 80px rgba(0, 0, 0, 0.4);
  /* ⚠️ ENTRY #297d — THE SPACING SCALE, AND IT IS NEW. The user asked for a panel to be spaced
     "using our normal spacing tokens nothing hardcoded", and there were none: this `:root` carried
     colour, radius, shadow and motion tokens and every gap/padding in the project was a literal.
     Rather than invent a local scale on one page, which is exactly what entries #74, #89, #111 and
     #274 each had to undo for colour, the scale lives here so the next screen reuses it.
     4px base, matching the literals already in use (8/12/16/20/24). Additive only: no existing rule
     was changed to consume these, so adopting them is per-rule and opt-in. */
  --space-2xs: 4px;
  --space-xs: 8px;
  --space-sm: 12px;
  --space-md: 16px;
  --space-lg: 24px;
  --space-xl: 32px;
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 16px;
  --radius-xl: 20px;
  /* ⚠️ ENTRY #322: the BOTTOM-SHEET top-corner radius, DERIVED not invented: 24px is the house
     sheet value, hardcoded 16 times across the shipped sheets (.wmode-sheet, .screened-bottom-sheet,
     .nhsq0-msg-card and siblings) with no token expressing it until now. New sheet corners consume
     this; the 16 existing literals are NOT retro-fitted here (each is guard-pinned territory). */
  --radius-sheet: 24px;
  --radius-pill: 999px;
  /* ⚠️⚠️ ENTRY #371 — THE VOCABULARY THAT DID NOT EXIST, AND EVERY VALUE IN IT IS ONE THIS
     CODEBASE ALREADY ANSWERS WITH. Her ask, on an annotated frame of the Next-steps drawer:
     *"nothing hardcoded everything using our standard token values"*, and separately, standing:
     *"ensure spacing tokens are not made up"*.
     The spacing scale (#297d) and the radius scale existed. TYPOGRAPHY, BORDER WIDTH, CONTROL
     HEIGHT and DRAWER GEOMETRY did not: an audit of the `.ns-*` block alone found 87 token uses
     against 131 hardcoded literals, with typography at 50 of 50 hardcoded and ZERO of its 120 px
     literals consuming the spacing scale even where an exact step existed.
     ⚠️ NOTHING HERE IS INVENTED. Every value below is lifted from a literal already shipped in
     this stylesheet, which is the same rule #297d wrote when it created `--space-*` ("4px base,
     matching the literals already in use") and #340 wrote when it named the house teal.
     ⚠️ ADDITIVE AND OPT-IN, exactly like #297d: not one existing rule was repointed at these, so
     adopting them stays per-rule. Only the `.ns-*` block consumes them today.
     ⚠️ THE ONE CATEGORY DELIBERATELY LEFT ALONE IS z-index. There is no z-index vocabulary in this
     file and inventing one would mean re-basing every overlay in the project against a new scale,
     which is a different job with a blast radius across every stacking guard. The three z-index
     literals in the `.ns-*` block stay literals, and that exclusion is stated rather than hidden. */

  /* THE HOUSE DRAWER WIDTH. Measured 2026-08-20 across every screen-edge panel in the demo: SIX
     drawers share `33vw` with a `460px` floor (nifhr's profile drawer, its screening drawer, and
     the messenger's document / location / PDF / questionnaire drawers). Only two disagreed, and
     both were fixed pixel outliers: `.ns-panel` at 400 and `.company-profile-drawer` at 600.
     ⚠️ THIS IS A WIDTH, NOT A DRAWER SYSTEM. There is NO single house drawer in this project: the
     panels fall into three incompatible families with different transforms (100% vs 104%),
     different durations (260 / 280 / 320ms) and z-indexes four orders of magnitude apart. What
     they DO agree on is this width, six to two, so it is the part worth naming. */
  --drawer-w: 33vw;
  --drawer-min-w: 460px;
  --shadow-drawer: -12px 0 32px rgba(0, 0, 0, 0.20);

  /* Half of `--space-2xs`, DERIVED rather than typed, so the base of the scale still governs it.
     It exists because the panel's 2px and 6px and 10px and 20px gaps are real and off-scale, and
     the standing rule is to derive rather than invent or silently round. */
  --space-3xs: calc(var(--space-2xs) / 2);

  /* THE TYPE SCALE. Eight steps, every one of them a size already shipped in the `.ns-*` block.
     ⚠️ ICONS ARE SIZED FROM THIS SCALE TOO, on purpose: an inline SVG that sits on a text baseline
     next to a 14px label is 14px BECAUSE the label is, and expressing that as `var(--fs-md)` says
     so, where a second parallel icon scale would let the two drift apart silently. */
  --fs-2xs: 10px;
  --fs-xs: 11px;
  --fs-sm: 12px;
  --fs-md: 14px;
  --fs-lg: 15px;
  --fs-xl: 16px;
  --fs-2xl: 18px;
  --fs-3xl: 24px;
  --lh-tight: 1.15;
  --lh-snug: 1.4;
  --lh-normal: 1.5;
  /* THE WEIGHT SCALE. ⚠️ ENTRY #374: IT USED TO STOP AT `--fw-bold` AND THE FILE DID NOT.
     Measured on the comment-stripped bytes of this stylesheet: 400 x5, 450 x1, 500 x32, 600 x77,
     650 x17, 700 x82, 725 x1, 750 x6, 800 x161, 850 x35, 900 x46, 950 x14 as `font-weight`, plus
     five more weights inside `font:` shorthands. Three tokens covered 191 of those and named
     nothing above bold, so 465 declarations in this file alone were typing a weight the
     vocabulary had no word for. THE `@font-face` DECLARES `font-weight: 100 900`, so this is a
     VARIABLE font and every one of those intermediate steps renders as a distinct weight: they
     are design, not typos.
     ⚠️ NOT ONE VALUE HERE IS INVENTED. Every number below was already shipped as a literal in
     this file, which is the rule #297d wrote creating `--space-*` and #371 wrote extending the
     type scale. Purely additive and opt-in: no existing rule is repointed by this declaration
     block, so NOTHING MOVES until a literal is deliberately swapped.
     ⚠️ THE NAMING IS DELIBERATE AND MIXED, and it is stated rather than hidden. The six steps CSS
     itself names get their CSS keyword (`regular` 400 through `black` 900). The four half-steps
     have NO standard name, so they carry their number rather than an invented word: a made-up
     `--fw-semibold-plus` would read as a semantic tier when it is really one stop on a continuous
     variable-font ramp. A numeric name still buys the one thing a token is for, which is one
     place to change it.
     ⚠️ 450 AND 725 ARE DELIBERATELY NOT TOKENISED, once each, and that exclusion is said out loud
     rather than left to be discovered: a scale member that appears once is a one-off, and naming
     it would claim a tier the design does not have. */
  /* ⚠️ ENTRY #374: THIS DECLARATION REVIVES THIRTEEN RULES THAT WERE DOING NOTHING AT ALL, and
     that is a change to what renders, not a tidy-up. `var(--font-sans)` was read 13 times in this
     file and DECLARED NOWHERE, so it resolved to the guaranteed-invalid value. In the eight of
     those that sit inside a `font:` SHORTHAND that kills the WHOLE declaration, taking the weight,
     the size and the line-height down with the family.
     ⚠️ MEASURED WITH A CONTROL ON THE RUNNING PAGE before this was written, not reasoned from the
     spec: `rj0wj.html`, `.search-box input`, headless chromium 1440x900. The rule says
     `font: 500 14px/1.3`; it rendered at weight 400 and line-height 21px, the INHERITED values.
     Injecting nothing but this one declaration took it to weight 500 and line-height 18.2px.
     ⚠️ WHY NOBODY EVER CAUGHT IT: `font-size` reads 14px in both states, because the parent is
     also 14px. The one property a person spot-checks agreed by coincidence.
     ⚠️ THE STACK IS NOT INVENTED. It is copied verbatim from this file's own `body` rule, which
     carries it as a literal; that literal is now the second copy and the two must move together.
     ⚠️ `.wmode-btn` KEEPS ITS `font-family: inherit` AND ITS COMMENT IS NOW SUPERSEDED, NOT WRONG:
     it argued against naming a token that did not exist, which was correct at the time. It is
     deliberately NOT repointed at this token, because inheriting is what that button wants. */
  --font-sans: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-650: 650;
  --fw-bold: 700;
  --fw-750: 750;
  --fw-extrabold: 800;
  --fw-850: 850;
  --fw-black: 900;
  --fw-950: 950;
  --ls-caps: 0.6px;

  /* Border widths. The audit found NO border-width token anywhere: `--border` and `--border-strong`
     are COLOURS, and every `1px solid` / `2px solid` in the file types its width. */
  --border-w: 1px;
  --border-w-strong: 2px;

  /* The house primary-control height. 50px is not a new number: `.qq-cta` on all seven cinematic
     intro sheets and `.ns-save` already both write it as a literal. */
  --control-h: 50px;

  /* Promoted from the `.ns-panel, .ns-scrim, .ns-toast` scope so a second consumer can reach them.
     `--warning` and `--warning-tint` were declared there AND re-declared at `collaborator-invite`'s
     own `:root` for its phone scene, which is two copies of one colour with nothing keeping them
     equal.
     ⚠️ ENTRY #373 DELETED `--indeed-blue` FROM HERE. #371 promoted it beside these two, #372 stopped
     the source tile using it, and #373 deleted the tile, so it finished with zero consumers in the
     whole served tree (its only remaining mention is prose). Superseded value: `#2557A7`. The
     identical inline declaration in `collaborator-invite.html`'s own `:root` goes in the same edit,
     and had no consumer there either, despite its comment claiming the phone job-ad scene used it. */
  --warning: #F59E0B;
  --warning-tint: #FFEDD5;
  /* ⚠️ ENTRY #340: THE HOUSE TEAL, GIVEN A NAME IT CAN BE USED BY. #14B8A6 is already this
     project's "this is playing / this is live" colour (the cinematics' time-left bar, and
     `.pause-coach-fill` on rj0wj which says so in its own comment), but until now the only
     :root handles on it were `--cand-priya` and `--collab-mia`, and BOTH are identity tokens
     that this file twice forbids reusing ("never hardcode a candidate hex again", "never point
     a collaborator at a --cand-* token"). `--accent-teal` is not a :root token at all: it is
     scoped to `.ns-panel/.ns-scrim/.ns-toast`, deliberately, so that declaring it would not
     move every screen's cache tag. So a generic consumer had no legal token to point at and
     the colour was being written as a literal instead. This is that token. It does NOT change
     any existing rule: the identity tokens keep their own declarations and their own meaning. */
  --cue-teal: #14B8A6;
  --motion-fast: 160ms;
  --motion-ui: 220ms;
  /* ⚠️ ENTRY #277: the messenger coach card's own travel time, and it is deliberately NOT
     `--motion-ui`. The card is a thing you READ, so it moves on a slower clock than the chrome it
     sits beside; and `--motion-ui` is what the menus/drawers animate on and what
     `FEATURE_COACH_SETTLE_MS` reads to stay ahead of them, so aliasing the two would make the
     settle pass race the card. Paired with a 1.2s minimum dwell in nifhr-watch-mode.js: travel
     0.55s, then be still for at least twice that. */
  --coach-move: 550ms;
  /* ⚠️ USER CALL, 2026-08-12, on two annotated frames: *"fade out the coach card at that point of
     the click"* (the questionnaire send) and *"then 200ms fade out before drawer is closed"* (the
     share picker). 200 is HER number, quoted, not a taste value derived from `--coach-move`. The
     card's disappearance used to be a hard `display: none` cut at every one of its step boundaries;
     this is the duration of the departure. Deliberately NOT aliased to `--motion-ui` (220ms): the
     card must be gone BEFORE the share drawer starts leaving at `SHARE_SEND + 0.22`, and 220 would
     tie that ordering to a token that exists for the chrome. */
  --coach-leave: 200ms;
  /* ⚠️ ENTRY #338f, USER CALL ON TWO ANNOTATED FRAMES: *"Fade and animate this coaching card in
     from this position to the current position its at in the live project just to visually
     emphasise and draw the viewers eyes to this section"* and *"fade and animate in from the right
     a little here too ... At the moment it tracks and transforms from previous position and
     coaching card state but I dont want this for this scene"*. This is the ARRIVAL, the third and
     last of the card's three motion tokens: `--coach-move` is pose to pose, `--coach-leave` is the
     departure, this is the entrance.
     ⚠️ 240 IS NOT INVENTED AND IT IS NOT DERIVED FROM `--coach-move`. It is `mmChatIn`'s duration,
     already in this stylesheet, which is this codebase's existing fade-and-slide-in-from-the-right
     for a panel. Reusing the number the project already answered with beats picking a new one.
     ⚠️ DELIBERATELY NOT ALIASED TO `--motion-ui`, for the same reason `--coach-leave` is not: the
     entrance has to be comfortably inside the 1.70s the hand takes to reach the invented row, and
     tying it to a chrome token would couple that to something with no stake in it. */
  --coach-enter: 240ms;
  --motion-drawer: 320ms;
  --ease-standard: cubic-bezier(.2, 0, 0, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);

  /* ENTRY #174: ONE headline treatment for every DARK bottom sheet and coaching modal, taken
     from x1riqt's welcome sheet (the user's reference: 34px/38px, weight 900, -0.04em).
     `--sheet-title-size` DEFAULTS to the reference's flat 34px. A family whose own copy would
     gain a rendered ROW at 34px overrides the token with a MEASURED clamp instead of being left
     out; the per-family ceilings are recorded at the tail block that consumes these.
     ⚠️ Capitalisation is deliberately NOT part of this token: the four cinematic sheets stay
     uppercase and the rest stay sentence case, per the user's call. */
  --sheet-title-size: 34px;
  --sheet-title-lh: 1.12;
  --sheet-title-ls: -0.04em;
  --sheet-title-weight: 900;
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
html.bundle-drag-lock,
body.bundle-drag-lock {
  overflow: hidden;
  overscroll-behavior: none;
  touch-action: none;
}
body {
  min-height: 100vh;
  background: var(--bg-app);
  color: var(--text-primary);
  font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  line-height: var(--lh-normal);
}
body.x1riqt-screen {
  width: 100%;
  height: 100vh;
  height: 100dvh;
  min-height: 0;
  overflow: hidden;
  overscroll-behavior: none;
}
button, input, a { font: inherit; }
a { color: inherit; text-decoration: none; }
button { border: 0; cursor: pointer; }

.app-shell {
  min-height: 100vh;
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  background: var(--bg-app);
}
body.x1riqt-screen .app-shell {
  height: 100vh;
  height: 100dvh;
  min-height: 0;
  overflow: hidden;
}

.sidebar {
  min-height: 100vh;
  background: var(--surface);
  border-right: 1px solid var(--border);
  padding: var(--space-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
body.x1riqt-screen .sidebar {
  height: 100vh;
  height: 100dvh;
  min-height: 0;
  overflow: hidden;
}
/* Inert since 2026-07-13 (user call): the brand lock-up is a <div>, not a link — it must not
   be clickable on any screen. Keep it non-interactive if this rule is ever touched. */
.side-brand {
  min-height: 44px;
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-xs);
  cursor: default;
  user-select: none;
}
.brand-mark, .workspace-mark {
  display: grid;
  place-items: center;
  color: var(--text-inverse);
  background: var(--brand);
  font-weight: var(--fw-bold);
}
.brand-mark {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: transparent url("mq50ezgz-hirelloLogo-BgVy4PP3.png") center / cover no-repeat;
  color: transparent;
  font-size: 0;
}
.workspace-mark {
  width: 24px;
  min-width: 24px;
  height: 24px;
  min-height: 24px;
  flex: 0 0 24px;
  aspect-ratio: 1;
  border-radius: var(--radius-pill);
  background: var(--text-primary);
  color: var(--text-inverse);
  font-size: var(--fs-xs);
}
.brand-name { font-size: var(--fs-2xl); font-weight: var(--fw-bold); letter-spacing: -0.3px; }
.workspace-switcher {
  min-height: 36px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs);
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-subtle);
}
.company-drawer-launch {
  width: 100%;
  text-align: left;
  cursor: pointer;
}
.company-drawer-launch:hover,
.company-drawer-launch:focus-visible {
  border-color: color-mix(in srgb, var(--brand) 48%, var(--border));
  box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.10);
  outline: none;
}
body.company-drawer-locked .company-drawer-launch {
  cursor: default;
  pointer-events: none;
}
body.company-drawer-locked .company-drawer-launch:hover,
body.company-drawer-locked .company-drawer-launch:focus-visible {
  border-color: var(--border);
  box-shadow: 0 1px 2px rgba(17, 24, 39, 0.04);
  outline: none;
}
.workspace-switcher-primary {
  margin-bottom: var(--space-xs);
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(17, 24, 39, 0.04);
}
.workspace-left { display: flex; align-items: center; gap: var(--space-xs); min-width: 0; flex: 1 1 auto; }
.workspace-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text-strong);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}
.nav-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.nav-item, .support-link {
  min-height: 34px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-sm);
  color: var(--text-strong);
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
}
.nav-item svg, .support-link svg { width: 18px; height: 18px; color: var(--text-secondary); flex: 0 0 auto; }
.nav-item.active { color: var(--brand); background: var(--brand-tint); font-weight: var(--fw-semibold); }
.nav-item.active svg { color: var(--brand); }
.demo-nav-disabled .nav-item {
  cursor: default;
  pointer-events: none;
}
.demo-nav-disabled .nav-item:hover,
.demo-nav-disabled .nav-item:focus-visible {
  background: transparent;
  outline: none;
}
.demo-nav-disabled .nav-item.active:hover,
.demo-nav-disabled .nav-item.active:focus-visible {
  background: var(--brand-tint);
}
.sidebar-spacer { flex: 1; }

.main {
  min-width: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
body.x1riqt-screen .main {
  height: 100vh;
  height: 100dvh;
  min-height: 0;
  overflow: hidden;
}
.topbar {
  min-height: 61px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-sm) 20px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.search-box {
  width: min(420px, 100%);
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 10px var(--space-md);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-tertiary);
  font-size: var(--fs-md);
}
.search-box svg { width: 16px; height: 16px; color: var(--text-secondary); flex: 0 0 auto; }
.search-box input {
  min-width: 0;
  width: 100%;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--text-primary);
  font: 500 14px/1.3 var(--font-sans);
  appearance: none;
}
.search-box input::placeholder { color: var(--text-tertiary); }
.topbar-right { display: flex; align-items: center; gap: var(--space-sm); }
.demo-curated-shell .sidebar,
.demo-curated-shell .topbar {
  pointer-events: none;
}

.demo-curated-shell .sidebar a,
.demo-curated-shell .topbar button,
.demo-curated-shell .topbar input,
.demo-curated-shell .topbar .user-dot {
  cursor: default;
}

.icon-button {
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-secondary);
}
.icon-button:hover { background: var(--bg-subtle); }
.user-dot {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-pill);
  background: var(--brand-tint);
  border: 1px solid var(--border);
  display: grid;
  place-items: center;
  color: var(--brand);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
}

.content {
  flex: 1;
  padding: var(--space-lg);
  display: flex;
  flex-direction: column;
  gap: 20px;
  position: relative;
}
.page-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-lg);
  min-height: 131px;
}
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--brand);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
  letter-spacing: 0.8px;
  text-transform: uppercase;
}
.screen-title {
  margin-top: 6px;
  max-width: 720px;
  font-size: clamp(34px, 4vw, 48px);
  line-height: 1.03;
  letter-spacing: -1.5px;
  font-weight: var(--fw-extrabold);
  text-wrap: balance;
}
.screen-sub {
  margin-top: 6px;
  color: var(--text-secondary);
  font-size: var(--fs-md);
}
.head-actions {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  flex-wrap: wrap;
}
.imported-search {
  width: 260px;
  min-height: 37px;
  padding-block: 9px;
}
.search-clear { color: var(--text-tertiary); flex: 0 0 auto; }
.btn-primary, .btn-secondary, .btn-success {
  min-height: 41px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  border-radius: var(--radius-pill);
  padding: 10px 20px;
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  white-space: nowrap;
}
.btn-primary { background: var(--brand); color: var(--text-inverse); box-shadow: var(--shadow-brand); }
.btn-primary:hover { background: var(--brand-hover); }
.btn-secondary { background: var(--surface); color: var(--text-strong); border: 1px solid var(--border-strong); }
.btn-success { background: var(--success-text); color: var(--text-inverse); box-shadow: 0 4px 14px rgba(5, 150, 105, 0.25); }
.is-inert-control,
.is-inert-control * {
  pointer-events: none !important;
  cursor: default !important;
}
.is-inert-control input:disabled {
  opacity: 1;
  -webkit-text-fill-color: var(--text-primary);
}
.banner {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: 68px;
  padding: var(--space-md);
  border-radius: var(--radius-lg);
  background: var(--brand-tint);
  border: 1px solid var(--border);
}
.banner-icon {
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  border-radius: var(--radius-sm);
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--brand);
}
.banner-text { flex: 1; min-width: 0; }
.banner-title { color: var(--text-primary); font-size: var(--fs-md); font-weight: var(--fw-bold); }
.banner-copy { color: var(--text-secondary); font-size: 13px; }
.banner-copy a { color: var(--brand); font-weight: var(--fw-extrabold); }

.section-block {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
}
.section-head h2 {
  font-size: var(--fs-2xl);
  font-weight: var(--fw-extrabold);
  letter-spacing: -0.3px;
}
.section-title-row {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}
.section-badge {
  display: inline-grid;
  place-items: center;
  min-width: 28px;
  height: 19px;
  border-radius: var(--radius-pill);
  background: var(--bg-subtle);
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
}
.section-count {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  color: var(--text-tertiary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}
.section-count b { color: var(--text-strong); font-weight: var(--fw-extrabold); }
.jobs-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.job-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 152px;
  gap: 20px;
  align-items: center;
  min-height: 104px;
  padding: 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.job-card.highlight {
  background: var(--bg-row-highlight);
  border-color: color-mix(in srgb, var(--brand) 42%, var(--border));
}
.empty-icon, .drop-icon {
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--brand-tint);
  color: var(--brand);
}
.job-body { min-width: 0; }
.job-title-row {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  flex-wrap: wrap;
}
.job-title {
  color: var(--text-primary);
  font-size: var(--fs-xl);
  font-weight: var(--fw-bold);
}
.status-pill {
  display: inline-flex;
  align-items: center;
  border-radius: var(--radius-pill);
  padding: 3px var(--space-xs);
  background: var(--info-bg);
  color: var(--info-text);
  font-size: var(--fs-xs);
  font-weight: var(--fw-extrabold);
  letter-spacing: 0.6px;
  text-transform: uppercase;
}
.status-pill.success { background: var(--success-bg); color: var(--success-text); }
.status-pill.danger { background: var(--danger-bg); color: var(--danger); }
.job-meta {
  margin-top: 6px;
  color: var(--text-secondary);
  font-size: 13px;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.job-meta span { display: inline-flex; align-items: center; gap: var(--space-2xs); }
.job-meta span + span::before {
  content: "";
  width: 4px;
  height: 4px;
  border-radius: var(--radius-pill);
  background: var(--border-strong);
  margin-right: 2px;
}
.job-tags { margin-top: var(--space-xs); display: flex; gap: 6px; flex-wrap: wrap; }
.tag {
  border-radius: var(--radius-pill);
  background: var(--bg-subtle);
  color: var(--text-strong);
  padding: 3px var(--space-xs);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
}
.job-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
}
.job-side {
  min-height: 64px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  gap: var(--space-xs);
}
.import-time {
  color: var(--text-tertiary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  white-space: nowrap;
}
.row-claim {
  min-height: 40px;
  padding: 10px 18px;
  font-size: 13px;
}
.divider-label {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  color: var(--text-tertiary);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-extrabold);
  letter-spacing: 0.8px;
}
.divider-label::before, .divider-label::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}
.empty-state {
  display: grid;
  grid-template-columns: 52px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-md);
  min-height: 108px;
  padding: 28px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  background: var(--surface);
}
.empty-icon { width: 52px; height: 52px; color: var(--text-secondary); background: var(--bg-subtle); }
.empty-title { font-size: var(--fs-2xl); font-weight: var(--fw-extrabold); letter-spacing: -0.3px; }
.empty-copy { color: var(--text-secondary); font-size: var(--fs-md); }

.coachmark-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 10;
}
.coachmark-overlay.is-dismissed { display: none; }
.coachmark-overlay::before {
  content: none;
}
.coach-ring {
  position: absolute;
  left: var(--tour-x);
  top: var(--tour-y);
  width: var(--tour-w);
  height: var(--tour-h);
  opacity: 0;
  border: 2px solid var(--brand);
  border-radius: var(--radius-md);
  /* ENTRY #111: the 9999px layer WAS rgba(17,24,39,0.34) and is the ONLY thing painting this
     step's dim (there is no overlay element). It now reads the shared token so it matches
     x1riqt's welcome-modal scrim. Keep it in lockstep with `.workspace-coach-ring` below. */
  box-shadow:
    0 0 0 9999px var(--scrim-dim),
    0 0 0 4px rgba(124, 58, 237, 0.12),
    0 12px 28px rgba(76, 29, 149, 0.18);
}
.coachmark-overlay.tour-ready .coach-ring { opacity: 1; }
.tooltip {
  position: absolute;
  right: 24px;
  /* (2026-07-15, user call) Snug just under the highlighted card — the old +20px gap
     pushed the tooltip onto the NEXT job card. */
  top: calc(var(--tour-y) + var(--tour-h) + 6px);
  width: min(380px, calc(100% - 80px));
  padding: var(--space-md);
  border-radius: var(--radius-md);
  background: #1F2937;
  color: var(--text-inverse);
  box-shadow: var(--shadow-popover);
  opacity: 0;
  pointer-events: auto;
}
.coachmark-overlay.tour-ready .tooltip { opacity: 1; }
.tooltip::before {
  content: "";
  position: absolute;
  top: -11px;
  /* Tail shifted toward the left (the "Claim & Review" CTA side) so it points up at the
     card near the action, matching the newly-raised tooltip position. */
  left: 40px;
  width: 20px;
  height: 12px;
  background: #1F2937;
  clip-path: polygon(50% 0, 0 100%, 100% 100%);
}

/* The landing sheet's dim + host (2026-07-17). This element IS the dim: a real background on
   a real fixed element, so it genuinely hit-tests and blocks the deck behind it (unlike a
   coachmark's box-shadow spotlight, which never does).
   ⚠️ z-index MUST stay above .job-card-chooser-overlay's 2200. Both are <body> children, so at
   equal z the later DOM node (the deck) paints OVER the sheet and the sheet silently vanishes.
   That was invisible while the two were mutually exclusive; they now coexist. */
.job-card-intro-overlay {
  position: fixed;
  inset: 0;
  z-index: 2300;
  display: none;
  align-items: flex-end;
  justify-content: center;
  padding: 0;
  background: rgba(17, 24, 39, 0.46);
  transition: background var(--motion-drawer) ease;
}

/* Gated on jc-sheet-active ALONE, deliberately not job-card-modal-active, which the deck also
   needs and which startWorkspaceSpotlight() removes. One gate, one writer. */
body.jc-sheet-active .job-card-intro-overlay {
  display: flex;
}

body.job-card-modal-active .app-shell {
  filter: blur(2px);
}

body.job-card-modal-active .workspace-coach-overlay,
body.job-card-modal-active .coachmark-overlay {
  opacity: 0;
  pointer-events: none;
}

.job-card-intro-modal {
  max-height: 88dvh;
  overflow: auto;
  display: none;
  animation: jcSheetUp 420ms var(--ease-out, cubic-bezier(.16, 1, .3, 1)) both;
}

body.jc-sheet-active .job-card-help-modal {
  display: grid;
}

.job-card-help-modal {
  width: min(560px, 100vw);
  container: job-card-modal / inline-size;
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: 1px solid var(--border);
  /* Bottom sheet on EVERY view (user call 2026-07-17): flush to the bottom edge, rounded top
     only. One pose and one exit everywhere, with no desktop-only branch to drift. */
  border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
  box-shadow:
    0 -18px 60px rgba(17, 24, 39, .26),
    0 0 0 1px rgba(124, 58, 237, .08);
  padding: 30px 28px 34px;
  gap: 22px;
  color: var(--text-primary);
}

/* The sheet rises. Replaces jobCardIntroIn on this element (that keyframe is still used by
   nothing else here; see the exit rule below for why its fill:both matters). */
@keyframes jcSheetUp {
  from { transform: translateY(100%); }
  to { transform: translateY(0); }
}

/* ==========================================================================================
   THE LANDING REDACTION (2026-07-17, user call). Everything BEHIND the sheet is blurred while
   the sheet is up, on EVERY view, then the blur lifts with the sheet.

   The value is opening.html's canonical "seek logo blur": `blur(3.6px)` + `opacity: .82`,
   authored there as `.is-redacted` (opening.html:551-559). ⚠️ That class lives ONLY in
   opening.html's inline <style> (0 hits in this file), so it cannot be reused by selector.
   The VALUE is the contract: keep these two numbers in lockstep with opening.html's, or the
   two redacted scenes stop matching by construction, which is the whole point of the pair.

   ⚠️ It goes on `.job-card-chooser-cam`, NOT on `.job-card-chooser-overlay`. The overlay owns
   the deck's own scrim (`rgba(11,15,26,.72)` + `backdrop-filter: blur(10px)`), which blurs the
   APP SHELL behind it; `filter` on that same element would blur the overlay's already-composited
   output (scrim + deck together) and the two effects stop being separately tunable.
   `-cam` is safe as a containing block: its subtree has NO position:fixed descendant (the deck
   cards are position:absolute against `.jc-deck`, which is itself inside `-cam`).

   The sheet and `.job-notif-push` are UNAFFECTED because they live in the sibling subtree
   `.job-card-intro-overlay`, not under `-cam`. That is load-bearing: a child cannot escape a
   parent's filter, so the notification MUST stay outside it or the viewer cannot read the gag. */
body.jc-sheet-active .job-card-chooser-cam {
  filter: blur(3.6px);
  opacity: 0.82;
  transition: filter 360ms ease, opacity 360ms ease;
}

/* Dismiss: the sheet slides down, the dim lifts, the blur clears, and the deck becomes live. */
body.jc-sheet-out .job-card-intro-overlay {
  background: rgba(17, 24, 39, 0);
  pointer-events: none;
}
body.jc-sheet-out .job-card-intro-modal {
  /* ⚠️ LOAD-BEARING. jcSheetUp is fill:both, so it pins transform:translateY(0) at
     animation-cascade priority, which outranks this transition. Without `animation: none`
     the sheet simply never moves, with no error. Do not "tidy" this line away. */
  animation: none;
  transform: translateY(100%);
  transition: transform 360ms cubic-bezier(.3, 0, .8, .15);
}
/* ⚠️ MUST stay AFTER the jc-sheet-active blur rule above: both are (0,2,0) and BOTH classes are
   on <body> during the 380ms exit, so only source order decides which wins. The blur therefore
   clears in lockstep with the sheet's own 360ms slide instead of snapping off at the gate drop. */
body.jc-sheet-out .job-card-chooser-cam {
  filter: blur(0px);
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  /* No rise, no slide, no fade: the sheet is simply there, then simply gone. The JS drops the
     gate on the same frame and never waits on a transitionend, so a suppressed transition
     cannot hang the dismiss. */
  .job-card-intro-modal { animation: none; }
  .job-card-intro-overlay,
  body.jc-sheet-out .job-card-intro-overlay,
  body.jc-sheet-out .job-card-intro-modal,
  body.jc-sheet-active .job-card-chooser-cam { transition: none; }
}

.job-card-intro-copy h2 {
  max-width: none;
  color: var(--text-primary);
  font-size: clamp(34px, 4vw, 58px);
  line-height: .98;
  letter-spacing: -0.055em;
  font-weight: var(--fw-black);
  text-align: left;
  text-wrap: pretty;
}

/* Car-odometer count-up: fixed-width tabular dials that physically roll upward.
   Tabular figures + fixed 1ch windows keep every value the same width (incl. the
   narrow leading "1" in 100-199) so the surrounding text never jumps. A hidden
   width-lock reserves the flow width + baseline; the reels overlay absolutely. */
.application-count-motion {
  position: relative;
  display: inline-block;
  margin: 0;
  padding-inline: 0;
  overflow: visible;
  vertical-align: baseline;
  line-height: inherit;
  isolation: isolate;
  color: var(--text-primary);
  font: inherit;
  letter-spacing: inherit;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  white-space: nowrap;
}

.odo-width-lock {
  display: inline-block;
  font: inherit;
  letter-spacing: inherit;
  font-variant-numeric: inherit;
  font-feature-settings: inherit;
  white-space: nowrap;
}
.application-count-motion.is-rolling .odo-width-lock {
  visibility: hidden;
}

.odo-reels {
  position: absolute;
  inset: 0;
  display: block;
  white-space: nowrap;
  letter-spacing: 0;
  font-variant-numeric: inherit;
  font-feature-settings: inherit;
  line-height: inherit;
  opacity: 0;
  pointer-events: none;
}
.application-count-motion.is-rolling .odo-reels {
  opacity: 1;
}

/* Each dial window is exactly one tabular glyph wide (max-content), so digits sit
   at their natural advance with no centering gap. The negative margin reproduces
   the headline's -0.055em tracking as the column pitch, so the dials read as one
   tight number and total the same width the width-lock reserves. Tabular figures
   keep every digit identical width -> the count never jumps between value ranges. */
.odo-digit {
  display: inline-block;
  width: max-content;
  height: 1lh;
  margin-right: -0.055em;
  overflow: hidden;
  vertical-align: top;
  font: inherit;
  letter-spacing: 0;
  font-variant-numeric: inherit;
  font-feature-settings: inherit;
}

.odo-reel {
  display: block;
  width: max-content;
  transform: translateY(calc(var(--o, 0) * -1lh));
  will-change: transform, filter;
  backface-visibility: hidden;
}

.odo-reel > span {
  display: block;
  height: 1lh;
  line-height: inherit;
  text-align: center;
  letter-spacing: 0;
  font: inherit;
  font-variant-numeric: inherit;
  font-feature-settings: inherit;
}

/* The 500 -> 501 tick's BUMP (2026-07-17, user call): the dial that actually changes grows,
   holds while it counts over, then settles back with a small bounce, so the change reads as
   a beat instead of a silent roll. Driven from tickOnesWheel() in rj0wj.html, which adds the
   class for the ones dial only and removes it on animationend.

   Why this is nudge-free BY CONSTRUCTION (the user's hard requirement: nothing else may move):
   a transform never reflows, so the sibling dials, the headline, "applications" and the CTA
   cannot be pushed by the growth. It goes on the WINDOW, never the reel: the reel's own
   transform is the roll (translateY(--o * -1lh)) and the two would fight. And the ones dial is
   the LAST .odo-digit in the reels host, so it already paints OVER its neighbours while it is
   big, which is why no z-index is needed.

   The envelope is tuned against tickOnesWheel's TICK_MS = 620: big by 180ms (the roll's fastest
   travel), still big at 620 (the roll lands), settled by 940. Retune one, retune the other.

   It also flashes --brand and settles back to --text-primary (2026-07-17, user call). BOTH ends
   of the colour leg are TOKENS, never literals, and 0%/100% must state --text-primary explicitly:
   a keyframe that only names the colour at its peak would interpolate from the UA default, not
   from the resting token. The class is removed on animationend, so the settled digit is back on
   plain inherited colour with nothing pinned. */
.odo-digit.is-bumping {
  animation: odoDigitBump 940ms linear;
}

@keyframes odoDigitBump {
  0%   { transform: scale(1) translateY(0); color: var(--text-primary); animation-timing-function: cubic-bezier(.22, .61, .36, 1); }
  19%  { transform: scale(1.13) translateY(-2px); color: var(--brand); animation-timing-function: linear; }
  66%  { transform: scale(1.13) translateY(-2px); color: var(--brand); animation-timing-function: cubic-bezier(.4, 0, .2, 1); }
  85%  { transform: scale(.985) translateY(.5px); animation-timing-function: cubic-bezier(.4, 0, .2, 1); }
  100% { transform: scale(1) translateY(0); color: var(--text-primary); }
}

/* The rj0wj intro modal's JOB-AD HERO CARD lived here (~231 lines: -hero, -ad-header, -logo,
   -title-row, -role h3, -source, -salary, -quote, -meta + its chips, -footer, -link, -original).
   DELETED 2026-07-17: the intro modal became a bottom sheet carrying only the headline+odometer
   and the CTA (user call: "totally remove screen 2"). With it went the entry #73 ONE CARD ANATOMY
   rules for this screen and the chip-burst guards, which existed only for this card.
   ⚠️ .job-card-intro-benefits below is the chooser DECK, not the hero. It deliberately reuses the
   intro class names so the panel-content CSS keeps matching. It is live. Do not sweep it.
   Entry #73's anatomy laws still govern opening.html's .featured-card. */

/* ── Benefits are now an ENDLESS CAROUSEL: 3 panels show at once, the user scrolls/drags
   (or uses the ‹ › arrows, which wrap) to reach the 4th (the embedded scheduling animation). ── */
.job-card-intro-benefits {
  position: relative;
  min-width: 0;                        /* don't let this flex item grow to its carousel content width */
}
.jc-track { width: 100%; max-width: 100%; }

.jc-track {
  display: flex;
  gap: 14px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;              /* Firefox */
  padding-bottom: 2px;
}
.jc-track::-webkit-scrollbar { display: none; }   /* WebKit */
.jc-track.is-grabbing { scroll-behavior: auto; cursor: grabbing; }

.jc-track > article {
  flex: 0 0 calc((100% - 28px) / 3);  /* 3 visible with two 14px gaps */
  scroll-snap-align: start;
  box-sizing: border-box;
  min-width: 0;                        /* allow flex items to shrink to their basis (no content-width overflow) */
}

.job-card-intro-benefits article {
  min-height: 206px;
  padding: var(--space-md);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-subtle);
}

/* Embed panel (4th) — no text, just the scheduling animation iframe filling the panel. */
.jc-track > article.jc-embed {
  padding: 0;
  overflow: hidden;
  position: relative;
  /* bg inherits var(--bg-subtle) from .job-card-intro-benefits article — same grey as the
     other panels; the iframe's own body is greyed to match (html.seg-embed). */
}
.jc-embed-scaler {
  position: absolute;
  inset: 0;
  overflow: hidden;
}
/* The <hirello-cinematic> host box. This IS the virtual viewport the cinematic sees:
   it replaced an <iframe> of exactly these dimensions (entry #97), and the graft reads
   the host's clientWidth/clientHeight wherever the page read the iframe's viewport.
   Keep it a fixed 390x844 — the graft resolves the source's vw/vh units and @media
   against this box at build time. */
.jc-embed-mount {
  display: block;
  position: absolute;
  top: 50%;
  left: 50%;
  border: 0;
  width: 390px;                        /* mobile-width viewport → the cinematic renders its MOBILE layout */
  height: 844px;
  transform-origin: center center;     /* JS sets --jc-scale to CONTAIN it in the panel */
  transform: translate(-50%, -50%) scale(var(--jc-scale, 0.4));
  pointer-events: none;                /* the deck handles drag; the graft is display-only */
}

/* Prev / next arrows — circular, overlaid at the vertical centre of the carousel edges. */
.jc-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.94);
  box-shadow: 0 6px 18px rgba(17, 24, 39, 0.16);
  color: var(--text-primary);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  z-index: 5;
  transition: background .16s ease, transform .16s ease;
}
.jc-nav:hover { background: var(--surface); }
.jc-nav:active { transform: translateY(-50%) scale(0.92); }
.jc-prev { left: -6px; }
.jc-next { right: -6px; }

.job-card-intro-benefits span {
  color: var(--brand);
  font-size: var(--fs-sm);
  font-weight: var(--fw-black);
  letter-spacing: .08em;
}

.job-card-intro-benefits h3 {
  margin-top: 10px;
  color: var(--text-primary);
  font-size: var(--fs-2xl);
  line-height: var(--lh-tight);
  letter-spacing: -0.02em;
  font-weight: var(--fw-850);
}

.job-card-intro-benefits p,
.job-card-intro-benefits li {
  margin-top: var(--space-xs);
  color: var(--text-secondary);
  font-size: 13px;
  line-height: 1.45;
  font-weight: var(--fw-semibold);
}

/* Reserve equal description height in the 3-column layout so every card's
   preview panel starts on the same line (aligned with the tallest, 5-line, copy). */
@media (min-width: 861px) {
  .job-card-intro-benefits p {
    min-height: calc(1.45em * 5);
  }
}

.job-card-intro-benefits ul {
  margin: 10px 0 0;
  padding-left: var(--space-md);
}

.job-card-import-preview {
  margin-top: var(--space-sm);
  width: 100%;
  min-width: 0;
  padding: 9px;
  border: 1px solid var(--border);
  border-radius: 13px;
  background: var(--bg-subtle);
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.job-card-import-dropzone {
  min-height: 58px;
  padding: 9px;
  border: 1.5px dashed var(--brand);
  border-radius: 10px;
  background: #F5F3FF;
  color: var(--brand);
  display: grid;
  place-items: center;
  text-align: center;
  gap: 2px;
}

.job-card-import-dropzone svg {
  width: 16px;
  height: 16px;
}

.job-card-import-dropzone strong {
  color: var(--brand);
  font-size: var(--fs-2xs);
  line-height: 1.1;
  font-weight: var(--fw-850);
}

.job-card-import-dropzone small,
.job-card-import-status,
.job-card-import-row small {
  color: var(--text-tertiary);
  font-size: 8px;
  line-height: 1.2;
  font-weight: var(--fw-bold);
}

.job-card-import-status {
  padding: 0 2px;
}

.job-card-import-row {
  min-width: 0;
  min-height: 37px;
  padding: 7px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--surface);
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr) 32px;
  align-items: center;
  gap: 7px;
}

.job-card-import-avatar {
  width: 24px;
  height: 24px;
  border-radius: var(--radius-pill);
  /* Gmail-style solid per-candidate colour + white initials — the demo-wide
     avatar treatment (matches .jc-chat-avatar / the kanban --avatar colours:
     MK #1D4ED8, AE #C2410C, PR #14B8A6). Colour comes from each row's own
     --jc-av (per-entity colour identity — never one shared tint). */
  background: var(--jc-av, var(--brand));
  color: var(--text-inverse);
  display: grid;
  place-items: center;
  font-size: 9px;
  font-weight: var(--fw-black);
}

.job-card-import-row div:nth-child(2) {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.job-card-import-row strong {
  overflow: hidden;
  color: var(--text-primary);
  font-size: var(--fs-2xs);
  line-height: 1.05;
  font-weight: var(--fw-850);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.job-card-import-row b {
  width: 32px;
  height: 28px;
  border-radius: var(--radius-sm);
  background: var(--bg-subtle);
  color: var(--text-secondary);
  display: grid;
  place-items: center;
  font-size: var(--fs-md);
  line-height: 1;
  font-weight: var(--fw-black);
}

.job-card-import-row b.is-green {
  background: var(--success-bg);
  color: var(--success-text);
}

.job-card-import-row b.is-amber {
  background: var(--warning-bg);
  color: var(--warning-text);
}

/* ============================================================
   Automated import mini-animation (job-card intro, panel 1).
   Fully scripted cinematic — no user input. Mirrors the x1riqt
   drag→parse→skeleton→final states in miniature. Everything that
   moves is a transform/opacity on an absolutely-positioned overlay,
   so the panel's flow box (dropzone + status + 3 rows) never changes
   height and the three preview panels stay top-aligned.
   ============================================================ */

.job-card-import-preview[data-import-demo] {
  position: relative;
  overflow: hidden;
}

/* Dropzone needs to host the parse overlay + animate its armed look */
.job-card-import-dropzone {
  position: relative;
  transition:
    transform 180ms var(--ease-standard, ease),
    background 180ms ease,
    border-color 180ms ease,
    color 180ms ease,
    box-shadow 180ms ease;
}

/* Rows sit in normal flow (reserve height) but are hidden until reveal */
.job-card-import-row {
  position: relative;
}
[data-import-state] .job-card-import-row {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--motion-drawer) ease, transform var(--motion-drawer) ease;
}
[data-import-state] .job-card-import-status {
  opacity: 0;
  transition: opacity var(--motion-drawer) ease;
}
[data-import-state="reveal"] .job-card-import-status,
[data-import-state="hold"] .job-card-import-status {
  opacity: 1;
}

/* Per-row skeleton → final reveal (driven by JS classes) */
.job-card-import-row.is-skeleton,
.job-card-import-row.is-final {
  opacity: 1;
  transform: translateY(0);
}
.job-card-import-skel {
  position: absolute;
  inset: 0;
  padding: 7px;
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr) 32px;
  align-items: center;
  gap: 7px;
  opacity: 0;
  pointer-events: none;
}
.job-card-import-row.is-skeleton .job-card-import-skel {
  opacity: 1;
}
.job-card-import-row.is-skeleton > .job-card-import-avatar,
.job-card-import-row.is-skeleton > div,
.job-card-import-row.is-skeleton > b {
  opacity: 0;
}
.job-card-import-skel i {
  display: block;
  border-radius: 6px;
  background: linear-gradient(
    100deg,
    #E5E7EB 20%,
    #F3F4F6 42%,
    #E5E7EB 64%
  );
  background-size: 260% 100%;
  animation: jc-import-sheen 1200ms ease-in-out infinite;
}
.job-card-import-skel i:nth-child(1) { width: 24px; height: 24px; border-radius: var(--radius-pill); }
.job-card-import-skel i:nth-child(2) { height: 9px; }
.job-card-import-skel i:nth-child(3) { width: 32px; height: 28px; border-radius: var(--radius-sm); }

@keyframes jc-import-sheen {
  0% { background-position: 130% 0; }
  100% { background-position: -30% 0; }
}

/* Armed / parse dropzone = dark purple (mini of the board's armed drop) */
[data-import-state="armed"] .job-card-import-dropzone,
[data-import-state="parse"] .job-card-import-dropzone {
  border-color: rgba(255, 255, 255, 0.72);
  background: linear-gradient(135deg, rgba(76, 29, 149, 0.96), rgba(91, 33, 182, 0.98));
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.22),
    0 12px 26px rgba(76, 29, 149, 0.30);
  transform: scale(1.03);
}
[data-import-state="armed"] .job-card-import-dropzone strong,
[data-import-state="armed"] .job-card-import-dropzone small,
[data-import-state="armed"] .job-card-import-dropzone svg {
  color: var(--text-inverse);
}
[data-import-state="parse"] .job-card-import-dropzone > svg,
[data-import-state="parse"] .job-card-import-dropzone > strong,
[data-import-state="parse"] .job-card-import-dropzone > small {
  opacity: 0;
}

/* Mini parse readout, shown only during the parse beat */
.job-card-import-parse {
  position: absolute;
  inset: 0;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 6px 10px;
}
[data-import-state="parse"] .job-card-import-parse {
  display: flex;
}
.job-card-import-parse strong {
  color: var(--text-inverse);
  font-size: 9.5px;
  line-height: 1.1;
  font-weight: var(--fw-850);
  letter-spacing: 0.01em;
}
.job-card-import-parsebar {
  width: 78%;
  height: 4px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.28);
  overflow: hidden;
}
.job-card-import-parsebar i {
  display: block;
  width: 0%;
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--surface);
  transition: width 110ms linear;
}

/* Fake cursor (only present while the JS loop is active) */
.job-card-import-cursor,
.job-card-import-dragstack {
  display: none;
}
[data-import-state] .job-card-import-cursor,
[data-import-state] .job-card-import-dragstack {
  display: block;
}
.job-card-import-cursor {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 6;
  opacity: 1;
  pointer-events: none;
  will-change: transform;
  transition: transform 520ms cubic-bezier(0.22, 1, 0.36, 1), opacity 260ms ease;
}
.job-card-import-cursor .cursor-inner {
  display: block;
  transform-origin: 3px 3px;
  transition: transform 140ms ease;
}
[data-import-state="grab"] .job-card-import-cursor .cursor-inner,
[data-import-state="drag"] .job-card-import-cursor .cursor-inner,
[data-import-state="armed"] .job-card-import-cursor .cursor-inner {
  transform: scale(0.82);
}
[data-import-state="hold"] .job-card-import-cursor {
  opacity: 0;
}

/* Dragged stack of three candidate applications */
.job-card-import-dragstack {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 5;
  width: 122px;
  pointer-events: none;
  will-change: transform;
  transition: transform 560ms cubic-bezier(0.22, 1, 0.36, 1), opacity 240ms ease;
}
[data-import-state="parse"] .job-card-import-dragstack,
[data-import-state="reveal"] .job-card-import-dragstack,
[data-import-state="hold"] .job-card-import-dragstack {
  opacity: 0;
}
.job-card-import-dragstack .dragstack-inner {
  position: relative;
  display: block;
  transform-origin: center;
  transition: transform 200ms ease, filter 200ms ease;
}
[data-import-state="grab"] .job-card-import-dragstack .dragstack-inner,
[data-import-state="drag"] .job-card-import-dragstack .dragstack-inner,
[data-import-state="armed"] .job-card-import-dragstack .dragstack-inner {
  transform: scale(1.04) rotate(-5deg);
  filter: drop-shadow(0 9px 16px rgba(76, 29, 149, 0.30));
}
.job-card-import-dragstack .dragstack-front {
  position: relative;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px var(--space-xs);
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--surface);
  box-shadow: 0 6px 14px rgba(17, 24, 39, 0.10);
}
.job-card-import-dragstack .dragstack-card {
  position: absolute;
  inset: 0;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--surface);
}
.job-card-import-dragstack .dragstack-card-2 {
  z-index: 2;
  transform: translate(3px, 4px) rotate(2deg);
  opacity: 0.92;
}
.job-card-import-dragstack .dragstack-card-3 {
  z-index: 1;
  transform: translate(6px, 8px) rotate(4deg);
  opacity: 0.78;
}
.job-card-import-dragstack .dragstack-icon {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  border-radius: 6px;
  background: var(--brand-tint);
  color: var(--brand);
  display: grid;
  place-items: center;
}
.job-card-import-dragstack .dragstack-icon svg {
  width: 12px;
  height: 12px;
}
.job-card-import-dragstack .dragstack-front b {
  display: block;
  color: var(--text-primary);
  font-size: 9px;
  line-height: var(--lh-tight);
  font-weight: var(--fw-850);
}
/* Red file-count badge on the dragged stack (mini of x1riqt's .count-badge) */
.job-card-import-dragstack .dragstack-badge {
  position: absolute;
  top: -8px;
  right: -7px;
  z-index: 4;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 7px;
  border-radius: var(--radius-pill);
  background: var(--danger);
  color: var(--text-inverse);
  font-size: 9px;
  font-weight: var(--fw-extrabold);
  line-height: 1;
  box-shadow: 0 3px 10px rgba(124, 58, 237, 0.25);
}
.job-card-import-dragstack .dragstack-badge svg {
  width: 9px;
  height: 9px;
}

/* Reduced motion: no cinematic — show the static three-row result */
@media (prefers-reduced-motion: reduce) {
  .job-card-import-preview[data-import-state] .job-card-import-row {
    opacity: 1 !important;
    transform: none !important;
  }
  .job-card-import-preview[data-import-state] .job-card-import-status {
    opacity: 1 !important;
  }
  .job-card-import-preview .job-card-import-cursor,
  .job-card-import-preview .job-card-import-dragstack,
  .job-card-import-preview .job-card-import-parse {
    display: none !important;
  }
  .job-card-import-preview[data-import-state] .job-card-import-dropzone {
    transform: none !important;
    border-color: var(--brand) !important;
    background: #F5F3FF !important;
  }
  .job-card-import-skel {
    animation: none !important;
  }
}

.job-card-rank-preview {
  margin-top: var(--space-sm);
  width: 100%;
  min-width: 0;
  padding: 9px;
  border: 1px solid var(--border);
  border-radius: 13px;
  background: var(--bg-subtle);
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.job-card-rank-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs);
  min-width: 0;
}

.job-card-rank-avatars {
  display: flex;
  align-items: center;
  min-width: 0;
}

.job-card-rank-avatars i {
  width: 20px;
  height: 20px;
  margin-left: -5px;
  border: 2px solid #F3F4F6;
  border-radius: var(--radius-pill);
  display: grid;
  place-items: center;
  font-style: normal;
  font-size: 8px;
  line-height: 1;
  font-weight: var(--fw-black);
}

.job-card-rank-avatars i:first-child {
  margin-left: 0;
  background: var(--brand-tint);
  color: var(--brand);
}

.job-card-rank-avatars i:nth-child(2) {
  background: var(--success-bg);
  color: var(--cue-teal);
}

.job-card-rank-avatars i:nth-child(3) {
  background: var(--danger-bg);
  color: #EC4899;
}

.job-card-rank-avatars i:nth-child(4) {
  background: var(--surface);
  color: var(--text-tertiary);
}

.job-card-rank-head b {
  overflow: hidden;
  color: var(--text-tertiary);
  font-size: 8px;
  line-height: 1;
  font-weight: var(--fw-850);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.job-card-rank-row {
  min-width: 0;
  min-height: 38px;
  padding: 7px 7px 7px var(--space-xs);
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--surface);
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr) 18px;
  align-items: center;
  gap: 7px;
}

.job-card-rank-row span {
  color: var(--text-tertiary);
  font-size: 13px;
  line-height: 1;
  font-weight: var(--fw-black);
  letter-spacing: 0;
}

.job-card-rank-row div:nth-child(2) {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.job-card-rank-row strong {
  overflow: hidden;
  color: var(--text-primary);
  font-size: var(--fs-2xs);
  line-height: 1.05;
  font-weight: var(--fw-850);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.job-card-rank-row small {
  overflow: hidden;
  color: var(--text-tertiary);
  font-size: 8px;
  line-height: 1.2;
  font-weight: var(--fw-bold);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.job-card-rank-order {
  color: var(--text-secondary);
  display: grid;
  gap: 0;
}

.job-card-rank-order svg {
  width: 14px;
  height: 14px;
}

.job-card-rank-order svg + svg {
  color: #D1D5DB;
  margin-top: -4px;
}

/* ============================================================
   Automated collaborator grading & ranking mini-animation (panel 2).
   Reviewers come alive (who is reviewing) → the active reviewer casts a
   vote that flies onto a candidate → the fake cursor drags that candidate
   up a rank. Transform/opacity only on overlays + rows; the panel's flow
   box is unchanged so its height stays stable and top-aligned.
   ============================================================ */
.job-card-rank-preview[data-rank-demo] {
  position: relative;
  overflow: hidden;
}

/* The ranking list scales up + fades out as one unit before each reset,
   then fades back in — so the loop restart never reads as a hard snap. */
.job-card-rank-stage {
  display: flex;
  flex-direction: column;
  gap: 6px;
  transform-origin: center;
  transition: transform 380ms cubic-bezier(0.4, 0, 0.2, 1), opacity var(--motion-drawer) ease;
}

[data-rank-state] .job-card-rank-row {
  position: relative;
  will-change: transform;
  transition: transform 460ms cubic-bezier(0.22, 1, 0.36, 1), box-shadow 200ms ease, border-color 200ms ease;
}
.job-card-rank-row.is-lifting {
  z-index: 5;
  border-color: rgba(124, 58, 237, 0.55);
  box-shadow: 0 12px 24px rgba(76, 29, 149, 0.22);
}
.job-card-rank-row.is-voted {
  border-color: rgba(124, 58, 237, 0.45);
}
.job-card-rank-votes {
  transition: color var(--motion-fast) ease, transform var(--motion-fast) ease;
}
.job-card-rank-row.is-voted .job-card-rank-votes {
  color: var(--brand);
  transform: translateX(1px) scale(1.04);
}

/* Winner emphasis — the highest-voted candidate (#1) is crowned + pulses */
.job-card-rank-row.is-winner {
  border-color: rgba(245, 158, 11, 0.6);
  animation: rank-winner-pulse 820ms ease-in-out 2;
}
@keyframes rank-winner-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0); }
  50% { box-shadow: 0 0 0 4px rgba(245, 158, 11, 0.26), 0 8px 18px rgba(245, 158, 11, 0.22); }
}
.job-card-rank-crown {
  position: absolute;
  top: -7px;
  right: -6px;
  z-index: 6;
  width: 16px;
  height: 16px;
  border-radius: var(--radius-pill);
  background: linear-gradient(135deg, var(--warning), #FBBF24);
  color: var(--text-inverse);
  display: grid;
  place-items: center;
  box-shadow: 0 3px 9px rgba(245, 158, 11, 0.42);
  opacity: 0;
  transform: scale(0.4) translateY(2px);
  transition: opacity 200ms ease, transform 300ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.job-card-rank-crown svg {
  width: 9px;
  height: 9px;
}
.job-card-rank-row.is-winner .job-card-rank-crown {
  opacity: 1;
  transform: scale(1) translateY(0);
}

/* Active reviewer avatar — pulses to show who is reviewing right now */
.job-card-rank-avatars i {
  transition: transform 180ms ease, box-shadow 180ms ease;
}
.job-card-rank-avatars i.is-reviewing {
  transform: translateY(-2px) scale(1.14);
  /* Ring uses the avatar's own colour (teal/pink/purple) so each reviewer reads distinctly. */
  box-shadow: 0 0 0 2px #F3F4F6, 0 0 0 4px color-mix(in srgb, currentColor 62%, transparent);
}

/* Head label morphs between "5 reviewers active" and "<name> is reviewing…" */
[data-rank-label] {
  transition: opacity 140ms ease;
}
[data-rank-label].is-swapping {
  opacity: 0;
}
.job-card-rank-head b.is-reviewing-label {
  color: var(--rev-color, #16A34A);
}

/* Fake cursor + flying vote — only present while the JS loop is active */
.job-card-rank-cursor,
.job-card-rank-vote {
  display: none;
}
[data-rank-state] .job-card-rank-cursor,
[data-rank-state] .job-card-rank-vote {
  display: block;
}
.job-card-rank-cursor {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 8;
  opacity: 0;
  pointer-events: none;
  will-change: transform;
  transition: transform 460ms cubic-bezier(0.22, 1, 0.36, 1), opacity var(--motion-ui) ease;
}
.job-card-rank-cursor .cursor-inner {
  display: block;
  transform-origin: 3px 3px;
  transition: transform 140ms ease;
}
[data-rank-state="grab"] .job-card-rank-cursor,
[data-rank-state="move"] .job-card-rank-cursor {
  opacity: 1;
}
[data-rank-state="grab"] .job-card-rank-cursor .cursor-inner,
[data-rank-state="move"] .job-card-rank-cursor .cursor-inner {
  transform: scale(0.82);
}
.job-card-rank-vote {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 7;
  opacity: 0;
  pointer-events: none;
  display: inline-flex;
  align-items: center;
  gap: 1px;
  padding: 2px 6px 2px var(--space-2xs);
  border-radius: var(--radius-pill);
  background: var(--vote-color, #16A34A);
  color: var(--text-inverse);
  font-size: 8.5px;
  line-height: 1;
  font-weight: var(--fw-black);
  box-shadow: 0 4px 10px color-mix(in srgb, var(--vote-color, #16A34A) 38%, transparent);
  will-change: transform, opacity;
  transition: transform 380ms cubic-bezier(0.22, 1, 0.36, 1), opacity 200ms ease;
}
.job-card-rank-vote svg {
  width: 9px;
  height: 9px;
}

/* Reduced motion: no cinematic — static ranked list, no cursor/vote */
@media (prefers-reduced-motion: reduce) {
  .job-card-rank-preview .job-card-rank-cursor,
  .job-card-rank-preview .job-card-rank-vote {
    display: none !important;
  }
  [data-rank-state] .job-card-rank-row {
    transform: none !important;
    box-shadow: none !important;
  }
  .job-card-rank-avatars i.is-reviewing {
    transform: none !important;
    box-shadow: none !important;
  }
  .job-card-rank-row.is-winner {
    animation: none !important;
  }
}

.job-card-availability-preview {
  margin-top: var(--space-sm);
  width: 100%;
  min-width: 0;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: 13px;
  background: var(--bg-subtle);
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.job-card-cal-header,
.job-card-cal-row {
  display: grid;
  grid-template-columns: 24px repeat(5, minmax(0, 1fr));
  gap: 5px;
  align-items: center;
}

.job-card-cal-day,
.job-card-cal-label {
  color: var(--text-tertiary);
  font-size: 9px;
  line-height: 1;
  font-weight: var(--fw-750);
  text-align: center;
}

.job-card-cal-cell {
  min-width: 0;
  height: 27px;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--surface);
  display: grid;
  place-items: center;
}

.job-card-cal-cell.is-on {
  border-color: var(--brand);
  background: var(--brand);
  color: var(--text-inverse);
}

.job-card-cal-cell svg {
  width: 12px;
  height: 12px;
}

.job-card-cal-legend {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding-top: 3px;
  color: var(--text-tertiary);
  font-size: 9px;
  line-height: 1;
  font-weight: var(--fw-bold);
}

.job-card-cal-legend span {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  color: inherit;
  font-size: inherit;
  letter-spacing: 0;
}

.job-card-cal-legend i {
  width: 9px;
  height: 9px;
  border-radius: 3px;
  border: 1px solid var(--border);
  background: var(--surface);
}

.job-card-cal-legend i.is-on {
  border-color: var(--brand);
  background: var(--brand);
}

/* The landing sheet's only exit. Full-width pill on every view (2026-07-17).
   Purple + white + the pill radius all arrive from .btn-primary (var(--brand),
   var(--radius-pill)); nothing is hardcoded here. The old `border-radius: 10px`
   was the ONLY thing squaring it off; do not reintroduce a radius on this rule. */
.job-card-intro-cta {
  justify-self: stretch;
  width: 100%;
  min-height: 52px;
  margin-top: var(--space-2xs);
  padding: 0 18px;
  font-size: var(--fs-lg);
  letter-spacing: .01em;
  transition: opacity 240ms ease;
}

/* Held at Material Design 3 disabled 39% until the odometer gag reaches 501. `opacity` on the
   button composites the label AND the arrow svg as one group: that is the "button and label at
   39%" ask, and the same mechanism the x1riqt Browse-files CTA uses. `disabled` is what makes it
   genuinely inert; these are styling only. */
.job-card-intro-cta[disabled],
.job-card-intro-cta[data-inactive-cta] {
  opacity: 0.39;
  cursor: default;
  box-shadow: none;
}
/* .btn-primary:hover swaps to --brand-hover, which would light the inactive button up under a
   desktop cursor. Hold it at rest until it is real. */
.job-card-intro-cta[data-inactive-cta]:hover {
  background: var(--brand);
}

@media (max-width: 860px) {
  .job-card-intro-modal {
    padding: 22px;
  }
  .jc-track > article {
    flex-basis: 100%;                 /* 1 panel per view on mobile */
  }
}

/* ⚠️ This container query ALWAYS MATCHES since 2026-07-17. The sheet is min(560px, 100vw), so
   .job-card-help-modal's inline-size can never exceed 560 and the 720px test is now vacuously
   true on every viewport. That is fine: cqi against a sheet whose width STOPS tracking the
   viewport at 560px is exactly the right unit, and better than the vw it replaced. But the old
   clamp was tuned for a 980px-wide centred modal and resolved to 44px here, so it was retuned to
   the sheet's real 320-560px container range. Measured content-box widths: 264px @320 viewport,
   334px @390, 374px @430, 504px @560+. */
@container job-card-modal (max-width: 720px) {
  .job-card-intro-copy h2 {
    font-size: clamp(22px, 7.4cqi, 34px);
    line-height: 1.12;
  }

  .application-count-motion {
    vertical-align: baseline;
  }
}

@media (prefers-reduced-motion: reduce) {
  .odo-reel,
  .odo-digit {
    filter: none !important;
    transition: none !important;
    animation: none !important;
  }
}

@keyframes jobCardIntroIn {
  from { opacity: 0; transform: translateY(10px) scale(.985); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
.tooltip-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs);
  color: var(--text-inverse);
  font-size: var(--fs-md);
  font-weight: var(--fw-extrabold);
}
.tooltip-head span { display: inline-flex; align-items: center; gap: var(--space-xs); }
.tooltip p {
  margin-top: 10px;
  color: #D1D5DB;
  font-size: var(--fs-sm);
}
/* rJ0WJ company profile drawer from Pencil FIH6o. */
.company-drawer-scrim {
  position: fixed;
  inset: 0;
  z-index: 80;
  background: rgba(17, 24, 39, 0.38);
  opacity: 0;
  transition: opacity var(--motion-ui) ease;
  /* (2026-07-15, user call) The dim must NEVER dismiss the drawer. There is no JS close-on-tap
     (verified on the physical Pixel: a real tap on the dim leaves the drawer open), and the drawer's
     × is deliberately inert — the ONLY exit is the "Try it now" CTA. These absorb any stray
     tap/swipe/overscroll gesture on the dim so it can't scroll or edge-navigate the page away. */
  touch-action: none;
  overscroll-behavior: none;
}
body.company-drawer-open .company-drawer-scrim {
  opacity: 1;
}
/* 2026-07-21 (user call, DESKTOP ONLY) — the page behind the company-profile drawer gets the
   SAME treatment as the "Start here" job coach (#118's `.coach-scrim`): the shared
   `--scrim-dim-coach` (rgba(0,0,0,0.55)) plus `blur(var(--blur-scrim))` (2px). It was
   rgba(17,24,39,0.38) with NO blur, so the workspace behind the drawer stayed legible and
   competed with the drawer for attention.
   Both values are consumed as TOKENS, never re-typed: `--blur-scrim` is the project's one named
   blur (#118) and re-hardcoding 2px here is exactly what that token exists to stop.
   ⚠️ Scoped to >=901px because the ask was for the desktop view. Phones/tablets keep the
   existing flat dim, where the drawer already covers most of the screen and a full-viewport
   backdrop-filter is the expensive path.
   ⚠️ A backdrop-filter samples THIS element's own box, which is why it works here at all: this
   is a real full-viewport element, not the `box-shadow: 0 0 0 9999px` pseudo-dim that #118 had
   to replace for exactly this reason. */
@media (min-width: 901px) {
  .company-drawer-scrim {
    background: var(--scrim-dim-coach);
    -webkit-backdrop-filter: blur(var(--blur-scrim));
    backdrop-filter: blur(var(--blur-scrim));
  }
}
.company-profile-drawer {
  position: fixed;
  inset: 0 0 0 auto;
  z-index: 90;
  width: min(600px, calc(100vw - 32px));
  height: 100vh;
  height: 100dvh;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  background: var(--surface);
  border-left: 1px solid var(--border);
  box-shadow: -12px 0 32px rgba(0, 0, 0, 0.20);
  transform: translateX(100%);
  transition: transform 280ms cubic-bezier(.22, 1, .36, 1);
}
body.company-drawer-open .company-profile-drawer {
  transform: translateX(0);
}
.company-drawer-head {
  min-height: 96px;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-md);
  padding: 20px var(--space-lg);
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}
.drawer-crumb {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--text-tertiary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
}
.drawer-crumb b {
  color: var(--brand);
  font-weight: var(--fw-bold);
}
.company-drawer-head h2 {
  margin-top: 6px;
  color: var(--text-primary);
  font-size: var(--fs-3xl);
  line-height: 1.14;
  letter-spacing: -0.3px;
  font-weight: var(--fw-extrabold);
}
.company-drawer-head p {
  margin-top: var(--space-2xs);
  color: var(--text-secondary);
  font-size: var(--fs-md);
  line-height: var(--lh-snug);
}
.company-drawer-close {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  border-radius: var(--radius-pill);
  background: var(--bg-subtle);
  color: var(--text-strong);
}
.company-drawer-close:hover,
.company-drawer-close:focus-visible {
  background: var(--border);
  outline: none;
}
.company-drawer-close-inert {
  pointer-events: none;
  cursor: default;
}
.company-drawer-body {
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 0 var(--space-lg) 120px;
  background: var(--surface);
}
.company-drawer-sticky-stack {
  position: sticky;
  top: 0;
  z-index: 4;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin: 0 -24px;
  padding: var(--space-sm) var(--space-lg) var(--space-md);
  background: var(--surface);
  transition: box-shadow 180ms ease, border-color 180ms ease;
}
.company-drawer-sticky-stack::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -18px;
  height: 18px;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.94), rgba(255, 255, 255, 0));
  opacity: 0;
  transition: opacity 180ms ease;
}
.company-drawer-body.is-scrolled .company-drawer-sticky-stack {
  box-shadow: 0 14px 22px -20px rgba(15, 23, 42, 0.62);
}
.company-drawer-body.is-scrolled .company-drawer-sticky-stack::after {
  opacity: 1;
}
.company-accordion {
  padding-top: var(--space-md);
}
.company-hero-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
}
.company-hero-main {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-width: 0;
}
.company-avatar {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  border-radius: var(--radius-pill);
  background: var(--text-primary);
  border: 1.5px solid var(--text-primary);
  color: var(--text-inverse);
  font-size: var(--fs-2xl);
  font-weight: var(--fw-extrabold);
}
.company-name-row {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  flex-wrap: wrap;
}
.company-name-row h3 {
  color: var(--text-primary);
  font-size: var(--fs-2xl);
  font-weight: var(--fw-extrabold);
  line-height: 1.2;
}
.company-contact-list {
  margin-top: 6px;
  display: grid;
  gap: 3px;
}
.company-contact-list span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  line-height: 1.3;
  font-weight: var(--fw-semibold);
}
.company-contact-list svg {
  width: 13px;
  height: 13px;
  flex: 0 0 auto;
  color: var(--text-tertiary);
}
.company-chip-row {
  margin-top: var(--space-xs);
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
.company-chip-row span {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: 3px var(--space-xs);
  border-radius: var(--radius-pill);
  background: var(--bg-subtle);
  color: var(--text-strong);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
}
.company-chip-row svg {
  width: 10px;
  height: 10px;
  color: var(--text-secondary);
}
.company-preview-link {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-2xs);
  color: var(--brand);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
}
.company-celebration {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: 20px;
  border: 1px solid #A7F3D0;
  border-radius: var(--radius-lg);
  background: var(--success-bg);
}
.celebration-icon {
  width: 56px;
  height: 56px;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  border-radius: var(--radius-pill);
  background: var(--success);
  color: var(--text-inverse);
  font-size: 17px;
  font-weight: var(--fw-black);
  letter-spacing: -0.4px;
  font-variant-numeric: tabular-nums;
}
.company-celebration h3 {
  color: var(--success-text);
  font-size: 28px;
  line-height: 1.08;
  letter-spacing: -0.5px;
  font-weight: var(--fw-extrabold);
}
.company-celebration p {
  margin-top: var(--space-2xs);
  color: var(--text-strong);
  font-size: var(--fs-md);
  line-height: var(--lh-snug);
}
.company-accordion {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.company-section {
  width: 100%;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
}
.company-section-open {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: 14px;
  border-color: var(--bg-subtle);
  border-width: 1.5px;
}
.company-basics-expandable {
  position: relative;
  border-color: var(--brand-dark);
  box-shadow: none;
}
.company-basics-expandable.is-collapsed {
  position: relative;
  overflow: visible;
  background: var(--surface);
}
.company-basics-expandable.is-collapsed::after {
  content: none;
}
.company-section-head,
.company-section-collapsed {
  min-height: 54px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  color: var(--text-primary);
}
.company-section-head {
  width: 100%;
  background: transparent;
  text-align: left;
}
.section-expand-cue {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  padding: 6px var(--space-xs) 6px 10px;
  border: 1px solid var(--brand);
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: var(--text-inverse);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
  box-shadow: var(--shadow-brand);
}
/* (Entry #121) `.company-basics-expandable.is-collapsed .section-expand-cue { animation: none }`
   USED TO SIT HERE. It was a no-op for as long as nothing bound an animation to the cue, but the
   #121 gate finally binds `basicsCuePulse` to exactly this element in exactly this state, and this
   rule beat it on source order. Removed, not overridden. */
.company-basics-expandable.is-collapsed .section-expand-cue .section-chevron {
  color: var(--text-inverse);
}
/* ══ (Entry #121) THE EXPLAINER IS NO LONGER A DRAWER SIDE-CARD ══════════════════════════
   It ran inside the company-profile drawer: a small dark card floating beside the drawer on
   desktop (top: clamp(330px,42vh,430px); right: calc(600px + 18px)) with a `::after` wedge
   pointing at it, and a bottom sheet at <=900px. The user's annotated screenshots moved it one
   step earlier, in front of the dimmed workspace switcher, and asked for a real MODAL on desktop.
   So: desktop is a CENTRED dialog, <=900px stays a bottom sheet (that block is further down), and
   the wedge is gone: it has nothing left to point at.
   ⚠️ z-index 1300, not 95. At <=900px this sits over the opened off-canvas nav, which forces
   `.workspace-coach-overlay` to 1200; 95 would have been buried. The Demo-sections FAB
   (z 2147483030) still deliberately outranks it, so a viewer is never trapped. */
.company-basics-coach {
  display: none;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(460px, calc(100vw - 48px));
  max-height: calc(100dvh - 48px);
  overflow-y: auto;
  z-index: 1300;
  padding: var(--space-lg);
  border: 1px solid rgba(148, 163, 184, 0.14);
  border-radius: 14px;
  background:
    radial-gradient(circle at 18% 14%, rgba(124, 58, 237, 0.28), rgba(124, 58, 237, 0) 34%),
    #0f1726;
  color: #f8fafc;
  box-shadow:
    0 28px 72px rgba(15, 23, 42, 0.42),
    0 0 0 1px rgba(124, 58, 237, 0.16);
  animation: companyCoachModalIn 260ms var(--ease-out, cubic-bezier(.16, 1, .3, 1));
}
@keyframes companyCoachModalIn {
  from { opacity: 0; transform: translate(-50%, calc(-50% + 12px)) scale(.97); }
  to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
@keyframes companyCoachModalOut {
  from { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  to { opacity: 0; transform: translate(-50%, calc(-50% + 12px)) scale(.97); }
}
/* ⚠️ THE SHOW RULES ARE NOW THE WORKSPACE STEP'S, NOT THE DRAWER'S. Every previous show/hide
   here was keyed on `.company-basics-expandable` ancestry (`body.company-drawer-open
   .company-basics-expandable.is-collapsed ...`). The sheet is a body-level sibling now, so not
   one of those selectors can reach it. Left in place they would simply have rendered it never.
   Phase A is `workspace-tour-active` WITHOUT `company-coach-dismissed`; the CTA adds that class
   and ends the phase. */
body.workspace-tour-active .company-basics-coach {
  display: block;
}
body.company-coach-dismissed .company-basics-coach,
.company-basics-coach.is-dismissed {
  display: none;
}
.company-basics-coach.is-closing {
  pointer-events: none;
  animation: companyCoachModalOut 260ms var(--ease-in, cubic-bezier(.4, 0, 1, 1)) forwards;
}
/* The full-screen dim for phase A. It was `position:absolute` inside the drawer (#109) and only
   ever painted at <=900px; it is now fixed, body-level, and runs at EVERY width, because it is
   what makes the switcher itself read as dimmed. It hit-tests, which is deliberate: during phase A
   the CTA is the only live control. */
.company-coach-scrim {
  /* ⚠️ THE BASE HIDE IS LOAD-BEARING AND WAS BRIEFLY LOST IN #121's FIRST DRAFT. Without it a
     <div> defaults to display:block, so on every route that never enters the workspace tour (the
     job-coach step, a bare deep link) this full-screen scrim sat over the page invisibly eating
     every click. It is a real element now, so it really does intercept: `display` is the only
     thing keeping it out of the way. The smoke guard on the job coachmark is what caught it. */
  display: none;
  position: fixed;
  inset: 0;
  z-index: 1290;
  background: color-mix(in srgb, var(--text-primary) 52%, transparent);
  touch-action: none;
}
body.workspace-tour-active .company-coach-scrim {
  display: block;
}
body.company-coach-dismissed .company-coach-scrim {
  display: none;
}
.company-coach-scrim.is-closing {
  transition: opacity 260ms ease;
  opacity: 0;
}
@media (prefers-reduced-motion: reduce) {
  .company-basics-coach,
  .company-basics-coach.is-closing { animation: none; }
  .company-coach-scrim.is-closing { transition: none; }
}
.company-basics-coach h2 {
  margin: 0 0 10px;
  color: #f8fafc;
  font-size: 28px;
  line-height: 32px;
  font-weight: var(--fw-black);
  letter-spacing: -0.03em;
}
.company-basics-coach p {
  color: #cbd5e1;
  font-size: 13px;
  line-height: 20px;
  font-weight: var(--fw-semibold);
}
.company-basics-coach p + p {
  margin-top: var(--space-xs);
}
.company-basics-coach .tooltip-foot {
  margin-top: var(--space-sm);
}
/* (Entry #121) The @media (max-width: 1040px) reposition of the old side-card is DELETED. It
   nudged the floating card to bottom-left so it cleared the drawer on narrow desktops; a centred
   modal has nothing to clear. */
.company-basics-expandable:not(.is-collapsed) .section-expand-cue {
  border-color: var(--border);
  background: var(--surface);
  color: var(--text-secondary);
  box-shadow: none;
}
.company-basics-expandable:not(.is-collapsed) .section-expand-cue .section-chevron {
  color: var(--text-tertiary);
}
body.company-basics-has-opened .company-basics-expandable.is-collapsed .section-expand-cue {
  border-color: var(--border);
  background: var(--surface);
  color: var(--text-secondary);
  box-shadow: none;
}
body.company-basics-has-opened .company-basics-expandable.is-collapsed .section-expand-cue .section-chevron {
  color: var(--text-tertiary);
}
.section-expand-cue svg {
  transition: transform 180ms ease;
}
.company-section:not(.is-collapsed) .section-expand-cue span {
  font-size: 0;
}
.company-section:not(.is-collapsed) .section-expand-cue span::before {
  content: "Hide";
  font-size: var(--fs-sm);
}
.company-section:not(.is-collapsed) .section-expand-cue svg {
  transform: rotate(180deg);
}
@keyframes basicsCuePulse {
  0%, 100% {
    transform: translateX(0);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand-dark) 10%, transparent);
  }
  45% {
    transform: translateX(-2px);
    box-shadow: 0 0 0 6px color-mix(in srgb, var(--brand-dark) 16%, transparent);
  }
}
.company-section-head:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--brand) 70%, #fff);
  outline-offset: 4px;
  border-radius: var(--radius-sm);
}
.company-section-collapsed {
  padding: 14px;
  text-align: left;
  cursor: pointer;
}
.company-section-inactive {
  cursor: default;
}
.company-section-inactive .section-head-left,
.company-section-inactive .section-dot,
.company-section-inactive .section-chevron-static {
  opacity: 0.39;
}
.company-section-inactive:hover,
.company-section-inactive:focus-visible {
  border-color: var(--border);
  outline: none;
}
.company-section-collapsed:hover,
.company-section-collapsed:focus-visible {
  border-color: color-mix(in srgb, var(--brand) 32%, var(--border));
  outline: none;
}
.company-section-inactive:disabled {
  opacity: 1;
  color: inherit;
}
.company-section-inactive:hover,
.company-section-inactive:focus-visible {
  border-color: var(--border);
  outline: none;
}
.section-head-left {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-width: 0;
}
.section-head-left b {
  display: block;
  color: var(--text-primary);
  font-size: var(--fs-md);
  font-weight: var(--fw-extrabold);
}
.section-head-left small {
  display: block;
  color: var(--text-tertiary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  line-height: 1.25;
}
.section-dot {
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  border-radius: var(--radius-pill);
  background: var(--bg-subtle);
  color: var(--text-tertiary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-black);
}
.section-dot-done {
  background: var(--success-bg);
  color: var(--success-text);
}
.section-chevron,
.section-chevron-static {
  color: var(--text-tertiary);
  flex: 0 0 auto;
}
.company-basics-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding-top: var(--space-sm);
  border-top: 1px solid var(--border);
}
.company-section-open.is-collapsed {
  gap: 0;
}
.company-section-open.is-collapsed .company-basics-body {
  display: none;
}
.company-section-open.is-collapsed .section-chevron {
  transform: rotate(-90deg);
}
.company-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  color: var(--text-strong);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  text-align: left;
}
.company-field input,
.company-field strong {
  min-height: 38px;
  display: flex;
  align-items: center;
  width: 100%;
  padding: var(--space-xs) 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-primary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  outline: none;
}
.company-phone-field strong {
  flex-direction: row;
  gap: var(--space-xs);
}
.idd-segment {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: 0 0 auto;
  padding-right: var(--space-xs);
  border-right: 1px solid var(--border);
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}
.idd-flag {
  width: 18px;
  height: 18px;
  display: inline-grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--bg-subtle);
  font-size: 13px;
  line-height: 1;
}
.phone-local {
  min-width: 0;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}
.company-field-known {
  background: transparent;
  cursor: pointer;
}
.company-field-known strong {
  font-weight: var(--fw-bold);
}
.company-field-known:hover strong,
.company-field-known:focus-visible strong {
  border-color: color-mix(in srgb, var(--brand) 36%, var(--border-strong));
  box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.08);
}
.company-field-known:focus-visible {
  outline: none;
}
.company-field-missing {
  background: transparent;
  cursor: pointer;
}
.company-field-missing strong {
  border-color: var(--brand);
  background: var(--brand-tint);
  color: var(--text-secondary);
  font-weight: var(--fw-bold);
}
.company-field-missing:hover strong,
.company-field-missing:focus-visible strong {
  box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.10);
}
.company-field-missing:focus-visible {
  outline: none;
}
.company-drawer-foot {
  position: sticky;
  bottom: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: stretch;
  padding: var(--space-md) var(--space-lg);
  border-top: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.96);
  backdrop-filter: blur(10px);
}
.drawer-ghost-btn,
.drawer-primary-btn {
  min-height: 43px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  font-size: var(--fs-md);
  font-weight: var(--fw-extrabold);
}
.drawer-ghost-btn {
  padding: var(--space-sm) 20px;
  background: transparent;
  color: var(--text-strong);
}
.drawer-primary-btn {
  width: 100%;
  padding: var(--space-sm) var(--space-lg);
  background: var(--brand);
  color: var(--text-inverse);
  box-shadow: var(--shadow-brand);
}
.drawer-primary-btn[disabled],
.drawer-primary-btn[aria-disabled="true"] {
  opacity: 0.39;
  box-shadow: none;
  cursor: default;
  pointer-events: none;
}
.company-basics-unlocked .drawer-primary-btn:not([disabled]) {
  opacity: 1;
  box-shadow: var(--shadow-brand);
  cursor: pointer;
  pointer-events: auto;
}
.drawer-ghost-btn:hover,
.drawer-ghost-btn:focus-visible,
.drawer-primary-btn:hover,
.drawer-primary-btn:focus-visible {
  outline: 2px solid rgba(124, 58, 237, 0.20);
  outline-offset: 2px;
}
.demo-detail-toast {
  position: fixed;
  right: 28px;
  bottom: 28px;
  z-index: 120;
  width: min(360px, calc(100vw - 40px));
  display: grid;
  gap: var(--space-2xs);
  padding: var(--space-md) 44px var(--space-md) var(--space-md);
  border: 1px solid #A7F3D0;
  border-radius: var(--radius-md);
  background: var(--success-bg);
  color: var(--text-strong);
  box-shadow: var(--shadow-popover);
  opacity: 0;
  transform: translateY(10px);
  pointer-events: none;
  transition: opacity 180ms ease, transform 180ms ease;
}
.company-source-toast {
  position: fixed;
  right: 28px;
  bottom: 28px;
  z-index: 121;
  width: min(420px, calc(100vw - 40px));
  display: grid;
  gap: var(--space-2xs);
  padding: var(--space-md) 44px var(--space-md) var(--space-md);
  border: 1px solid color-mix(in srgb, var(--brand) 28%, var(--border));
  border-radius: var(--radius-md);
  background: var(--brand-tint);
  color: var(--text-strong);
  box-shadow: var(--shadow-popover);
  opacity: 0;
  transform: translateY(10px);
  pointer-events: none;
  transition: opacity 180ms ease, transform 180ms ease;
}
.company-source-toast.is-visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.company-source-toast strong {
  color: var(--brand-dark);
  font-size: var(--fs-md);
  font-weight: var(--fw-black);
}
.company-source-toast span {
  color: var(--text-strong);
  font-size: 13px;
  line-height: 1.35;
}
.company-source-toast-close {
  position: absolute;
  top: 9px;
  right: 10px;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--brand-dark);
  font-size: 20px;
  line-height: 1;
}
.demo-detail-toast.is-visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.demo-detail-toast strong {
  color: var(--success-text);
  font-size: var(--fs-md);
  font-weight: var(--fw-black);
}
.demo-detail-toast span {
  color: var(--text-strong);
  font-size: 13px;
  line-height: 1.35;
}
.demo-toast-close {
  position: absolute;
  top: 9px;
  right: 10px;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--success-text);
  font-size: 20px;
  line-height: 1;
}

/* rJ0WJ initial workspace spotlight. */
body.workspace-tour-active .coachmark-overlay {
  display: none;
}
.workspace-coach-overlay {
  position: fixed;
  inset: 0;
  z-index: 70;
  pointer-events: none;
}
.workspace-coach-overlay.is-dismissed {
  display: none;
}
.workspace-coach-ring {
  position: fixed;
  left: var(--workspace-x);
  top: var(--workspace-y);
  width: var(--workspace-w);
  height: var(--workspace-h);
  opacity: 0;
  /* ⚠️⚠️ (#357) NO BORDER, NO HALO: THE ELEMENT IS PUNCHED THROUGH THE DIM AND NOTHING IS DRAWN
     AROUND IT. Her words: "i dont want the harsh square highlight around the pulsing company
     highlight element i want the highlight to be the pulsing company name element only ... like its
     punched through the dim as it pulses in highlight."
     What she was seeing as the "harsh square" was this rule's own chrome, not the hole: a 2px solid
     brand BORDER plus a 4px purple halo plus a drop shadow, all drawn as a rounded rectangle around
     a 207x42 switcher. Superseded declarations, kept so the change is readable:
         border: 2px solid var(--brand);
         box-shadow: 0 0 0 9999px var(--scrim-dim),
                     0 0 0 4px rgba(124, 58, 237, 0.12),
                     0 12px 28px rgba(76, 29, 149, 0.18);
     ⚠️ THE 9999px SPREAD STAYS, AND IT IS THE WHOLE MECHANISM. That single shadow IS the dim, and
     the ring's own box is the hole in it, so deleting it would remove the dim from the entire step
     rather than tidy an outline. Guards assert it by name: one checks this shadow still
     `toContain('9999')` on phones, another that it still consumes the shared `--scrim-dim` token.
     ⚠️ THE RADIUS IS MEASURED, NOT CHOSEN: `.workspace-switcher-primary` computes `border-radius:
     8px`, and `--radius-sm` is 8px, so the hole already followed the element's own corners. Kept as
     the token rather than restated as a literal.
     ⚠️ ENTRY #111 STILL HOLDS: it required this ring and `.coach-ring` to DIM identically, so the
     screen does not darken between consecutive steps. That is about the `--scrim-dim` layer, which
     is untouched here; only this ring's decorative outline is gone. */
  border: 0;
  border-radius: var(--radius-sm);
  box-shadow: 0 0 0 9999px var(--scrim-dim);
}
.workspace-coach-overlay.tour-ready .workspace-coach-ring {
  opacity: 1;
}
/* (Entry #121) `.workspace-tooltip` and its rules are DELETED, together with the dead
   `.workspace-tour-next`. The dark "Company profile" card that ran on the workspace step is
   replaced by the "NO WE DIDN'T HACK YOU" sheet, which now runs in front of it in phase A.
   #108's live-aimed up-arrow tail went with it (see the =900px block below). */

.drop-zone {
  position: relative;
  min-height: 251px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  padding: var(--space-xl) var(--space-lg);
  border-radius: var(--radius-md);
  background: var(--brand-tint);
  outline: 2px dashed var(--brand);
  outline-offset: -2px;
  text-align: center;
}
.drop-icon {
  width: 64px;
  height: 64px;
  background: var(--surface);
  border: 1px solid var(--brand);
}
.drop-zone h2 {
  font-size: var(--fs-3xl);
  font-weight: var(--fw-extrabold);
  letter-spacing: -0.5px;
}
.drop-zone p {
  max-width: 640px;
  color: var(--text-secondary);
  font-size: var(--fs-md);
}
.drop-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: center;
}
.kanban {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-md);
}
.kanban-col {
  min-height: 201px;
  padding: var(--space-md);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--bg-subtle);
}
.kanban-title {
  margin-bottom: var(--space-sm);
  color: var(--text-strong);
  font-size: var(--fs-md);
  font-weight: var(--fw-extrabold);
}
.ghost-card {
  height: 140px;
  border-radius: var(--radius-sm);
  background:
    linear-gradient(90deg, rgba(255,255,255,0.8), rgba(255,255,255,0.35)),
    repeating-linear-gradient(0deg, transparent 0 26px, rgba(17,24,39,0.05) 27px 28px);
  border: 1px solid var(--border);
}
.stage-hint {
  display: flex;
  justify-content: center;
  gap: 6px;
  color: var(--text-tertiary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}

[data-screen-id="x1RIQT"] .claim-head {
  min-height: 96px;
}
body.x1riqt-screen .content {
  height: calc(100vh - 61px);
  height: calc(100dvh - 61px);
  min-height: 0;
  overflow: hidden;
  padding: clamp(var(--space-sm), 1.8vh, 18px) 20px;
  gap: clamp(var(--space-xs), 1.2vh, var(--space-sm));
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .claim-head {
  min-height: 0;
  padding-block: 0;
  gap: var(--space-md);
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .screen-title {
  margin-top: 0;
  font-size: clamp(28px, 3.1vw, 38px);
  line-height: 1.02;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .screen-sub {
  margin-top: var(--space-2xs);
  font-size: 13px;
}
[data-screen-id="x1RIQT"] .import-demo {
  position: relative;
  isolation: isolate;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo {
  flex: 0 0 auto;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"],
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="grabbing"],
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"],
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"],
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"],
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] {
  z-index: 15;
}
[data-screen-id="x1RIQT"] .drag-focus-dim {
  position: fixed;
  inset: 0;
  z-index: 1;
  background: rgba(17, 24, 39, 0.62);
  opacity: 0;
  pointer-events: none;
  transition: opacity 180ms ease;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .drag-focus-dim,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="grabbing"] .drag-focus-dim,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .drag-focus-dim,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .drag-focus-dim,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .drag-focus-dim,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .drag-focus-dim {
  opacity: 1;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .drag-focus-dim {
  inset: 0;
  width: auto;
  height: auto;
  border-radius: 0;
  background: rgba(17, 24, 39, 0.62);
  box-shadow: none;
}
[data-screen-id="x1RIQT"] .manual-source {
  position: absolute;
  left: 4px;
  top: calc(100% + 474px);
  z-index: 8;
  width: 392px;
  height: 178px;
  min-height: 0;
  padding: var(--space-md);
  border-radius: var(--radius-md);
  border: 2px dashed rgba(124, 58, 237, 0.58);
  background: rgba(255, 255, 255, 0.86);
  box-shadow: 0 18px 38px rgba(17, 24, 39, 0.12);
  overflow: visible;
  opacity: 0;
  pointer-events: none;
  transform: translateY(10px);
  transition:
    opacity 280ms ease,
    transform 420ms cubic-bezier(.22, 1, .36, 1),
    height 260ms cubic-bezier(.22, 1, .36, 1),
    padding 260ms cubic-bezier(.22, 1, .36, 1),
    border-color 220ms ease,
    box-shadow 220ms ease;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .manual-source {
  left: 0;
  top: calc(100% + clamp(238px, 29vh, 304px));
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .manual-source,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="grabbing"] .manual-source,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .manual-source,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .manual-source,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .manual-source {
  z-index: 3;
}
[data-screen-id="x1RIQT"] .manual-source::before {
  content: "";
  position: absolute;
  left: 16px;
  top: 16px;
  width: 356px;
  height: 84px;
  border-radius: calc(var(--radius-md) - 4px);
  background:
    linear-gradient(135deg, rgba(124, 58, 237, 0.16), rgba(17, 24, 39, 0.08)),
    repeating-linear-gradient(135deg, rgba(124, 58, 237, 0.12) 0 8px, rgba(124, 58, 237, 0.04) 8px 16px);
  border: 1px dashed rgba(124, 58, 237, 0.38);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.72);
  opacity: 0;
  transform: scale(.985);
  transition:
    opacity 120ms ease,
    transform 180ms cubic-bezier(.22, 1, .36, 1);
  pointer-events: none;
}
[data-screen-id="x1RIQT"] .manual-source::after {
  content: "";
  position: absolute;
  right: -68px;
  top: 68px;
  width: 58px;
  height: 2px;
  border-radius: var(--radius-pill);
  background:
    repeating-linear-gradient(90deg, rgba(124, 58, 237, 0.95) 0 6px, transparent 6px 12px);
  opacity: 0;
  transform: translateX(-10px);
  pointer-events: none;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .manual-source,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="grabbing"] .manual-source,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .manual-source,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .manual-source,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .manual-source {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .manual-source::after {
  opacity: 1;
  animation: x1riqt-trail-cue 1800ms cubic-bezier(.22, 1, .36, 1) 420ms 3 both;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="grabbing"] .manual-source,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .manual-source,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .manual-source,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .manual-source {
  height: 116px;
  padding: var(--space-md);
  border-color: rgba(124, 58, 237, 0.82);
  box-shadow: 0 22px 54px rgba(124, 58, 237, 0.20);
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="grabbing"] .manual-source::before,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .manual-source::before,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .manual-source::before,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .manual-source::before {
  opacity: 1;
  transform: scale(1);
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .manual-source {
  opacity: 0;
  pointer-events: none;
}
[data-screen-id="x1RIQT"] .import-drop {
  overflow: hidden;
  position: relative;
  z-index: 2;
  margin: 0;
  min-height: 251px;
  padding: var(--space-xl) var(--space-lg);
  border: 2px dashed var(--brand);
  border-radius: var(--radius-md);
  background: var(--brand-tint);
  outline: none;
  transition:
    min-height 460ms cubic-bezier(.22, 1, .36, 1),
    margin 320ms cubic-bezier(.22, 1, .36, 1),
    padding 460ms cubic-bezier(.22, 1, .36, 1),
    border-color 320ms ease,
    background 320ms ease,
    outline-color 320ms ease,
    transform 320ms ease;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-drop {
  min-height: clamp(198px, 26vh, 232px);
  padding: clamp(20px, 2.6vh, 28px) 22px;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .import-drop,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="grabbing"] .import-drop,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .import-drop,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .import-drop,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .import-drop,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .import-drop {
  z-index: 4;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .import-drop {
  z-index: 30;
}
[data-screen-id="x1RIQT"] .import-drop::after {
  content: "";
  position: absolute;
  inset: var(--space-2xs);
  z-index: 0;
  display: none;
  border: 3px dashed rgba(255, 255, 255, 0.96);
  border-radius: calc(var(--radius-md) - 4px);
  pointer-events: none;
}
[data-screen-id="x1RIQT"] .import-drop > * {
  position: relative;
  z-index: 1;
}
[data-screen-id="x1RIQT"] .drop-active,
[data-screen-id="x1RIQT"] .drop-processing {
  display: none;
}
[data-screen-id="x1RIQT"] .drop-idle {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-sm);
  width: 100%;
  min-height: 248px;
  justify-content: center;
  border: 0;
  border-radius: 0;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .drop-idle {
  min-height: clamp(160px, 21vh, 190px);
  gap: 10px;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .import-drop {
  box-shadow: 0 18px 48px rgba(124, 58, 237, 0.14);
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .import-drop {
  min-height: 251px;
  margin: 0;
  padding: var(--space-xl) var(--space-lg);
  border-color: transparent;
  background: var(--brand-dark);
  outline-color: transparent;
  transform: translateY(0);
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .import-drop {
  min-height: clamp(198px, 26vh, 232px);
  padding: clamp(20px, 2.6vh, 28px) 22px;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .import-drop,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .import-drop {
  min-height: 251px;
  margin: 0;
  padding: var(--space-xl) var(--space-lg);
  border-color: transparent;
  background: var(--brand-dark);
  outline-color: transparent;
  transform: translateY(-1px) scale(1.035);
  transform-origin: center center;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .import-drop,
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .import-drop {
  min-height: clamp(198px, 26vh, 232px);
  padding: clamp(20px, 2.6vh, 28px) 22px;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .import-drop::after,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .import-drop::after,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .import-drop::after {
  display: block;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .import-drop {
  transform: translateY(-1px) scale(1.02);
  outline-color: transparent;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .drop-idle,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .drop-idle,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .drop-idle,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .drop-idle,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .drop-active {
  display: none;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .drop-active,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .drop-active,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .drop-active {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  width: 100%;
  border: 0;
  border-radius: 0;
  color: var(--text-inverse);
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .drop-active {
  min-height: 183px;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .drop-active {
  min-height: clamp(150px, 19vh, 172px);
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .drop-active,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .drop-active {
  min-height: 183px;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .drop-active,
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .drop-active {
  min-height: clamp(150px, 19vh, 172px);
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .drop-active {
  animation: drop-confirm 348ms cubic-bezier(.22, 1, .36, 1) both;
}
[data-screen-id="x1RIQT"] .drop-active .drop-icon {
  color: var(--brand);
  border-color: var(--brand);
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .import-drop {
  min-height: 88px;
  margin: 0;
  align-items: stretch;
  padding: 20px var(--space-lg);
  border-color: transparent;
  background: var(--brand-tint);
  outline: none;
  box-shadow: none;
  opacity: 1;
  filter: none;
  mix-blend-mode: normal;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .import-drop {
  min-height: 78px;
  padding: var(--space-md) 20px;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .drop-processing {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-lg);
  width: 100%;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .import-drop::after {
  display: block;
  border-color: var(--brand);
  border-width: var(--border-w-strong);
}
[data-screen-id="x1RIQT"] .drop-left {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
  text-align: left;
}
[data-screen-id="x1RIQT"] .drop-copy {
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
}
[data-screen-id="x1RIQT"] .drop-left h2 {
  color: var(--text-primary);
  font-size: var(--fs-2xl);
  letter-spacing: -0.5px;
  text-align: left;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .drop-left h2 {
  color: var(--text-primary);
}

[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"].is-complete .parse-timer {
  display: none;
}

[data-screen-id="x1RIQT"] .drop-left p {
  color: var(--text-strong);
  text-align: left;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .drop-left p {
  color: var(--text-strong);
}
.parse-timer {
  min-width: 54px;
  height: 34px;
  display: inline-grid;
  place-items: center;
  flex: 0 0 auto;
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: var(--text-inverse);
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
  letter-spacing: -0.2px;
  box-shadow: 0 8px 18px rgba(124, 58, 237, 0.22);
}
.progress-wrap {
  width: 280px;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  flex: 0 0 auto;
}
.progress-label {
  display: flex;
  justify-content: space-between;
  color: var(--brand-hover);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-extrabold);
  letter-spacing: 0.8px;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .progress-label {
  color: var(--brand-dark);
}
.progress-label strong {
  font-size: var(--fs-sm);
  letter-spacing: 0;
}
.progress-track {
  height: 8px;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: var(--surface);
}
.progress-track span {
  display: block;
  width: 0%;
  height: 100%;
  border-radius: inherit;
  background: var(--brand);
  transition: width 147ms linear;
}
[data-screen-id="x1RIQT"] .drag-stack {
  position: relative;
  left: auto;
  top: auto;
  z-index: 2;
  width: 356px;
  height: 126px;
  display: block;
  border: 0;
  background: transparent;
  color: inherit;
  opacity: 1;
  pointer-events: auto;
  cursor: grab;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  transform: translate3d(0, 0, 0) rotate(0deg) scale(1);
  transition:
    filter 260ms ease,
    transform 240ms ease;
  backface-visibility: hidden;
  transform-style: preserve-3d;
  will-change: transform;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .drag-stack {
  opacity: 1;
  pointer-events: auto;
  transform: translate3d(0, 0, 0) rotate(0deg) scale(1);
  animation: x1riqt-stack-invite 2200ms linear 140ms infinite both;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="grabbing"] .drag-stack,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .drag-stack,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .drag-stack {
  opacity: 0;
  cursor: grabbing;
  filter: saturate(1.06);
  transform: rotate(-5deg) scale(.985);
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .drag-stack {
  opacity: 0;
  transform: rotate(-5deg) scale(.96);
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .drag-stack,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .demo-caption {
  opacity: 0;
  pointer-events: none;
}
.drag-ghost {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 10000;
  width: 356px;
  height: 126px;
  display: block;
  border: 0;
  background: transparent;
  color: inherit;
  opacity: 0;
  pointer-events: none;
  filter: saturate(1.06) drop-shadow(0 22px 30px rgba(17, 24, 39, 0.28));
  transition: opacity 90ms ease;
  will-change: transform;
}
.drag-ghost.is-visible {
  opacity: 1;
}
.drag-ghost .count-badge {
  opacity: 1;
  transform: translateY(0) scale(1);
}
.stack-card {
  position: absolute;
  left: 44px;
  width: 288px;
  height: 70px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  box-shadow: 0 4px 10px rgba(17, 24, 39, 0.05);
  backface-visibility: hidden;
  will-change: transform;
}
.stack-card-3 { top: 18px; opacity: .6; }
.stack-card-2 { top: 9px; left: 26px; opacity: .85; }
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .stack-card-3 {
  animation: x1riqt-stack-sheet-3 2200ms linear 140ms infinite both;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .stack-card-2 {
  animation: x1riqt-stack-sheet-2 2200ms linear 140ms infinite both;
}
.stack-front {
  position: absolute;
  left: 0;
  top: 0;
  width: 356px;
  min-height: 84px;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm) 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25);
  text-align: left;
  backface-visibility: hidden;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .stack-front {
  box-shadow:
    0 18px 34px rgba(17, 24, 39, 0.30),
    0 0 0 1px rgba(124, 58, 237, 0.08);
}
.stack-front::after {
  content: none;
  display: none;
  pointer-events: none;
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .stack-front::after {
  animation: none;
}
.stack-front b {
  display: block;
  /* ENTRY #393: the width is now a TOKEN with its original literal as the fallback, so the one
     screen that has to shrink it (x1riqt, to make room for the auto-drag countdown disc) does it by
     setting the property rather than by re-typing 260 in a second rule that could drift. Nothing
     else can be affected: `.stack-front` renders on x1riqt.html ALONE. A `grep -l stack-front`
     also lists rj0wj.html and zz-baseline.html, and that is a SUBSTRING match on their
     `.dragstack-front`, which is a different class with its own rule at :1679. Checked in a browser
     rather than by reading the grep: `.stack-front b` returns an empty list on both. */
  width: var(--x1-stack-name-w, 260px);
  overflow: hidden;
  color: var(--text-primary);
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  text-overflow: ellipsis;
  white-space: nowrap;
}
.stack-front small {
  color: var(--text-tertiary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}
.file-icon {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  border-radius: var(--radius-sm);
  background: var(--brand-tint);
  color: var(--brand-hover);
}
.count-badge {
  position: absolute;
  top: -16px;
  left: 274px;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-2xs) 10px;
  border-radius: var(--radius-pill);
  background: var(--danger);
  color: var(--text-inverse);
  font-size: var(--fs-xs);
  font-weight: var(--fw-extrabold);
  box-shadow: 0 3px 12px rgba(124, 58, 237, 0.25);
  opacity: 0;
  transform: translateY(4px) scale(.96);
  transition: opacity var(--motion-fast) ease, transform 180ms cubic-bezier(.22, 1, .36, 1);
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="grabbing"] .count-badge,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .count-badge,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .count-badge {
  opacity: 1;
  transform: translateY(0) scale(1);
}
.demo-caption {
  position: relative;
  left: auto;
  top: auto;
  z-index: 1;
  width: 356px;
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: -18px;
  padding: var(--space-sm) 14px;
  border-radius: var(--radius-md);
  background: #111827;
  color: var(--text-inverse);
  font-size: 13px;
  font-weight: var(--fw-bold);
  box-shadow:
    0 10px 26px rgba(0, 0, 0, 0.28),
    0 0 0 1px rgba(255, 255, 255, 0.08);
  opacity: 0;
  transform: translateY(5px);
  transition: opacity 240ms ease, transform 240ms ease;
}
[data-screen-id="x1RIQT"] .demo-caption::before {
  content: "";
  position: absolute;
  left: 22px;
  top: -7px;
  width: 14px;
  height: 14px;
  border-radius: 3px 0 0 0;
  background: inherit;
  transform: rotate(45deg);
}
[data-screen-id="x1RIQT"] .demo-caption svg {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  padding: 2px;
  border-radius: var(--radius-pill);
  background: rgba(124, 58, 237, 0.38);
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .demo-caption {
  opacity: 1;
  transform: translateY(0);
}
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="grabbing"] .demo-caption,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .demo-caption,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .demo-caption,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .demo-caption {
  opacity: 0;
  pointer-events: none;
  transform: translateY(2px);
}
.drag-helper {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 10020;
  width: max-content;
  max-width: 286px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 11px 14px;
  border-radius: var(--radius-md);
  background: #111827;
  color: var(--text-inverse);
  font-size: 13px;
  font-weight: var(--fw-extrabold);
  line-height: 1.18;
  letter-spacing: -0.01em;
  text-align: center;
  box-shadow:
    0 16px 34px rgba(17, 24, 39, 0.28),
    0 0 0 1px rgba(255, 255, 255, 0.08);
  opacity: 0;
  pointer-events: none;
  white-space: nowrap;
  transition:
    opacity 120ms ease,
    box-shadow 140ms ease;
  will-change: transform, opacity;
  backface-visibility: hidden;
}
.drag-helper::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -6px;
  width: 12px;
  height: 12px;
  border-radius: 0 0 3px 0;
  background: inherit;
  transform: translateX(-50%) rotate(45deg);
}
.drag-helper.is-visible {
  opacity: 1;
}
.drag-helper.is-armed {
  box-shadow:
    0 18px 38px rgba(17, 24, 39, 0.30),
    0 0 0 1px rgba(255, 255, 255, 0.16);
}
.import-board {
  min-height: 488px;
}

@keyframes x1riqt-stack-invite {
  0%, 100% { transform: translate3d(0, 0, 0) rotate(0deg) scale(1); }
  5% { transform: translate3d(0, -0.8px, 0) rotate(-0.03deg) scale(1.0008); }
  10% { transform: translate3d(0, -2.5px, 0) rotate(-0.09deg) scale(1.0022); }
  15% { transform: translate3d(0, -5px, 0) rotate(-0.17deg) scale(1.0041); }
  20% { transform: translate3d(0, -8px, 0) rotate(-0.27deg) scale(1.0064); }
  25% { transform: translate3d(0, -11.3px, 0) rotate(-0.37deg) scale(1.0087); }
  30% { transform: translate3d(0, -14.5px, 0) rotate(-0.45deg) scale(1.0108); }
  35% { transform: translate3d(0, -17.1px, 0) rotate(-0.50deg) scale(1.0124); }
  40% { transform: translate3d(0, -19px, 0) rotate(-0.50deg) scale(1.0134); }
  45% { transform: translate3d(0, -20.1px, 0) rotate(-0.45deg) scale(1.0138); }
  50% { transform: translate3d(0, -20.4px, 0) rotate(-0.34deg) scale(1.0136); }
  55% { transform: translate3d(0, -19.6px, 0) rotate(-0.19deg) scale(1.0126); }
  60% { transform: translate3d(0, -17.7px, 0) rotate(-0.02deg) scale(1.0111); }
  65% { transform: translate3d(0, -14.9px, 0) rotate(0.14deg) scale(1.0091); }
  70% { transform: translate3d(0, -11.5px, 0) rotate(0.25deg) scale(1.0069); }
  75% { transform: translate3d(0, -8px, 0) rotate(0.30deg) scale(1.0048); }
  80% { transform: translate3d(0, -4.9px, 0) rotate(0.27deg) scale(1.003); }
  85% { transform: translate3d(0, -2.6px, 0) rotate(0.19deg) scale(1.0017); }
  90% { transform: translate3d(0, -1.1px, 0) rotate(0.10deg) scale(1.0007); }
  95% { transform: translate3d(0, -0.3px, 0) rotate(0.03deg) scale(1.0002); }
}

@keyframes x1riqt-stack-sheet-2 {
  0%, 100% { transform: translate3d(0, 0, 0) rotate(0deg); }
  12.5% { transform: translate3d(0.35px, -0.65px, 0) rotate(.018deg); }
  25% { transform: translate3d(0.85px, -1.7px, 0) rotate(.048deg); }
  37.5% { transform: translate3d(1.35px, -2.8px, 0) rotate(.078deg); }
  50% { transform: translate3d(1.7px, -3.55px, 0) rotate(.098deg); }
  62.5% { transform: translate3d(1.55px, -3.1px, 0) rotate(.082deg); }
  75% { transform: translate3d(1px, -1.95px, 0) rotate(.052deg); }
  87.5% { transform: translate3d(0.38px, -0.7px, 0) rotate(.022deg); }
}

@keyframes x1riqt-stack-sheet-3 {
  0%, 100% { transform: translate3d(0, 0, 0) rotate(0deg); }
  12.5% { transform: translate3d(0.48px, -0.8px, 0) rotate(.024deg); }
  25% { transform: translate3d(1.15px, -2.1px, 0) rotate(.062deg); }
  37.5% { transform: translate3d(1.95px, -3.55px, 0) rotate(.108deg); }
  50% { transform: translate3d(2.6px, -4.65px, 0) rotate(.148deg); }
  62.5% { transform: translate3d(2.25px, -4px, 0) rotate(.124deg); }
  75% { transform: translate3d(1.38px, -2.45px, 0) rotate(.078deg); }
  87.5% { transform: translate3d(0.55px, -0.95px, 0) rotate(.032deg); }
}

@keyframes x1riqt-trail-cue {
  0%, 100% {
    opacity: 0;
    transform: translateX(-10px);
  }
  24%, 58% {
    opacity: .9;
    transform: translateX(0);
  }
  72% {
    opacity: 0;
    transform: translateX(14px);
  }
}

@keyframes x1riqt-caption-nudge {
  0%, 100% { transform: translateY(0); }
  24% { transform: translateY(-3px); }
  48% { transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .manual-source::after,
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .drag-stack,
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .stack-card-2,
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .stack-card-3,
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .demo-caption,
  [data-screen-id="x1RIQT"] .score-skeleton {
    animation: none !important;
  }
}
.import-board .kanban-col {
  min-height: 488px;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-board {
  flex: 1 1 auto;
  min-height: 0;
  height: auto;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-sm);
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-board .kanban-col {
  min-height: 0;
  height: 100%;
  max-height: 100%;
  padding: 14px;
  gap: 10px;
  overflow: hidden;
}
/* ENTRY #89 (user call). The Applied column wears the SAME lilac surface as nifhr's curated-move
   Applied column, so the same column reads as the same thing across the walkthrough. All views:
   body.x1riqt-screen is present at every width, so this deliberately carries no media query.
   Only .new-col. (.review-col/.shortlist-col keep the plain var(--bg-subtle) they inherit from
   .kanban-col, which is what makes Applied the one column that stands out.) */
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-board .new-col {
  background: var(--col-applied-surface);
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .candidate-stream {
  min-height: 0;
  overflow: hidden;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .candidate-card {
  padding: 10px;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .candidate-final {
  gap: 6px;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .candidate-stream {
  gap: 7px;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board {
  position: relative;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .new-col {
  position: relative;
  z-index: 25;
  /* ENTRY #89. Was a hardcoded #fff, which outranked the base rule above ((0,6,0) vs (0,4,0)) and
     flashed the column white for the whole parse spotlight. Same token, so the surface holds. */
  background: var(--col-applied-surface);
  box-shadow: 0 22px 58px rgba(17, 24, 39, 0.16);
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .review-col,
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .shortlist-col {
  opacity: .42;
  filter: grayscale(.35);
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .candidate-card {
  padding: 9px 10px;
  border-radius: var(--radius-md);
  font-size: 13px;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .candidate-upload,
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .candidate-extract {
  min-height: 48px;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .candidate-final {
  gap: 5px;
  border-color: var(--border);
  border-width: var(--border-w);
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .candidate-top {
  display: grid;
  grid-template-columns: 32px minmax(0, 1fr) 34px;
  gap: var(--space-xs);
  align-items: center;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .candidate-file,
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .candidate-pill,
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .ai-pill {
  font-size: var(--fs-md);
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .candidate-file svg,
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .candidate-pill svg,
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .ai-pill svg {
  width: 16px;
  height: 16px;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .candidate-avatar {
  width: 32px;
  height: 32px;
  font-size: 13px;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .candidate-name-lines strong {
  font-size: 13px;
  line-height: 1.14;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .match-score {
  width: 34px;
  height: 34px;
  font-size: 13px;
  justify-self: end;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .score-skeleton {
  width: 34px;
  height: 34px;
  justify-self: end;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .col-drag-placeholder,
body.x1riqt-screen [data-screen-id="x1RIQT"] .shortlist-ghost {
  min-height: clamp(84px, 12vh, 112px);
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .stage-hint {
  flex: 0 0 auto;
  font-size: var(--fs-xs);
  line-height: 1.2;
}

@media (max-height: 760px) {
  body.x1riqt-screen [data-screen-id="x1RIQT"] .content {
    padding-block: 10px;
    gap: var(--space-xs);
  }
  body.x1riqt-screen [data-screen-id="x1RIQT"] .screen-title {
    font-size: clamp(25px, 2.8vw, 32px);
  }
  body.x1riqt-screen [data-screen-id="x1RIQT"] .screen-sub {
    display: none;
  }
  body.x1riqt-screen [data-screen-id="x1RIQT"] .import-drop,
  body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .import-drop {
    min-height: 186px;
    padding: 18px 20px;
  }
  body.x1riqt-screen [data-screen-id="x1RIQT"] .drop-idle {
    min-height: 148px;
    gap: var(--space-xs);
  }
  body.x1riqt-screen [data-screen-id="x1RIQT"] .drop-icon {
    width: 52px;
    height: 52px;
  }
  body.x1riqt-screen [data-screen-id="x1RIQT"] .drop-zone h2 {
    font-size: 20px;
  }
  body.x1riqt-screen [data-screen-id="x1RIQT"] .drop-zone p {
    font-size: var(--fs-sm);
  }
  body.x1riqt-screen [data-screen-id="x1RIQT"] .manual-source {
    top: calc(100% + 166px);
  }
  body.x1riqt-screen [data-screen-id="x1RIQT"] .import-board .kanban-col {
    padding: var(--space-sm);
    gap: var(--space-xs);
  }
  body.x1riqt-screen [data-screen-id="x1RIQT"] .stage-hint {
    display: none;
  }
}
/* ⚠️ ENTRY #268: THE COUNT SITS NEXT TO THE LABEL AND THE KEBAB IS PUSHED TO THE FAR RIGHT.
   This used to be `justify-content: space-between`, which with three children (label, kebab, count)
   spread them evenly and parked the KEBAB in the middle with the count at the edge. Every one of
   the eight cinematic mockups already renders label -> count -> kebab-right (their own inline
   `.col-head` puts the count first in source order and gives `.kebab` a `margin-left: auto`), so
   the real boards were the odd ones out. User's words: "for visual consistency across the project".
   ⚠️ DONE WITH `order`, NOT BY MOVING THE MARKUP. nifhr's DOM order is label, kebab, count, and
   x1riqt is a LOCKED reference screen: re-ordering flex children touches no node, no id and no
   listener, so neither board's drag engine nor any structural check can notice.
   ⚠️ `flex-start` + `gap`, never `space-between` with a spacer: a board with NO kebab (x1riqt,
   whose heads are label + count only) must land the count in the same place as one with. */
.col-head {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-xs);
}

.col-head .column-kebab {
  order: 1;
  margin-left: auto;
}
.col-head span {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
}
.col-head b {
  min-width: 24px;
  display: inline-grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-strong);
  font-size: var(--fs-xs);
  line-height: 17px;
}
.col-dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--brand);
}
.col-dot.warning { background: var(--warning); }
.col-dot.success { background: var(--success); }
.candidate-stream {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.candidate-slot {
  animation: candidate-enter 482ms cubic-bezier(.22, 1, .36, 1) both;
  transform-origin: top center;
}
.candidate-slot[data-state-change="true"] {
  animation: candidate-morph 415ms cubic-bezier(.22, 1, .36, 1) both;
}
.candidate-card {
  width: 100%;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--surface);
  padding: var(--space-sm);
  box-shadow: 0 1px 2px rgba(17, 24, 39, 0.03);
  transition:
    border-color 241ms ease,
    box-shadow 241ms ease,
    transform 241ms ease,
    background 241ms ease;
}
.candidate-upload,
.candidate-extract {
  min-height: 48px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs);
}
.candidate-file {
  min-width: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  color: var(--text-strong);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}
.candidate-file svg {
  color: var(--text-tertiary);
  flex: 0 0 auto;
}
.candidate-file span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.candidate-pill,
.ai-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  flex: 0 0 auto;
  padding: 3px var(--space-xs);
  border-radius: var(--radius-pill);
  background: var(--bg-subtle);
  color: var(--text-secondary);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
}
.candidate-pill { border: 1px solid var(--border); }
.busy-pill { min-width: 116px; border: 0; }
.busy-dots {
  display: inline-flex;
  gap: 2px;
  width: 18px;
}
.busy-dots i {
  width: 3px;
  height: 3px;
  border-radius: var(--radius-pill);
  background: currentColor;
  opacity: .25;
  animation: busy-dot 844ms steps(1, end) infinite;
}
.busy-dots i:nth-child(2) { animation-delay: 281ms; }
.busy-dots i:nth-child(3) { animation-delay: 562ms; }
.candidate-skeleton {
  display: flex;
  flex-direction: column;
  gap: 10px;
  border-color: rgba(124, 58, 237, 0.28);
  background:
    linear-gradient(90deg, rgba(255,255,255,0), rgba(124,58,237,.08), rgba(255,255,255,0)) -160px 0 / 160px 100% no-repeat,
    #fff;
  animation: skeleton-sheen 1005ms ease-in-out infinite;
}
.candidate-top,
.candidate-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs);
}
.candidate-avatar {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  border-radius: var(--radius-pill);
  background: var(--avatar, var(--brand));
  color: var(--text-inverse);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-extrabold);
}
.candidate-avatar.muted {
  background: var(--bg-subtle);
  color: var(--text-tertiary);
}
.candidate-avatar.warn {
  color: var(--avatar-fg, #B45309);
}
.candidate-name-lines {
  min-width: 0;
  flex: 1;
  display: flex;
  flex-direction: column;
}
.candidate-name-lines strong {
  color: var(--text-primary);
  font-size: var(--fs-sm);
  line-height: 1.25;
}
.candidate-name-lines span,
.candidate-foot {
  color: var(--text-secondary);
  font-size: var(--fs-2xs);
}
.ai-pill {
  background: var(--brand);
  color: var(--text-inverse);
  font-size: var(--fs-xs);
}
.score-skeleton {
  width: 34px;
  height: 34px;
  display: block;
  flex: 0 0 auto;
  border-radius: var(--radius-pill);
  background:
    linear-gradient(90deg, rgba(255,255,255,0), rgba(255,255,255,.72), rgba(255,255,255,0)) -38px 0 / 38px 100% no-repeat,
    var(--bg-subtle);
  box-shadow: inset 0 0 0 1px rgba(17, 24, 39, 0.04);
  animation: score-skeleton-sheen 1005ms ease-in-out infinite;
}
.candidate-final {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.candidate-final:first-child {
  border-color: var(--border);
  border-width: var(--border-w);
}
.match-score {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--success-bg);
  color: var(--success-text);
  font-size: var(--fs-md);
}
.match-score.warn {
  background: var(--warning-bg);
  color: var(--warning-text);
}
.col-drag-placeholder,
.shortlist-ghost {
  min-height: 104px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border-radius: var(--radius-md);
  text-align: center;
}
.col-drag-placeholder {
  border: 1.5px solid var(--brand);
  background: var(--brand-tint);
  color: var(--brand-hover);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
}
.col-drag-placeholder span,
.shortlist-ghost span {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--surface);
}
.shortlist-ghost {
  min-height: 140px;
  border: 1px solid var(--border-strong);
  color: var(--text-secondary);
  font-size: var(--fs-sm);
}
.shortlist-ghost small {
  color: var(--text-tertiary);
  font-size: var(--fs-2xs);
}
.modal-scrim.is-hidden {
  display: none;
}
.tour-modal {
  position: fixed;
  inset: 0;
  z-index: 18;
  pointer-events: none;
  opacity: 0;
  transition: opacity 240ms ease;
}
.tour-modal.is-visible {
  opacity: 1;
  pointer-events: auto;
}
.tour-modal::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(rgba(0,0,0,.35), rgba(0,0,0,.35));
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
.parsing-tour::before {
  background: transparent;
}
.parsing-tour.is-visible {
  pointer-events: none;
}
/* Only the VISIBLE tour card is interactive. When the tour is hidden it is
   opacity:0 but still laid out; without scoping to .is-visible the card kept
   pointer-events:auto and — once repositioned to the bottom on mobile — sat on
   top of the resume drag-stack and swallowed the touch, so the drag could never
   start. Scoping to .is-visible lets the hidden card inherit the parent's
   pointer-events:none. */
.parsing-tour.is-visible .tour-step {
  pointer-events: auto;
}
.tour-step {
  position: absolute;
  right: 11.8vw;
  bottom: 13.5vh;
  width: min(470px, calc(100vw - 48px));
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 28px;
  border: 1px solid rgba(148, 163, 184, 0.14);
  border-radius: 14px;
  background:
    radial-gradient(circle at 18% 14%, rgba(124, 58, 237, 0.28), rgba(124, 58, 237, 0) 34%),
    #0f1726;
  color: #f8fafc;
  box-shadow:
    0 28px 72px rgba(15, 23, 42, 0.42),
    0 0 0 1px rgba(124, 58, 237, 0.16);
}
.tour-step h2 {
  max-width: 390px;
  color: #f8fafc;
  font-size: 28px;
  line-height: 32px;
  font-weight: var(--fw-black);
  letter-spacing: -0.03em;
}
.tour-step p {
  max-width: 390px;
  color: #cbd5e1;
  font-size: 13px;
  line-height: 20px;
  font-weight: var(--fw-semibold);
}
.tour-step-actions {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.tour-step .tour-primary,
.tour-step .btn-success {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  padding: 10px 18px;
  border: 0;
  /* ENTRY #114: was a literal 12px. That was the SECOND of the two outliers #113
     documented knocking the shared button system off its pill; the base .btn-* rule
     at :394 already supplies var(--radius-pill), so this only restores it. No @media
     gate, so "all views incl. desktop" holds by construction. */
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: var(--text-inverse);
  font-size: 13px;
  font-weight: var(--fw-850);
  box-shadow: 0 14px 32px rgba(124, 58, 237, 0.28);
}

.tour-step .tour-primary:hover,
.tour-step .tour-primary:focus-visible,
.tour-step .btn-success:hover,
.tour-step .btn-success:focus-visible {
  background: var(--brand-hover);
  outline: none;
  box-shadow:
    0 14px 32px rgba(124, 58, 237, 0.32),
    0 0 0 4px rgba(124, 58, 237, 0.18);
}

.tour-step .modal-skip {
  min-height: 44px;
  padding: 10px 6px;
  border-radius: 10px;
  color: #94a3b8;
  background: transparent;
  font-size: 13px;
  font-weight: var(--fw-extrabold);
}

.tour-step .modal-skip:hover,
.tour-step .modal-skip:focus-visible {
  color: #f8fafc;
  background: rgba(148, 163, 184, 0.10);
  outline: none;
}

@keyframes hirello-spin {
  to { transform: rotate(360deg); }
}
@keyframes busy-dot {
  0%, 32% { opacity: 1; transform: translateY(0); }
  33%, 100% { opacity: .25; transform: translateY(1px); }
}
@keyframes candidate-enter {
  from { opacity: 0; transform: translateY(16px) scale(.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes candidate-morph {
  0% { opacity: .2; transform: translateY(9px) scale(.985); }
  48% { opacity: 1; transform: translateY(-2px) scale(1.006); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes drop-confirm {
  0% { transform: scale(.98); }
  50% { transform: scale(1.035); }
  100% { transform: scale(1); }
}
@keyframes skeleton-sheen {
  0% { background-position: -170px 0, 0 0; }
  100% { background-position: 320px 0, 0 0; }
}
@keyframes score-skeleton-sheen {
  0% { background-position: -38px 0, 0 0; }
  100% { background-position: 42px 0, 0 0; }
}

/* Activated workspace screens: nIfHr, gHnIj, rGiKg, cgo6Y, P9phv4, wZsCB */
body.workspace-screen {
  width: 100%;
  height: 100vh;
  height: 100dvh;
  min-height: 0;
  overflow: hidden;
}
body.workspace-screen .app-shell,
body.workspace-screen .sidebar,
body.workspace-screen .main {
  height: 100vh;
  height: 100dvh;
  min-height: 0;
  overflow: hidden;
}
body.workspace-screen .content {
  height: calc(100vh - 61px);
  height: calc(100dvh - 61px);
  min-height: 0;
  overflow: hidden;
  padding: 18px var(--space-lg);
  gap: 14px;
}
.workspace-head {
  min-height: 82px;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
}
.workspace-head h1 {
  color: var(--text-primary);
  font-size: clamp(30px, 3vw, 42px);
  line-height: 1.05;
  font-weight: var(--fw-extrabold);
  letter-spacing: -1.1px;
}
.workspace-head p {
  margin-top: 5px;
  max-width: 680px;
  color: var(--text-muted);
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
}
.workspace-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 0 0 auto;
}
.workspace-tabs {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-2xs);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
}
.workspace-tabs a,
.workspace-tabs button {
  min-height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 14px;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
}
.workspace-tabs .active {
  background: var(--brand);
  color: var(--text-inverse);
}
.workspace-grid {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-md);
}
.workspace-grid.with-drawer {
  grid-template-columns: minmax(0, 1fr) minmax(360px, 400px);
}
.workspace-board {
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, .9fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-md);
}
.workspace-column {
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-subtle);
  transition: border-color var(--motion-fast) ease, background var(--motion-fast) ease, box-shadow var(--motion-fast) ease;
}
.workspace-column.is-over {
  border-color: var(--brand);
  background: var(--brand-tint);
  box-shadow: inset 0 0 0 2px rgba(124, 58, 237, 0.18);
}
.workspace-column-head {
  min-height: 38px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
}
.workspace-column-head span {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  color: var(--text-strong);
  font-size: var(--fs-md);
  font-weight: var(--fw-extrabold);
}
.workspace-column-head b {
  min-width: 28px;
  display: inline-grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-strong);
  font-size: var(--fs-sm);
  line-height: 20px;
}
.board-card-list {
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow: hidden;
}
.workspace-candidate {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: var(--space-sm);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(17, 24, 39, 0.03);
  cursor: grab;
  touch-action: none;
  transition: opacity var(--motion-fast) ease, transform var(--motion-fast) ease, border-color var(--motion-fast) ease, box-shadow var(--motion-fast) ease;
}
.workspace-candidate:hover {
  border-color: rgba(124, 58, 237, 0.38);
  box-shadow: 0 10px 28px rgba(17, 24, 39, 0.08);
}
.workspace-candidate.is-dragging {
  opacity: .42;
  transform: scale(.985);
  cursor: grabbing;
}
.candidate-board-ghost {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 10000;
  width: auto;
  pointer-events: none;
  transition: none !important;
  animation: none !important;
  will-change: transform;
  filter: drop-shadow(0 20px 32px rgba(17, 24, 39, 0.28));
}

.candidate-board-stack-ghost {
  filter:
    drop-shadow(0 24px 34px rgba(76, 29, 149, 0.22))
    drop-shadow(0 10px 18px rgba(17, 24, 39, 0.16));
}

.candidate-board-stack-ghost .candidate-stack-card {
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.92),
    0 16px 32px rgba(17, 24, 39, 0.12);
}

.candidate-board-stack-ghost .candidate-stack-card:not(:last-of-type) {
  opacity: 0.94;
}

.candidate-stack-chip {
  position: absolute;
  right: -8px;
  top: -12px;
  z-index: 40;
  min-width: 74px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 11px;
  border-radius: var(--radius-pill);
  border: 2px solid var(--text-inverse);
  background: var(--danger);
  color: var(--text-inverse);
  box-shadow: 0 12px 28px rgba(239, 68, 68, 0.30);
  font-size: var(--fs-sm);
  line-height: 1;
  font-weight: var(--fw-extrabold);
  letter-spacing: 0;
  white-space: nowrap;
}
.workspace-candidate-top {
  display: grid;
  grid-template-columns: 38px minmax(0, 1fr) 42px;
  align-items: center;
  gap: 10px;
}
.workspace-avatar {
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--avatar, var(--brand));
  color: var(--text-inverse);
  font-size: var(--fs-md);
  font-weight: var(--fw-extrabold);
}
.workspace-candidate-name {
  min-width: 0;
  color: var(--text-primary);
  font-size: var(--fs-lg);
  font-weight: var(--fw-extrabold);
  line-height: var(--lh-tight);
}
.workspace-score {
  width: 42px;
  height: 42px;
  display: grid;
  place-items: center;
  justify-self: end;
  border-radius: var(--radius-pill);
  background: var(--success-bg);
  color: var(--success-text);
  font-size: var(--fs-lg);
  font-weight: var(--fw-extrabold);
}
.workspace-score.warn {
  background: var(--warning-bg);
  color: var(--warning-text);
}
.board-dragging {
  cursor: grabbing;
  user-select: none;
}
.drawer-panel {
  min-height: 0;
  display: flex;
  flex-direction: column;
  border-left: 1px solid var(--border);
  background: var(--surface);
  box-shadow: -12px 0 28px rgba(17, 24, 39, 0.08);
}
.drawer-head {
  min-height: 64px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-md) 20px;
  border-bottom: 1px solid var(--border);
}
.drawer-head h2 {
  color: var(--text-primary);
  font-size: var(--fs-2xl);
  line-height: 1.2;
  font-weight: var(--fw-extrabold);
}
.drawer-body {
  min-height: 0;
  flex: 1;
  overflow: hidden;
  padding: var(--space-lg);
}
.profile-card {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.profile-hero {
  display: grid;
  grid-template-columns: 72px minmax(0, 1fr);
  gap: var(--space-md);
  align-items: center;
}
.profile-avatar {
  width: 72px;
  height: 72px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: var(--text-inverse);
  font-size: 22px;
  font-weight: var(--fw-extrabold);
}
.profile-hero h3 {
  color: var(--text-primary);
  font-size: var(--fs-3xl);
  line-height: 1.1;
  font-weight: var(--fw-extrabold);
}
.profile-hero p {
  margin-top: var(--space-2xs);
  color: var(--text-muted);
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
}
.profile-section {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.profile-section h4 {
  color: var(--text-primary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
  letter-spacing: .8px;
  text-transform: uppercase;
}
.profile-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-sm) 0;
  border-bottom: 1px solid var(--border);
  color: var(--text-strong);
  font-size: var(--fs-md);
}
.resume-viewer {
  height: 100%;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 360px;
  gap: var(--space-md);
}
.resume-page {
  min-height: 0;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  padding: 26px;
  box-shadow: var(--shadow-float);
}
.resume-page h2 {
  color: var(--text-primary);
  font-size: 28px;
  line-height: 1.1;
  font-weight: var(--fw-extrabold);
}
.resume-page h3 {
  margin-top: 20px;
  color: var(--brand-hover);
  font-size: 13px;
  font-weight: var(--fw-extrabold);
  letter-spacing: .7px;
  text-transform: uppercase;
}
.resume-page p,
.resume-page li {
  color: var(--text-strong);
  font-size: var(--fs-md);
}
.resume-page ul {
  margin-top: var(--space-xs);
  padding-left: 18px;
}
.resume-side {
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.insight-card {
  padding: var(--space-md);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
}
.insight-card strong {
  display: block;
  color: var(--text-primary);
  font-size: var(--fs-md);
}
.insight-card span,
.insight-card p {
  color: var(--text-secondary);
  font-size: 13px;
}
.curation-strip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 14px var(--space-md);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--shadow-float);
}
.selection-dock {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}
.selection-dot {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: var(--text-inverse);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
  box-shadow: 0 0 0 3px var(--text-inverse);
}
.comparison-table {
  min-height: 0;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--shadow-float);
}
.comparison-row {
  display: grid;
  grid-template-columns: 180px repeat(3, minmax(0, 1fr));
  min-height: 56px;
  border-bottom: 1px solid var(--border);
}
.comparison-row:last-child { border-bottom: 0; }
.comparison-cell {
  display: flex;
  align-items: center;
  padding: 14px var(--space-md);
  border-right: 1px solid var(--border);
  color: var(--text-strong);
  font-size: var(--fs-md);
}
.comparison-cell:last-child { border-right: 0; }
.comparison-header .comparison-cell {
  min-height: 86px;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
  text-align: center;
  color: var(--text-primary);
  font-weight: var(--fw-extrabold);
}
.comparison-label {
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
  letter-spacing: .7px;
  text-transform: uppercase;
}
.doc-tabs {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}
.doc-tabs button {
  min-height: 34px;
  padding: 0 var(--space-sm);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
}
.doc-tabs button.active {
  border-color: var(--brand);
  background: var(--brand-tint);
  color: var(--brand);
}
.original-grid {
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-md);
}
.original-card {
  min-height: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  overflow: hidden;
}
.original-card-head {
  min-height: 68px;
  padding: 14px var(--space-md);
  border-bottom: 1px solid var(--border);
}
.original-card-head strong {
  display: block;
  color: var(--text-primary);
  font-size: var(--fs-lg);
}
.original-card-head span {
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
}
.original-doc {
  flex: 1;
  min-height: 0;
  padding: var(--space-md);
  background:
    linear-gradient(#fff, #fff) padding-box,
    repeating-linear-gradient(0deg, transparent 0 28px, rgba(17, 24, 39, .06) 29px 30px);
  color: var(--text-strong);
  font-size: 13px;
}

/* nIfHr source-faithful activated workspace tutorial */
body.nifhr-screen .content {
  padding: var(--space-lg);
  background: var(--bg-app);
}

.nifhr-stage {
  position: relative;
  width: 100%;
  height: 100%;
  min-height: 0;
  overflow: hidden;
}

.nifhr-stage::before {
  content: "";
  position: absolute;
  left: 291px;
  top: 180px;
  width: 273px;
  height: 132px;
  z-index: 8;
  border: 2px solid var(--brand);
  border-radius: var(--radius-md);
  background: transparent;
  /* ENTRY #111: a FOURTH spotlight dim, found by grepping the value rather than trusting the
     two the annotation pointed at (the #74 law: the grep says when you are done). It already
     held the token's exact value, so this substitution is a provable zero-pixel no-op; it is
     here so a fifth site cannot drift in. */
  box-shadow: 0 0 0 2000px var(--scrim-dim);
  pointer-events: none;
}

.nifhr-job-head {
  position: relative;
  z-index: 1;
  width: 100%;
  min-height: 82px;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 22px;
  padding: var(--space-2xs) 0 15px;
  border-bottom: 1px solid var(--border);
}

.nifhr-job-head h1 {
  color: var(--text-primary);
  font-size: 22px;
  line-height: var(--lh-tight);
  font-weight: var(--fw-extrabold);
  letter-spacing: -0.3px;
}

.nifhr-job-head p {
  margin-top: var(--space-xs);
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: var(--fw-medium);
}

.nifhr-head-actions {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

.nifhr-action {
  min-height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--space-sm);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text-strong);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
}

.nifhr-board-shell {
  position: relative;
  z-index: 1;
  width: 856px;
  height: 569px;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  align-content: start;
  gap: var(--space-sm);
  margin-top: var(--space-md);
}

.nifhr-column {
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.nifhr-column-head {
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 0 2px;
}

.nifhr-column-head span {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--text-strong);
  font-size: 13px;
  font-weight: var(--fw-extrabold);
}

.nifhr-column-head b {
  min-width: 25px;
  height: 22px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-muted);
  font-size: var(--fs-xs);
  font-weight: var(--fw-extrabold);
}

.nifhr-card-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.nifhr-candidate {
  position: relative;
  min-height: 76px;
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr) 34px;
  align-items: center;
  gap: 10px;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text-primary);
  box-shadow: 0 1px 2px rgba(17, 24, 39, 0.04);
  cursor: grab;
  touch-action: none;
  transition: transform var(--motion-fast) ease, border-color var(--motion-fast) ease, box-shadow var(--motion-fast) ease, opacity var(--motion-fast) ease;
}

.nifhr-candidate:hover,
.nifhr-candidate.focus-card,
.nifhr-candidate.source-selected {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.18), 0 14px 30px rgba(124, 58, 237, 0.16);
}

.nifhr-candidate.focus-card {
  z-index: 2;
  background: var(--surface);
}

.nifhr-candidate.is-dragging {
  opacity: .45;
  transform: scale(.985);
}

.nifhr-avatar {
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: var(--text-inverse);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
}

.nifhr-avatar.teal { background: var(--cue-teal); }
.nifhr-avatar.amber { background: var(--warning); }
.nifhr-avatar.violet { background: #8B5CF6; }
.nifhr-avatar.red { background: var(--danger); }
.nifhr-avatar.pink { background: #EC4899; }
.nifhr-avatar.blue { background: #0EA5E9; }

.nifhr-card-copy {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.nifhr-card-copy strong {
  color: var(--text-primary);
  font-size: 13px;
  line-height: var(--lh-tight);
  font-weight: var(--fw-extrabold);
}

.nifhr-card-copy small {
  color: var(--text-secondary);
  font-size: var(--fs-xs);
  line-height: 1.2;
  font-weight: var(--fw-semibold);
}

.nifhr-tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  margin-top: 2px;
}

.nifhr-tags em {
  display: inline-flex;
  align-items: center;
  min-height: 22px;
  padding: 0 10px;
  border-radius: var(--radius-pill);
  background: var(--brand-tint);
  color: var(--brand);
  font-size: var(--fs-xs);
  font-style: normal;
  font-weight: var(--fw-semibold);
}

.nifhr-note,
.nifhr-muted,
.nifhr-warning {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 2px;
  color: var(--brand-hover);
  font-size: var(--fs-xs);
  line-height: 1.25;
  font-weight: var(--fw-semibold);
}

.nifhr-muted {
  color: var(--text-tertiary);
}

.nifhr-warning {
  padding: 7px 9px;
  border-radius: var(--radius-sm);
  background: var(--warning-bg);
  color: var(--warning-text);
}

.nifhr-score {
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--success-bg);
  color: var(--success-text);
  font-size: var(--fs-xs);
  font-weight: var(--fw-extrabold);
}

.nifhr-score.warn {
  background: var(--warning-bg);
  color: var(--warning-text);
}

.nifhr-score.low {
  background: var(--danger-bg);
  color: var(--danger-text);
}

.nifhr-board-foot {
  margin-top: 10px;
  color: var(--text-tertiary);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
}

.nifhr-board-foot strong {
  color: var(--text-secondary);
  font-weight: var(--fw-bold);
}

.nifhr-drop-strip {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 37px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  margin-top: var(--space-md);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
}

.nifhr-drop-strip .drop-icon {
  color: var(--brand);
  font-size: var(--fs-md);
  line-height: 1;
  font-weight: var(--fw-extrabold);
}

.nifhr-drop-strip strong {
  color: var(--brand);
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
}

.nifhr-drop-strip small {
  color: var(--text-tertiary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
}

.nifhr-coach {
  position: absolute;
  z-index: 20;
  top: 122px;
  left: 601px;
  width: 360px;
  min-height: 259px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-sm);
  padding: 28px;
  border: 1px solid rgba(148, 163, 184, 0.14);
  border-radius: 14px;
  background:
    radial-gradient(circle at 18% 14%, rgba(124, 58, 237, 0.28), rgba(124, 58, 237, 0) 34%),
    #0f1726;
  color: #f8fafc;
  box-shadow:
    0 28px 72px rgba(15, 23, 42, 0.42),
    0 0 0 1px rgba(124, 58, 237, 0.16);
}

.nifhr-coach h2 {
  color: #f8fafc;
  font-size: 28px;
  line-height: 32px;
  font-weight: var(--fw-black);
  letter-spacing: -0.03em;
}

.nifhr-coach p {
  max-width: 300px;
  color: #cbd5e1;
  font-size: 13px;
  line-height: 20px;
  font-weight: var(--fw-semibold);
}

.coach-cta {
  min-height: 41px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 18px;
  /* ENTRY #114 (user call): the coach CTA is a PILL on every view, matching the x1riqt tour
     card's "Show me" it hands off to and `.mm-intro-cta`. Both this and the nifhr override
     below said var(--radius-md); changing only one would silently do nothing on nifhr, since
     the override wins. This also reaches rj0wj's "Try it now" company coach (.company-coach-cta
     reuses this class), which the user asked for: one button system everywhere. */
  border-radius: var(--radius-pill);
  border: 0;
  background: var(--brand);
  color: var(--text-inverse);
  font-size: 13px;
  font-weight: var(--fw-850);
  box-shadow: 0 14px 32px rgba(124, 58, 237, 0.28);
}

body.nifhr-screen .candidate-board-ghost {
  width: auto;
}

/* nIfHr correction: reuse the locked x1RIQT parsing-board language. */
body.nifhr-screen .content {
  height: calc(100vh - 61px);
  height: calc(100dvh - 61px);
  overflow: hidden;
  padding: var(--space-lg) 28px;
}

body.nifhr-screen .nifhr-stage {
  isolation: auto;
  height: 100%;
}

body.nifhr-screen::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 9000;
  background: rgba(17, 24, 39, 0.58);
  pointer-events: none;
}

body.nifhr-screen .nifhr-stage::before {
  content: none;
}

body.nifhr-screen .nifhr-stage::after {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: auto;
  height: auto;
  inset: 0;
  z-index: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  pointer-events: none;
}

body.nifhr-screen .nifhr-job-head,
body.nifhr-screen .nifhr-drop-strip,
body.nifhr-screen .nifhr-board-shell,
body.nifhr-screen .nifhr-board-foot {
  z-index: auto;
}

body.nifhr-screen .nifhr-job-head {
  min-height: 78px;
  padding: 0 0 14px;
}

body.nifhr-screen .nifhr-job-head h1 {
  font-size: 30px;
  letter-spacing: -0.45px;
}

body.nifhr-screen .nifhr-job-head p {
  font-size: var(--fs-md);
}

body.nifhr-screen .nifhr-drop-strip {
  height: 44px;
  margin-top: var(--space-sm);
}

body.nifhr-screen .nifhr-board-shell {
  width: 856px;
  height: calc(100% - 184px);
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-sm);
  margin-top: var(--space-sm);
  align-content: stretch;
}

body.nifhr-screen .nifhr-column {
  position: relative;
  min-height: 0;
  height: 100%;
  padding: 14px;
  gap: 10px;
  overflow: hidden;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--bg-subtle);
}

body.nifhr-screen .nifhr-column:first-child {
  z-index: 9020;
  background: var(--surface);
  box-shadow: 0 22px 58px rgba(17, 24, 39, 0.16);
}

body.nifhr-screen .nifhr-card-list {
  min-height: 0;
  gap: 7px;
  overflow: hidden;
}

body.nifhr-screen .nifhr-candidate.candidate-card {
  min-height: 76px;
  display: grid;
  grid-template-columns: none;
  align-items: center;
  gap: 6px;
  padding: 9px 10px;
  border-radius: var(--radius-md);
  font-size: var(--fs-sm);
  cursor: grab;
}

body.nifhr-screen .nifhr-candidate .candidate-top {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr) 38px;
  gap: 10px;
  align-items: center;
}

body.nifhr-screen .nifhr-candidate .candidate-avatar {
  width: 36px;
  height: 36px;
  font-size: var(--fs-md);
}

body.nifhr-screen .nifhr-candidate .candidate-name-lines strong {
  font-size: 13px;
  line-height: 1.14;
}

body.nifhr-screen .nifhr-candidate .candidate-name-lines small {
  display: block;
  margin-top: 2px;
  color: var(--text-secondary);
  font-size: var(--fs-xs);
  line-height: var(--lh-tight);
  font-weight: var(--fw-semibold);
}

body.nifhr-screen .nifhr-candidate .match-score {
  width: 38px;
  height: 38px;
  font-size: var(--fs-md);
  justify-self: end;
}

body.nifhr-screen .nifhr-candidate .candidate-tags {
  gap: 5px;
  margin-left: 46px;
}

body.nifhr-screen .nifhr-candidate .candidate-tags span {
  padding: var(--space-2xs) var(--space-xs);
  font-size: var(--fs-xs);
  line-height: 1.12;
}

body.nifhr-screen .nifhr-candidate .candidate-mini {
  margin-left: 46px;
  color: var(--brand-hover);
  font-size: var(--fs-xs);
  line-height: 1.2;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen .nifhr-candidate .candidate-mini.muted {
  color: var(--text-tertiary);
}

body.nifhr-screen .nifhr-candidate.focus-card,
body.nifhr-screen .nifhr-candidate.source-selected {
  position: relative;
  z-index: 9020;
  border-color: var(--brand);
  border-width: var(--border-w-strong);
  background: var(--surface);
  box-shadow:
    0 0 0 3px rgba(124, 58, 237, 0.28),
    0 20px 44px rgba(17, 24, 39, 0.18);
  transform: translateZ(0);
}

body.nifhr-screen .nifhr-candidate:not(.focus-card) {
  box-shadow: 0 1px 2px rgba(17, 24, 39, 0.03);
}

body.nifhr-screen .nifhr-coach {
  z-index: 9030;
  top: 760px;
  left: 816px;
  width: 360px;
  min-height: 259px;
  background:
    radial-gradient(circle at 18% 14%, rgba(124, 58, 237, 0.28), rgba(124, 58, 237, 0) 34%),
    #0f1726;
  color: #f8fafc;
  box-shadow:
    0 28px 72px rgba(15, 23, 42, 0.42),
    0 0 0 1px rgba(124, 58, 237, 0.16);
}

body.nifhr-screen .coach-cta {
  background: var(--brand);
  color: var(--text-inverse);
  border-radius: var(--radius-pill);
  box-shadow: 0 14px 32px rgba(124, 58, 237, 0.28);
}

body.nifhr-screen .nifhr-board-foot {
  position: relative;
  margin-top: var(--space-xs);
}

@media (max-width: 1180px) {
  .nifhr-board-shell {
    width: min(100%, 760px);
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
  .nifhr-coach {
    right: 24px;
    left: auto;
    width: 320px;
  }
}

@media (max-width: 980px) {
  body.nifhr-screen {
    overflow: auto;
  }
  .nifhr-stage {
    height: auto;
    min-height: 900px;
  }
  .nifhr-stage::before {
    position: fixed;
    inset: auto;
    left: 24px;
    top: 280px;
    width: min(273px, calc(100vw - 48px));
    height: 132px;
  }
  .nifhr-board-shell {
    width: 100%;
    height: auto;
    grid-template-columns: 1fr;
    padding-bottom: 280px;
  }
  .nifhr-coach {
    top: auto;
    left: 24px;
    right: 24px;
    bottom: 24px;
    width: auto;
  }
}
@media (max-width: 900px) {
  body.workspace-screen {
    overflow: auto;
  }
  body.workspace-screen .app-shell {
    grid-template-columns: 1fr;
    height: auto;
  }
  body.workspace-screen .sidebar {
    display: none;
  }
  body.workspace-screen .main,
  body.workspace-screen .content {
    height: auto;
    overflow: visible;
  }
  .workspace-grid.with-drawer,
  .resume-viewer,
  .original-grid {
    grid-template-columns: 1fr;
  }
  .workspace-board {
    grid-template-columns: 1fr;
  }
}
.modal-scrim {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: var(--space-lg);
  background: var(--scrim-dim); /* ENTRY #111: was rgba(0,0,0,0.35), the value the token holds. */
  z-index: 20;
}
.welcome-modal {
  width: min(520px, 100%);
  overflow: hidden;
  border-radius: 18px;
  border: 1px solid rgba(255, 255, 255, .08);
  background: #111827;
  color: #F9FAFB;
  box-shadow:
    0 34px 90px rgba(17, 24, 39, .42),
    0 0 0 1px rgba(124, 58, 237, .16);
}
.modal-header {
  padding: 34px var(--space-xl) var(--space-sm);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  text-align: left;
  background: transparent;
}
.modal-eyebrow {
  color: #C4B5FD;
  font-size: var(--fs-2xs);
  line-height: 13px;
  font-weight: var(--fw-black);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.modal-header h1 {
  max-width: 420px;
  color: #F9FAFB;
  font-size: 34px;
  line-height: 38px;
  font-weight: var(--fw-black);
  letter-spacing: -0.04em;
}
.modal-header p {
  max-width: 410px;
  color: #CBD5E1;
  font-size: var(--fs-md);
  line-height: 21px;
  font-weight: var(--fw-semibold);
}
.modal-body {
  padding: 18px var(--space-xl) var(--space-xl);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-md);
}
.modal-body .btn-success {
  width: 100%;
  min-height: 47px;
  border: 0;
  /* ENTRY #113 (2026-07-18, user call): "our standard pill radius on all views (yes even modals
     on desktop view)". `--radius-pill` (999px) IS the standard: the shared base rule at :394
     already gives every .btn-primary/.btn-secondary/.btn-success the pill, and this local `12px`
     was one of only TWO overrides knocking it back down. So this corrects an outlier rather than
     inventing a value. No @media gate on this rule, so desktop is covered by construction. */
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: var(--text-inverse);
  font-size: var(--fs-lg);
  font-weight: var(--fw-850);
  box-shadow: 0 14px 32px rgba(124, 58, 237, .28);
}
.modal-skip {
  color: #94a3b8;
  background: transparent;
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
}
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

@media (max-width: 900px) {
  .app-shell { grid-template-columns: 1fr; }
  body.x1riqt-screen .app-shell {
    grid-template-columns: 168px minmax(0, 1fr);
  }
  .sidebar {
    min-height: auto;
    border-right: 0;
    border-bottom: 1px solid var(--border);
  }
  body.x1riqt-screen .sidebar {
    height: 100dvh;
    min-height: 0;
    border-right: 1px solid var(--border);
    border-bottom: 0;
    padding: var(--space-sm);
  }
  .side-brand, .workspace-switcher, .support-link { display: none; }
  body.x1riqt-screen .side-brand {
    display: flex;
  }
  .nav-list { flex-direction: row; overflow-x: auto; padding-bottom: 2px; }
  body.x1riqt-screen .nav-list {
    flex-direction: column;
    overflow: hidden;
    padding-bottom: 0;
  }
  .nav-item { white-space: nowrap; }
  .topbar { align-items: stretch; flex-direction: column; }
  body.x1riqt-screen .topbar {
    min-height: 52px;
    align-items: center;
    flex-direction: row;
    padding: var(--space-xs) 14px;
  }
  .search-box { width: 100%; }
  body.x1riqt-screen .search-box {
    width: min(320px, 100%);
    padding: var(--space-xs) var(--space-sm);
  }
  .page-head { align-items: flex-start; flex-direction: column; }
  body.x1riqt-screen .page-head {
    align-items: center;
    flex-direction: row;
  }
  .head-actions { width: 100%; }
  .head-actions > * { flex: 1 1 auto; }
  /* (Entry #72) `.coachmark-overlay { display: none }` USED TO BE HERE. It switched the "Start
     here" job coachmark off on every phone, which is why a phone viewer who closed the company
     -profile modal was dumped on a bare jobs list with no onward guidance at all. The class is
     rj0wj-ONLY (0 hits in every other screen), and its two hiding rules — `.is-dismissed` and
     `body.workspace-tour-active .coachmark-overlay` — are both (0,2,0), so they still win.
     Do not re-add it: it is the phone's only route to the next scene. */
  .kanban { grid-template-columns: 1fr; }
  .empty-state, .job-card { grid-template-columns: 1fr; }
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .drop-processing {
    align-items: stretch;
    flex-direction: column;
  }
  .progress-wrap { width: 100%; }
  [data-screen-id="x1RIQT"] .manual-source {
    position: relative;
    left: auto;
    top: auto;
    width: 100%;
    height: 178px;
    min-height: 0;
    margin-top: var(--space-sm);
    margin-bottom: var(--space-md);
    transform: translateY(8px);
  }
  body.x1riqt-screen [data-screen-id="x1RIQT"] .manual-source {
    position: absolute;
    left: 0;
    top: calc(100% + clamp(210px, 27vh, 272px));
    width: 392px;
    margin: 0;
    transform: translateY(0);
  }
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="grabbing"] .manual-source,
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .manual-source,
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .manual-source,
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .manual-source {
    height: 116px;
  }
  [data-screen-id="x1RIQT"] .drag-stack,
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .drag-stack {
    transform: rotate(0deg) scale(.94);
  }
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="grabbing"] .drag-stack,
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .drag-stack,
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .drag-stack {
    transform: rotate(-5deg) scale(.92);
  }
  .demo-caption {
    width: 100%;
    margin-top: -10px;
    transform: translateY(8px);
  }
  body.x1riqt-screen .demo-caption {
    width: 356px;
    margin-top: var(--space-xs);
    transform: translateY(0);
  }
  body.x1riqt-screen [data-screen-id="x1RIQT"] .import-board {
    grid-template-columns: minmax(0, 1fr) minmax(0, .82fr) minmax(0, .82fr);
  }
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .demo-caption {
    transform: translateY(0);
  }
  .tour-step {
    right: 24px;
    bottom: 24px;
  }
  .job-side {
    min-height: auto;
    align-items: flex-start;
  }
  .job-actions { justify-content: flex-start; }
}

@media (max-width: 560px) {
  .content { padding: var(--space-md); }
  body.x1riqt-screen .content {
    padding: 10px var(--space-sm);
  }
  .screen-title { font-size: 28px; }
  .banner { align-items: flex-start; }
  .drop-zone { min-height: 220px; }
  body.x1riqt-screen [data-screen-id="x1RIQT"] .drop-zone,
  body.x1riqt-screen [data-screen-id="x1RIQT"] .import-drop {
    min-height: 176px;
  }
  body.x1riqt-screen [data-screen-id="x1RIQT"] .manual-source {
    transform: scale(.84);
    transform-origin: top left;
  }
  .candidate-upload,
  .candidate-extract,
  .candidate-top,
  .candidate-foot {
    align-items: flex-start;
    flex-direction: column;
  }
  .ai-pill,
  .match-score {
    align-self: flex-start;
  }
  .tour-step-actions {
    align-items: stretch;
    flex-direction: column;
  }
}

/* Final nIfHr cascade guard: keep the tutorial fixed and consistent. */
body.nifhr-screen {
  overflow: hidden;
}

body.nifhr-screen .app-shell {
  height: 100vh;
  height: 100dvh;
  grid-template-columns: 240px minmax(0, 1fr);
}

body.nifhr-screen .sidebar {
  display: flex;
  height: 100vh;
  height: 100dvh;
  border-right: 1px solid var(--border);
  border-bottom: 0;
}

body.nifhr-screen .side-brand,
body.nifhr-screen .workspace-switcher,
body.nifhr-screen .support-link {
  display: flex;
}

body.nifhr-screen .main {
  min-height: 0;
  overflow: hidden;
}

body.nifhr-screen .topbar {
  min-height: 61px;
  align-items: center;
  flex-direction: row;
}

body.nifhr-screen .search-box {
  width: min(420px, 100%);
}

body.nifhr-screen .nifhr-content {
  height: calc(100vh - 61px);
  height: calc(100dvh - 61px);
  overflow: hidden;
}

body.nifhr-screen .messenger-content {
  display: none;
  height: calc(100vh - 61px);
  height: calc(100dvh - 61px);
  min-height: 0;
  overflow: hidden;
  background: var(--bg-app);
}

body.nifhr-screen.screening-step-messenger .nifhr-content {
  display: none;
}

body.nifhr-screen.screening-step-messenger .messenger-content {
  display: grid;
  grid-template-columns: 340px minmax(0, 1fr);
  animation: nifhr-messenger-in 260ms cubic-bezier(0.2, 0.9, 0.22, 1) both;
}

/* ⛔ ENTRY #335 (her call, annotated frame): *"I want this bottomsheet and screen dimmed on this
   actual messenger screen"*. The messenger DOORWAY used to open with the KANBAN BOARD behind its
   dim, so the viewer was told about the messenger while looking at something else. The dim and the
   sheet were already wired to `mode-messenger`; the only thing missing was the messenger itself.

   ⚠️⚠️ `height: 0`, NOT `display: none`, AND THAT IS THE WHOLE TRAP. The rule directly above uses
   `display: none` on `.nifhr-content` for the real segment, and copying it here would have been
   self-defeating: `.nifhr-screening-overlay` is a DOM DESCENDANT of `.nifhr-content` (verified by a
   tag-depth walk, not by indentation), and that overlay CONTAINS the doorway sheet. `display: none`
   removes an entire subtree from the render tree, so the obvious rule would have deleted the very
   bottom sheet this entry exists to show, and no `position: fixed` or z-index rescues a child of a
   `display: none` ancestor. A collapsed box keeps the subtree alive, and a `position: fixed`
   descendant is not clipped by an ancestor's `overflow` (checked: nothing on `.nifhr-stage` carries
   a transform, filter or will-change that would make it a containing block).
   ⚠️ THE BOARD MUST COLLAPSE, not merely be covered: `.main` is `overflow: hidden` and both panels
   are `calc(100dvh - 61px)` tall, so leaving the board at full height pushes the messenger out of
   the clip and it is never seen.
   ⚠️ THE ANIMATION SHORTHAND IS COPIED VERBATIM FROM THE SEGMENT RULE ON PURPOSE. A CSS animation
   restarts only when its name changes, so an IDENTICAL computed value across the doorway -> segment
   class swap means `nifhr-messenger-in` plays ONCE, on arrival, instead of replaying under the
   viewer when the sheet is dismissed. Change one and change the other. */
body.nifhr-screen.screening-step-mode-messenger .nifhr-content {
  /* ⚠️ `height: 0` ALONE LEAVES A 48px STRIP, MEASURED, and the numbers hid it: the probe reported
     the messenger visible with five threads and every assertion happy while the screenshot showed
     it pushed 48px down the pane. `.main` is `display: flex; flex-direction: column`, so this is a
     FLEX ITEM, and a flex item's `min-height: auto` resolves to its min-content size no matter what
     `height` says. `min-height: 0` plus a zero `flex` basis is what actually collapses it. */
  height: 0;
  min-height: 0;
  flex: 0 0 0;
  /* ⚠️ AND `padding: 0` IS THE THIRD OF THREE, MEASURED AFTER THE FIRST TWO STILL LEFT 48px.
     `height` sizes the CONTENT box, so this element's own `padding: 24px 28px` survived every
     height and flex rule and held the pane open by exactly 24+24. The children were already 0.
     A container's box includes its own padding: three separate mechanisms had to be named, and the
     screenshot is what said so each time while the assertions read healthy. */
  padding: 0;
  overflow: hidden;
}

body.nifhr-screen.screening-step-mode-messenger .messenger-content {
  display: grid;
  grid-template-columns: 340px minmax(0, 1fr);
  animation: nifhr-messenger-in 260ms cubic-bezier(0.2, 0.9, 0.22, 1) both;
}

/* The nav rail is part of "the actual messenger screen" she pointed at: without these the sidebar
   would still be highlighting Jobs while the messenger fills the pane behind the sheet. */
body.nifhr-screen.screening-step-mode-messenger .nav-item[data-nav-id="jobs"] {
  color: var(--text-strong);
  background: transparent;
  font-weight: var(--fw-medium);
}

body.nifhr-screen.screening-step-mode-messenger .nav-item[data-nav-id="messenger"] {
  color: var(--brand);
  background: var(--brand-tint);
  font-weight: var(--fw-semibold);
}

body.nifhr-screen.screening-step-messenger .nav-item[data-nav-id="jobs"] {
  color: var(--text-strong);
  background: transparent;
  font-weight: var(--fw-medium);
}

body.nifhr-screen.screening-step-messenger .nav-item[data-nav-id="messenger"] {
  color: var(--brand);
  background: var(--brand-tint);
  font-weight: var(--fw-semibold);
}

body.nifhr-screen .messenger-list {
  min-width: 0;
  height: 100%;
  display: grid;
  grid-template-rows: 66px 37px 37px minmax(0, 1fr);
  border-right: 1px solid var(--border);
  background: var(--surface);
}

body.nifhr-screen .messenger-list-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px var(--space-md) var(--space-sm);
}

body.nifhr-screen .messenger-list-head h1 {
  color: var(--text-primary);
  font-size: 20px;
  font-weight: var(--fw-bold);
  letter-spacing: -0.3px;
}

/* ⚠️ ENTRY #326: `> button`, NOT a descendant. The scope dropdown's trigger now lives inside this
   header (in `.messenger-scope-wrap > h1`), and as a descendant rule this would paint it as a
   32x32 brand-tint icon square. The new-message button is the header's only DIRECT button child,
   which is exactly the distinction that keeps this rule pointed at it and nothing else. */
body.nifhr-screen .messenger-list-head > button {
  /* ⚠️ ENTRY #326, MEASURED: flex items shrink by DEFAULT, and this one cannot (it is a fixed
     32px icon), so it still absorbed its proportional share of the overflow a long role title
     caused and left the scope wrapper 13.6px over its allotment, pushing this button from x=531
     to x=544.6. `0 0 auto` sends the whole shrink to the wrapper, where the ellipsis lives. */
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: var(--radius-md);
  background: var(--brand-tint);
  color: var(--brand);
  display: grid;
  place-items: center;
  padding: 0;
}

body.nifhr-screen .messenger-list-head > button svg,
body.nifhr-screen .messenger-chat-actions button svg,
body.nifhr-screen .messenger-composer > button svg,
body.nifhr-screen .messenger-attachment-wrap > button svg,
body.nifhr-screen .messenger-emoji svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

body.nifhr-screen .messenger-list-head > button svg {
  stroke-width: 2;
}

body.nifhr-screen .messenger-search {
  margin: 0 var(--space-md);
  height: 37px;
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 0 var(--space-sm);
  border: 0;
  border-radius: var(--radius-md);
  background: var(--bg-app);
  color: var(--text-tertiary);
}

body.nifhr-screen .messenger-search svg {
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
}

body.nifhr-screen .messenger-search input,
body.nifhr-screen .messenger-composer input {
  width: 100%;
  min-width: 0;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--text-primary);
  font: 500 13px/1.3 var(--font-sans);
}

body.nifhr-screen .messenger-search input::placeholder,
body.nifhr-screen .messenger-composer input::placeholder {
  color: var(--text-tertiary);
}

body.nifhr-screen .messenger-filter-row {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 7px var(--space-md);
}

body.nifhr-screen .messenger-filter-row button {
  height: 23px;
  padding: 0 var(--space-sm);
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-secondary);
  font: 600 12px/1 var(--font-sans);
}

body.nifhr-screen .messenger-filter-row button.active {
  background: var(--brand);
  color: var(--text-inverse);
}

body.nifhr-screen .messenger-thread-list {
  min-height: 0;
  overflow: hidden;
  padding-top: var(--space-xs);
}

body.nifhr-screen .messenger-thread {
  position: relative;
  min-height: 68px;
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  color: var(--text-primary);
}

body.nifhr-screen .messenger-thread.active {
  background: var(--bg-row-highlight);
}

body.nifhr-screen .messenger-thread strong {
  display: block;
  color: var(--text-primary);
  font-size: 13px;
  font-weight: var(--fw-bold);
  line-height: 1.3;
}

body.nifhr-screen .messenger-thread p {
  margin-top: 3px;
  overflow: hidden;
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  line-height: 1.25;
  text-overflow: ellipsis;
  white-space: nowrap;
}

body.nifhr-screen .messenger-thread time {
  display: block;
  color: var(--text-tertiary);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
}

body.nifhr-screen .thread-meta {
  align-self: stretch;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: flex-start;
  gap: 9px;
  padding-top: var(--space-2xs);
}

body.nifhr-screen .messenger-thread > time {
  align-self: start;
  margin-top: var(--space-2xs);
}

body.nifhr-screen .messenger-thread b,
body.nifhr-screen .thread-meta b {
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: var(--text-inverse);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
}

body.nifhr-screen .messenger-avatar {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-pill);
  /* ENTRY #74 one-candidate-palette: the colour comes from the row's own `--mm-c`, set inline to
     a `--cand-*` token. This is the SAME mechanism the mobile inbox already uses (:16146), so
     desktop and mobile now read one authority. The five hardcoded colour subclasses that used to
     live below are deleted: `.orange` rendered Priya as #F59E0B, which is not a candidate token
     at all, and names like `.blue` holding Clare's violet would lie about what they produce. */
  background: var(--mm-c, var(--brand));
  color: var(--text-inverse);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
  letter-spacing: -0.2px;
  /* ⚠️⚠️ (#401) NEVER SQUASHED, HER CALL: *"Always ensure whatever view that this avatar is always
     circle and not crushed like this from either side leading to this retarded shape that is
     wrong"*.

     ⚠️ THE MECHANISM, MEASURED RATHER THAN GUESSED: this is a flex item with a fixed width and the
     DEFAULT `flex-shrink: 1`, so the moment its row runs short of space the browser takes that
     space out of the avatar's WIDTH while the height stays put, and a circle becomes an egg. The
     chat header's own avatar, measured across the widths where the messenger is still the desktop
     layout: 40.00 x 40.00 (ratio 1.000) at 1920, 1440, 1280 and 1100; 39.19 x 40 (0.980) at 1024;
     29.83 x 40 (0.746) at 960; and 22.38 x 40 (**0.559**) at 901. That last one is her screenshot.
     ⚠️ NO `min-width` IS INVOLVED AND THE FIRST DIAGNOSIS SAID OTHERWISE. There is a `min-width: 0`
     two rules below this one, on `.messenger-chat`, and reading it as the avatar's was wrong: the
     avatar's computed `min-width` is `auto`. Written down because the wrong cause was published
     before the bytes were re-read, and a plausible neighbouring declaration is exactly how that
     happens.
     ⚠️ `aspect-ratio` IS A BELT, NOT THE FIX. `flex: 0 0 auto` is what takes the avatar out of the
     negotiation; the ratio catches a future rule that sets only one axis. Both were proven in a
     real browser BEFORE being written here: injected at 901, 960 and 1024 they moved the ratio
     from 0.559 / 0.746 / 0.980 to exactly 1.000, 40 x 40, at all three.
     ⚠️ SCOPED TO THE WHOLE CLASS, DELIBERATELY. Her words are "whatever view", and an oval avatar
     is wrong wherever it appears, so the thread list, the shared-with strip and the collaborator
     rows are covered by the same rule rather than by a chat-header-only patch. The side effect
     that would matter, a tight row overflowing instead of squashing, was measured rather than
     assumed. -> today-update.md #401 */
  flex: 0 0 auto;
  aspect-ratio: 1;
}


body.nifhr-screen .messenger-chat {
  min-width: 0;
  /* ⚠️ `min-height: 0` IS LOAD-BEARING AND WAS MISSING. This is a GRID ITEM of
     `.messenger-content`, and a grid item's default `min-height: auto` refuses to shrink below its
     content, so the `minmax(0, 1fr)` row below sized to the thread instead of to the box and the
     whole chat grew past its own area. Measured at 1440x900 with five cards in the thread:
     `.messenger-content` 832px tall, `.messenger-chat` 1227px, computed rows
     `64px 1098.56px 64px`, and the composer pushed to y=1230 on a 900px viewport, i.e. off the
     bottom of the screen entirely.
     The rest of this component was always written for a bounded box: the row is already
     `minmax(0, 1fr)` and every builder ends with `messengerThread.scrollTop =
     messengerThread.scrollHeight`. Both were dead code without this line. It bites the interactive
     walk too, which appends the same five cards. */
  min-height: 0;
  height: 100%;
  display: grid;
  grid-template-rows: 64px minmax(0, 1fr) 64px;
  background: var(--bg-app);
}

body.nifhr-screen .messenger-chat-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-sm) 20px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}

body.nifhr-screen .messenger-chat-person {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-width: 0;
}

body.nifhr-screen .messenger-chat-person .messenger-avatar {
  width: 40px;
  height: 40px;
}

body.nifhr-screen .messenger-chat-person strong {
  color: var(--text-primary);
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
}

body.nifhr-screen .messenger-chat-person p {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 2px;
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
}

body.nifhr-screen .messenger-chat-person i {
  width: 7px;
  height: 7px;
  border-radius: var(--radius-pill);
  background: var(--success);
}

body.nifhr-screen .messenger-chat-actions {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

body.nifhr-screen .messenger-chat-actions button,
body.nifhr-screen .messenger-composer > button,
body.nifhr-screen .messenger-attachment-wrap > button {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text-secondary);
  padding: 0;
}

body.nifhr-screen .messenger-chat-body {
  position: relative;
  min-height: 0;
  overflow: hidden;
  padding: var(--space-lg);
}

body.nifhr-screen .messenger-chat-body > time {
  display: block;
  width: max-content;
  margin: 0 auto 18px;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text-tertiary);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
}

/* ⚠️⚠️ ENTRY #318 (2026-08-11, her annotation on the live thread): "Add our usual spacing token
   here not this one you're using as way too close margin from the tick row to the top border of
   the next chat bubble for example - nothing hard coded tokens either - use ONLY our tokens we
   currently have."
   The gap between two messages in this thread was `margin-bottom: 10px`, a bespoke literal that
   is not on the token scale at all (it sits between `--space-xs` 8 and `--space-sm` 12). It was
   authored FIVE separate times, once per message-wrapper class, so a fix to one would have left
   four different message types at the old value and produced visibly uneven gaps down the same
   thread. All five now consume `--space-md`:
     `.message-bubble` (here), `.message-email-stack`, `.message-document-stack`,
     `.message-quest-stack`, `.message-typing-row`.
   ⚠️ SAID OUT LOUD, because "for example" is a sweep instruction and a sweep must name what it
   did NOT touch: the `.message-status` timestamp keeps its own `margin-top: 3px` (it is part of
   the message above it, not a gap between messages), and nothing outside this desktop thread
   moved. `messenger-mobile.html` and the mobile universal inbox carry their own rules and were
   not opened. */
body.nifhr-screen .message-bubble {
  width: fit-content;
  max-width: 344px;
  margin-bottom: var(--space-md);
}

body.nifhr-screen .message-bubble p {
  padding: 10px var(--space-sm);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text-primary);
  font-size: 13px;
  font-weight: var(--fw-medium);
  line-height: 1.35;
}

body.nifhr-screen .message-status {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  margin-top: 3px;
  color: var(--text-tertiary);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-semibold);
}

body.nifhr-screen .message-bubble.outgoing {
  margin-left: auto;
}

/* #318: one of the five thread gaps, see the block above `.message-bubble`. */
body.nifhr-screen .message-email-stack {
  width: 344px;
  max-width: 344px;
  margin-left: auto;
  margin-bottom: var(--space-md);
}

body.nifhr-screen .message-bubble.outgoing p {
  border-color: var(--brand);
  background: var(--brand);
  color: var(--text-inverse);
}

body.nifhr-screen .message-bubble.outgoing .message-status {
  justify-content: flex-end;
}

body.nifhr-screen .message-bubble.short { max-width: 294px; }
body.nifhr-screen .message-bubble.mini { max-width: 188px; }

body.nifhr-screen .message-bubble.email {
  width: 344px;
  max-width: 344px;
  margin-bottom: 0;
  padding: 10px var(--space-sm) 9px;
  border: 1px solid color-mix(in srgb, var(--brand) 18%, var(--border));
  border-radius: 14px;
  background: var(--surface);
  box-shadow: 0 8px 18px rgba(17, 24, 39, 0.06);
}

body.nifhr-screen .message-bubble.email p {
  margin-top: 10px;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--text-primary);
}

body.nifhr-screen .message-email-head {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--brand);
}

body.nifhr-screen .message-email-head svg,
body.nifhr-screen .message-status.email-read svg {
  width: 13px;
  height: 13px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

body.nifhr-screen .message-email-head strong,
body.nifhr-screen .message-read-label {
  color: var(--brand);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
}

body.nifhr-screen .message-read-label {
  color: var(--text-tertiary);
}

body.nifhr-screen .message-status.email-read {
  justify-content: flex-end;
  margin-top: var(--space-2xs);
  color: var(--text-tertiary);
}

body.nifhr-screen .message-status.email-read svg {
  color: #0EA5E9;
}

body.nifhr-screen .read-ticks {
  position: relative;
  display: inline-block;
  width: 17px;
  height: 10px;
}

body.nifhr-screen .read-ticks::before,
body.nifhr-screen .read-ticks::after {
  content: "";
  position: absolute;
  top: 1px;
  width: 5px;
  height: 9px;
  border-right: 1.8px solid var(--info-text);
  border-bottom: 1.8px solid var(--info-text);
  transform: rotate(45deg);
  border-radius: 0 0 1px 0;
}

body.nifhr-screen .read-ticks::before {
  left: 3px;
}

body.nifhr-screen .read-ticks::after {
  left: 8px;
}

body.nifhr-screen .read-ticks.is-opened::before,
body.nifhr-screen .read-ticks.is-opened::after {
  border-color: #0EA5E9;
}

body.nifhr-screen .messenger-composer {
  position: relative;
  display: grid;
  grid-template-columns: auto 36px minmax(0, 1fr) 40px;
  align-items: center;
  gap: 10px;
  padding: var(--space-sm) var(--space-md);
  border-top: 1px solid var(--border);
  background: var(--surface);
}

body.nifhr-screen .messenger-attachment-wrap {
  position: relative;
  width: 36px;
  height: 36px;
}

body.nifhr-screen .messenger-channel-wrap {
  position: relative;
}

body.nifhr-screen .messenger-channel {
  width: auto !important;
  min-width: 139px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 10px;
  gap: 6px;
  border: 1px solid color-mix(in srgb, var(--brand) 18%, var(--border));
  border-radius: var(--radius-pill);
  border-color: color-mix(in srgb, var(--brand) 18%, var(--border));
  background: var(--bg-row-highlight) !important;
  color: var(--brand) !important;
  font-size: var(--fs-sm) !important;
  font-weight: var(--fw-bold) !important;
}

body.nifhr-screen .messenger-channel-menu {
  position: absolute;
  left: 0;
  bottom: calc(100% + 8px);
  width: 184px;
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--surface);
  box-shadow: var(--shadow-popover);
  transform: translateY(0) scale(1);
  transform-origin: 24px 100%;
  opacity: 1;
  visibility: visible;
  transition:
    opacity var(--motion-ui) cubic-bezier(0.4, 0, 0.2, 1),
    transform var(--motion-ui) cubic-bezier(0.4, 0, 0.2, 1),
    visibility 0s;
  z-index: 20;
}

/* Entry #272t: the channel menu is the SAME mechanism as the attachment menu above and gets the
   same treatment, because fixing only the one in the screenshot leaves the identical pop firing
   twice more later in the same film. */
body.nifhr-screen .messenger-channel-menu[hidden] {
  display: block;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateY(8px) scale(0.97);
  transition:
    opacity 150ms var(--ease-in, cubic-bezier(.4, 0, 1, 1)),
    transform 150ms var(--ease-in, cubic-bezier(.4, 0, 1, 1)),
    visibility 0s linear 150ms;
}

body.nifhr-screen .messenger-channel-menu button,
body.nifhr-screen .messenger-scope-menu button {
  width: 100%;
  min-height: 34px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 10px;
  border-radius: 10px;
  background: transparent;
  color: var(--text-primary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  text-align: left;
}

/* ⚠️ ENTRY #321, USER CALL ON AN ANNOTATED FRAME: *"the spacing between the instant message and
   email rows has zero normal spacing token so rollover purple effect has no space ... use our
   standard token nothing hardcoded"*. The two rows were block boxes sharing an edge, so the two
   10px-rounded --brand-tint rollovers touched. One token gap between adjacent rows; the menu's own
   6px rim is untouched. */
body.nifhr-screen .messenger-channel-menu button + button,
body.nifhr-screen .messenger-scope-menu button + button {
  margin-top: var(--space-2xs);
}

body.nifhr-screen .messenger-channel-menu button[aria-checked="true"],
body.nifhr-screen .messenger-scope-menu button[aria-checked="true"] {
  background: var(--brand-tint);
  color: var(--brand);
}

body.nifhr-screen .messenger-channel-menu button[aria-checked="true"]::after,
body.nifhr-screen .messenger-scope-menu button[aria-checked="true"]::after {
  content: "";
  width: 5px;
  height: 9px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
}

/* ⚠️ ENTRY #272. The channel menu had NO rollover at all, on either arm, and it is the same
   interaction as the attachment menu one beat later: a menu opens, an option is chosen. It gets the
   same treatment for the same reason, and the same `[data-watch-hover]` hook, so the watch's last
   menu beat does not read as a dead list after the three before it lit up.
   ⚠️ THIS SITS AFTER THE `aria-checked` RULE ON PURPOSE. Both are (0,3,2) and ties go to whichever
   is written later, so the rollover is what paints while the pointer is on an option, checked or
   not. The tick is what still says which one is selected. */
body.nifhr-screen .messenger-channel-menu button {
  transition:
    background var(--motion-fast) var(--ease-standard),
    color var(--motion-fast) var(--ease-standard),
    transform var(--motion-fast) var(--ease-standard);
}

body.nifhr-screen .messenger-channel-menu button:hover,
body.nifhr-screen .messenger-channel-menu button:focus-visible,
body.nifhr-screen .messenger-channel-menu button[data-watch-hover],
body.nifhr-screen .messenger-scope-menu button:hover,
body.nifhr-screen .messenger-scope-menu button:focus-visible,
body.nifhr-screen .messenger-scope-menu button[data-watch-hover] {
  background: var(--brand-tint);
  color: var(--brand);
  transform: translateX(2px);
  outline: none;
}

/* ══ ENTRY #326: THE INBOX SCOPE DROPDOWN ═══════════════════════════════════════════════════
   The heading "Messages" became a dropdown reading "All messages", and picking a role filters the
   inbox to that role's threads. It reuses the CHANNEL menu wholesale (item box, the #321 token
   gap, the checked tick, the rollover incl. the watch film's `[data-watch-hover]` hook) by joining
   its selector lists above, so not one font size, radius or colour is retyped here.
   ⚠️ WHAT IS GENUINELY DIFFERENT AND SO IS WRITTEN OUT: this menu opens DOWNWARD from a header,
   where the channel menu opens upward from the composer footer, so `top` and `transform-origin`
   are re-derived rather than copied. Copying `bottom: calc(100% + 8px)` would hang it above the
   panel's top edge, outside `.messenger-content`'s `overflow: hidden`.
   ⚠️ THE WRAPPER TAKES THE HEADER'S FREE WIDTH (`flex: 1 1 auto`) rather than sizing to the label.
   That is what lets the panel be `left: 0; right: 0` with NO width literal, and it is measured
   against the new-message button rather than guessed: the button keeps its own intrinsic width at
   the far end of the same `space-between` row, so the two can never overlap. */
/* ⚠️ MEASURED, NOT ASSUMED: with only `min-width: 0` on the wrapper, a long role title grew the
   HEADER itself from 339px to 567.2px and pushed the new-message button from x=531 to x=759.2,
   straight out of the 340px list column. A flex item's automatic minimum size is its content, and
   every box in the chain (header, wrapper, h1, button) was still `min-width: auto` or free to
   overflow, so the ellipsis had no definite width to resolve against and never fired. The whole
   chain is pinned here, and the guard asserts the button's left edge against the trigger's right. */
body.nifhr-screen .messenger-list-head {
  min-width: 0;
}

body.nifhr-screen .messenger-scope-wrap,
body.nifhr-screen .messenger-scope-wrap > h1 {
  min-width: 0;
  max-width: 100%;
}

/* `1 1 auto`, so the wrapper claims the header's free width and the panel below it can be
   `left: 0; right: 0` with no width literal. Paired with the `min-width: 0` chain above, that is
   also what makes the trigger's ellipsis fire instead of the label shoving the icon button. */
body.nifhr-screen .messenger-scope-wrap {
  position: relative;
  flex: 1 1 auto;
}

body.nifhr-screen .messenger-scope {
  max-width: 100%;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: 0;
  border: 0;
  background: transparent;
  /* Inherited from `.messenger-list-head h1`, which is still this button's parent: the 20px/700
     and the -0.3px tracking are NOT restated. Move that rule and this follows it. */
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  cursor: pointer;
}

body.nifhr-screen .messenger-scope > span:first-child {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  /* Safety valve only. A role label is measured against the panel width at build time and must
     fit; this exists so an over-long scraped title clips gracefully instead of pushing the
     new-message button out of the 66px header track. */
  text-overflow: ellipsis;
}

/* ⚠️⚠️ ENTRY #326b, USER CALL ON AN ANNOTATED FRAME: *"Vertically centre the chevron in relation to
   the text label"*. IT ALREADY WAS, BY EVERY RECT: the trigger, the label span and the caret span
   all report the same 30px box and the same centre (104.66 at 1440), and a `Range` over each text
   node agrees. Rects measure LINE BOXES; her eye measures INK, and `⌄` (U+2304) carries its mark
   low in its em box.
   MEASURED with canvas `actualBoundingBox*`, which is real glyph ink relative to the baseline:
     cap height (H)      14.55px  -> the label's visual block runs cap-top to baseline, mid -7.28
     caret ink           asc 4.18 / desc 2.64                                       -> mid -0.77
   so the mark sits 6.50px BELOW the label's optical centre at this size.
   ⚠️ THE VALUE IS `em`, AND IT IS DERIVED, NOT TUNED. 6.50/20 = 0.3252em, and the SAME ratio falls
   out of the 12px composer trigger (3.90/12 = 0.3252em), because it is a pure font-metric ratio of
   Inter's cap height against this glyph's ink. It therefore stays correct if the type size moves,
   which a pixel nudge would not.
   ⚠️ CENTRED ON THE CAP BAND, NOT ON THIS STRING'S INK BOX. "All messages" has a descender that
   drags its full-ink centre down; centring on that would move the chevron every time the label
   changed to a role name with different letters, which is a visible jitter. */
body.nifhr-screen .messenger-scope > span:last-child {
  flex: 0 0 auto;
  transform: translateY(-0.3252em);
  transition: transform var(--motion-fast) var(--ease-standard);
}

/* ⚠️⚠️ THE OPEN STATE NEEDS ITS OWN NUMBER AND IT GOES THE OTHER WAY, which is the part worth
   writing down: a 180deg rotation MIRRORS the ink about the BOX centre, not about the baseline.
   Measured: the box centre sits 7.00px above the baseline (half-leading 3 + font ascent 19 in a
   30px line box), so the mark's ink centre moves from -0.77 to -13.23, i.e. from below the target
   to well ABOVE it, and the correction is DOWNWARD. 5.95/20 = 0.2977em.
   ⚠️ I first wrote this as a mirrored guess (-0.2482em) and it was wrong. The rotation's pivot is
   the box centre, so the two states are not symmetric about anything you can eyeball. */
body.nifhr-screen .messenger-scope[aria-expanded="true"] > span:last-child {
  transform: translateY(0.2977em) rotate(180deg);
}

body.nifhr-screen .messenger-scope-menu {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(100% + var(--space-xs));
  padding: var(--space-2xs);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--shadow-popover);
  transform: translateY(0) scale(1);
  transform-origin: var(--space-lg) 0;
  opacity: 1;
  visibility: visible;
  transition:
    opacity var(--motion-ui) cubic-bezier(0.4, 0, 0.2, 1),
    transform var(--motion-ui) cubic-bezier(0.4, 0, 0.2, 1),
    visibility 0s;
  z-index: 20;
}

/* The interpolable close, same as entry #272t gave the other two menus: `display` stays block so
   opacity and transform can actually animate out. */
body.nifhr-screen .messenger-scope-menu[hidden] {
  display: block;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateY(calc(var(--space-xs) * -1)) scale(0.97);
  transition:
    opacity 150ms var(--ease-in, cubic-bezier(.4, 0, 1, 1)),
    transform 150ms var(--ease-in, cubic-bezier(.4, 0, 1, 1)),
    visibility 0s linear 150ms;
}

/* EQUAL ROWS WHATEVER THE LABEL LENGTH (her words: "even height rows even when shorter job role or
   longer job role"). The shared item rule already carries `min-height` and `align-items: center`;
   what a long role title could still do is WRAP to two lines and make one row taller than its
   siblings, so wrapping is refused here and the fit is asserted by a guard. */
body.nifhr-screen .messenger-scope-menu button {
  white-space: nowrap;
}

/* An option's label is a span (the config role's carries `data-employer-job-title`), and a span in
   a flex button keeps its content width, so a very long scraped title spilled 301.7px of text
   through a 245px row. Measured capacity at the shipped type: 245px, about 38 characters. Beyond
   that the row ellipses rather than growing, because growing is what breaks the equal heights and
   the row it would break is the one nobody is looking at. */
body.nifhr-screen .messenger-scope-menu button > span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

body.nifhr-screen .messenger-attachment-menu {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 14px);
  width: 284px;
  padding: 14px 6px;
  border: 2px solid #6D5DF8;
  border-radius: var(--radius-xl);
  background: var(--surface);
  box-shadow: 0 18px 42px rgba(17, 24, 39, 0.16);
  /* Entry #272t: the open state, written out so the closed state above has somewhere to come
     from. The origin is the tail's own corner, so the menu grows OUT OF the paperclip it belongs
     to rather than out of its own middle. */
  transform: translateX(-44px) translateY(0) scale(1);
  transform-origin: 46px 100%;
  opacity: 1;
  visibility: visible;
  will-change: transform, opacity;
  /* ⚠️ NOT `--ease-out`, AND THIS IS #272m's MEASUREMENT RE-APPLIED RATHER THAN TASTE. That token
     is an EXPO-out: measured on this very menu it painted 0.228 opacity on its FIRST frame and was
     past halfway in 16ms of a 150ms move, which is what "janky" looks like from the front. A first
     replacement, an ease-out cubic, still reached halfway in 42ms of 192. The curve below spends
     its time where the eye can actually see it. */
  transition:
    opacity var(--motion-ui) cubic-bezier(0.4, 0, 0.2, 1),
    transform var(--motion-ui) cubic-bezier(0.4, 0, 0.2, 1),
    visibility 0s;
  z-index: 80;
}

body.nifhr-screen .messenger-attachment-menu::before,
body.nifhr-screen .messenger-attachment-menu::after {
  content: "";
  position: absolute;
  bottom: -9px;
  left: 46px;
  width: 16px;
  height: 16px;
  transform: rotate(45deg);
  border-radius: 0 0 4px 0;
}

body.nifhr-screen .messenger-attachment-menu::before {
  background: #6D5DF8;
  box-shadow: 8px 8px 16px rgba(17, 24, 39, 0.08);
}

body.nifhr-screen .messenger-attachment-menu::after {
  bottom: -6px;
  background: var(--surface);
}

/* ⚠️⚠️ ENTRY #272t, USER CALL ON AN ANNOTATED FRAME: *"the modal JANKILY APPEARING ... make it
   smooth 60fps this is currently shit"*. THE MENU USED TO FLIP `display: none` TO SHOWN, so the
   `opacity`/`transform` transition declared on the base rule above had nothing to run from: the
   element arrived already at its final values and simply POPPED. This is the identical defect
   #272m found and fixed on the three drawers, on the identical mechanism, and it is why the two
   DIRECTIONS are declared separately here rather than shared.
   ⚠️ `display: block`, NOT `none`: an interpolable state is the whole point, and the menu is
   `position: absolute` so keeping its box costs no layout. `pointer-events: none` is what stops a
   menu nobody can see from eating clicks, and `visibility` is delayed by the transition's own
   length on the way OUT so the fade is not cut off on its first frame.
   ⚠️ THE EXIT USES `--ease-in`, NOT THE ENTRANCE'S EASE. #272m measured `--ease-out` as HALFWAY
   GONE IN 41ms, which is what "janky" looks like from the other side. */
body.nifhr-screen .messenger-attachment-menu[hidden] {
  display: block;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateX(-44px) translateY(10px) scale(0.96);
  transition:
    opacity 150ms var(--ease-in, cubic-bezier(.4, 0, 1, 1)),
    transform 150ms var(--ease-in, cubic-bezier(.4, 0, 1, 1)),
    visibility 0s linear 150ms;
}

body.nifhr-screen .messenger-attachment-menu button {
  position: relative;
  z-index: 1;
  width: 100%;
  min-height: 54px;
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr);
  align-items: center;
  gap: 14px;
  padding: 0 14px;
  border: 0;
  border-radius: 14px;
  background: transparent;
  color: var(--text-primary);
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  letter-spacing: 0;
  text-align: left;
  transition:
    background var(--motion-fast) var(--ease-standard),
    color var(--motion-fast) var(--ease-standard),
    transform var(--motion-fast) var(--ease-standard);
}

/* ⚠️ ENTRY #272, USER CALL ON AN ANNOTATED FRAME: *"I want [a rollover effect] added to the menu
   options as the menu option in this segment is selected"*. The old rollover was
   `background: var(--bg-row-highlight)` and nothing else, which is the same near-white the row
   already sits on and reads as no rollover at all on a 1440 screen at arm's length. It now takes
   the brand tint, the brand text colour, the brand stroke on the icon, and a 2px nudge along the
   direction the eye is already travelling.
   ⚠️ `[data-watch-hover]` IS THE SAME RULE, NOT A SECOND LOOK. The watch segment cannot produce a
   real `:hover`, so it stamps that attribute (see `setMenuHover` in nifhr.html) and rides this
   declaration. Adding a separate watch-only rule would be two treatments that agree today and
   drift on the next edit to either one.
   ⚠️ `:not([disabled])` because #207d's lock leaves the options the current beat does NOT want at
   `opacity: .4; pointer-events: none`. A real pointer cannot hover through that, but an attribute
   written by JS can, and a lit-up disabled option is an affordance that does nothing. */
body.nifhr-screen .messenger-attachment-menu button:hover,
body.nifhr-screen .messenger-attachment-menu button:focus-visible,
body.nifhr-screen .messenger-attachment-menu button[data-watch-hover]:not([disabled]) {
  background: var(--brand-tint);
  color: var(--brand);
  transform: translateX(2px);
  outline: none;
}

body.nifhr-screen .messenger-attachment-menu button:hover svg,
body.nifhr-screen .messenger-attachment-menu button:focus-visible svg,
body.nifhr-screen .messenger-attachment-menu button[data-watch-hover]:not([disabled]) svg {
  stroke: var(--brand);
}

body.nifhr-screen .messenger-attachment-menu svg {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: var(--text-tertiary);
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
  justify-self: center;
}

/* #318: one of the five thread gaps, see the block above `.message-bubble`. ⚠️ This one carries
   the gap inside a `margin` SHORTHAND, which is exactly the shape a grep for `margin-bottom: 10px`
   misses. */
body.nifhr-screen .message-document-stack {
  width: 344px;
  max-width: 344px;
  margin: 0 0 var(--space-md) auto;
}

body.nifhr-screen .message-document-stack.demo-message {
  animation: messengerDocumentBubbleIn var(--motion-ui) var(--ease-out) both;
}

@keyframes messengerDocumentBubbleIn {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  body.nifhr-screen .message-document-stack.demo-message {
    animation: none;
  }

  body.nifhr-screen .message-document-bubble.is-read-cued,
  body.nifhr-screen .message-document-bubble.is-read-cued::after,
  body.nifhr-screen .message-document-bubble.is-read-cued .message-document-affordance {
    animation: none;
  }

  body.nifhr-screen .messenger-document-drawer,
  body.nifhr-screen .messenger-location-drawer,
  body.nifhr-screen .messenger-pdf-drawer,
  body.nifhr-screen .messenger-attachment-menu,
  body.nifhr-screen .document-file-row,
  body.nifhr-screen .location-office-row,
  body.nifhr-screen .document-check,
  body.nifhr-screen .document-drawer-foot button {
    transition: none;
  }
}

body.nifhr-screen .message-document-stack .message-status {
  justify-content: flex-end;
}

/* ⚠️⚠️ #318, AND THIS IS THE ONE HER ARROW ACTUALLY LANDED ON. It is the sixth thread gap and it
   was the worst of them: this rule set `margin-left` and NO `margin-bottom` at all, so the gap
   under the location/map card measured 0.0px against the next bubble's top border, not the 10px
   the other five carried. That is why her words were "way too close" rather than "a bit tight".
   A grep for the literal `10px` would never have found it, and a fix scoped to the questionnaire
   card she appeared to be circling would have left it at zero. Measured after: 16.0px, the same as
   its five siblings. */
body.nifhr-screen .message-location-stack {
  width: min(340px, 100%);
  margin-left: auto;
  margin-bottom: var(--space-md);
}

body.nifhr-screen .message-location-stack .message-status {
  justify-content: flex-end;
}

body.nifhr-screen .message-location-bubble {
  width: 100%;
  display: grid;
  grid-template-rows: 150px auto;
  overflow: hidden;
  /* ⚠️ ENTRY #272w, USER CALL ON AN ANNOTATED FRAME: *"Why the fuck is this border so thick. Make
     it normal border width token and colour and transparency"*. It was never a border: it was
     `background: var(--brand)` showing through 10px of padding, so the card wore a solid 10px purple
     frame while every other card in the thread wears a 1px tinted hairline. It now takes the SAME
     border `.message-quest` has, token for token, and the same surface behind it. */
  border: 1px solid color-mix(in srgb, var(--brand) 20%, var(--border));
  border-radius: 18px;
  background: var(--surface);
  box-shadow: 0 8px 18px rgba(17, 24, 39, 0.06);
  color: var(--text-primary);
  padding: 6px;
  text-align: left;
  cursor: pointer;
  animation: pdfBubbleLand var(--motion-drawer) var(--ease-out) both;
}

body.nifhr-screen .message-location-bubble:hover,
body.nifhr-screen .message-location-bubble:focus-visible {
  outline: 0;
  box-shadow: 0 18px 34px rgba(124, 58, 237, 0.24);
}

body.nifhr-screen .message-map-preview {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 150px;
  overflow: hidden;
  border-radius: 10px 10px 0 0;
  background:
    linear-gradient(90deg, rgba(255,255,255,0.22) 1px, transparent 1px) 0 0 / 42px 42px,
    linear-gradient(rgba(255,255,255,0.20) 1px, transparent 1px) 0 0 / 42px 42px,
    linear-gradient(135deg, #DDE5F4, #B9C7DA);
}

body.nifhr-screen .message-map-preview::before,
body.nifhr-screen .message-map-preview::after,
body.nifhr-screen .message-map-preview i {
  content: "";
  position: absolute;
  display: block;
  pointer-events: none;
}

body.nifhr-screen .message-map-preview::before {
  width: 180px;
  height: 42px;
  left: -20px;
  top: 34px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.36);
  transform: rotate(-13deg);
}

body.nifhr-screen .message-map-preview::after {
  width: 220px;
  height: 38px;
  right: -54px;
  bottom: 26px;
  border-radius: var(--radius-pill);
  background: rgba(124, 58, 237, 0.18);
  transform: rotate(-18deg);
}

body.nifhr-screen .message-map-preview i {
  inset: 18px var(--space-lg);
  border: 1px solid rgba(255, 255, 255, 0.46);
  border-radius: 18px;
}

body.nifhr-screen .message-map-preview svg {
  position: relative;
  z-index: 1;
  width: 48px;
  height: 48px;
  fill: none;
  stroke: var(--brand);
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  filter: drop-shadow(0 8px 12px rgba(17, 24, 39, 0.18));
}

body.nifhr-screen .message-location-copy {
  display: grid;
  gap: var(--space-2xs);
  padding: 14px 14px var(--space-sm);
  border-radius: 0 0 10px 10px;
  background: var(--surface);
}

body.nifhr-screen .message-location-copy strong {
  color: var(--text-primary);
  font-size: 13px;
  font-weight: var(--fw-extrabold);
  line-height: 1.2;
}

body.nifhr-screen .message-location-copy span {
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-650);
  line-height: 1.35;
}

body.nifhr-screen .message-location-copy small {
  color: var(--brand);
  font-size: var(--fs-xs);
  font-weight: var(--fw-extrabold);
  line-height: 1.2;
}

body.nifhr-screen .message-document-bubble {
  width: 100%;
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr);
  align-items: center;
  gap: 10px;
  padding: 10px;
  border: 1px solid color-mix(in srgb, var(--brand) 24%, var(--border));
  border-radius: 14px;
  background: var(--surface);
  box-shadow: 0 8px 18px rgba(17, 24, 39, 0.06);
  color: var(--text-primary);
  text-align: left;
  transition:
    border-color var(--motion-fast) var(--ease-standard),
    box-shadow var(--motion-fast) var(--ease-standard),
    transform var(--motion-fast) var(--ease-standard);
}

body.nifhr-screen .message-document-bubble:hover,
body.nifhr-screen .message-document-bubble:focus-visible {
  border-color: var(--brand);
  outline: 0;
}

body.nifhr-screen .message-document-bubble.is-read-cued {
  position: relative;
  border-color: color-mix(in srgb, var(--brand) 64%, var(--border));
  animation: pdfReadCuePulse 1600ms var(--ease-out) 520ms 2 both;
}

body.nifhr-screen .message-document-bubble.is-read-cued::after {
  content: "";
  position: absolute;
  inset: -5px;
  border: 2px solid color-mix(in srgb, var(--brand) 42%, transparent);
  border-radius: 18px;
  opacity: 0;
  pointer-events: none;
  animation: pdfReadCueRing 1600ms var(--ease-out) 520ms 2 both;
}

body.nifhr-screen .message-document-affordance {
  justify-self: end;
  grid-column: 2;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 6px;
  color: var(--brand);
  font-size: var(--fs-xs);
  font-weight: var(--fw-extrabold);
  letter-spacing: 0;
  opacity: 0;
  transform: translateY(3px);
  transition:
    opacity var(--motion-ui) var(--ease-standard),
    transform var(--motion-ui) var(--ease-out);
}

body.nifhr-screen .message-document-bubble.is-read-cued .message-document-affordance {
  opacity: 1;
  transform: translateY(0);
  animation: pdfReadCueAffordance 1600ms var(--ease-out) 720ms 2 both;
}

body.nifhr-screen .message-document-affordance svg {
  width: 13px;
  height: 13px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

@keyframes pdfReadCuePulse {
  0%, 100% {
    transform: translateY(0);
    box-shadow: 0 8px 18px rgba(17, 24, 39, 0.06);
  }
  38% {
    transform: translateY(-3px);
    box-shadow: 0 14px 28px rgba(124, 58, 237, 0.16);
  }
}

@keyframes pdfReadCueRing {
  0% {
    opacity: 0;
    transform: scale(0.985);
  }
  30% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: scale(1.045);
  }
}

@keyframes pdfReadCueAffordance {
  0%, 100% {
    transform: translateY(0);
  }
  42% {
    transform: translateY(-2px);
  }
}

body.nifhr-screen .message-document-icon,
body.nifhr-screen .document-file-icon {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border-radius: var(--radius-md);
  background: var(--danger-bg);
  color: var(--danger-text);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-extrabold);
  letter-spacing: 0.03em;
}

body.nifhr-screen .message-document-copy strong {
  display: block;
  color: var(--text-primary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
  line-height: 1.25;
}

body.nifhr-screen .message-document-copy small {
  display: block;
  margin-top: 3px;
  color: var(--text-tertiary);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
}

body.nifhr-screen .messenger-document-drawer,
body.nifhr-screen .messenger-location-drawer,
body.nifhr-screen .messenger-pdf-drawer {
  position: fixed;
  inset: 0 0 0 auto;
  z-index: 2147483010;
  width: 33vw;
  min-width: 460px;
  height: 100vh;
  height: 100dvh;
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  border-left: 1px solid var(--border);
  background: var(--surface);
  box-shadow: -22px 0 58px rgba(17, 24, 39, 0.24);
  color: var(--text-primary);
  transform: translateX(104%);
  opacity: 0;
  pointer-events: none;
  /* ⚠️⚠️ ENTRY #272m, USER CALL: *"When dismissing the drawers throughout this animation dismiss
     them so drawer slides out at moment they just instantly close not good ux"*. THIS IS THE
     CLOSING TRANSITION AND IT IS DELIBERATELY NOT THE OPENING ONE. The pair used to share one
     declaration, so a dismissal faded the drawer out over `--motion-ui` while it was still
     travelling: measured per rAF under real playback at 1920, the library drawer painted 8 frames
     after its body class came off, its opacity crashing 1 -> 0.437 in about 130ms while the
     transform had only carried it 436px of the 634px it needed. The eye reads a 130ms fade as a
     snap, not as a slide. Holding opacity flat for the whole `--motion-drawer` and dropping it at
     the END, when the drawer is already off screen, makes the dismissal the slide the user asked
     for while leaving a never-opened drawer just as invisible as before.
     ⚠️⚠️ AND THE EASE HAD TO CHANGE TOO, WHICH THE OPACITY FIX ALONE DID NOT REVEAL. `--ease-out` is
     `cubic-bezier(.16, 1, .3, 1)`, an expo-out: perfect for an ENTRANCE, and an exit that is HALFWAY
     GONE IN 41ms. Measured per rAF under real playback, painted window for the location drawer's
     dismissal: `--ease-out` 0.149s on screen, halfway at 0.041; `--ease-standard` 0.223s, halfway at
     0.075; `--ease-in` 0.317s, halfway at 0.219. Only the last one spends its time where the eye is.
     `var(--ease-in, ...)` with that literal fallback is this stylesheet's own existing exit pattern
     (`companyCoachModalOut`); the token is not declared in `:root` and the fallback is what resolves.
     ⚠️ THE OPEN DIRECTION KEEPS THE FADE AND KEEPS `--ease-out`, in the open-state rule below, which
     outranks this one on specificity (three classes to two). Arriving fast and settling is the
     entrance the user has never complained about; do not "tidy" the two into one declaration again. */
  transition:
    transform var(--motion-drawer) var(--ease-in, cubic-bezier(.4, 0, 1, 1)),
    opacity 0s linear var(--motion-drawer);
}

body.nifhr-screen .messenger-pdf-drawer {
  grid-template-rows: auto minmax(0, 1fr);
}

/* ⚠️⚠️ ENTRY #272l, USER CALL: *"make sure these are as a sticky on bottom of slide out drawer"*.
   THE FOOTER WAS NOT UNSTUCK, IT WAS IN THE WRONG ROW. The shared rule above declares FOUR rows
   (`auto auto minmax(0, 1fr) auto`) for the document library drawer's four children: head, search,
   body, foot. The LOCATION drawer has only THREE children, so its footer landed in the third
   track, the `minmax(0, 1fr)`, and stretched to fill it. Measured at 1920 before the fix: rows
   `98px 142px 840px 0px`, footer at y=240 with a height of 840, its border-top sitting directly
   under the single row and 754px of its own empty box below. The last track was 0px and nothing was
   in it. A `position: sticky` or a `margin-top: auto` would each have papered over that without
   fixing it, and the fourth track would still have been dead.
   ⚠️ The document drawer keeps the four-row rule: it really does have four children. Count the
   children before reaching for the shared rule. */
body.nifhr-screen .messenger-location-drawer {
  grid-template-rows: auto minmax(0, 1fr) auto;
}

body.nifhr-screen.messenger-document-library-open .messenger-document-drawer,
body.nifhr-screen.messenger-location-open .messenger-location-drawer,
body.nifhr-screen.messenger-pdf-preview-open .messenger-pdf-drawer {
  transform: translateX(0);
  opacity: 1;
  pointer-events: auto;
  /* The ENTRANCE (entry #272m): fade in over `--motion-ui` while the transform slides. See the
     closed-state rule above for why the two directions are declared separately. */
  transition:
    transform var(--motion-drawer) var(--ease-out),
    opacity var(--motion-ui) var(--ease-standard);
}

body.nifhr-screen.messenger-document-library-open .messenger-guide-overlay,
body.nifhr-screen.messenger-location-open .messenger-guide-overlay,
body.nifhr-screen.messenger-pdf-preview-open .messenger-guide-overlay {
  display: none !important;
}

body.nifhr-screen .document-drawer-head,
body.nifhr-screen .pdf-drawer-head {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: 28px var(--space-lg) 18px;
}

body.nifhr-screen .document-drawer-head h2,
body.nifhr-screen .pdf-drawer-head h2 {
  color: var(--text-primary);
  font-size: 22px;
  font-weight: var(--fw-extrabold);
  letter-spacing: -0.4px;
  line-height: 1.1;
}

body.nifhr-screen .document-drawer-back,
body.nifhr-screen .pdf-drawer-head button {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--brand-tint);
  color: var(--brand);
  flex: 0 0 auto;
}

body.nifhr-screen .document-drawer-back svg,
body.nifhr-screen .pdf-drawer-head button svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

body.nifhr-screen .document-search {
  height: 56px;
  margin: 0 var(--space-lg) 18px;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 0 18px;
  border: 1px solid var(--border);
  border-radius: 28px;
  background: var(--bg-app);
  color: var(--text-tertiary);
  opacity: 0.38;
  filter: blur(0.45px) grayscale(0.2);
  pointer-events: none;
}

body.nifhr-screen .document-search svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
}

body.nifhr-screen .document-search input {
  width: 100%;
  min-width: 0;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--text-secondary);
  font: 600 14px/1.2 var(--font-sans);
}

body.nifhr-screen .document-library-body {
  min-height: 0;
  overflow: hidden;
  padding: 0 var(--space-lg) var(--space-md);
}

/* ⚠️ ENTRY #272e, USER CALL: *"Add our normal spacing gap between the row panels"*. This container
   was a plain block, so its rows stacked edge to edge with nothing between them. "Our normal" is not
   a new number: `.document-file-list` twenty lines below is `display: grid; gap: 10px`, and it is the
   list in the sibling drawer of this same family, so the two now space identically.
   ⚠️ Not scoped to the questionnaire drawer, unlike #272c's row restyle, and that is deliberate:
   the location drawer renders ONE row, where a gap between siblings can have no effect. Measured
   before and after to confirm that rather than assumed. */
body.nifhr-screen .location-library-body {
  min-height: 0;
  overflow: hidden;
  padding: 14px var(--space-lg) var(--space-md);
  display: grid;
  /* ⚠️⚠️ ENTRY #272l: PACK THE ROWS AT THE TOP. This was invisible until the drawer's footer moved
     into the right grid track in the same entry: while the body sat in an `auto` track it was
     content-height and there was no free space for its own single row to absorb. In a
     `minmax(0, 1fr)` track there is 896px of it at 1080, and a one-child grid stretches that child
     to fill, so the location row rendered as a 860px-tall purple box. The fix is on the BODY, not
     on the row: a row height is content, and any track this body ever sits in is now safe. */
  align-content: start;
  gap: 10px;
}

body.nifhr-screen .document-library-note {
  margin: 0 0 14px;
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  line-height: 1.45;
}

body.nifhr-screen .document-file-list {
  display: grid;
  gap: 10px;
}

body.nifhr-screen .document-file-row {
  width: 100%;
  font-family: inherit;
  text-align: left;
  min-height: 76px;
  display: grid;
  grid-template-columns: 24px 42px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: inherit;
  transition:
    border-color var(--motion-fast) var(--ease-standard),
    background var(--motion-fast) var(--ease-standard),
    opacity var(--motion-fast) var(--ease-standard),
    filter var(--motion-fast) var(--ease-standard);
}

body.nifhr-screen .document-file-row.is-target {
  border-color: color-mix(in srgb, var(--brand) 52%, var(--border));
  background: var(--surface);
  box-shadow: none;
  cursor: pointer;
}

/* ⚠️ ENTRY #272l: the library row's rollover, on the same hook and in the same language as every
   other rolled-over thing in this segment (brand tint, brand border). Scoped to `.is-target`
   because the four `.is-muted` rows are `pointer-events: none` and the hand never visits them. */
body.nifhr-screen .document-file-row.is-target:hover,
body.nifhr-screen .document-file-row.is-target:focus-visible,
body.nifhr-screen .document-file-row.is-target[data-watch-hover] {
  outline: 0;
  border-color: var(--brand);
  background: var(--brand-tint);
}

body.nifhr-screen .document-file-row.is-target.is-selected {
  border-color: color-mix(in srgb, var(--brand) 46%, var(--border));
}

body.nifhr-screen .document-file-row.is-muted {
  opacity: 0.38;
  filter: blur(0.45px) grayscale(0.2);
  pointer-events: none;
}

body.nifhr-screen .location-office-row {
  width: 100%;
  min-height: 112px;
  display: grid;
  grid-template-columns: 24px 52px minmax(0, 1fr);
  align-items: center;
  gap: 14px;
  padding: 18px;
  border: 1px solid color-mix(in srgb, var(--brand) 52%, var(--border));
  border-radius: 22px;
  background: color-mix(in srgb, var(--brand) 5%, var(--surface));
  color: inherit;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition:
    border-color var(--motion-fast) var(--ease-standard),
    background var(--motion-fast) var(--ease-standard),
    box-shadow var(--motion-fast) var(--ease-standard);
}

/* ⚠️ ENTRY #272l: `[data-watch-hover]` JOINS THE EXISTING RULE, it does not get one of its own.
   Same call as #272 on the two menus and #272e on the questionnaire row: the film cannot produce a
   real `:hover`, and two rules would be two treatments that agree today and drift on the next edit
   to either. */
body.nifhr-screen .location-office-row:hover,
body.nifhr-screen .location-office-row:focus-visible,
body.nifhr-screen .location-office-row[data-watch-hover] {
  outline: 0;
  border-color: var(--brand);
  /* ⚠️ ENTRY #272l: `--brand-tint`, ADDED because the measurement caught the two drawers speaking
     two rollover languages the moment the library row got one. This rule set only a border and a
     shadow, so the location row's rollover rendered as its own resting wash at #F9F5FE, which is
     the exact value the user threw out at #272 on the attachment menu: at arm's length on a 1440
     screen it "reads as no rollover at all". The library row measured `rgb(237, 233, 254)` at the
     same beat. One segment, one rollover language. */
  background: var(--brand-tint);
  box-shadow: 0 12px 24px rgba(124, 58, 237, 0.10);
}

/* ⚠️ ENTRY #272c, USER CALL ON AN ANNOTATED FRAME: *"Fix the layout on these questionnaire row
   elements they're total eye cancer and remove the icons and what are they all purple background
   and border ... not our ux and ui spect at all ... nothing hardcoded tokens only"*.

   THE LAYOUT BUG, MEASURED: `.location-office-row` is shared with the LOCATION drawer, whose rows
   carry THREE children (a check, an icon, the copy) against its `24px 52px minmax(0, 1fr)` grid.
   The questionnaire rows only ever had TWO, so the copy landed in the 52px ICON column and every
   title wrapped: measured at 1920, the `<strong>` was 83px wide over 2 lines and "4 questions ·
   values" ran to 3, which is the "Culture / fit" the user is looking at. The icons are now gone
   from the markup, so this is a ONE-column grid and the copy gets the whole row.

   THE TREATMENT is `.document-file-row`'s, four rules above, which is the house row in this very
   drawer family: `--surface` with a `--border` hairline, not a purple wash inside a purple border.
   Every value here is a token or is inherited; the only literals are the grid and the radius, which
   are copied from that same neighbour so the two read as one system.
   ⚠️ SCOPED TO THE QUESTIONNAIRE DRAWER. The location drawer keeps its three-column rows and its
   own look untouched, which is why this is an override here rather than an edit to the base rule.
   ⚠️⚠️ ENTRY #272m: TWO COLUMNS NOW, NOT ONE, because these rows gained a `.document-check`. The
   count is the whole bug this rule was written for: #272c cut it to one column when the icons were
   deleted and the rows had two children, and a tick box added over a one-column grid would have
   stacked the box ABOVE the copy rather than beside it. `24px` matches the check column the
   document and location rows already use, so the three drawers' checkboxes line up.
   ⚠️ THE `gap` GOES 4 -> 14 AND NOTHING MOVES BUT THE NEW COLUMN. On a one-column grid holding one
   child there is no gap to apply, so #272c's 4px was already dead; the 7px between each row's title
   and its sub-line comes from `.location-office-copy`'s own grid and is untouched. 14 matches the
   location row's, so the two drawers' check-to-copy distance is the same. */
body.nifhr-screen .messenger-questionnaire-drawer .location-office-row {
  min-height: 76px;
  grid-template-columns: 24px minmax(0, 1fr);
  gap: 14px;
  padding: var(--space-md) 18px;
  border-color: var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

/* ⚠️ ENTRY #272e, USER CALL: *"Add the cursor moving and rollover and selecting the option to
   send"*. `[data-watch-hover]` joins the same rule rather than getting one of its own, exactly as it
   did on the attachment and channel menus at #272: the film cannot produce a real `:hover`, and two
   rules would be two treatments that agree today and drift on the next edit to either. */
body.nifhr-screen .messenger-questionnaire-drawer .location-office-row:hover,
body.nifhr-screen .messenger-questionnaire-drawer .location-office-row:focus-visible,
body.nifhr-screen .messenger-questionnaire-drawer .location-office-row[data-watch-hover] {
  border-color: color-mix(in srgb, var(--brand) 52%, var(--border));
  /* ⚠️ `--brand-tint`, NOT `--bg-row-highlight`, and that is the user's own verdict applied rather
     than a second opinion: #272 replaced exactly this token on the attachment menu because at
     #F9F7FF against a white row it "reads as no rollover at all on a 1440 screen at arm's length".
     The same eye is looking at the same distance here, so the segment gets ONE rollover language:
     brand tint, brand text, brand border. */
  background: var(--brand-tint);
  color: var(--brand);
  box-shadow: none;
}

/* ⚠️⚠️ USER CALL ON AN ANNOTATED FRAME, 2026-08-12: *"This location row element has a purple
   highlight fill which it needs to be the same layout and styling as the row elements of the files
   slideout drawer, but not this location icon colour token though. This background of purple always
   there on this location row element is visually inconsistent with the files slideout drawer, and
   rollover and select styling of the animation and location needs harmonising"*.

   ⚠️ THE PURPLE IS NOT A STATE, WHICH IS THE WHOLE BUG. It is in the BASE `.location-office-row`
   rule above (`color-mix(--brand 5%, --surface)` inside a 52%-brand border), with no `is-target`,
   `is-selected` or hover gate, so the row is painted permanently as if it were already chosen. The
   file rows next door rest on `--surface` with a `--border` hairline and earn their tint on hover.
   A row that always looks selected cannot show being selected.

   ⚠️ THIS IS THE #272c OVERRIDE APPLIED TO THE DRAWER IT WAS DELIBERATELY WITHHELD FROM. That entry
   gave the questionnaire drawer's copy of this row the file row's treatment and wrote, out loud,
   *"SCOPED TO THE QUESTIONNAIRE DRAWER. The location drawer keeps its three-column rows and its own
   look untouched"*. That exclusion is the thing she is now overturning, so this is its twin rather
   than a new invention: same shape, same tokens, one drawer along. The THREE-column grid is kept,
   because this row really does have three children (check, icon, copy) where the questionnaire's has
   two; only the column widths, spacing, border, radius and background adopt the file row's.

   ⚠️ THE ICON CHIP IS EXPLICITLY EXCLUDED on her instruction: `.location-office-icon` keeps its
   `--brand-tint` / `--brand` pair. Nothing below touches it.
   ⚠️ AND THE SELECTED RULE IS NOT OPTIONAL DRESSING. The location row has never had one, because
   the permanent tint was doing that job by accident; removing the tint without adding it would
   leave selection visible only on the checkbox, which is a regression on today. */
body.nifhr-screen .messenger-location-drawer .location-office-row {
  min-height: 76px;
  grid-template-columns: 24px 42px minmax(0, 1fr);
  gap: var(--space-sm);
  padding: var(--space-sm);
  border-color: var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

/* The file drawer's INTERACTIVE row rests on a 52%-brand hairline while its three muted siblings
   rest on plain `--border`. This row is that drawer's interactive row (it carries `is-target` too),
   so it takes the same hint rather than the muted one: measured against the leader, every property
   in the pair now matches. Without this the two drawers would agree on six values and differ on the
   seventh, which is the kind of near-match that reads as an accident. */
body.nifhr-screen .messenger-location-drawer .location-office-row.is-target {
  border-color: color-mix(in srgb, var(--brand) 52%, var(--border));
}

body.nifhr-screen .messenger-location-drawer .location-office-row:hover,
body.nifhr-screen .messenger-location-drawer .location-office-row:focus-visible,
body.nifhr-screen .messenger-location-drawer .location-office-row[data-watch-hover] {
  border-color: var(--brand);
  background: var(--brand-tint);
  /* Cancels the base rule's `0 12px 24px rgba(124,58,237,.10)`, which the file row's rollover has
     never had. The two drawers now share ONE rollover language, which is the ask. */
  box-shadow: none;
}

body.nifhr-screen .messenger-location-drawer .location-office-row.is-selected {
  border-color: color-mix(in srgb, var(--brand) 46%, var(--border));
}

/* ⚠️ ENTRY #272m: THE PULSE IS SCOPED TO `.is-target`, which is what `.document-file-row`'s own
   pulse rule four blocks down has always done. The location drawer's single row carries `is-target`
   so it is unaffected; the questionnaire drawer's three rows do not, and without this they would
   have lit THREE infinite purple pulses side by side the moment they gained a checkbox. This user
   has had a pulse removed from the composer (#272), the drawers (#272d), the questionnaire card
   (#272g), the two payoffs (#272h), the attachment control (#272k) and the channel pill (#272m);
   adding three back as a side effect of a tick box would be the same defect wearing new markup. */
body.nifhr-screen .location-office-row.is-target:not(.is-selected) .document-check {
  border-color: var(--brand);
  animation: documentTickPulse 1.15s var(--ease-standard) infinite;
}

body.nifhr-screen .location-office-row.is-selected .document-check {
  position: relative;
  border-color: var(--brand);
  background: var(--brand);
  animation: none;
}

body.nifhr-screen .location-office-row.is-selected .document-check::after {
  content: "";
  display: block;
  width: 5px;
  height: 10px;
  border-right: 2px solid var(--text-inverse);
  border-bottom: 2px solid var(--text-inverse);
  transform: translateY(-1px) rotate(45deg);
}

body.nifhr-screen .location-office-icon {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: var(--radius-lg);
  background: var(--brand-tint);
  color: var(--brand);
}

body.nifhr-screen .location-office-icon svg {
  width: 25px;
  height: 25px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

body.nifhr-screen .location-office-copy {
  display: grid;
  gap: 7px;
}

body.nifhr-screen .location-office-copy strong {
  color: var(--text-primary);
  font-size: var(--fs-lg);
  font-weight: var(--fw-850);
  line-height: 1.2;
}

body.nifhr-screen .location-office-copy span {
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: var(--fw-650);
  line-height: 1.35;
}

body.nifhr-screen .document-check {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border: 1.5px solid var(--border);
  border-radius: 7px;
  background: var(--surface);
  transition:
    border-color var(--motion-fast) var(--ease-standard),
    background var(--motion-fast) var(--ease-standard),
    transform var(--motion-fast) var(--ease-standard),
    box-shadow var(--motion-fast) var(--ease-standard);
}

/* ⚠️⚠️ THE PICKER'S PULSE IS SCOPED TO THE PICKER BEING OPEN, AND TWO PER-FRAME GUARDS CAUGHT WHY.
   `documentTickPulse` animates `box-shadow` SPREAD plus `transform`, infinitely. The library drawer
   has ONE pulsing row; the picker has FIVE, and unscoped they ran from page load for the whole film,
   whether the panel was on screen or not. Measured immediately: `#272K`'s per-frame sampler dropped
   from over 40 painted frames in its window to 33, and `#272R`'s questionnaire slide from over 20 to
   11. That is the same continuously-repainting-shadow cost #272s traced to three stacked
   `drop-shadow()`s, in a cheaper-looking form.
   Scoping it is also the correct behaviour, not just the cheap one: a pulse that says "tick me" is
   meaningless while the thing it is on is off screen. */
body.nifhr-screen .document-file-row.is-target:not(.is-selected) .document-check,
body.nifhr-screen.messenger-share-open .share-collab-row:not(.is-selected) .document-check {
  border-color: var(--brand);
  animation: documentTickPulse 1.15s var(--ease-standard) infinite;
}

/* ⚠️⚠️ ENTRY #274: `.share-collab-row` JOINS THESE TWO RULES RATHER THAN GETTING ITS OWN COPY, AND
   IT WENT RED FIRST FOR EXACTLY THIS REASON. The picker reuses `.document-check`, the house tick
   component, so the BOX appeared straight away and read as working; but the FILL and the checkmark
   live on selectors scoped to `.document-file-row` and `.location-office-row`, so a row could be
   `.is-selected`, `aria-pressed="true"` and tinted while its tick stayed empty and white. Measured:
   `backgroundColor rgb(255,255,255)` with `is-selected` true.
   Reusing a component means reusing its STATE selectors too, not just the element. A third copy of
   these declarations would have been the drift this project keeps outlawing. */
body.nifhr-screen .document-file-row.is-selected .document-check,
body.nifhr-screen .share-collab-row.is-selected .document-check {
  position: relative;
  border-color: var(--brand);
  background: var(--brand);
  animation: none;
}

body.nifhr-screen .document-file-row.is-selected .document-check::after,
body.nifhr-screen .share-collab-row.is-selected .document-check::after {
  content: "";
  display: block;
  width: 5px;
  height: 10px;
  border-right: 2px solid var(--text-inverse);
  border-bottom: 2px solid var(--text-inverse);
  transform: translateY(-1px) rotate(45deg);
}

@keyframes documentTickPulse {
  0%, 100% {
    transform: scale(1);
    box-shadow: 0 0 0 0 rgba(124, 58, 237, 0.22);
  }
  50% {
    transform: scale(1.18);
    box-shadow: 0 0 0 5px rgba(124, 58, 237, 0);
  }
}

body.nifhr-screen .document-file-copy strong {
  display: block;
  color: var(--text-primary);
  font-size: 13px;
  font-weight: var(--fw-extrabold);
  line-height: 1.25;
}

body.nifhr-screen .document-file-copy small {
  display: none;
  margin-top: var(--space-2xs);
  color: var(--text-secondary);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
}

body.nifhr-screen .document-drawer-foot {
  min-height: 86px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: var(--space-md) var(--space-lg) 22px;
  border-top: 1px solid var(--border);
  background: var(--surface);
}

body.nifhr-screen .document-drawer-foot span {
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: var(--fw-bold);
}

body.nifhr-screen .document-drawer-foot b {
  color: var(--success-text);
}

body.nifhr-screen .document-drawer-foot button {
  height: 44px;
  padding: 0 18px;
  border: 0;
  border-radius: 14px;
  background: var(--brand);
  color: var(--text-inverse);
  font-size: 13px;
  font-weight: var(--fw-extrabold);
  display: inline-flex;
  align-items: center;
  gap: 9px;
  transition:
    background var(--motion-ui) var(--ease-standard),
    color var(--motion-ui) var(--ease-standard),
    opacity var(--motion-fast) var(--ease-standard),
    transform var(--motion-fast) var(--ease-standard),
    box-shadow var(--motion-ui) var(--ease-standard);
}

body.nifhr-screen .document-drawer-foot button:disabled {
  background: color-mix(in srgb, var(--text-tertiary) 14%, var(--surface));
  color: var(--text-tertiary);
  cursor: not-allowed;
  opacity: 1;
}

body.nifhr-screen .document-drawer-foot button:not(:disabled) {
  background: var(--brand);
  color: var(--text-inverse);
  cursor: pointer;
  box-shadow: 0 10px 22px rgba(124, 58, 237, 0.24);
}

body.nifhr-screen .document-drawer-foot button:not(:disabled) span,
body.nifhr-screen .document-drawer-foot button:not(:disabled) svg {
  color: currentColor;
  stroke: currentColor;
}

body.nifhr-screen .document-drawer-foot button:not(:disabled):active {
  transform: translateY(1px);
}

body.nifhr-screen .document-drawer-foot button svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

body.nifhr-screen .pdf-drawer-head {
  justify-content: space-between;
  border-bottom: 1px solid var(--border);
}

body.nifhr-screen .pdf-drawer-head > div {
  min-width: 0;
}

body.nifhr-screen .pdf-drawer-head p {
  margin: 0 0 var(--space-2xs);
  color: var(--brand);
  font-size: var(--fs-xs);
  font-weight: var(--fw-extrabold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

body.nifhr-screen .pdf-drawer-head h2 {
  max-width: 330px;
  overflow: hidden;
  font-size: var(--fs-xl);
  text-overflow: ellipsis;
  white-space: nowrap;
}

body.nifhr-screen .pdf-preview-body {
  min-height: 0;
  overflow: auto;
  padding: 22px var(--space-lg) var(--space-xl);
  background: var(--bg-app);
}

body.nifhr-screen .pdf-page {
  position: relative;
  margin-bottom: var(--space-md);
  padding: 26px var(--space-lg) var(--space-lg);
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  box-shadow: 0 12px 26px rgba(17, 24, 39, 0.08);
}

body.nifhr-screen .pdf-page-number {
  position: absolute;
  right: 18px;
  top: 16px;
  color: var(--text-tertiary);
  font-size: var(--fs-xs);
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen .pdf-eyebrow {
  color: var(--brand);
  font-size: var(--fs-xs);
  font-weight: var(--fw-extrabold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

body.nifhr-screen .pdf-page h3 {
  margin-top: var(--space-xs);
  color: var(--text-primary);
  font-size: 22px;
  font-weight: var(--fw-extrabold);
  letter-spacing: -0.4px;
  line-height: 1.12;
}

body.nifhr-screen .pdf-lead,
body.nifhr-screen .pdf-page li,
body.nifhr-screen .pdf-details dd {
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: var(--fw-medium);
  line-height: 1.48;
}

body.nifhr-screen .pdf-details {
  display: grid;
  gap: 10px;
  margin-top: 18px;
}

body.nifhr-screen .pdf-details div {
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

body.nifhr-screen .pdf-details dt {
  color: var(--text-primary);
  font-size: var(--fs-xs);
  font-weight: var(--fw-extrabold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

body.nifhr-screen .pdf-page ul,
body.nifhr-screen .pdf-page ol {
  display: grid;
  gap: 10px;
  margin: 18px 0 0 18px;
}

body.nifhr-screen .messenger-composer label {
  height: 38px;
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 0 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--bg-app);
}

body.nifhr-screen .messenger-emoji {
  flex: 0 0 28px;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-tertiary);
  padding: 0;
}

body.nifhr-screen .messenger-emoji:hover {
  background: var(--brand-tint);
  color: var(--brand);
}

body.nifhr-screen .messenger-send {
  width: 40px !important;
  height: 40px !important;
  border-color: var(--brand) !important;
  background: var(--brand) !important;
  color: var(--text-inverse) !important;
}

body.nifhr-screen .messenger-guide-overlay[hidden] {
  display: none !important;
}

body.nifhr-screen .messenger-guide-overlay {
  position: fixed;
  inset: 0;
  z-index: 2147483000;
  display: none;
  pointer-events: none;
  --guide-left: 0px;
  --guide-top: 0px;
  --guide-width: 0px;
  --guide-height: 0px;
  --guide-callout-left: 596px;
  --guide-callout-bottom: 72px;
  --guide-tail-right: 28px;
}

body.nifhr-screen.messenger-guide-active .messenger-guide-overlay {
  display: block;
}

body.nifhr-screen .messenger-guide-mask {
  position: fixed;
  inset: 0;
  z-index: 2147483001;
  width: 100vw;
  height: 100vh;
  pointer-events: auto;
}

body.nifhr-screen .messenger-guide-mask rect[data-messenger-mask-panel] {
  fill: rgba(17, 24, 39, 0.58);
}

body.nifhr-screen .messenger-guide-focus {
  position: fixed;
  z-index: 2147483004;
  display: none;
  align-items: center;
  pointer-events: none;
  color: var(--brand);
  font-size: 13px;
  font-weight: var(--fw-bold);
  line-height: 1;
  background: var(--surface);
  border: 1px solid rgba(124, 58, 237, 0.42);
  box-shadow:
    0 0 0 3px rgba(124, 58, 237, 0.2),
    0 14px 34px rgba(17, 24, 39, 0.2);
}

body.nifhr-screen.messenger-guide-type .messenger-guide-focus,
body.nifhr-screen.messenger-guide-attach .messenger-guide-focus,
body.nifhr-screen.messenger-guide-compose .messenger-guide-focus {
  display: flex;
}

body.nifhr-screen .messenger-guide-focus.is-type {
  justify-content: center;
  border-radius: 14px;
}

body.nifhr-screen .messenger-guide-focus.is-attach {
  justify-content: center;
  border-radius: var(--radius-md);
}

body.nifhr-screen .messenger-guide-focus.is-compose {
  gap: 10px;
  padding: 0 var(--space-xs);
  border-radius: 18px;
}

body.nifhr-screen .messenger-guide-focus .focus-channel {
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 0 17px;
  border-radius: 14px;
  color: var(--brand);
  background: var(--brand-tint);
}

body.nifhr-screen .messenger-guide-focus .focus-icon,
body.nifhr-screen .messenger-guide-focus .focus-emoji,
body.nifhr-screen .messenger-guide-focus .focus-send {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
}

body.nifhr-screen .messenger-guide-focus .focus-icon {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-pill);
  color: var(--text-secondary);
  background: var(--surface);
}

body.nifhr-screen .messenger-guide-focus .focus-icon svg,
body.nifhr-screen .messenger-guide-focus .focus-emoji svg,
body.nifhr-screen .messenger-guide-focus .focus-send svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
}

body.nifhr-screen .messenger-guide-focus .focus-input {
  min-width: 0;
  flex: 1 1 auto;
  height: 40px;
  display: flex;
  align-items: center;
  padding: 0 14px;
  border-radius: var(--radius-pill);
  color: var(--text-tertiary);
  background: var(--bg-app);
}

body.nifhr-screen .messenger-guide-focus .focus-emoji {
  width: 28px;
  height: 28px;
  color: var(--text-tertiary);
}

body.nifhr-screen .messenger-guide-focus .focus-send {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-pill);
  color: var(--text-inverse);
  background: var(--brand);
}

body.nifhr-screen .messenger-guide-intro,
body.nifhr-screen .messenger-guide-callout {
  position: fixed;
  z-index: 2147483005;
  pointer-events: auto;
  color: var(--text-inverse);
  background: #111827;
  box-shadow: 0 28px 70px rgba(17, 24, 39, 0.38);
}

body.nifhr-screen .messenger-guide-intro {
  top: 50%;
  left: 50%;
  width: 520px;
  max-width: calc(100vw - 40px);
  transform: translate(-50%, -50%);
  border-radius: var(--radius-xl);
  padding: 30px;
}

body.nifhr-screen .messenger-guide-icon {
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  margin-bottom: 18px;
  border-radius: var(--radius-lg);
  background: var(--brand);
  color: var(--text-inverse);
}

body.nifhr-screen .messenger-guide-icon svg {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
}

body.nifhr-screen .messenger-guide-kicker {
  color: #C4B5FD;
  font-size: var(--fs-xs);
  font-weight: var(--fw-extrabold);
  line-height: 1;
  letter-spacing: 0.11em;
  text-transform: uppercase;
}

body.nifhr-screen .messenger-guide-intro h2 {
  margin-top: 10px;
  color: var(--text-inverse);
  font-size: 28px;
  font-weight: var(--fw-extrabold);
  line-height: 1.08;
  letter-spacing: -0.8px;
}

body.nifhr-screen .messenger-guide-intro p:not(.messenger-guide-kicker) {
  margin-top: 14px;
  color: rgba(255, 255, 255, 0.78);
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  line-height: 1.52;
}

body.nifhr-screen .messenger-guide-primary {
  height: 42px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-top: 22px;
  padding: 0 18px;
  border-radius: var(--radius-md);
  background: var(--brand);
  color: var(--text-inverse);
  font-size: var(--fs-md);
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen .messenger-guide-callout {
  width: 340px;
  border-radius: var(--radius-lg);
  padding: 18px;
}

body.nifhr-screen .messenger-guide-callout::after {
  content: "";
  position: absolute;
  right: 28px;
  bottom: -8px;
  width: 16px;
  height: 16px;
  background: #111827;
  transform: rotate(45deg);
}

body.nifhr-screen .messenger-guide-callout h3 {
  margin-top: var(--space-xs);
  color: var(--text-inverse);
  font-size: var(--fs-2xl);
  font-weight: var(--fw-extrabold);
  line-height: var(--lh-tight);
  letter-spacing: -0.3px;
}

body.nifhr-screen .messenger-guide-callout p:not(.messenger-guide-kicker) {
  margin-top: var(--space-xs);
  color: rgba(255, 255, 255, 0.74);
  font-size: 13px;
  font-weight: var(--fw-medium);
  line-height: 1.42;
}

body.nifhr-screen .messenger-guide-callout[aria-hidden="true"],
body.nifhr-screen .messenger-guide-intro[aria-hidden="true"] {
  display: none;
}

body.nifhr-screen.messenger-guide-active .sidebar,
body.nifhr-screen.messenger-guide-active .topbar {
  pointer-events: none;
}

body.nifhr-screen.messenger-guide-active .messenger-content {
  opacity: 1 !important;
  filter: none !important;
  pointer-events: none;
}

body.nifhr-screen.messenger-guide-type .messenger-list,
body.nifhr-screen.messenger-guide-type .messenger-chat,
body.nifhr-screen.messenger-guide-attach .messenger-list,
body.nifhr-screen.messenger-guide-attach .messenger-chat,
body.nifhr-screen.messenger-guide-compose .messenger-list,
body.nifhr-screen.messenger-guide-compose .messenger-chat {
  opacity: 1 !important;
  filter: none !important;
}

body.nifhr-screen.messenger-guide-type .messenger-guide-callout,
body.nifhr-screen.messenger-guide-attach .messenger-guide-callout,
body.nifhr-screen.messenger-guide-compose .messenger-guide-callout {
  left: var(--guide-callout-left);
  bottom: var(--guide-callout-bottom);
}

body.nifhr-screen.messenger-guide-compose .messenger-guide-callout {
  width: 380px;
}

body.nifhr-screen.messenger-guide-type .messenger-guide-callout::after,
body.nifhr-screen.messenger-guide-attach .messenger-guide-callout::after,
body.nifhr-screen.messenger-guide-compose .messenger-guide-callout::after {
  right: var(--guide-tail-right);
}

body.nifhr-screen.messenger-demo-live .messenger-guide-overlay {
  display: block !important;
  visibility: visible !important;
  opacity: 1 !important;
  pointer-events: none !important;
}

body.nifhr-screen.messenger-demo-live .messenger-guide-mask {
  pointer-events: none !important;
}

body.nifhr-screen.messenger-demo-live .sidebar,
body.nifhr-screen.messenger-demo-live .topbar {
  opacity: 0.32;
  filter: grayscale(1);
  pointer-events: none;
}

body.nifhr-screen.messenger-demo-live .messenger-content {
  position: relative;
  z-index: 2147483002;
  opacity: 1;
  filter: none;
  pointer-events: auto;
  box-shadow: none;
  border-radius: 0;
}

body.nifhr-screen.messenger-demo-live .messenger-guide-focus,
body.nifhr-screen.messenger-demo-live .messenger-guide-intro,
body.nifhr-screen.messenger-demo-live .messenger-guide-callout {
  display: none !important;
}

@keyframes nifhr-messenger-in {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

body.nifhr-screen .nifhr-stage {
  height: 100%;
  min-height: 0;
}

body.nifhr-screen .nifhr-stage::before {
  position: absolute;
  inset: 0;
  width: auto;
  height: auto;
}

body.nifhr-screen .nifhr-board-shell {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  padding-bottom: 0;
}

body.nifhr-screen .nifhr-candidate .candidate-top {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr) 38px;
  align-items: center;
}

body.nifhr-screen .nifhr-candidate .match-score {
  align-self: center;
}

/* nIfHr final board correction: keep the Pencil overlay/coach,
   but return the kanban and cards to the locked x1RIQT parsing scale. */
body.nifhr-screen .nifhr-board-shell {
  width: 856px;
  height: calc(100% - 184px);
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-sm);
  align-content: stretch;
}

body.nifhr-screen .nifhr-column {
  min-height: 0;
  height: 100%;
  padding: 14px;
  gap: 10px;
  overflow: hidden;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--bg-subtle);
}

body.nifhr-screen .nifhr-card-list {
  min-height: 0;
  gap: 7px;
  overflow: hidden;
}

body.nifhr-screen .nifhr-candidate.candidate-card {
  min-height: auto;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 5px;
  padding: 10px 11px;
  border-radius: var(--radius-md);
  font-size: var(--fs-md);
  border: 1px solid var(--border);
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(17, 24, 39, 0.04);
}

body.nifhr-screen .nifhr-candidate .candidate-top {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr) 38px;
  gap: 10px;
  align-items: center;
}

body.nifhr-screen .nifhr-candidate .candidate-avatar {
  width: 36px;
  height: 36px;
  font-size: var(--fs-md);
}

body.nifhr-screen .nifhr-candidate .candidate-name-lines strong {
  font-size: var(--fs-md);
  line-height: 1.14;
}

body.nifhr-screen .nifhr-candidate .candidate-name-lines small {
  display: block;
  margin-top: 2px;
  color: var(--text-secondary);
  font-size: var(--fs-md);
  line-height: 1.14;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen .nifhr-candidate .match-score {
  width: 38px;
  height: 38px;
  justify-self: end;
  align-self: center;
  font-size: var(--fs-md);
}

body.nifhr-screen .nifhr-candidate .candidate-tags {
  gap: 5px;
  margin-left: 46px;
}

body.nifhr-screen .nifhr-candidate .candidate-tags span {
  padding: var(--space-2xs) var(--space-xs);
  font-size: var(--fs-md);
  line-height: 1.12;
}

body.nifhr-screen .nifhr-candidate .candidate-mini,
body.nifhr-screen .nifhr-candidate .nifhr-warning {
  display: none;
}

body.nifhr-screen .nifhr-candidate.focus-card,
body.nifhr-screen .nifhr-candidate.source-selected {
  position: relative;
  z-index: 1;
  border-color: var(--success);
  border-width: 1.5px;
  background: var(--surface);
  box-shadow:
    0 0 0 4px rgba(34, 197, 94, 0.18),
    0 22px 58px rgba(17, 24, 39, 0.16);
  transform: none;
}

/* nIfHr must use the same kanban/card asset contract as the x1RIQT parsing board. */
body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-board-shell.import-board {
  position: relative;
  z-index: 9020;
  width: 1152px;
  height: 569px;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-md);
  align-content: stretch;
  margin-top: var(--space-md);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-column.kanban-col {
  position: relative;
  min-height: 0;
  height: 100%;
  padding: var(--space-md);
  gap: 10px;
  overflow: hidden;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--bg-subtle);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-column.new-col {
  z-index: 9021;
  background: var(--surface);
  box-shadow: 0 22px 58px rgba(17, 24, 39, 0.16);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-card-list.candidate-stream {
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 7px;
  overflow: hidden;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-candidate.candidate-card {
  width: 100%;
  min-height: auto;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 5px;
  padding: 10px 11px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text-primary);
  box-shadow: 0 1px 2px rgba(17, 24, 39, 0.04);
  cursor: grab;
  touch-action: none;
  font-size: var(--fs-md);
  text-decoration: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-candidate .candidate-top {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr) 38px;
  gap: 10px;
  align-items: center;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-candidate .candidate-avatar {
  width: 36px;
  height: 36px;
  font-size: var(--fs-md);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-candidate .candidate-name-lines strong {
  font-size: var(--fs-md);
  line-height: 1.14;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-candidate .match-score {
  width: 38px;
  height: 38px;
  justify-self: end;
  align-self: center;
  font-size: var(--fs-md);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-candidate .candidate-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-left: 46px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-candidate .candidate-tags span {
  padding: var(--space-2xs) var(--space-xs);
  border-radius: var(--radius-pill);
  background: var(--brand-tint);
  color: var(--brand-hover);
  font-size: var(--fs-md);
  line-height: 1.12;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-candidate.source-selected {
  border-color: var(--success);
  border-width: 1.5px;
  box-shadow: 0 0 0 4px rgba(34, 197, 94, 0.18), 0 22px 58px rgba(17, 24, 39, 0.16);
}

body.nifhr-screen [data-screen-id="nIfHr"] .review-col,
body.nifhr-screen [data-screen-id="nIfHr"] .shortlist-col {
  opacity: .42;
  filter: grayscale(.35);
}

body.nifhr-screen [data-screen-id="nIfHr"] .col-drag-placeholder,
body.nifhr-screen [data-screen-id="nIfHr"] .shortlist-ghost {
  min-height: clamp(84px, 12vh, 112px);
}

/* o8QGBe kanban asset contract for nIfHr.
   Source geometry: board 1152x814, columns 373.333px, cards 349.333px inner. */
body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-board-shell.o8-kanban {
  position: relative;
  z-index: 9020;
  width: 1152px;
  height: 814px;
  min-height: 814px;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-md);
  align-content: stretch;
  margin-top: var(--space-md);
  padding: 0;
  overflow: visible;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-column {
  width: 100%;
  height: 814px;
  min-height: 814px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: var(--space-sm);
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-subtle);
  box-shadow: none;
  filter: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-column.new-col {
  z-index: 9021;
  background: var(--bg-subtle);
  box-shadow: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-column.review-col,
body.nifhr-screen [data-screen-id="nIfHr"] .o8-column.shortlist-col {
  opacity: 1;
  filter: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-column .nifhr-column-head {
  height: 17px;
  min-height: 17px;
  padding: 0;
  gap: var(--space-xs);
  flex: 0 0 17px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-column .nifhr-column-head span {
  gap: var(--space-xs);
  color: var(--text-primary);
  font-size: var(--fs-md);
  line-height: 17px;
  font-weight: var(--fw-bold);
  letter-spacing: .5px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-column .nifhr-column-head b {
  min-width: 0;
  height: auto;
  padding: 1px var(--space-xs);
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-strong);
  font-size: var(--fs-xs);
  line-height: 15px;
  font-weight: var(--fw-bold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-column .col-dot {
  width: 8px;
  height: 8px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-column .nifhr-card-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-height: 0;
  overflow: hidden;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card {
  width: 100%;
  flex: 0 0 auto;
  display: flex;
  align-items: stretch;
  flex-direction: column;
  gap: var(--space-xs);
  padding: var(--space-sm);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text-primary);
  box-shadow: none;
  cursor: grab;
  text-decoration: none;
  touch-action: none;
  transform: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card-final {
  height: 109px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card-final.source-selected {
  border-color: var(--success);
  border-width: 1.5px;
  box-shadow: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card-skeleton {
  height: 74px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-file-row {
  height: 42px;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-warning-row {
  height: 52px;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs);
  border-color: var(--warning);
  background: var(--warning-bg);
  color: var(--warning-text);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .candidate-top {
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .candidate-avatar {
  width: 28px;
  height: 28px;
  flex: 0 0 28px;
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .candidate-avatar.muted {
  background: var(--bg-subtle);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .candidate-name-lines {
  min-width: 0;
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: 0;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .candidate-name-lines strong {
  color: var(--text-primary);
  font-size: var(--fs-sm);
  line-height: 15px;
  font-weight: var(--fw-bold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .candidate-name-lines small {
  display: block;
  color: var(--text-secondary);
  font-size: var(--fs-2xs);
  line-height: 13px;
  font-weight: var(--fw-regular);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .match-score {
  width: 34px;
  height: 34px;
  flex: 0 0 34px;
  justify-self: auto;
  align-self: center;
  border-radius: var(--radius-pill);
  background: var(--success-bg);
  color: var(--success-text);
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .candidate-tags,
body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .skeleton-tags {
  height: 23px;
  display: flex;
  flex-wrap: nowrap;
  gap: var(--space-2xs);
  margin-left: 0;
  overflow: hidden;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .candidate-tags span {
  padding: var(--space-2xs) 10px;
  border-radius: var(--radius-pill);
  background: var(--brand-tint);
  color: var(--brand-hover);
  font-size: var(--fs-sm);
  line-height: 15px;
  font-weight: var(--fw-medium);
  white-space: nowrap;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .candidate-foot {
  height: 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--text-tertiary);
  font-size: var(--fs-2xs);
  line-height: 12px;
  font-weight: var(--fw-regular);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .ai-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  flex: 0 0 auto;
  padding: 3px var(--space-xs);
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: var(--text-inverse);
  font-size: var(--fs-xs);
  line-height: 17px;
  font-weight: var(--fw-bold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .skeleton-line,
body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .skeleton-tags i {
  display: block;
  border-radius: var(--radius-pill);
  background: var(--bg-subtle);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .skeleton-line {
  height: 8px;
  margin-bottom: var(--space-2xs);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .skeleton-line:last-child {
  height: 6px;
  margin-bottom: 0;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .w50 { width: 50px; }
body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .w60 { width: 60px; }
body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .w70 { width: 70px; }
body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .w80 { width: 80px; }
body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .w90 { width: 90px; }
body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .w120 { width: 120px; }
body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .w140 { width: 140px; }

body.nifhr-screen [data-screen-id="nIfHr"] .o8-card .skeleton-tags i {
  height: 14px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-file-row .candidate-file {
  min-width: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  color: var(--text-strong);
  font-size: var(--fs-sm);
  line-height: 16px;
  font-weight: var(--fw-medium);
  white-space: nowrap;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-file-row .candidate-file svg {
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
  color: var(--text-tertiary);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-file-row .candidate-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  padding: 3px var(--space-xs);
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--bg-subtle);
  color: var(--text-secondary);
  font-size: var(--fs-2xs);
  line-height: 12px;
  font-weight: var(--fw-medium);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-file-row .candidate-pill.uploaded {
  border: 1px solid var(--border);
  color: var(--text-tertiary);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-warning-row .warning-copy {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-warning-row .warning-copy b,
body.nifhr-screen [data-screen-id="nIfHr"] .o8-warning-row .warning-refresh {
  color: var(--warning-text);
  font-size: var(--fs-md);
  line-height: 14px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-warning-row .warning-copy span {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-warning-row strong {
  color: var(--warning-text);
  font-size: var(--fs-sm);
  line-height: 15px;
  font-weight: var(--fw-bold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-warning-row small {
  color: var(--warning-text);
  font-size: var(--fs-2xs);
  line-height: 12px;
  font-weight: var(--fw-regular);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-column .col-drag-placeholder {
  height: 104px;
  min-height: 104px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 6px;
  border: 1.5px solid var(--brand);
  border-radius: var(--radius-md);
  background: var(--brand-tint);
  color: var(--brand-hover);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-column .col-drag-placeholder span {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--brand);
  font-size: var(--fs-xl);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-column .shortlist-ghost {
  height: 140px;
  min-height: 140px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 6px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  background: transparent;
  text-align: center;
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-column .shortlist-ghost span {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text-tertiary);
  font-size: var(--fs-xl);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-column .shortlist-ghost strong {
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  line-height: 15px;
  font-weight: var(--fw-medium);
}

body.nifhr-screen [data-screen-id="nIfHr"] .o8-column .shortlist-ghost small {
  color: var(--text-tertiary);
  font-size: var(--fs-2xs);
  line-height: 12px;
  font-weight: var(--fw-regular);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-coach {
  top: 548px;
  left: 800px;
}

/* nIfHr live-board contract: mirror the actual x1RIQT parsing implementation.
   The drag/drop screen keeps only data-* hooks; kanban/card visuals come from
   the same class family as x1riqt.html's generated parsed candidates. */
body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board {
  position: relative;
  z-index: 9020;
  width: 100%;
  height: calc(100% - 184px);
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-sm);
  align-content: stretch;
  margin-top: var(--space-sm);
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .kanban-col {
  min-height: 0;
  height: 100%;
  max-height: 100%;
  padding: 14px;
  gap: 10px;
  overflow: hidden;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--bg-subtle);
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .new-col {
  position: relative;
  z-index: 9021;
  background: var(--surface);
  box-shadow: 0 22px 58px rgba(17, 24, 39, 0.16);
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .review-col,
body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .shortlist-col {
  opacity: .42;
  filter: grayscale(.35);
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .candidate-stream {
  display: flex;
  flex-direction: column;
  min-height: 0;
  gap: 7px;
  overflow: hidden;
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .candidate-card {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 5px;
  padding: 9px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text-primary);
  box-shadow: 0 1px 2px rgba(17, 24, 39, 0.03);
  cursor: grab;
  font-size: 13px;
  text-decoration: none;
  touch-action: none;
  transition:
    border-color 360ms ease,
    box-shadow 360ms ease,
    transform 360ms ease,
    background 360ms ease;
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .candidate-final {
  gap: 5px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .candidate-top {
  display: grid;
  grid-template-columns: 32px minmax(0, 1fr) 34px;
  gap: var(--space-xs);
  align-items: center;
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .candidate-avatar {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  border-radius: var(--radius-pill);
  background: var(--avatar, var(--brand));
  color: var(--text-inverse);
  font-size: 13px;
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .candidate-avatar.warn {
  color: var(--warning-text);
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .candidate-name-lines {
  min-width: 0;
  flex: 1;
  display: flex;
  flex-direction: column;
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .candidate-name-lines strong {
  color: var(--text-primary);
  font-size: 13px;
  line-height: 1.14;
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .match-score {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  justify-self: end;
  border-radius: var(--radius-pill);
  background: var(--success-bg);
  color: var(--success-text);
  font-size: 13px;
  font-weight: var(--fw-bold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .match-score.warn {
  background: var(--warning-bg);
  color: var(--warning-text);
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .candidate-final:first-child,
body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .source-selected {
  border-color: var(--success);
  border-width: 1.5px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .col-drag-placeholder,
body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .shortlist-ghost {
  min-height: clamp(84px, 12vh, 112px);
}

/* nIfHr curated walkthrough focus layer.
   Keep the dimmer global, then explicitly lift only Marta's card, the
   tour card, and the profile drawer. The drop-more-resumes strip stays
   dimmed on this state. */
body.nifhr-screen::before {
  z-index: 9000;
  background: rgba(17, 24, 39, 0.58);
  pointer-events: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-stage {
  position: relative;
  isolation: auto;
  --nifhr-board-gap: 12px;
  --nifhr-col-pad: 14px;
  --nifhr-coach-gap: 24px;
  --nifhr-first-card-right: calc(((100% - (2 * var(--nifhr-board-gap))) / 3) - var(--nifhr-col-pad));
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-drop-strip {
  position: relative;
  z-index: auto;
  width: min(1152px, 100%);
  height: 37px;
  min-height: 37px;
  margin-top: 10px;
  padding: 0 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--surface);
  box-shadow: 0 18px 38px rgba(17, 24, 39, 0.14);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-drop-strip .drop-icon {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border: 1.5px solid var(--brand);
  border-radius: var(--radius-pill);
  color: var(--brand);
  font-size: var(--fs-md);
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-drop-strip strong {
  color: var(--brand-hover);
  font-size: var(--fs-md);
  line-height: 1;
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-drop-strip small {
  color: var(--text-tertiary);
  font-size: var(--fs-sm);
  line-height: 1;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board {
  z-index: auto;
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .new-col {
  z-index: auto;
  box-shadow: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .candidate-final:first-child,
body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board .source-selected {
  position: relative;
  z-index: 9034;
  border-color: var(--success);
  border-width: 1.5px;
  background: var(--surface);
  box-shadow:
    0 0 0 3px rgba(16, 185, 129, 0.16),
    0 18px 42px rgba(17, 24, 39, 0.20);
  transform: translateZ(0);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-coach {
  z-index: 9036;
  top: 174px;
  left: calc(var(--nifhr-first-card-right) + var(--nifhr-coach-gap));
  width: min(420px, 34vw);
  min-height: 244px;
  padding: 26px;
  border: 1px solid rgba(148, 163, 184, 0.14);
  border-radius: 14px;
  background:
    radial-gradient(circle at 18% 14%, rgba(124, 58, 237, 0.28), rgba(124, 58, 237, 0) 34%),
    #0f1726;
  color: #f8fafc;
  box-shadow:
    0 28px 72px rgba(15, 23, 42, 0.42),
    0 0 0 1px rgba(124, 58, 237, 0.16);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-coach h2 {
  max-width: 360px;
  color: #f8fafc;
  font-size: 28px;
  line-height: 32px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-coach p {
  max-width: 350px;
  color: #cbd5e1;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer {
  position: fixed;
  top: 0;
  right: 0;
  z-index: 9040;
  width: 33vw;
  min-width: 460px;
  max-width: none;
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  border-left: 1px solid var(--border);
  background: var(--surface);
  box-shadow: -24px 0 54px rgba(17, 24, 39, 0.24);
  transform: translateX(104%);
  opacity: 0;
  pointer-events: none;
  transition:
    transform 260ms cubic-bezier(.22, 1, .36, 1),
    opacity 180ms ease;
}

body.nifhr-screen.profile-drawer-open [data-screen-id="nIfHr"] .nifhr-profile-drawer {
  transform: translateX(0);
  opacity: 1;
  pointer-events: auto;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .drawer-body {
  overflow: auto;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .profile-avatar {
  background: var(--cand-marta);
}

/* nIfHr Marta profile drawer mirrors Pencil node nHsq0. */
body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer {
  inset: 0 0 0 auto;
  width: 33vw;
  min-width: 0;
  max-width: none;
  height: 100vh;
  height: 100dvh;
  max-height: none;
  background: var(--surface);
  color: var(--text-primary);
  border-left: 1px solid var(--border);
  box-shadow: -18px 0 42px rgba(17, 24, 39, 0.18);
  font-family: inherit;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .nhsq0-head {
  min-height: 76px;
  height: 76px;
  padding: 0 var(--space-xl);
  border-bottom: 0;
  align-items: center;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .nhsq0-head h2 {
  color: var(--text-primary);
  font-size: var(--fs-lg);
  line-height: 22px;
  font-weight: var(--fw-bold);
  letter-spacing: 0;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-close {
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text-tertiary);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .nhsq0-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 28px var(--space-xl) 112px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-hero {
  width: 100%;
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-md);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-avatar {
  /* ENTRY #74 — was hard `var(--brand)`, so Marta was brand-purple here and blue on
     her own card two inches away. Candidate identity ALWAYS comes from --cand-*. */
  width: 56px;
  height: 56px;
  margin-top: 2px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--cand-marta);
  color: var(--text-inverse);
  font-size: var(--fs-xl);
  line-height: 1;
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-identity h3 {
  margin: 0;
  color: var(--text-primary);
  font-size: 28px;
  line-height: 34px;
  font-weight: var(--fw-extrabold);
  letter-spacing: -0.02em;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-identity p {
  margin: 1px 0 0;
  color: var(--text-strong);
  font-size: var(--fs-lg);
  line-height: 22px;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-identity span {
  display: block;
  margin-top: 2px;
  color: var(--text-tertiary);
  font-size: var(--fs-md);
  line-height: 20px;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-score {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2xs);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-score strong {
  width: 56px;
  height: 56px;
  display: grid;
  place-items: center;
  border: 2px solid var(--success);
  border-radius: var(--radius-pill);
  color: var(--success-text);
  background: var(--success-bg);
  font-size: var(--fs-md);
  font-weight: var(--fw-extrabold);
}

/* ENTRY #215: the drawer chip finally takes the derived tone every other score badge already had.
   `hirello-config-apply.js` toggles `.warn` on the CHILD `<strong>` (the wrapper's own text is
   "92%MATCH" and cannot match the walker's bare-number test), so the rule lives on `strong.warn`.
   ⚠️ UNLIKE THE PILL BADGES, THIS ONE HAS A BORDER. `.match-score.warn` only swaps background and
   colour because those badges have no ring; this chip carries `border: 2px solid var(--success)`,
   so a copy of that rule would leave a GREEN ring around an amber fill. The border colour moves too.
   ⚠️ `--warning` is NOT a :root token here (it is page-scoped to `.ns-*` at the tail of this file),
   so the ring uses `--warning-text`, which is the darker amber and the correct analogue. */
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-score strong.warn {
  border-color: var(--warning-text);
  color: var(--warning-text);
  background: var(--warning-bg);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-score span {
  color: var(--text-secondary);
  font-size: var(--fs-xs);
  line-height: 14px;
  font-weight: var(--fw-bold);
  letter-spacing: 0.6px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-contact {
  width: 100%;
  margin-top: 28px;
  display: grid;
  gap: var(--space-sm);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-contact p,
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-certifications p {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text-strong);
  font-size: var(--fs-lg);
  line-height: 22px;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-contact span,
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-certifications span {
  width: 16px;
  color: var(--success);
  text-align: center;
  font-size: 13px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-rule {
  width: 100%;
  height: 1px;
  margin: 28px 0;
  border: 0;
  background: var(--border);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-section {
  width: 100%;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-section h4 {
  margin: 0 0 var(--space-md);
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  line-height: 17px;
  font-weight: var(--fw-extrabold);
  letter-spacing: 0.8px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-role {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: 14px;
  row-gap: 6px;
  padding-bottom: var(--space-md);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-role + .nhsq0-role {
  padding-top: var(--space-2xs);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-role strong {
  display: block;
  color: var(--text-primary);
  font-size: var(--fs-lg);
  line-height: 22px;
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-role span {
  display: block;
  margin-top: 2px;
  color: var(--text-secondary);
  font-size: var(--fs-md);
  line-height: 20px;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-role time {
  color: var(--text-tertiary);
  font-size: 13px;
  line-height: 20px;
  font-weight: var(--fw-semibold);
  white-space: nowrap;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-role p {
  grid-column: 1 / -1;
  margin: 0;
  color: var(--text-muted);
  font-size: 13px;
  line-height: 20px;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-chip-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-chip-grid span {
  min-height: 34px;
  display: inline-flex;
  align-items: center;
  padding: 7px 14px;
  border-radius: var(--radius-pill);
  background: var(--bg-subtle);
  color: var(--text-strong);
  font-size: 13px;
  line-height: 18px;
  font-weight: var(--fw-bold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-actions {
  /* ENTRY #74 — "View original upload" deleted; one 46px button + 22px padding. */
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  /* ENTRY #165: behind a var because the #165 guide card must sit ABOVE this footer, which makes
     this the third site for the same number. #74's --nhsq0-head-h exists for the same reason. */
  height: var(--nhsq0-actions-h, 90px);
  padding: 22px var(--space-xl);
  display: grid;
  gap: 10px;
  align-content: start;
  background: var(--surface);
  border-top: 1px solid var(--border);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-primary {
  width: 100%;
  height: 46px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  font-size: var(--fs-lg);
  line-height: 22px;
  font-weight: var(--fw-extrabold);
  text-decoration: none;
  /* ENTRY #114 (user call): "Message Marta" is LIVE. It was MD3-inactive
     (opacity .39 / pointer-events none) and wired to nothing; it is now the drawer's
     only exit, opening the `.nhsq0-msg-sheet` below. */
  cursor: pointer;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-primary {
  border: 0;
  background: var(--brand);
  color: var(--text-inverse);
  box-shadow: none;
}

body.nifhr-screen.profile-drawer-open [data-screen-id="nIfHr"] .nifhr-coach {
  top: auto;
  left: auto;
  right: calc(33vw + 24px);
  bottom: 76px;
  width: min(520px, calc(67vw - 340px));
  min-height: 300px;
  padding: 34px 36px;
}

/* nIfHr next walkthrough step: drawer closes, three candidate cards become
   selectable drag targets, and Maybe becomes the only active drop zone. */
body.nifhr-screen.candidate-move-step::before {
  z-index: 9000;
  background: rgba(17, 24, 39, 0.62);
  /* #83 (user call): CAPTURE taps on the dimmed area so the burger + all app-shell chrome under
     this dim are INERT. Every intended-interactive element is already raised above z9000 (source
     col 9042 / target 9044 / cards 9050-9065 / coach 9200 / FAB 2147483030), so they stay tappable;
     the drag engine uses pointer CAPTURE + rect math (not elementFromPoint), so a curated drag is
     unaffected. A dim with no handler simply absorbs the tap. Desktop: burger is display:none there
     anyway; this only tightens the already-intended spotlight. */
  pointer-events: auto;
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .nifhr-profile-drawer {
  transform: translateX(104%);
  opacity: 0;
  pointer-events: none;
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board {
  position: relative;
  z-index: auto;
  overflow: visible;
}

body.nifhr-screen.candidate-move-step[data-candidate-move-source="reviewing"][data-candidate-move-target="shortlist"] [data-screen-id="nIfHr"] .kanban.import-board {
  padding-right: 10px;
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board .new-col,
body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board .is-curated-target {
  position: relative;
  z-index: auto;
}

body.nifhr-screen.candidate-move-step[data-candidate-move-source="new"] [data-screen-id="nIfHr"] .kanban.import-board .new-col {
  border-color: rgba(124, 58, 237, 0.28);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.92), rgba(249, 247, 255, 0.92)),
    var(--bg-row-highlight);
  overflow: visible;
  padding-left: 14px;
}

body.nifhr-screen.candidate-move-step[data-candidate-move-source="reviewing"] [data-screen-id="nIfHr"] .kanban.import-board .review-col {
  position: relative;
  z-index: 9042;
  align-self: start;
  height: fit-content;
  max-height: none;
  opacity: 1;
  filter: none;
  overflow: visible;
  transition:
    opacity 180ms ease,
    filter 180ms ease,
    background 180ms ease,
    border-color 180ms ease,
    box-shadow 180ms ease;
}

body.nifhr-screen.candidate-move-step[data-candidate-move-source="reviewing"].candidate-move-source-complete [data-screen-id="nIfHr"] .kanban.import-board .review-col {
  pointer-events: none;
  z-index: auto;
  border-color: rgba(229, 231, 235, 0.46);
  background: var(--bg-card);
  box-shadow: none;
  filter: grayscale(0.22);
  opacity: 1;
}

body.nifhr-screen.candidate-move-step[data-candidate-move-source="reviewing"].candidate-move-source-complete [data-screen-id="nIfHr"] .kanban.import-board .review-col [data-move-disabled] {
  opacity: 1;
}

body.nifhr-screen.candidate-move-step[data-candidate-move-source="reviewing"].candidate-move-source-complete [data-screen-id="nIfHr"] .kanban.import-board .review-col .kanban-title b {
  opacity: 0.46;
  color: var(--text-primary);
  border-color: var(--border);
  background: var(--bg-card);
  box-shadow: none;
}

body.nifhr-screen.candidate-move-step[data-candidate-move-source="reviewing"].candidate-move-source-complete [data-screen-id="nIfHr"] .kanban.import-board .review-col [data-candidate-card] {
  pointer-events: none;
  z-index: auto;
  border-color: var(--border) !important;
  border-width: var(--border-w) !important;
  background: var(--bg-card) !important;
  box-shadow: none !important;
  filter: grayscale(0.18);
  opacity: 0.46 !important;
  cursor: default;
}

body.nifhr-screen.candidate-move-step[data-candidate-move-source="reviewing"].candidate-move-source-complete [data-screen-id="nIfHr"] .kanban.import-board .review-col [data-candidate-card] .candidate-score {
  border-color: inherit !important;
  background: inherit !important;
  color: inherit !important;
  box-shadow: none !important;
}

body.nifhr-screen.candidate-move-step[data-candidate-move-source="reviewing"].candidate-move-source-complete [data-screen-id="nIfHr"] .kanban.import-board .review-col [data-candidate-card]::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  background: rgba(17, 24, 39, 0.10);
  pointer-events: none;
}

body.nifhr-screen.candidate-move-step[data-candidate-move-source="reviewing"] [data-screen-id="nIfHr"] .kanban.import-board .new-col {
  z-index: auto;
  border-color: var(--border);
  background: var(--bg-card);
  box-shadow: none;
  filter: grayscale(0.2);
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board .is-curated-target {
  z-index: 9044;
  opacity: 1;
  filter: none;
  border: 2px dashed var(--brand);
  background: var(--col-applied-surface);
  box-shadow:
    0 0 0 4px rgba(255, 255, 255, 0.86),
    0 22px 54px rgba(124, 58, 237, 0.24);
  isolation: isolate;
  overflow: visible;
  transition:
    height 560ms cubic-bezier(.2, .9, .16, 1),
    max-height 560ms cubic-bezier(.2, .9, .16, 1),
    box-shadow 560ms ease,
    background 560ms ease,
    border-color 560ms ease;
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board .is-curated-target *,
body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board .is-curated-target .candidate-stream,
body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board .is-curated-target .col-drag-placeholder {
  opacity: 1;
  filter: none;
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board .is-curated-target.is-over {
  border-color: var(--brand);
  background: var(--col-applied-surface);
  box-shadow:
    0 0 0 4px rgba(255, 255, 255, 0.86),
    0 22px 54px rgba(124, 58, 237, 0.24);
  transform: none;
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board .is-curated-target.is-over .kanban-title {
  color: var(--text-primary);
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board [data-select-target] {
  position: relative;
  z-index: 9050;
  isolation: isolate;
  overflow: visible;
  width: 100%;
  margin-left: 0;
  border-color: var(--success);
  border-width: 1.5px;
  background: var(--surface);
  box-shadow:
    0 0 0 3px rgba(16, 185, 129, 0.16),
    0 18px 42px rgba(17, 24, 39, 0.20);
}

body.nifhr-screen.candidate-move-step[data-candidate-move-source="new"] [data-screen-id="nIfHr"] .kanban.import-board .new-col .candidate-stream,
body.nifhr-screen.candidate-move-step[data-candidate-move-source="reviewing"] [data-screen-id="nIfHr"] .kanban.import-board .review-col .candidate-stream {
  overflow: visible;
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board .kanban-col,
body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board .candidate-stream,
body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board .candidate-card {
  overflow: visible;
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .candidate-select-layer {
  position: fixed;
  inset: 0;
  z-index: 9065;
  pointer-events: none;
  overflow: visible;
}

body.nifhr-screen .candidate-select-layer {
  display: none;
}

body.nifhr-screen.candidate-move-step .candidate-select-layer {
  display: block;
  position: fixed;
  inset: 0;
  z-index: 9065;
  overflow: visible;
  pointer-events: none;
  contain: none;
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board [data-select-target][data-selected="true"] {
  border-color: var(--brand);
  border-width: var(--border-w-strong);
  box-shadow:
    0 0 0 1px rgba(124, 58, 237, 0.95),
    0 0 0 5px rgba(124, 58, 237, 0.13),
    0 18px 42px rgba(76, 29, 149, 0.22);
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board [data-move-disabled] {
  pointer-events: none;
  border-color: var(--border);
  border-width: var(--border-w);
  background: var(--bg-card);
  box-shadow: none;
  filter: none;
  opacity: 0.39;
  cursor: default;
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board [data-move-disabled][data-selected="true"] {
  border-color: var(--border);
  border-width: var(--border-w);
  box-shadow: none;
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board [data-candidate-card] {
  touch-action: none;
  user-select: none;
  -webkit-user-drag: none;
  cursor: default;
}

body.nifhr-screen.candidate-move-step[data-candidate-move-source="reviewing"] [data-screen-id="nIfHr"] .kanban.import-board .new-col [data-candidate-card] {
  pointer-events: none;
  z-index: auto;
  border-color: var(--border);
  border-width: var(--border-w);
  background: var(--bg-card);
  box-shadow: none;
  filter: grayscale(0.22);
  opacity: 0.46;
  cursor: default;
}

body.nifhr-screen.candidate-move-step[data-candidate-move-source="reviewing"] [data-screen-id="nIfHr"] .kanban.import-board .new-col [data-candidate-card]::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  background: rgba(17, 24, 39, 0.12);
  pointer-events: none;
}

body.nifhr-screen.candidate-move-step.board-dragging,
body.nifhr-screen.candidate-move-step.board-dragging * {
  cursor: default !important;
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board [data-select-target].is-dragging-origin {
  opacity: 1;
  background:
    linear-gradient(135deg, rgba(124, 58, 237, 0.12), rgba(17, 24, 39, 0.04)),
    repeating-linear-gradient(135deg, rgba(124, 58, 237, 0.10) 0 8px, rgba(124, 58, 237, 0.03) 8px 16px);
  border: 1.5px dashed rgba(124, 58, 237, 0.58);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.72);
  cursor: default;
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board [data-select-target].is-dragging-origin > :not(.candidate-select-circle) {
  opacity: 0.18;
}

body.nifhr-screen [data-screen-id="nIfHr"] .candidate-select-circle {
  display: none;
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .candidate-select-circle {
  display: none;
}

body.nifhr-screen.candidate-move-step .candidate-select-proxy {
  position: absolute;
  transform: translate(-50%, -50%);
  z-index: 9060;
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  isolation: isolate;
  border: 2px solid var(--text-inverse);
  border-radius: var(--radius-pill);
  background: var(--surface);
  box-shadow: 0 4px 12px rgba(17, 24, 39, 0.16);
  pointer-events: auto;
  cursor: default;
}

body.nifhr-screen.candidate-move-step .candidate-select-proxy[hidden],
body.nifhr-screen.candidate-move-step.selection-drag-active .candidate-select-proxy {
  display: none !important;
}

body.nifhr-screen.candidate-move-step .candidate-select-proxy::before {
  content: none;
}

body.nifhr-screen.candidate-move-step .candidate-select-proxy::after {
  position: relative;
  z-index: 2;
}

body.nifhr-screen.candidate-move-step .candidate-select-proxy[data-selected="true"] {
  border-color: var(--surface);
  background: var(--brand);
  box-shadow: 0 5px 14px rgba(76, 29, 149, 0.26);
}

body.nifhr-screen.candidate-move-step .candidate-select-proxy[data-selected="true"]::after {
  content: "";
  width: 12px;
  height: 7px;
  border-left: 3px solid var(--text-inverse);
  border-bottom: 3px solid var(--text-inverse);
  transform: translate(1px, -1px) rotate(-45deg);
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .review-drop-target {
  min-height: 178px;
  max-height: 178px;
  gap: var(--space-xs);
  border: 2px dashed rgba(124, 58, 237, 0.55);
  background:
    linear-gradient(135deg, rgba(124, 58, 237, 0.14), rgba(17, 24, 39, 0.04)),
    repeating-linear-gradient(135deg, rgba(124, 58, 237, 0.10) 0 8px, rgba(124, 58, 237, 0.03) 8px 16px);
  color: var(--brand-hover);
  position: relative;
  z-index: 1;
  opacity: 1;
  transform: scale(1);
  transform-origin: center center;
  transition:
    min-height 560ms cubic-bezier(.2, .9, .16, 1),
    max-height 560ms cubic-bezier(.2, .9, .16, 1),
    padding 560ms cubic-bezier(.2, .9, .16, 1),
    margin 560ms cubic-bezier(.2, .9, .16, 1),
    opacity 300ms ease,
    transform 180ms cubic-bezier(.2, .9, .2, 1),
    border-color 140ms ease,
    background 140ms ease,
    box-shadow 140ms ease,
    color 140ms ease;
  will-change: transform;
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .review-drop-target.is-armed-drop-target {
  border-color: rgba(255, 255, 255, 0.86) !important;
  background:
    linear-gradient(135deg, rgba(76, 29, 149, 0.96), rgba(91, 33, 182, 0.98)),
    repeating-linear-gradient(135deg, rgba(255, 255, 255, 0.10) 0 8px, rgba(255, 255, 255, 0.02) 8px 16px) !important;
  color: var(--text-inverse) !important;
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.22),
    0 0 0 4px rgba(255, 255, 255, 0.88),
    0 22px 54px rgba(76, 29, 149, 0.36) !important;
  transform: scale(1.06) !important;
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .review-drop-target.is-armed-drop-target small {
  color: rgba(255, 255, 255, 0.82);
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .review-drop-target strong {
  font-size: var(--fs-lg);
  line-height: 20px;
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .review-drop-target small {
  max-width: 210px;
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  line-height: 17px;
  font-weight: var(--fw-semibold);
  text-align: center;
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .shortlist-col.is-curated-target .shortlist-ghost {
  display: none;
}

body.nifhr-screen.candidate-move-step .candidate-board-ghost.is-returning {
  transform-origin: center center;
  filter: drop-shadow(0 10px 18px rgba(17, 24, 39, 0.18));
  animation: nifhr-snapback-return var(--return-duration, 320ms) cubic-bezier(0.16, 1, 0.2, 1) both !important;
}

@keyframes nifhr-snapback-return {
  0% {
    opacity: 1;
  }
  56% {
    transform: translate(var(--return-over-x), var(--return-over-y)) rotate(2.4deg) scale(1.035);
    opacity: 1;
  }
  74% {
    transform: translate(var(--return-recoil-x), var(--return-recoil-y)) rotate(-0.9deg) scale(0.985);
    opacity: 0.96;
  }
  88% {
    transform: translate(var(--return-x), var(--return-y)) rotate(0.35deg) scale(1.006);
    opacity: 0.82;
  }
  100% {
    transform: translate(var(--return-x), var(--return-y)) rotate(0deg) scale(1);
    opacity: 0;
  }
}

body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .nifhr-coach {
  z-index: 9200;
  top: auto;
  left: auto;
  right: 42px;
  bottom: 44px;
  width: min(430px, 30vw);
  min-height: 218px;
  padding: 28px;
  transition:
    bottom 560ms cubic-bezier(.2, .9, .16, 1),
    transform 560ms cubic-bezier(.2, .9, .16, 1),
    box-shadow 360ms ease;
}

body.nifhr-screen.candidate-move-step.candidate-move-complete [data-screen-id="nIfHr"] .coach-cta {
  opacity: 1;
  background: var(--brand);
}

body.nifhr-screen.candidate-move-step.candidate-move-complete [data-screen-id="nIfHr"] .kanban.import-board .is-curated-target {
  align-self: start;
  height: var(--nifhr-review-complete-h, 342px);
  max-height: var(--nifhr-review-complete-h, 342px);
  border-color: var(--brand);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(249, 247, 255, 0.98)),
    var(--bg-row-highlight);
  box-shadow:
    0 0 0 4px rgba(255, 255, 255, 0.88),
    0 18px 44px rgba(124, 58, 237, 0.18);
}

body.nifhr-screen.candidate-move-step.candidate-move-complete [data-screen-id="nIfHr"] .kanban.import-board .is-curated-target .candidate-stream {
  overflow: visible;
}

body.nifhr-screen.candidate-move-step.candidate-move-complete [data-screen-id="nIfHr"] .review-drop-target {
  min-height: 0;
  max-height: 0;
  margin: 0;
  padding: 0;
  border-width: 0;
  opacity: 0;
  color: transparent;
  overflow: hidden;
  pointer-events: none;
  transform: translateY(-10px) scale(.985);
  box-shadow: none;
}

body.nifhr-screen.candidate-move-step.candidate-move-complete [data-screen-id="nIfHr"] .review-drop-target > * {
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity var(--motion-ui) ease, transform 300ms ease;
}

body.nifhr-screen.candidate-move-step.candidate-move-complete [data-screen-id="nIfHr"] .is-curated-target [data-select-target] {
  border-color: var(--brand);
  box-shadow:
    0 0 0 1px rgba(124, 58, 237, 0.54),
    0 10px 24px rgba(76, 29, 149, 0.12);
}

body.nifhr-screen.candidate-move-step.candidate-move-complete [data-screen-id="nIfHr"] .is-curated-target [data-select-target].completion-rise-card {
  z-index: 2;
}

body.nifhr-screen.candidate-move-step.candidate-move-complete [data-screen-id="nIfHr"] .nifhr-coach {
  bottom: calc(44px + var(--nifhr-complete-coach-lift, 128px));
}

body.nifhr-screen.candidate-move-step.candidate-move-complete [data-screen-id="nIfHr"] .kanban.import-board .new-col {
  z-index: auto;
  border-color: var(--border);
  /* ENTRY #89 (user call). Once the move is DONE the Applied column keeps its lilac surface and
     goes MD3-inactive at 39%, instead of dropping to a flat neutral.
     ⚠️ This previously said `background: var(--bg-card)`, a token that is used in 10 places and
     DECLARED NOWHERE, so it was invalid at computed-value time and silently resolved to
     transparent. The column had no surface at all here; the token both fixes that and carries the
     colour the user asked for. (The other 9 --bg-card sites are still dead: out of scope here.) */
  background: var(--col-applied-surface);
  opacity: 0.39;
  box-shadow: none;
  /* (entry #88 follow-up, user call) the source column shrinks its bottom up to match the
     target column's completed height, instead of staying full-board-height with dead space. */
  align-self: start;
  height: var(--nifhr-review-complete-h, 342px);
  max-height: var(--nifhr-review-complete-h, 342px);
  transition: height 520ms ease, max-height 520ms ease, box-shadow 520ms ease, background 520ms ease, opacity 520ms ease;
}

body.nifhr-screen.candidate-move-step.candidate-move-complete [data-screen-id="nIfHr"] .kanban.import-board .new-col [data-candidate-card] {
  pointer-events: none;
  z-index: auto;
  border-color: var(--border);
  border-width: var(--border-w);
  background: var(--bg-card);
  box-shadow: none;
  cursor: default;
  /* ENTRY #89. The COLUMN now carries the single 39% MD3-inactive dim (see the tail rule at the
     end of this file), so the card's own dim would MULTIPLY with it: these cards are
     data-move-eligible="false", whose (0,7,1) rule sets opacity .39, and .39 × .39 = .152: a card
     so faint it reads as a rendering fault. This rule is (0,8,1), so it outranks that and hands the
     dimming job entirely to the parent; the column then fades as ONE unit at exactly 39%.
     The old `filter: grayscale(0.18)` and the rgba(17,24,39,0.10) ::after wash were doing this same
     job back when the column had no surface of its own (its `background: var(--bg-card)` is an
     UNDECLARED token, so it resolved to transparent). Both are redundant under the parent dim and
     are deliberately gone. Measured on a real drag-to-complete, not forced classes.
     `filter: none` is load-bearing, not tidying: dropping the old grayscale(0.18) let the
     data-move-eligible="false" rule's grayscale(0.35) fall through from below, which would
     desaturate the candidate avatars and quietly break the ONE CANDIDATE PALETTE (entry #74). */
  opacity: 1;
  filter: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-compare-overlay {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: var(--space-xl);
  background: rgba(17, 24, 39, 0.62);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  opacity: 0;
  pointer-events: none;
  z-index: 9400;
  transition: opacity var(--motion-ui) ease;
}

body.nifhr-screen.comparison-modal-open [data-screen-id="nIfHr"] .nifhr-compare-overlay {
  opacity: 1;
  pointer-events: auto;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-compare-modal {
  width: min(1060px, calc(100vw - 64px));
  max-height: min(748px, calc(100vh - 64px));
  position: relative;
  display: grid;
  grid-template-rows: 79px auto 128px minmax(0, 1fr) 69px;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--text-primary);
  box-shadow: 0 32px 84px rgba(17, 24, 39, 0.32);
  transform: translateY(12px) scale(0.985);
  transition: transform 260ms cubic-bezier(.2, .9, .16, 1);
}

body.nifhr-screen.comparison-modal-open [data-screen-id="nIfHr"] .nifhr-compare-modal {
  transform: translateY(0) scale(1);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px var(--space-lg);
  border-bottom: 1px solid var(--border);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-modal-head h2 {
  margin: 0;
  font-size: var(--fs-lg);
  line-height: 22px;
  font-weight: var(--fw-extrabold);
  letter-spacing: -0.01em;
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-modal-head p {
  margin: 2px 0 0;
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  line-height: 15px;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-close {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text-tertiary);
  font-size: var(--fs-2xl);
  line-height: 1;
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-close:disabled,
body.nifhr-screen [data-screen-id="nIfHr"] .compare-close[aria-disabled="true"] {
  opacity: 0.39;
  cursor: default;
  pointer-events: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-candidate-heads,
body.nifhr-screen [data-screen-id="nIfHr"] .compare-row {
  display: grid;
  grid-template-columns: 180px repeat(3, minmax(0, 1fr));
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-photo-cell {
  background: var(--bg-subtle);
  border-right: 1px solid var(--border);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-person {
  position: relative;
  min-width: 0;
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 6px;
  padding: 20px var(--space-md);
  border-right: 1px solid var(--border);
  text-align: center;
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-person:last-child,
body.nifhr-screen [data-screen-id="nIfHr"] .compare-row > span:last-child {
  border-right: 0;
}

/* ENTRY #177 (user call): Ahmed's column no longer carries a green tint. It read as a
   "recommended" flag on the middle candidate, which is not the intent, so his header cell and his
   data cells (the rule at the compare-row block below) now match Marta's and Priya's plain surface.
   The `.compare-ahmed .compare-avatar` colour below is his avatar, NOT the column, and stays. */
body.nifhr-screen [data-screen-id="nIfHr"] .compare-avatar {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: var(--text-inverse);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-marta .compare-avatar {
  background: var(--cand-marta);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-ahmed .compare-avatar {
  background: var(--cand-ahmed);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-priya .compare-avatar {
  background: var(--cand-priya);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-person strong {
  display: block;
  min-width: 0;
  color: var(--text-primary);
  font-size: 13px;
  line-height: 17px;
  font-weight: var(--fw-extrabold);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-person small {
  display: block;
  max-width: 170px;
  color: var(--text-secondary);
  font-size: var(--fs-xs);
  line-height: 13px;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-match {
  position: absolute;
  top: 12px;
  left: 12px;
  padding: 3px var(--space-xs);
  border-radius: var(--radius-pill);
  background: var(--success);
  color: var(--text-inverse);
  font-size: 9px;
  line-height: 10px;
  font-weight: var(--fw-extrabold);
  letter-spacing: 0.02em;
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-table {
  min-height: 0;
  overflow: auto;
  border-top: 1px solid var(--border);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-row {
  min-height: 56px;
  border-bottom: 1px solid var(--border);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-row > span {
  min-width: 0;
  display: flex;
  align-items: center;
  padding: 14px var(--space-md);
  border-right: 1px solid var(--border);
  color: var(--text-primary);
  font-size: var(--fs-sm);
  line-height: 17px;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-experience-row {
  min-height: 148px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-experience-row > span:not(.compare-label) {
  align-items: flex-start;
  justify-content: flex-start;
  flex-direction: column;
  gap: 3px;
  padding-top: var(--space-sm);
  padding-bottom: var(--space-sm);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-experience-row b,
body.nifhr-screen [data-screen-id="nIfHr"] .compare-experience-row em,
body.nifhr-screen [data-screen-id="nIfHr"] .compare-experience-row small {
  display: block;
  font-style: normal;
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-experience-row b {
  color: var(--text-primary);
  font-size: var(--fs-xs);
  line-height: 14px;
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-experience-row em {
  color: var(--text-secondary);
  font-size: var(--fs-2xs);
  line-height: 13px;
  font-weight: var(--fw-bold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-experience-row small {
  margin-bottom: 6px;
  color: var(--text-secondary);
  font-size: var(--fs-2xs);
  line-height: 13px;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-label {
  background: var(--bg-subtle);
  color: var(--text-secondary) !important;
  font-size: var(--fs-xs) !important;
  font-weight: var(--fw-bold) !important;
}

/* ENTRY #177: Ahmed's data cells drop their green tint too (see the header note above). */
body.nifhr-screen [data-screen-id="nIfHr"] .compare-skills-row {
  min-height: 80px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-skills-row > span:not(.compare-label) {
  flex-wrap: wrap;
  justify-content: center;
  align-content: center;
  gap: 6px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-skills-row i {
  display: inline-flex;
  align-items: center;
  min-height: 19px;
  padding: 3px var(--space-xs);
  border-radius: var(--radius-pill);
  background: var(--brand-tint);
  color: var(--brand-dark);
  font-style: normal;
  font-size: var(--fs-2xs);
  line-height: 13px;
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-available {
  background: rgba(209, 250, 229, 0.9) !important;
  color: var(--success-text) !important;
  justify-content: center;
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-modal-foot {
  display: flex;
  align-items: center;
  /* ENTRY #177: the privacy line is gone, so the Submit control is the only child. flex-end keeps
     it on the right (space-between would have shoved a lone child to the left). The landscape-row
     footer block inherits this; the column mobile block sets its own vertical stacking. */
  justify-content: flex-end;
  gap: 18px;
  padding: var(--space-md) var(--space-lg);
  border-top: 1px solid var(--border);
  background: var(--surface);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-modal-foot span {
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  line-height: 17px;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-modal-foot div {
  display: flex;
  gap: 10px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-secondary,
body.nifhr-screen [data-screen-id="nIfHr"] .compare-primary {
  min-height: 37px;
  padding: 0 var(--space-md);
  border-radius: var(--radius-pill);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-secondary {
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-primary);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-primary {
  background: var(--brand);
  color: var(--text-inverse);
  box-shadow: var(--shadow-brand);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-rank-labels {
  display: grid;
  grid-template-columns: 180px repeat(3, minmax(0, 1fr));
  min-height: 0;
  height: 0;
  overflow: hidden;
  border-bottom: 0 solid var(--border);
  /* ENTRY #89 (user call). The preference TRAFFIC LIGHT: green at the most-preferred end running
     to red at the least-preferred end, so the ranking axis reads at a glance instead of relying on
     the two words alone.
     ⚠️ The gradient must start where the CANDIDATE columns start, not at the row's left edge. The
     first grid track is the 180px row-label gutter, which stays plain --surface. Sizing the image
     to `calc(100% - 180px)` and offsetting it by 180px keys it to the SAME 180px the grid declares
     (:9744), so the two cannot drift; a percentage colour-stop would be a guess that silently
     shifts with the modal width. */
  background-color: var(--surface);
  background-image: linear-gradient(90deg, var(--rank-most), var(--rank-mid), var(--rank-least));
  background-size: calc(100% - 180px) 100%;
  background-position: 180px 0;
  background-repeat: no-repeat;
  opacity: 0;
  transition:
    height 240ms cubic-bezier(.2, .9, .16, 1),
    opacity 180ms ease,
    border-color 180ms ease;
}

body.nifhr-screen.comparison-rank-active [data-screen-id="nIfHr"] .compare-rank-labels {
  height: 46px;
  opacity: 1;
  border-bottom-width: var(--border-w);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-rank-labels span,
body.nifhr-screen [data-screen-id="nIfHr"] .compare-rank-labels strong {
  display: flex;
  align-items: center;
  min-width: 0;
  padding: 0 var(--space-md);
  /* ENTRY #89. The per-cell dividers are GONE across the candidate tracks: they used to chop the
     row into three boxes, which sliced the traffic light into three disconnected chunks instead of
     one continuous sweep. Only the gutter keeps a divider (see the :first-child rule below). */
  border-right: 0;
  color: var(--text-secondary);
  font-size: var(--fs-xs);
  line-height: 14px;
  font-weight: var(--fw-extrabold);
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

/* the 180px row-label gutter is the one place a divider still earns its keep: it separates the
   plain --surface gutter from the start of the gradient. */
body.nifhr-screen [data-screen-id="nIfHr"] .compare-rank-labels span:first-child {
  border-right: 1px solid var(--border);
}

/* ENTRY #89. The labels sit ON the gradient now, so the flat --brand-tint chip behind them is
   gone (it would punch a lilac hole through the traffic light). Each label takes the TEXT token
   paired with the tint it sits on, which is what keeps it legible: --success-text on --success-bg
   at the green end, --danger-text on --danger-bg at the red end. */
body.nifhr-screen [data-screen-id="nIfHr"] .compare-rank-labels strong {
  background: transparent;
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-rank-labels strong:first-of-type {
  color: var(--success-text);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-rank-labels strong:last-child {
  justify-content: flex-end;
  color: var(--danger-text);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-rank-handle {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-tertiary);
  font-size: var(--fs-sm);
  line-height: 1;
  font-weight: var(--fw-black);
  letter-spacing: -0.18em;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-4px);
  transition:
    opacity 160ms ease,
    transform 220ms cubic-bezier(.2, .9, .16, 1),
    border-color 160ms ease,
    color 160ms ease,
    background 160ms ease;
}

body.nifhr-screen.comparison-rank-active [data-screen-id="nIfHr"] .compare-rank-handle {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
  cursor: grab;
}

body.nifhr-screen.comparison-rank-active [data-screen-id="nIfHr"] .compare-rank-handle:hover,
body.nifhr-screen.comparison-rank-active [data-screen-id="nIfHr"] .compare-rank-handle:focus-visible {
  border-color: var(--brand);
  background: var(--brand-tint);
  color: var(--brand-dark);
  outline: none;
}

body.nifhr-screen.comparison-rank-dragging [data-screen-id="nIfHr"] .compare-rank-handle {
  cursor: grabbing;
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-person[data-rank-position="1"]::after,
body.nifhr-screen [data-screen-id="nIfHr"] .compare-person[data-rank-position="3"]::after {
  content: "";
  position: absolute;
  top: 12px;
  bottom: 12px;
  width: 1px;
  background: transparent;
}

body.nifhr-screen.comparison-modal-open:not(.comparison-rank-active) [data-screen-id="nIfHr"] .nifhr-compare-modal {
  filter: saturate(0.92);
}

body.nifhr-screen.comparison-modal-open:not(.comparison-rank-active) [data-screen-id="nIfHr"] .nifhr-compare-modal::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 4;
  border-radius: inherit;
  background: rgba(17, 24, 39, 0.10);
  pointer-events: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-rank-coach {
  position: fixed;
  right: max(36px, calc((100vw - min(1060px, calc(100vw - 64px))) / 2 + 28px));
  bottom: 88px;
  width: 392px;
  min-height: 218px;
  display: grid;
  align-content: start;
  gap: var(--space-sm);
  padding: 28px;
  border: 1px solid rgba(148, 163, 184, 0.14);
  border-radius: 14px;
  background:
    radial-gradient(circle at 18% 14%, rgba(124, 58, 237, 0.28), rgba(124, 58, 237, 0) 34%),
    #0f1726;
  color: #f8fafc;
  box-shadow:
    0 28px 72px rgba(15, 23, 42, 0.42),
    0 0 0 1px rgba(124, 58, 237, 0.16);
  z-index: 9600;
  transition:
    opacity 220ms ease,
    transform 260ms cubic-bezier(.2, .9, .16, 1);
}

body.nifhr-screen.comparison-rank-active [data-screen-id="nIfHr"] .compare-rank-coach {
  opacity: 0;
  pointer-events: none;
  transform: translateY(14px) scale(0.985);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-rank-coach h2 {
  margin: 0;
  max-width: 310px;
  color: #f8fafc;
  font-size: 28px;
  line-height: 32px;
  font-weight: var(--fw-black);
  letter-spacing: -0.03em;
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-rank-coach p {
  margin: 0;
  color: #cbd5e1;
  font-size: 13px;
  line-height: 20px;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-person.is-rank-origin,
body.nifhr-screen [data-screen-id="nIfHr"] .compare-row > [data-compare-candidate].is-rank-origin {
  opacity: 0.36;
  background: color-mix(in srgb, var(--brand-tint) 52%, var(--surface));
}

body.nifhr-screen [data-screen-id="nIfHr"] .compare-person,
body.nifhr-screen [data-screen-id="nIfHr"] .compare-row > [data-compare-candidate] {
  transition:
    background 180ms ease,
    opacity 160ms ease,
    box-shadow 200ms ease;
}

.compare-rank-ghost {
  border: 1px solid var(--brand) !important;
  box-shadow: 0 22px 56px rgba(76, 29, 149, 0.26), 0 0 0 4px rgba(124, 58, 237, 0.12) !important;
  background: var(--surface) !important;
}

.compare-rank-ghost .compare-rank-handle {
  opacity: 1 !important;
  background: var(--brand) !important;
  border-color: var(--brand) !important;
  color: var(--text-inverse) !important;
}

.compare-rank-insertion {
  position: fixed;
  width: 4px;
  border-radius: var(--radius-pill);
  background: var(--brand);
  box-shadow: 0 0 0 5px rgba(124, 58, 237, 0.16), 0 14px 34px rgba(76, 29, 149, 0.25);
  pointer-events: none;
  z-index: 10010;
}

.compare-rank-insertion[hidden] {
  display: none !important;
}

/* nIfHr screening-questionnaire sequence: Pencil dhwRK + m8nor states. */
body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-screening-overlay {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: var(--space-xl);
  background: rgba(17, 24, 39, 0.64);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  opacity: 0;
  pointer-events: none;
  z-index: 9800;
  transition: opacity var(--motion-ui) ease;
}

body.nifhr-screen.screening-step-qr:not(.screening-reference-transitioning) [data-screen-id="nIfHr"] .nifhr-screening-drawer,
body.nifhr-screen.screening-step-live:not(.screening-reference-transitioning) [data-screen-id="nIfHr"] .nifhr-screening-drawer,
body.nifhr-screen.screening-step-screened:not(.screening-reference-transitioning) [data-screen-id="nIfHr"] .nifhr-screening-drawer,
body.nifhr-screen.screening-step-selfscreen:not(.screening-reference-transitioning) [data-screen-id="nIfHr"] .nifhr-screening-drawer {
  transform: translateX(0);
  opacity: 1;
  pointer-events: auto;
}

body.nifhr-screen.screening-reference-transitioning:not(.screening-step-try) [data-screen-id="nIfHr"] .nifhr-screening-drawer {
  transform: translateX(104%);
  opacity: 0;
  pointer-events: none;
  transition:
    transform 380ms cubic-bezier(0.32, 0.72, 0, 1),
    opacity 220ms ease;
}

body.nifhr-screen.screening-step-try.screening-reference-transitioning [data-screen-id="nIfHr"] .nifhr-screening-drawer {
  opacity: 1;
  pointer-events: none;
  animation: nifhr-marta-drawer-exit 420ms cubic-bezier(0.32, 0.72, 0, 1) both;
}

@keyframes nifhr-marta-drawer-exit {
  0% {
    transform: translateX(0);
    opacity: 1;
  }
  100% {
    transform: translateX(104%);
    opacity: 0;
  }
}

body.nifhr-screen.screening-step-qr [data-screen-id="nIfHr"] .nifhr-screening-overlay,
body.nifhr-screen.screening-step-live [data-screen-id="nIfHr"] .nifhr-screening-overlay,
body.nifhr-screen.screening-step-screened [data-screen-id="nIfHr"] .nifhr-screening-overlay,
/* #333: the `screening-step-feature` selector was removed from this list with its step. */
body.nifhr-screen.screening-step-schedule [data-screen-id="nIfHr"] .nifhr-screening-overlay,
body.nifhr-screen.screening-step-schedule-demo [data-screen-id="nIfHr"] .nifhr-screening-overlay,
body.nifhr-screen.screening-step-try [data-screen-id="nIfHr"] .nifhr-screening-overlay,
body.nifhr-screen.screening-step-selfscreen [data-screen-id="nIfHr"] .nifhr-screening-overlay {
  opacity: 1;
  pointer-events: auto;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-modal,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-live-modal,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-out-modal,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-feature-modal,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-modal,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-modal,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-follow-modal {
  display: none;
}

body.nifhr-screen.screening-step-qr [data-screen-id="nIfHr"] .screening-qr-modal,
body.nifhr-screen.screening-step-live [data-screen-id="nIfHr"] .screening-live-modal,
body.nifhr-screen.screening-step-screened [data-screen-id="nIfHr"] .screening-out-modal,
/* #333: the feature card's own reveal selector was removed here, the SECOND of the two lists a
   `[data-screening-step]` panel lives in (T002). ⚠️ THE `display: none` DEFAULT ABOVE KEEPS ITS
   `.screening-feature-modal` SELECTOR, because the scheduling card carries that class too and
   would otherwise stop being hidden by default. */
body.nifhr-screen.screening-step-schedule [data-screen-id="nIfHr"] .scheduling-feature-modal,
body.nifhr-screen.screening-step-schedule-demo [data-screen-id="nIfHr"] .screening-schedule-demo-modal,
body.nifhr-screen.screening-step-try [data-screen-id="nIfHr"] .screening-try-modal,
body.nifhr-screen.screening-step-selfscreen [data-screen-id="nIfHr"] .screening-follow-modal {
  display: grid;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-modal {
  width: min(736px, calc(100vw - 64px - 33vw));
  min-height: 472px;
  grid-template-columns: 1fr;
  align-content: center;
  justify-items: start;
  gap: var(--space-lg);
  padding: 56px 56px 48px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--text-primary);
  box-shadow: 0 32px 88px rgba(17, 24, 39, 0.34);
  transform: translateX(-9vw);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-copy {
  max-width: 396px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-eyebrow {
  display: inline-flex;
  margin-bottom: 14px;
  color: var(--brand-dark);
  font-size: var(--fs-xs);
  line-height: 14px;
  font-weight: var(--fw-850);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-eyebrow.danger {
  color: var(--danger);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-copy h2,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-live-modal h2,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-out-copy h2 {
  margin: 0;
  color: var(--text-primary);
  font-size: 25px;
  line-height: 31px;
  font-weight: var(--fw-850);
  letter-spacing: -0.025em;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-copy p,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-live-modal p,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-out-copy p {
  margin: var(--space-sm) 0 0;
  color: var(--text-muted);
  font-size: var(--fs-md);
  line-height: 21px;
  font-weight: var(--fw-650);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-copy ul {
  margin: 18px 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-xs);
  list-style: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-copy li {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  color: var(--text-strong);
  font-size: 13px;
  line-height: 17px;
  font-weight: var(--fw-750);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-copy li span {
  width: 17px;
  height: 17px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--success-bg);
  color: var(--success-text);
  font-size: var(--fs-2xs);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-box {
  display: grid;
  grid-template-columns: 124px minmax(0, 1fr);
  align-items: center;
  gap: 18px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-code {
  width: 116px;
  height: 116px;
  padding: var(--space-xs);
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  grid-template-rows: repeat(5, 1fr);
  gap: 5px;
  border: 1px solid var(--border);
  background: var(--surface);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-code span {
  background: #111827;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-code span:nth-child(4n),
body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-code span:nth-child(7),
body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-code span:nth-child(12),
body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-code span:nth-child(19) {
  background: transparent;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-box strong {
  display: block;
  color: var(--text-primary);
  font-size: 13px;
  line-height: 17px;
  font-weight: var(--fw-850);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-box small {
  display: block;
  margin-top: 3px;
  color: var(--text-tertiary);
  font-size: var(--fs-xs);
  line-height: 15px;
  font-weight: var(--fw-650);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-action,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-secondary-action {
  min-height: 41px;
  padding: 0 20px;
  border-radius: var(--radius-pill);
  font-size: 13px;
  line-height: 1;
  font-weight: var(--fw-850);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-action {
  border: 0;
  background: var(--brand);
  color: var(--text-inverse);
  box-shadow: var(--shadow-brand);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-secondary-action {
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-primary);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-live-modal {
  width: min(360px, calc(100vw - 64px));
  min-height: 251px;
  justify-self: end;
  align-self: start;
  align-content: start;
  justify-items: start;
  gap: 14px;
  padding: 28px var(--space-lg) 22px;
  border: 1px solid rgba(5, 150, 105, 0.32);
  border-radius: 22px;
  background: var(--success-bg);
  color: var(--success-text);
  box-shadow: 0 28px 72px rgba(17, 24, 39, 0.28);
  margin-top: 104px;
  margin-right: calc(33vw + 24px);
  transform: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-live-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  color: var(--success-text);
  font-size: var(--fs-xs);
  line-height: 14px;
  font-weight: var(--fw-black);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-live-pill i {
  width: 8px;
  height: 8px;
  display: inline-block;
  border-radius: var(--radius-pill);
  background: var(--danger);
  box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.42);
  animation: nifhr-live-dot 1050ms ease-in-out infinite;
}

body.nifhr-screen [data-screen-id="nIfHr"] .panel-status.live i {
  content: "";
  width: 6px;
  height: 6px;
  display: inline-block;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.98);
  box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.5);
  animation: nifhr-live-white-dot 980ms ease-in-out infinite;
}

@keyframes nifhr-live-dot {
  0%, 100% { transform: scale(0.86); box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.46); }
  50% { transform: scale(1); box-shadow: 0 0 0 7px rgba(239, 68, 68, 0); }
}

@keyframes nifhr-live-white-dot {
  0%, 100% { opacity: 0.48; transform: scale(0.72); }
  48% { opacity: 1; transform: scale(1.08); }
}

@keyframes nifhr-waiting-dot {
  0%, 64%, 100% {
    opacity: 0.46;
    transform: translate3d(0, 1px, 0) scale(0.72);
  }
  24% {
    opacity: 1;
    transform: translate3d(0, -2px, 0) scale(1);
  }
  42% {
    opacity: 0.74;
    transform: translate3d(0, 0, 0) scale(0.84);
  }
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-screening-drawer {
  position: fixed;
  inset: 0 0 0 auto;
  z-index: 9900;
  width: 33vw;
  min-width: 460px;
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  border-left: 1px solid var(--border);
  background: var(--surface);
  box-shadow: -22px 0 58px rgba(17, 24, 39, 0.24);
  color: var(--text-primary);
  transform: translateX(104%);
  opacity: 0;
  pointer-events: none;
  transition:
    transform 260ms cubic-bezier(.22, 1, .36, 1),
    opacity 180ms ease;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-drawer-head {
  height: 64px;
  min-height: 64px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--space-lg);
  border-bottom: 1px solid var(--border);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-drawer-head h2 {
  margin: 0;
  font-size: var(--fs-md);
  line-height: 18px;
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-drawer-head button {
  width: 32px;
  height: 32px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text-tertiary);
  pointer-events: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-drawer-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-lg) var(--space-lg) 132px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-profile-card {
  min-height: 72px;
  display: grid;
  grid-template-columns: 48px minmax(0, 1fr) 56px;
  align-items: center;
  gap: 14px;
  margin-bottom: 20px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-profile-avatar {
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--cand-marta);
  color: var(--text-inverse);
  font-size: 13px;
  font-weight: var(--fw-black);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-profile-card h3 {
  margin: 0;
  color: var(--text-primary);
  font-size: 19px;
  line-height: 24px;
  font-weight: var(--fw-850);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-profile-card p,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-profile-card span {
  margin: 1px 0 0;
  display: block;
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  line-height: 17px;
  font-weight: var(--fw-650);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-profile-card > strong {
  width: 52px;
  height: 52px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  border: 2px solid var(--success);
  background: var(--success-bg);
  color: var(--success-text);
  font-size: var(--fs-sm);
  font-weight: var(--fw-black);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-question-panel {
  min-height: 126px;
  display: grid;
  gap: 7px;
  margin-bottom: 20px;
  padding: 14px;
  border: 2px solid var(--success);
  border-radius: var(--radius-md);
  background: var(--success-bg);
  color: var(--success-text);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-panel-head,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-answer-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-panel-head span {
  color: var(--success-text);
  font-size: var(--fs-xs);
  line-height: 14px;
  font-weight: var(--fw-black);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

body.nifhr-screen [data-screen-id="nIfHr"] .panel-status {
  display: none;
  align-items: center;
  gap: 6px;
  min-height: 20px;
  padding: 0 9px;
  border-radius: var(--radius-pill);
  color: var(--text-inverse);
  font-size: var(--fs-2xs);
  line-height: 1;
  font-weight: var(--fw-black);
}

body.nifhr-screen [data-screen-id="nIfHr"] .panel-status.waiting {
  background: var(--success);
}

body.nifhr-screen [data-screen-id="nIfHr"] .waiting-typing-dots {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  height: 12px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .waiting-typing-dots i {
  width: 4px;
  height: 4px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.92);
  opacity: 0.72;
  transform: translate3d(0, 1px, 0) scale(0.72);
  animation: nifhr-waiting-dot 1140ms cubic-bezier(0.42, 0, 0.24, 1) infinite;
  will-change: transform, opacity;
}

body.nifhr-screen [data-screen-id="nIfHr"] .waiting-typing-dots i:nth-child(1) {
  background: rgba(255, 255, 255, 0.98);
}

body.nifhr-screen [data-screen-id="nIfHr"] .waiting-typing-dots i:nth-child(2) {
  background: rgba(245, 247, 250, 0.9);
  animation-delay: 150ms;
}

body.nifhr-screen [data-screen-id="nIfHr"] .waiting-typing-dots i:nth-child(3) {
  background: rgba(229, 233, 240, 0.84);
  animation-delay: 300ms;
}

body.nifhr-screen [data-screen-id="nIfHr"] .panel-status.live {
  background: var(--danger);
  gap: 5px;
  min-height: 20px;
  padding: 0 7px;
  width: auto;
}

body.nifhr-screen [data-screen-id="nIfHr"] .panel-status.screened {
  background: var(--danger-text);
}

body.nifhr-screen.screening-step-qr [data-screen-id="nIfHr"] .panel-status.waiting,
body.nifhr-screen.screening-step-live [data-screen-id="nIfHr"] .panel-status.live,
body.nifhr-screen.screening-step-listening [data-screen-id="nIfHr"] .panel-status.live,
body.nifhr-screen.screening-step-screened:not(.screening-rule-triggered) [data-screen-id="nIfHr"] .panel-status.live,
body.nifhr-screen.screening-rule-triggered [data-screen-id="nIfHr"] .panel-status.screened {
  display: inline-flex;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-answer-row {
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  line-height: 16px;
  font-weight: var(--fw-650);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-scenario-row {
  cursor: default;
}

body.nifhr-screen.screening-step-live [data-screen-id="nIfHr"] .screening-scenario-row,
body.nifhr-screen.screening-step-listening [data-screen-id="nIfHr"] .screening-scenario-row {
  outline: none;
}

body.nifhr-screen.screening-step-live [data-screen-id="nIfHr"] .screening-scenario-row:focus-visible,
body.nifhr-screen.screening-step-listening [data-screen-id="nIfHr"] .screening-scenario-row:focus-visible {
  border-radius: var(--radius-sm);
  box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.24);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-answer-row > span {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-width: 0;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-question-index {
  width: 18px;
  height: 18px;
  flex: 0 0 18px;
  display: inline-grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.74);
  color: var(--success-text);
  font-style: normal;
  font-size: var(--fs-2xs);
  line-height: 1;
  font-weight: var(--fw-950);
  box-shadow: inset 0 0 0 1px rgba(6, 95, 70, 0.16);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-answer-row strong {
  color: var(--text-primary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-850);
  text-align: right;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-typing-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0;
  min-height: 24px;
  min-width: 34px;
  padding: 0 var(--space-xs);
  border-radius: var(--radius-pill);
  background: var(--success);
  color: var(--text-inverse);
  font-size: var(--fs-2xs);
  line-height: 1;
  font-weight: var(--fw-black);
}

body.nifhr-screen.screening-step-qr [data-screen-id="nIfHr"] .screening-answer-row strong {
  opacity: 0;
}

body.nifhr-screen.screening-rule-triggered [data-screen-id="nIfHr"] .screening-question-panel {
  border-color: var(--danger);
  background: var(--danger-bg);
  color: #991B1B;
}

body.nifhr-screen.screening-rule-triggered [data-screen-id="nIfHr"] .screening-panel-head span {
  color: #991B1B;
}

body.nifhr-screen.screening-rule-triggered [data-screen-id="nIfHr"] .screening-answer-row.is-rule-match strong,
body.nifhr-screen.screening-rule-triggered [data-screen-id="nIfHr"] .screening-answer-row:nth-of-type(3) strong {
  color: var(--danger);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-answer-row.is-typing strong {
  justify-content: center;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-answer-row strong.is-pop-in {
  animation: nifhr-answer-pop 420ms cubic-bezier(0.2, 0.9, 0.22, 1);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-question-panel.is-rule-pulse {
  animation: nifhr-rule-pulse 760ms cubic-bezier(0.2, 0.9, 0.22, 1);
}

@keyframes nifhr-answer-pop {
  0% {
    opacity: 0;
    transform: translate3d(0, 5px, 0) scale(0.96);
  }
  64% {
    opacity: 1;
    transform: translate3d(0, -1px, 0) scale(1.02);
  }
  100% {
    opacity: 1;
    transform: translate3d(0, 0, 0) scale(1);
  }
}

@keyframes nifhr-rule-pulse {
  0% {
    box-shadow: 0 0 0 0 rgba(239, 68, 68, 0);
    transform: translate3d(0, 0, 0) scale(1);
  }
  38% {
    box-shadow: 0 0 0 5px rgba(239, 68, 68, 0.24);
    transform: translate3d(0, -1px, 0) scale(1.012);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(239, 68, 68, 0);
    transform: translate3d(0, 0, 0) scale(1);
  }
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-profile-section {
  padding: 18px 0;
  border-top: 1px solid rgba(17, 24, 39, 0.08);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-profile-section h4 {
  margin: 0 0 10px;
  color: rgba(17, 24, 39, 0.46);
  font-size: var(--fs-xs);
  line-height: 14px;
  font-weight: var(--fw-extrabold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-profile-section p,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-profile-section small,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-profile-section span {
  color: rgba(17, 24, 39, 0.38);
  font-size: 13px;
  line-height: 19px;
  font-weight: var(--fw-medium);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-profile-section article {
  display: grid;
  gap: 2px;
  margin-bottom: var(--space-sm);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-profile-section article strong {
  color: rgba(17, 24, 39, 0.46);
  font-size: 13px;
  line-height: 17px;
  font-weight: var(--fw-bold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-out-modal {
  width: min(520px, calc(100vw - 64px - 33vw));
  min-height: 648px;
  grid-template-columns: 1fr;
  grid-template-rows: auto minmax(0, 1fr);
  align-items: start;
  justify-items: center;
  gap: 22px;
  padding: 30px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: 0 32px 88px rgba(17, 24, 39, 0.34);
  transform: translateX(-8vw);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-phone-shell {
  position: relative;
  display: grid;
  justify-items: center;
  align-items: start;
  width: 305px;
  height: 625px;
  overflow: visible;
  border-radius: 40px;
  background: #000;
  box-shadow:
    0 0 0 1.5px #333,
    0 16px 42px rgba(17, 24, 39, 0.26),
    0 0 0 1px rgba(255,255,255,0.08) inset;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-phone-frame {
  width: 390px;
  height: 844px;
  padding: var(--space-sm);
  border-radius: 54px;
  background: #000;
  transform: scale(0.58);
  transform-origin: top left;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-phone-screen {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: 42px;
  background: var(--surface);
  font-family: var(--font-sans);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-dynamic-island {
  position: absolute;
  top: 11px;
  left: 50%;
  z-index: 20;
  width: 126px;
  height: 37px;
  transform: translateX(-50%);
  border-radius: var(--radius-xl);
  background: #000;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-status-bar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 19;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  height: 54px;
  padding: 0 28px var(--space-xs);
  color: #000;
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  pointer-events: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-status-icons {
  display: flex;
  align-items: center;
  gap: 5px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-home-indicator {
  position: absolute;
  bottom: 8px;
  left: 50%;
  z-index: 20;
  width: 134px;
  height: 5px;
  transform: translateX(-50%);
  border-radius: var(--radius-pill);
  background: #000;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-sheet-bg {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 70% 30%, rgba(124, 58, 237, 0.13), transparent 42%),
    linear-gradient(180deg, #1a1a2e 0%, #0f0f1a 100%);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-bottom-sheet {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 4;
  max-height: 92%;
  overflow: hidden;
  padding: var(--space-sm) 20px 28px;
  border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
  background: var(--surface);
  box-shadow: 0 -16px 36px rgba(15, 23, 42, 0.18);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-sheet-handle {
  width: 36px;
  height: 4px;
  margin: 0 auto var(--space-md);
  border-radius: var(--radius-pill);
  background: #d1d5db;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-bottom-sheet h3 {
  margin: 0 0 var(--space-2xs);
  color: var(--text-primary);
  font-size: 20px;
  line-height: 25px;
  font-weight: var(--fw-black);
  letter-spacing: -0.02em;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-bottom-sheet > p {
  margin: 0 0 20px;
  color: var(--text-secondary);
  font-size: var(--fs-md);
  line-height: 20px;
  font-weight: var(--fw-650);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-form-card {
  display: block;
  margin-bottom: var(--space-sm);
  padding: 14px;
  border: 1px solid transparent;
  border-radius: var(--radius-lg);
  background: var(--bg-subtle);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-form-card.is-screened-out {
  border-color: #f5a0a0;
  background: #fce7e7;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-form-label {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  margin-bottom: 10px;
  color: var(--text-primary);
  font-size: var(--fs-md);
  line-height: 18px;
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-form-label span {
  min-width: 20px;
  color: var(--brand);
  font-size: 13px;
  font-weight: var(--fw-950);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-answer-display {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 40px;
  padding: 10px var(--space-sm);
  border: 1px solid #e5e7eb;
  border-radius: 10px;
  background: var(--surface);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-mini-check {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 18px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--brand);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-mini-check.is-red {
  background: var(--danger-text);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-mini-check svg {
  width: 10px;
  height: 10px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-answer-display strong {
  color: var(--text-primary);
  font-size: var(--fs-md);
  line-height: 18px;
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-submit {
  width: 100%;
  height: 52px;
  margin-top: var(--space-xs);
  border: 0;
  border-radius: var(--radius-pill);
  background: linear-gradient(135deg, var(--brand), var(--brand-dark));
  color: var(--text-inverse);
  font: 850 15px/1 var(--font-sans);
  box-shadow: 0 4px 16px rgba(124, 58, 237, 0.32);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-explainer-bubble {
  position: absolute;
  right: 24px;
  bottom: 160px;
  z-index: 7;
  width: 244px;
  padding: 14px 15px;
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: 0 12px 40px rgba(0,0,0,0.25), 0 4px 12px rgba(0,0,0,0.15);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-explainer-bubble::before {
  content: "";
  position: absolute;
  left: -12px;
  top: 50%;
  width: 0;
  height: 0;
  transform: translateY(-50%);
  border-top: 10px solid transparent;
  border-bottom: 10px solid transparent;
  border-right: 13px solid var(--text-inverse);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-explainer-bubble p {
  margin: 0;
  color: var(--text-primary);
  font-size: var(--fs-md);
  line-height: 19px;
  font-weight: var(--fw-850);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-phone {
  --screened-demo-accent: #8b5cf6;
  --screened-demo-accent-deep: #7c3aed;
  --screened-demo-fg: #111827;
  --screened-demo-muted: #6b7280;
  --screened-demo-border: #e5e7eb;
  --screened-demo-danger: #b91c1c;
  --screened-demo-danger-bg: #fce7e7;
  width: 390px;
  height: 844px;
  padding: var(--space-sm);
  border-radius: 54px;
  background: #000;
  position: absolute;
  top: 5px;
  left: 50%;
  transform: translateX(-50%) scale(0.725);
  transform-origin: top center;
  font-family: var(--font-sans);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-screen {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: 42px;
  background: var(--surface);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-island {
  position: absolute;
  top: 11px;
  left: 50%;
  z-index: 50;
  width: 126px;
  height: 37px;
  transform: translateX(-50%);
  border-radius: var(--radius-xl);
  background: #000;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-status {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 49;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  height: 54px;
  padding: 0 28px var(--space-xs);
  color: #000;
  font-size: var(--fs-lg);
  line-height: 1;
  font-weight: var(--fw-extrabold);
  pointer-events: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-icons {
  display: flex;
  align-items: center;
  gap: 5px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-home {
  position: absolute;
  bottom: 8px;
  left: 50%;
  z-index: 50;
  width: 134px;
  height: 5px;
  transform: translateX(-50%);
  border-radius: var(--radius-pill);
  background: #000;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-scene {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 500ms ease;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-scene.is-active {
  opacity: 1;
  pointer-events: auto;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-fade {
  position: absolute;
  inset: 0;
  z-index: 40;
  border-radius: 42px;
  background: #000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 350ms ease;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-inbox-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 58px var(--space-md) var(--space-sm);
  border-bottom: 1px solid var(--screened-demo-border);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-inbox-head strong {
  color: var(--screened-demo-fg);
  font-size: 22px;
  font-weight: var(--fw-black);
  letter-spacing: -0.02em;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-inbox-head span,
body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-avatar {
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--screened-demo-accent), var(--screened-demo-accent-deep));
  color: var(--text-inverse);
  font-weight: var(--fw-black);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-inbox-head span {
  width: 32px;
  height: 32px;
  font-size: 13px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-search {
  display: flex;
  align-items: center;
  height: 44px;
  margin: var(--space-sm) var(--space-md);
  padding: 0 var(--space-md);
  border-radius: var(--radius-sheet);
  background: var(--bg-subtle);
  color: var(--text-tertiary);
  font-size: var(--fs-lg);
  font-weight: var(--fw-650);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-email-list {
  padding: 0 var(--space-md);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-email {
  position: relative;
  display: flex;
  gap: var(--space-sm);
  min-height: 68px;
  padding: 14px 0;
  border-bottom: 1px solid #f3f4f6;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-email.is-skeleton::before,
body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-email.is-skeleton::after {
  content: "";
  display: block;
  border-radius: 10px;
  background: #e5e7eb;
  animation: screenedDemoShimmer 1600ms ease-in-out infinite;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-email.is-skeleton::before {
  width: 40px;
  height: 40px;
  border-radius: 50%;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-email.is-skeleton::after {
  width: 238px;
  height: 36px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-email.is-hirello::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: -12px;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--screened-demo-accent);
  opacity: 0;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-email.is-hirello.is-pulse::before {
  opacity: 1;
  animation: screenedDemoPulseBar 1800ms ease-in-out infinite;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-avatar {
  flex: 0 0 40px;
  width: 40px;
  height: 40px;
  font-size: var(--fs-md);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-email strong,
body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-email b,
body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-email small {
  display: block;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-email strong {
  color: var(--screened-demo-fg);
  font-size: var(--fs-md);
  line-height: 17px;
  font-weight: var(--fw-black);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-email b {
  margin-top: 2px;
  color: var(--screened-demo-fg);
  font-size: var(--fs-md);
  line-height: 17px;
  font-weight: var(--fw-750);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-email small {
  margin-top: 2px;
  color: var(--screened-demo-muted);
  font-size: 13px;
  line-height: 16px;
  font-weight: var(--fw-650);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-modal-bg,
body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-sheet-bg {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background:
    radial-gradient(circle at 30% 20%, rgba(67, 56, 202, 0.17), transparent 40%),
    linear-gradient(180deg, #1a1a2e 0%, #0f0f1a 100%);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-card {
  position: relative;
  z-index: 1;
  width: 340px;
  padding: var(--space-xl) var(--space-lg) 28px;
  border-radius: var(--radius-xl);
  background: var(--surface);
  text-align: center;
  box-shadow: 0 20px 60px rgba(0,0,0,0.4);
  opacity: 0;
  transform: scale(0.9);
  transition: transform 500ms cubic-bezier(0.34, 1.56, 0.64, 1), opacity 400ms ease;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-card.is-show {
  opacity: 1;
  transform: scale(1);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-modal-avatar {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  margin: 0 auto var(--space-md);
  border-radius: var(--radius-lg);
  background: linear-gradient(135deg, var(--screened-demo-accent), var(--screened-demo-accent-deep));
  color: var(--text-inverse);
  font-size: var(--fs-3xl);
  font-weight: var(--fw-950);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-card small,
body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-card p,
body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-card em {
  color: var(--screened-demo-muted);
  font-style: normal;
  font-weight: var(--fw-650);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-card h3 {
  margin: var(--space-sm) 0 14px;
  color: var(--screened-demo-fg);
  font-size: 22px;
  line-height: 28px;
  font-weight: var(--fw-950);
  letter-spacing: -0.02em;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-card p {
  margin: 0 0 20px;
  font-size: var(--fs-md);
  line-height: 21px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-card button,
body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-form-sheet > button,
body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-success button {
  position: relative;
  width: 100%;
  height: 52px;
  border: 0;
  border-radius: var(--radius-pill);
  background: linear-gradient(135deg, var(--screened-demo-accent), var(--screened-demo-accent-deep));
  color: var(--text-inverse);
  font: 850 15px/1 var(--font-sans);
  box-shadow: 0 4px 16px rgba(139, 92, 246, 0.35);
  overflow: hidden;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-card button.is-glow,
body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-form-sheet > button.is-glow {
  animation: screenedDemoButtonPulse 1600ms ease-in-out infinite;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-form-sheet {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 5;
  max-height: 92%;
  overflow-y: auto;
  padding: var(--space-sm) 20px 28px;
  border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
  background: var(--surface);
  transform: translateY(105%);
  transition: transform 500ms cubic-bezier(0.32, 0.72, 0, 1);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-form-sheet.is-open {
  transform: translateY(0);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-handle {
  width: 36px;
  height: 4px;
  margin: 0 auto var(--space-md);
  border-radius: var(--radius-pill);
  background: #d1d5db;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-form-sheet h3,
body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-success h3 {
  margin: 0 0 var(--space-2xs);
  color: var(--screened-demo-fg);
  font-size: 20px;
  line-height: 25px;
  font-weight: var(--fw-950);
  letter-spacing: -0.02em;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-form-sheet p,
body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-success p {
  margin: 0 0 20px;
  color: var(--screened-demo-muted);
  font-size: var(--fs-md);
  line-height: 20px;
  font-weight: var(--fw-650);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-form-card {
  margin-bottom: var(--space-sm);
  padding: 14px;
  border: 1px solid transparent;
  border-radius: var(--radius-lg);
  background: var(--bg-subtle);
  transition: background 260ms ease, border-color 260ms ease;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-form-card.is-screened-out {
  border-color: #f5a0a0;
  background: var(--screened-demo-danger-bg);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-label {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  margin-bottom: 10px;
  color: var(--screened-demo-fg);
  font-size: var(--fs-md);
  line-height: 18px;
  font-weight: var(--fw-850);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-label span {
  min-width: 20px;
  color: var(--screened-demo-accent);
  font-size: 13px;
  font-weight: var(--fw-950);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-answer {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 40px;
  padding: 10px var(--space-sm);
  border: 1px solid var(--screened-demo-border);
  border-radius: 10px;
  background: var(--surface);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-answer i,
body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-options-list i {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 18px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--screened-demo-accent);
  opacity: 0;
  transform: scale(0);
  transition: opacity 260ms ease, transform var(--motion-drawer) cubic-bezier(0.34, 1.7, 0.55, 1.05), background var(--motion-ui) ease;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-answer i::before,
body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-options-list i::before {
  content: "✓";
  color: var(--text-inverse);
  font-size: var(--fs-sm);
  font-weight: var(--fw-950);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-answer i.is-checked,
body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-options-list i.is-checked {
  opacity: 1;
  transform: scale(1);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-answer i.is-red {
  background: var(--screened-demo-danger);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-answer strong {
  color: var(--screened-demo-fg);
  font-size: var(--fs-md);
  line-height: 18px;
  font-weight: var(--fw-850);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-answer strong.is-empty {
  color: var(--text-tertiary);
  font-weight: var(--fw-650);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-answer strong.is-bounce {
  animation: screenedDemoAnswerBounce 450ms cubic-bezier(0.34, 1.7, 0.55, 1.05);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-form-sheet > button.is-disabled {
  opacity: 0.39;
  box-shadow: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-option-layer {
  position: absolute;
  inset: 0;
  z-index: 30;
  background: rgba(0,0,0,0);
  pointer-events: none;
  transition: background 350ms ease;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-option-layer.is-open {
  background: rgba(0,0,0,0.28);
  pointer-events: auto;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-option-sheet {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  max-height: 92%;
  overflow-y: auto;
  padding: var(--space-sm) 0 28px;
  border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
  background: var(--surface);
  transform: translateY(105%);
  transition: transform 500ms cubic-bezier(0.32, 0.72, 0, 1);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-option-sheet.is-open {
  transform: translateY(0);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-option-sheet h4,
body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-option-sheet p {
  padding: 0 20px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-option-sheet h4 {
  margin: 0 0 var(--space-2xs);
  color: var(--screened-demo-fg);
  font-size: 20px;
  font-weight: var(--fw-950);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-option-sheet p {
  margin: 0 0 var(--space-xs);
  color: var(--screened-demo-muted);
  font-size: var(--fs-md);
  font-weight: var(--fw-650);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-options-list button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  min-height: 54px;
  padding: var(--space-md) 20px;
  border: 0;
  border-bottom: 1px solid #f3f4f6;
  background: var(--surface);
  color: var(--screened-demo-fg);
  font: 800 16px/1.25 var(--font-sans);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-options-list button.is-pressing {
  background: var(--brand-tint);
  transform: scale(0.985);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-success {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 6;
  padding: 20px var(--space-lg) var(--space-xl);
  border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
  background: var(--surface);
  text-align: center;
  transform: translateY(105%);
  transition: transform 500ms cubic-bezier(0.32, 0.72, 0, 1);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-success.is-open {
  transform: translateY(0);
}

body.nifhr-screen [data-screen-id="nIfHr"] [data-demo-success-icon] {
  display: grid;
  place-items: center;
  width: 64px;
  height: 64px;
  margin: var(--space-xs) auto 20px;
  border-radius: 50%;
  background: var(--success-bg);
  transform: scale(0);
  transition: transform 400ms cubic-bezier(0.34, 1.56, 0.64, 1);
}

body.nifhr-screen [data-screen-id="nIfHr"] [data-demo-success-icon]::before {
  content: "✓";
  color: var(--success);
  font-size: 30px;
  font-weight: var(--fw-950);
}

body.nifhr-screen [data-screen-id="nIfHr"] [data-demo-success-icon].is-pop {
  transform: scale(1);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-bubble {
  position: absolute;
  top: 356px;
  left: calc(100% + 60px);
  z-index: 90;
  width: 244px;
  padding: 14px 15px;
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: 0 18px 52px rgba(17, 24, 39, 0.28), 0 5px 16px rgba(17, 24, 39, 0.16);
  opacity: 0;
  transform: translate3d(-10px, 0, 0) scale(0.92);
  transform-origin: -22px 50%;
  transition:
    opacity 320ms cubic-bezier(0.22, 1, 0.36, 1),
    transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-bubble.is-show {
  opacity: 1;
  transform: translate3d(0, 0, 0) scale(1);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-bubble.is-fade-out {
  opacity: 0;
  transform: translate3d(10px, 0, 0) scale(0.96);
  transition:
    opacity 360ms ease,
    transform 360ms cubic-bezier(0.25, 1, 0.5, 1);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-bubble::before {
  content: "";
  position: absolute;
  top: 50%;
  left: -12px;
  transform: translateY(-50%);
  border-top: 10px solid transparent;
  border-bottom: 10px solid transparent;
  border-right: 13px solid var(--text-inverse);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-bubble p {
  margin: 0;
  color: var(--screened-demo-fg);
  font-size: var(--fs-md);
  line-height: 19px;
  font-weight: var(--fw-850);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-ripple {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 56px;
  height: 56px;
  margin: -28px 0 0 -28px;
  border-radius: 50%;
  background: rgba(139,92,246,0.28);
  pointer-events: none;
  animation: screenedDemoRipple 620ms ease-out forwards;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-replay {
  position: absolute;
  inset: 0;
  z-index: 45;
  display: grid;
  place-items: center;
  border-radius: 42px;
  background: rgba(0,0,0,0.4);
  backdrop-filter: blur(4px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 260ms ease;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-replay.is-show {
  opacity: 1;
  pointer-events: auto;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-replay button {
  border: 0;
  border-radius: var(--radius-lg);
  padding: var(--space-md) 26px;
  background: var(--surface);
  color: var(--screened-demo-fg);
  font: 850 16px/1 var(--font-sans);
  box-shadow: 0 8px 32px rgba(0,0,0,0.3);
}

@keyframes screenedDemoShimmer {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.48; }
}

@keyframes screenedDemoPulseBar {
  0%, 100% { opacity: 0.55; }
  50% { opacity: 1; }
}

@keyframes screenedDemoButtonPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(139,92,246,0.4); }
  50% { box-shadow: 0 0 0 12px rgba(139,92,246,0); }
}

@keyframes screenedDemoAnswerBounce {
  0% { opacity: 0; transform: scale(0.62); }
  60% { opacity: 1; transform: scale(1.08); }
  82% { transform: scale(0.98); }
  100% { opacity: 1; transform: scale(1); }
}

@keyframes screenedDemoRipple {
  to { opacity: 0; transform: scale(4); }
}

@media (prefers-reduced-motion: reduce) {
  body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-phone *,
  body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-phone *::before,
  body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-phone *::after {
    animation: none !important;
    transition: none !important;
  }
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-out-copy {
  width: 100%;
  padding: 0;
  text-align: left;
}

/* Screening sequence overrides: hide the finished kanban coaching/highlights
   once the QR/live/screened flow takes over. */
body.nifhr-screen.screening-flow-active [data-screen-id="nIfHr"] .nifhr-compare-overlay,
body.nifhr-screen.screening-flow-active [data-screen-id="nIfHr"] .nifhr-compare-modal,
body.nifhr-screen.screening-flow-active [data-screen-id="nIfHr"] .compare-rank-coach,
body.nifhr-screen.screening-flow-active [data-screen-id="nIfHr"] .candidate-select-layer,
body.nifhr-screen.screening-flow-active [data-screen-id="nIfHr"] .nifhr-coach {
  display: none !important;
}

body.nifhr-screen.screening-flow-active [data-screen-id="nIfHr"] .kanban.import-board [data-select-target],
body.nifhr-screen.screening-flow-active [data-screen-id="nIfHr"] .kanban.import-board .review-col [data-select-target],
body.nifhr-screen.screening-flow-active [data-screen-id="nIfHr"] .kanban.import-board [data-select-target][data-selected="true"] {
  z-index: auto !important;
  border-color: var(--border) !important;
  border-width: var(--border-w) !important;
  background: var(--surface) !important;
  box-shadow: none !important;
  filter: none !important;
}

body.nifhr-screen.screening-flow-active [data-screen-id="nIfHr"] .kanban.import-board .review-col {
  z-index: auto !important;
  border-color: var(--border) !important;
  background: var(--bg-card) !important;
  box-shadow: none !important;
}

body.nifhr-screen.screening-result-visible [data-screen-id="nIfHr"] .nifhr-screening-overlay {
  display: grid !important;
  opacity: 1 !important;
  pointer-events: auto !important;
  z-index: 9800;
}

body.nifhr-screen.screening-result-visible [data-screen-id="nIfHr"] .screening-out-modal {
  display: grid !important;
  opacity: 1 !important;
  visibility: visible !important;
}

body.nifhr-screen.screening-result-visible [data-screen-id="nIfHr"] .nifhr-screening-drawer {
  transform: translateX(0) !important;
  opacity: 1 !important;
  pointer-events: auto !important;
  z-index: 9900;
}

@media (prefers-reduced-motion: reduce) {
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-live-pill i,
  body.nifhr-screen [data-screen-id="nIfHr"] .panel-status.live i,
  body.nifhr-screen [data-screen-id="nIfHr"] .waiting-typing-dots i,
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-answer-row strong.is-pop-in,
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-question-panel.is-rule-pulse {
    animation: none;
  }
}

@media (max-width: 1180px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-coach {
    left: auto;
    right: 24px;
    width: min(380px, calc(100vw - 48px));
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer {
    width: min(33vw, 88vw);
    min-width: 0;
  }

  body.nifhr-screen.profile-drawer-open [data-screen-id="nIfHr"] .nifhr-coach {
    right: calc(min(33vw, 88vw) + 16px);
    bottom: 48px;
    width: min(420px, calc(100vw - min(33vw, 88vw) - 40px));
  }
}

/* nIfHr follow-on self-screening CTA and live drawer panel */
body.nifhr-screen [data-screen-id="nIfHr"] .screening-drawer-body {
  padding-bottom: 116px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-drawer-actions {
  position: sticky;
  bottom: 0;
  z-index: 8;
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1fr);
  gap: 10px;
  padding: 14px 18px 18px;
  border-top: 1px solid rgba(124, 58, 237, 0.12);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.86), var(--surface) 34%);
  box-shadow: 0 -16px 38px rgba(17, 24, 39, 0.08);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-secondary-action,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-primary-action {
  height: 44px;
  border-radius: var(--radius-pill);
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1;
  font-weight: var(--fw-850);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-secondary-action {
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-primary);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-primary-action {
  border: 0;
  background: var(--brand);
  color: var(--text-inverse);
  box-shadow: var(--shadow-brand);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-secondary-action[disabled],
body.nifhr-screen [data-screen-id="nIfHr"] .screening-primary-action[disabled] {
  opacity: 0.39;
  cursor: default;
  box-shadow: none;
}

body.nifhr-screen.screening-phone-loop-complete [data-screen-id="nIfHr"] .screening-drawer-actions {
  animation: nifhr-actions-ready 520ms cubic-bezier(0.2, 0.9, 0.22, 1);
}

@keyframes nifhr-actions-ready {
  0% { transform: translate3d(0, 10px, 0); opacity: 0.72; }
  100% { transform: translate3d(0, 0, 0); opacity: 1; }
}

/* Reference intro modal (entry #70). It is NOT drawer-flanked: `screening-step-try` is
   absent from the drawer's visible-state list above, so the old `margin-right: 33vw` +
   `width: calc(100vw - 33vw - 96px)` reserved a gutter for a drawer that never appears —
   and because the overlay centres the modal's MARGIN box, that asymmetric margin shoved
   the card ~16.5vw left of true centre at every width >900px. Both are gone: the overlay's
   own `place-items:center` now centres it for real, at every size.
   Layout = title (full width) / copy + QR card / CTA (full width, bottom). */
body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-modal {
  width: min(724px, calc(100vw - 96px));
  min-height: 0;
  grid-template-columns: minmax(0, 1fr) clamp(176px, 26%, 208px);
  align-items: stretch;
  gap: 20px var(--space-lg);
  padding: var(--space-lg);
  border-radius: var(--radius-xl);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-primary);
  box-shadow: 0 24px 64px rgba(17, 24, 39, 0.24);
  transform: translate3d(0, 0, 0);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-title {
  grid-column: 1 / -1;
  margin: 0;
  color: var(--text-primary);
  font-size: 28px;
  line-height: 34px;
  font-weight: var(--fw-850);
  letter-spacing: -0.025em;
  text-wrap: pretty;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-cta {
  grid-column: 1 / -1;
  width: 100%;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-copy {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  min-width: 0;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-copy p {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fs-md);
  line-height: 21px;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-qr-head span {
  display: block;
  color: var(--brand-dark);
  font-size: var(--fs-2xs);
  line-height: 13px;
  font-weight: var(--fw-850);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-qr-card {
  display: grid;
  grid-template-rows: auto auto auto auto;
  align-content: start;
  gap: 10px;
  min-width: 0;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-subtle);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-qr-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-qr-head b {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: 20px;
  padding: 0 var(--space-xs);
  border-radius: var(--radius-pill);
  background: var(--success);
  color: var(--text-inverse);
  font-size: var(--fs-2xs);
  line-height: 1;
  font-weight: var(--fw-black);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-qr-head b i {
  width: 4px;
  height: 4px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.92);
  animation: nifhr-waiting-dot 1140ms cubic-bezier(0.42, 0, 0.24, 1) infinite;
}

/* Fluid: the QR never overflows its (clamped) column — it fills the width it is given,
   up to its natural size, and stays square. */
body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-qr-code {
  width: min(132px, 100%);
  height: auto;
  aspect-ratio: 1 / 1;
  justify-self: center;
  border-radius: var(--radius-md);
  border-color: var(--border-strong);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-qr-card > strong {
  margin-top: 2px;
  color: var(--text-primary);
  font-size: 13px;
  line-height: 17px;
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-qr-card > small {
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  line-height: 17px;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-feature-modal {
  width: min(500px, calc(100vw - 64px));
  min-height: 452px;
  grid-template-rows: auto 1fr auto;
  align-content: start;
  gap: 18px;
  padding: var(--space-xl);
  border: 1px solid rgba(148, 163, 184, 0.14);
  border-radius: 14px;
  background: #0f1726;
  color: #f8fafc;
  box-shadow: 0 28px 72px rgba(15, 23, 42, 0.42);
  animation: nifhr-feature-modal-in var(--motion-drawer) cubic-bezier(0.2, 0.9, 0.22, 1) both;
}

/* ⛔ #333 DELETED `.screening-feature-head`, `.screening-feature-icon` and
   `.screening-feature-icon svg` (the 40px brand square and its envelope glyph) with the feature
   card that was their ONLY user. Measured before deleting, not assumed: a grep for each class in
   every served `class="..."` attribute returned NONE for these three, and `nifhr.html` for
   `-copy`, `-actions`, `-primary` and `-modal`, which is why those four stay.
   ⚠️ EVERYTHING ELSE IN THIS BLOCK IS SHARED WITH THE SCHEDULING CARD, which carries BOTH
   `screening-feature-modal` and `scheduling-feature-modal`. Its own override block sets only four
   properties and inherits background, shadow, padding, gap, border, colour and the entrance
   animation from the bare `.screening-feature-modal` rule above. Deleting a rule here because its
   NAME contains "feature" would strip the scheduling card. */

body.nifhr-screen [data-screen-id="nIfHr"] .screening-feature-copy {
  display: grid;
  align-content: start;
  gap: 14px;
}

/* ⛔ #333 DELETED `.screening-feature-eyebrow` with its only user. ⚠️ ITS VALUES DID NOT DIE WITH
   IT: #332 had already copied them into `.wmode-eyebrow` (near the end of this file) so the
   scheduling END sheet could carry this card's copy AND its treatment. That rule holds its own
   literals and never selected this class, so it is unaffected; its comment has been corrected in
   the same edit to say the source rule is gone. */

body.nifhr-screen [data-screen-id="nIfHr"] .screening-feature-copy h2 {
  margin: 0;
  max-width: 360px;
  color: #f8fafc;
  font-size: 25px;
  line-height: 29px;
  font-weight: var(--fw-black);
  letter-spacing: -0.03em;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-feature-copy p {
  max-width: 400px;
  margin: 0;
  color: #cbd5e1;
  font-size: 13px;
  line-height: 20px;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-feature-actions {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  width: 100%;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-feature-actions button {
  min-height: 40px;
  border: 0;
  border-radius: 10px;
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  line-height: 1;
  font-weight: var(--fw-850);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-feature-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: 0 18px;
  border-radius: 10px;
  background: var(--brand);
  color: var(--text-inverse);
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  line-height: 1;
  font-weight: var(--fw-850);
  text-decoration: none;
  box-shadow: var(--shadow-brand);
}

/* ENTRY #183 (user call). The radial gradient is DELETED, so this card falls back to the base
   rule's flat `#0f1726` - the same house navy every other dark coaching card on this screen uses,
   and the same correction #181b applied to the three screening cards. The `#090b12` that sat under
   the gradient goes with it; there is no second dark in this system.

   ⚠️ `grid-template-rows` IS LOAD-BEARING AND HAD TO MOVE WITH THE MARKUP. The base rule is
   `auto 1fr auto` for head / copy / actions. Deleting the icon header leaves TWO children, so the
   copy would land in the `auto` track and the FOOTER would inherit the `1fr` and stretch, dropping
   the CTA to the bottom of a 482px box. `1fr auto` restores the original relationship: the copy
   absorbs the free space, the actions row stays its natural height.

   ⚠️ The dead `.scheduling-feature-modal .screening-feature-icon` rule was DELETED, not left behind:
   the element it styled no longer exists in this card. The `feature` card's own icon is untouched. */
body.nifhr-screen [data-screen-id="nIfHr"] .scheduling-feature-modal {
  width: min(560px, calc(100vw - 64px));
  /* 482 - 40 (the deleted icon header) - 18 (its row gap) = 424. The removed element's height comes
     OUT of the box: leaving 482 would have kept its space as a hole, because the copy is the `1fr`
     track and silently absorbs it (measured: the copy track went 300 -> 358, a 58px gap above the
     CTA that nothing occupies). At 424 the copy track is back to its original 300. */
  min-height: 424px;
  grid-template-rows: 1fr auto;
  border-color: rgba(196, 181, 253, 0.18);
}

body.nifhr-screen [data-screen-id="nIfHr"] .scheduling-feature-modal .screening-feature-copy h2 {
  max-width: 430px;
  font-size: 30px;
  line-height: 34px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .scheduling-feature-modal .screening-feature-copy p {
  max-width: 458px;
  color: #d6d3e8;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-modal {
  width: min(1120px, calc(100vw - 48px));
  height: min(760px, calc(100vh - 56px));
  height: min(760px, calc(100dvh - 56px));
  grid-template-rows: auto minmax(0, 1fr);
  gap: 14px;
  padding: var(--space-md);
  border: 1px solid rgba(196, 181, 253, 0.18);
  border-radius: 18px;
  background: #090b12;
  color: #f8fafc;
  box-shadow: 0 34px 92px rgba(3, 7, 18, 0.50);
  animation: nifhr-feature-modal-in var(--motion-drawer) cubic-bezier(0.2, 0.9, 0.22, 1) both;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-chrome {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  min-height: 44px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-back,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-next {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 38px;
  border: 1px solid rgba(196, 181, 253, 0.18);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.08);
  color: #f8fafc;
  font-size: var(--fs-sm);
  line-height: 1;
  font-weight: var(--fw-850);
  transition:
    background var(--motion-fast) var(--ease-standard),
    border-color var(--motion-fast) var(--ease-standard),
    transform var(--motion-fast) var(--ease-standard);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-back {
  gap: 5px;
  padding: 0 var(--space-sm) 0 10px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-next {
  gap: 6px;
  padding: 0 14px;
  border-color: transparent;
  background: var(--brand);
  color: var(--text-inverse);
  box-shadow: 0 14px 32px rgba(124, 58, 237, 0.28);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-back:hover,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-next:hover {
  background: rgba(255, 255, 255, 0.13);
  border-color: rgba(196, 181, 253, 0.32);
  transform: translateY(-1px);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-next:hover {
  background: var(--brand-hover);
  border-color: transparent;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-back svg,
body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-next svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-title {
  min-width: 0;
  text-align: center;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-title span {
  display: block;
  margin-bottom: 2px;
  color: #c4b5fd;
  font-size: 9px;
  line-height: 12px;
  font-weight: var(--fw-black);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-title h2 {
  margin: 0;
  overflow: hidden;
  color: #f8fafc;
  font-size: var(--fs-2xl);
  line-height: 23px;
  font-weight: var(--fw-black);
  text-overflow: ellipsis;
  white-space: nowrap;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-stage {
  position: relative;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  border: 1px solid rgba(148, 163, 184, 0.18);
  border-radius: var(--radius-lg);
  background:
    radial-gradient(circle at 78% 16%, rgba(124, 58, 237, 0.20), rgba(124, 58, 237, 0) 28%),
    linear-gradient(135deg, #F9FAFB 0%, #EEF2FF 46%, #F8FAFC 100%);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-stage hirello-interview-demo {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 0;
}

@media (max-width: 700px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-modal {
    width: min(100vw - 48px, 520px);
    height: calc(100dvh - 56px);
    padding: var(--space-sm);
    border-radius: var(--radius-lg);
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-chrome {
    grid-template-columns: auto 1fr auto;
    gap: var(--space-xs);
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-back span {
    display: none;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-back {
    width: 38px;
    padding: 0;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-title h2 {
    font-size: var(--fs-lg);
    line-height: 20px;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-schedule-demo-next {
    min-width: 64px;
    padding: 0 var(--space-sm);
  }
}

@keyframes nifhr-feature-modal-in {
  0% { transform: translate3d(0, 12px, 0) scale(0.985); opacity: 0; }
  100% { transform: translate3d(0, 0, 0) scale(1); opacity: 1; }
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-follow-modal {
  width: min(420px, calc(100vw - 64px - 33vw));
  justify-self: end;
  align-self: start;
  margin-top: 104px;
  margin-right: calc(33vw + 24px);
  gap: 10px;
  padding: 22px var(--space-lg);
  border-radius: var(--radius-xl);
  border: 1px solid color-mix(in srgb, var(--brand) 20%, var(--border));
  background: var(--surface);
  color: var(--text-primary);
  box-shadow: 0 24px 70px rgba(17, 24, 39, 0.24);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-follow-modal h2 {
  margin: 0;
  color: var(--text-primary);
  font-size: 22px;
  line-height: 28px;
  font-weight: var(--fw-black);
  letter-spacing: -0.025em;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-follow-modal p {
  margin: 0;
  color: var(--text-strong);
  font-size: var(--fs-md);
  line-height: 21px;
  font-weight: var(--fw-650);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-panel {
  display: none;
  gap: 10px;
  margin-top: 2px;
  padding: 14px;
  border: 1px solid rgba(16, 185, 129, 0.42);
  border-radius: var(--radius-lg);
  background: #DFFBEA;
  box-shadow: 0 16px 38px rgba(16, 185, 129, 0.16);
}

body.nifhr-screen.screening-step-selfscreen [data-screen-id="nIfHr"] .screening-question-panel {
  display: none;
}

body.nifhr-screen.screening-step-selfscreen [data-screen-id="nIfHr"] .screening-next-panel {
  display: grid;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-kicker {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-kicker span {
  color: var(--success-text);
  font-size: var(--fs-xs);
  line-height: 14px;
  font-weight: var(--fw-black);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-kicker b {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 9px;
  border-radius: var(--radius-pill);
  background: var(--danger-text);
  color: var(--text-inverse);
  font-size: var(--fs-2xs);
  line-height: 1;
  font-weight: var(--fw-black);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-kicker b i {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--surface);
  animation: nifhr-live-white-dot 900ms ease-in-out infinite;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-card {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr);
  gap: 10px;
  align-items: center;
  padding: var(--space-sm);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.78);
  border: 1px solid rgba(16, 185, 129, 0.22);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-avatar {
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--info-text);
  color: var(--text-inverse);
  font-size: var(--fs-sm);
  font-weight: var(--fw-950);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-card strong {
  display: block;
  color: var(--text-primary);
  font-size: 13px;
  line-height: 17px;
  font-weight: var(--fw-black);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-card span {
  display: block;
  color: var(--text-muted);
  font-size: var(--fs-xs);
  line-height: 15px;
  font-weight: var(--fw-650);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-feed {
  display: grid;
  gap: 9px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-row {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) auto;
  gap: 10px;
  align-items: center;
  padding: var(--space-sm);
  min-height: 58px;
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.68);
  border: 1px solid rgba(16, 185, 129, 0.16);
  color: var(--text-primary);
  transition: transform 240ms cubic-bezier(0.2, 0.9, 0.22, 1), opacity var(--motion-ui) ease;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-row.is-hidden {
  display: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-row.is-pending {
  background: var(--surface);
  border-color: var(--brand);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand) 70%, transparent), 0 10px 24px rgba(124, 58, 237, 0.12);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-row.is-answered {
  background: rgba(255, 255, 255, 0.62);
  border-color: rgba(16, 185, 129, 0.12);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-row.is-pop {
  animation: nifhr-selfscreen-pop 520ms cubic-bezier(0.2, 0.9, 0.22, 1);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-index {
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--brand) 11%, #fff);
  color: var(--brand-dark);
  font-size: var(--fs-xs);
  font-weight: var(--fw-950);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-row.is-answered .screening-next-index {
  background: color-mix(in srgb, var(--success-text) 11%, #fff);
  color: var(--success-text);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-row strong {
  display: block;
  color: var(--text-primary);
  font-size: var(--fs-sm);
  line-height: 16px;
  font-weight: var(--fw-850);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-row em {
  display: block;
  margin-top: 1px;
  color: var(--text-muted);
  font-size: var(--fs-xs);
  line-height: 15px;
  font-style: normal;
  font-weight: var(--fw-650);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-chip {
  min-width: 64px;
  min-height: 24px;
  display: inline-grid;
  place-items: center;
  padding: 0 10px;
  border-radius: var(--radius-pill);
  background: var(--success-bg);
  color: var(--success-text);
  font-size: var(--fs-2xs);
  line-height: 1;
  font-weight: var(--fw-950);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-row.is-pending .screening-next-chip {
  background: color-mix(in srgb, var(--brand) 13%, #fff);
  color: var(--brand-dark);
}

@keyframes nifhr-selfscreen-pop {
  0% { opacity: 0; transform: translate3d(0, 8px, 0) scale(0.98); }
  70% { opacity: 1; transform: translate3d(0, -1px, 0) scale(1.01); }
  100% { opacity: 1; transform: translate3d(0, 0, 0) scale(1); }
}

/* nIfHr reference answer panel: full drawer lane, moJUV row rhythm. */
body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-panel {
  width: 100%;
  height: 405px;
  justify-self: stretch;
  overflow: hidden;
  display: none;
  grid-template-rows: 16px 351px;
  gap: 10px;
  margin-top: 0;
  padding: 14px;
  border: 1px solid color-mix(in srgb, var(--brand) 28%, var(--border));
  border-radius: var(--radius-lg);
  background: var(--bg-row-highlight);
  box-shadow: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-kicker {
  display: grid;
  grid-template-columns: 14px minmax(0, 1fr) auto;
  align-items: center;
  gap: 6px;
  min-height: 16px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-shield {
  width: 14px;
  height: 14px;
  display: grid;
  place-items: center;
  color: var(--brand);
  font-size: 13px;
  line-height: 1;
  font-weight: var(--fw-black);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-kicker > span:not(.screening-next-shield) {
  color: var(--brand-dark);
  font-size: 13px;
  line-height: 16px;
  font-weight: var(--fw-950);
  letter-spacing: 0.045em;
  text-transform: uppercase;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-kicker b {
  display: inline-flex;
  align-items: center;
  min-width: 0;
  min-height: 20px;
  justify-content: center;
  gap: 5px;
  padding: 0 7px;
  border-radius: var(--radius-pill);
  background: var(--danger);
  color: var(--text-inverse);
  font-size: var(--fs-2xs);
  line-height: 1;
  font-weight: var(--fw-black);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-kicker b i {
  width: 6px;
  height: 6px;
  display: inline-block;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.98);
  box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.5);
  animation: nifhr-live-white-dot 980ms ease-in-out infinite;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-feed {
  display: grid;
  gap: 10px;
  grid-template-rows: 46px minmax(0, 1fr) 46px;
  width: 100%;
  height: 351px;
  min-height: 0;
  overflow: hidden;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-reference {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-xs);
  width: 100%;
  height: 46px;
  min-height: 46px;
  max-height: 46px;
  overflow: hidden;
  padding: var(--space-xs) 10px;
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.74);
  border: 1px solid rgba(229, 231, 235, 0.82);
  color: var(--text-primary);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-reference.is-complete {
  opacity: 0.55;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-reference.is-active {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: 36px minmax(0, 1fr);
  align-items: start;
  gap: var(--space-sm);
  width: 100%;
  height: 100%;
  min-height: 0;
  max-height: none;
  overflow: hidden;
  padding: var(--space-sm) 10px;
  border: 2px solid var(--brand);
  border-radius: var(--radius-md);
  background: var(--surface);
  opacity: 1;
  box-shadow: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-reference-head {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-xs);
  width: 100%;
  height: 36px;
  min-height: 36px;
  overflow: hidden;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-avatar {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--brand-tint);
  color: var(--brand-hover);
  font-size: 13px;
  line-height: 1;
  font-weight: var(--fw-950);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-reference.is-active .screening-next-avatar {
  background: #111827;
  color: var(--text-inverse);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-summary strong {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text-primary);
  font-size: var(--fs-lg);
  line-height: 17px;
  font-weight: var(--fw-850);
  letter-spacing: -0.02em;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-summary span {
  display: block;
  margin-top: 2px;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text-tertiary);
  font-size: 13px;
  line-height: 16px;
  font-weight: 725;
  letter-spacing: -0.015em;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-complete {
  padding: 2px var(--space-xs);
  border-radius: var(--radius-pill);
  background: var(--success-bg);
  color: var(--success-text);
  font-size: var(--fs-sm);
  line-height: 1;
  font-weight: var(--fw-850);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-chip {
  min-width: 0;
  max-width: none;
  min-height: 20px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 0 9px;
  border-radius: var(--radius-pill);
  background: var(--success);
  color: var(--text-inverse);
  font-size: var(--fs-2xs);
  line-height: 1;
  font-weight: var(--fw-black);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-chip .waiting-typing-dots {
  gap: 3px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-chip .waiting-typing-dots i {
  width: 4px;
  height: 4px;
  background: rgba(255, 255, 255, 0.92);
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-answer-block {
  display: grid;
  grid-template-rows: minmax(0, 1fr) minmax(0, 1fr) 37px;
  gap: var(--space-sm);
  width: 100%;
  height: 100%;
  min-height: 0;
  margin-top: 0;
  overflow: hidden;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-answer-block p {
  margin: 0;
  display: grid;
  grid-template-rows: 14px minmax(0, 1fr);
  gap: 3px;
  overflow: hidden;
  min-height: 0;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-answer-block p:nth-child(-n + 2) {
  height: auto;
  min-height: 0;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-answer-block p:nth-child(3) {
  height: 37px;
  min-height: 37px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-answer-block p strong {
  display: block;
  height: 15px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text-tertiary);
  font-size: var(--fs-sm);
  line-height: 15px;
  font-weight: var(--fw-850);
  letter-spacing: -0.015em;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-answer-block p span {
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  color: var(--text-primary);
  font-size: 13px;
  line-height: 16px;
  font-weight: var(--fw-medium);
  letter-spacing: -0.018em;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-answer-block p span.is-typing {
  display: inline-flex;
  align-items: center;
  width: fit-content;
  min-height: 8px;
  padding: 0;
  border-radius: 0;
  background: transparent;
  color: var(--text-tertiary);
  -webkit-line-clamp: unset;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-answer-block p span.is-typing .waiting-typing-dots {
  gap: 2px;
  height: 8px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-answer-block p span.is-typing .waiting-typing-dots i {
  width: 3px;
  height: 3px;
  background: #9CA3AF;
  opacity: 0.82;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-answer-block p:nth-child(3) span {
  -webkit-line-clamp: 1;
}

body.nifhr-screen [data-screen-id="nIfHr"] .screening-next-answer-block p span i {
  display: inline-block;
  width: 2px;
  height: 14px;
  margin-left: 3px;
  border-radius: var(--radius-pill);
  background: var(--brand);
  vertical-align: -2px;
  animation: nifhr-reference-caret 920ms steps(1) infinite;
}

@keyframes nifhr-reference-caret {
  0%, 48% { opacity: 1; }
  49%, 100% { opacity: 0; }
}

/* Shortlist scheduling feature beat. */
body.nifhr-screen [data-screen-id="nIfHr"] .column-kebab {
  width: 30px;
  height: 30px;
  display: none;
  place-items: center;
  border: 1px solid rgba(124, 58, 237, 0.18);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--brand);
  font-size: var(--fs-sm);
  line-height: 1;
  letter-spacing: -0.12em;
  font-weight: var(--fw-black);
  transform: rotate(90deg);
  box-shadow: 0 10px 24px rgba(76, 29, 149, 0.12);
}

body.nifhr-screen.shortlist-scheduling-step [data-screen-id="nIfHr"] .column-kebab {
  display: grid;
}

body.nifhr-screen.shortlist-scheduling-step::before {
  z-index: 9000;
  background: rgba(17, 24, 39, 0.64);
  /* #83: same as the move-step dim above — the scheduling-step spotlight dims the board but its
     ::before was pointer-events:none, so the burger/shell leaked taps. Capture them; only the
     raised .shortlist-col/kebab + FAB stay tappable. */
  pointer-events: auto;
}

body.nifhr-screen.shortlist-scheduling-step [data-screen-id="nIfHr"] .kanban.import-board {
  position: relative;
  overflow: visible;
}

body.nifhr-screen.shortlist-scheduling-step [data-screen-id="nIfHr"] .kanban.import-board .kanban-col {
  position: relative;
  z-index: auto;
  filter: grayscale(0.18);
}

body.nifhr-screen.shortlist-scheduling-step [data-screen-id="nIfHr"] .kanban.import-board .shortlist-col {
  filter: none;
  background: var(--bg-card);
  border-color: rgba(229, 231, 235, 0.82);
  box-shadow: none;
}

body.nifhr-screen.shortlist-scheduling-step [data-screen-id="nIfHr"] .kanban.import-board .shortlist-col .kanban-title,
body.nifhr-screen.shortlist-scheduling-step [data-screen-id="nIfHr"] .kanban.import-board .shortlist-col .candidate-stream {
  position: relative;
  z-index: 9044;
}

body.nifhr-screen.shortlist-scheduling-step [data-screen-id="nIfHr"] .kanban.import-board .shortlist-col .candidate-stream {
  padding: var(--space-sm);
  margin: -12px;
  border: 2px dashed var(--brand);
  border-radius: var(--radius-lg);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(249, 247, 255, 0.98)),
    var(--bg-row-highlight);
  box-shadow:
    0 0 0 4px rgba(255, 255, 255, 0.88),
    0 24px 58px rgba(76, 29, 149, 0.22);
}

body.nifhr-screen.shortlist-scheduling-step [data-screen-id="nIfHr"] .shortlist-kebab {
  position: relative;
  z-index: 9052;
  color: var(--text-inverse);
  border-color: var(--brand);
  background: var(--brand);
  box-shadow:
    0 0 0 0 rgba(124, 58, 237, 0.56),
    0 16px 34px rgba(76, 29, 149, 0.22);
  animation: nifhr-shortlist-kebab-pulse 1.35s cubic-bezier(0.42, 0, 0.24, 1) infinite;
}

body.nifhr-screen.shortlist-scheduling-step [data-screen-id="nIfHr"] .shortlist-kebab::after {
  content: "";
  position: absolute;
  inset: -9px;
  border: 2px solid rgba(124, 58, 237, 0.82);
  border-radius: var(--radius-pill);
  pointer-events: none;
}

@keyframes nifhr-shortlist-kebab-pulse {
  0%, 100% {
    box-shadow:
      0 0 0 0 rgba(124, 58, 237, 0.50),
      0 16px 34px rgba(76, 29, 149, 0.22);
  }
  50% {
    box-shadow:
      0 0 0 14px rgba(124, 58, 237, 0),
      0 16px 34px rgba(76, 29, 149, 0.26);
  }
}

/* Messenger guide guard.
   Keep this at the end of the file so old screening/result overlays cannot
   repaint above the single SVG mask guide when stale classes survive. */
body.nifhr-screen.screening-step-messenger [data-screen-id="nIfHr"] .nifhr-screening-overlay,
body.nifhr-screen.messenger-guide-active [data-screen-id="nIfHr"] .nifhr-screening-overlay,
body.nifhr-screen.messenger-demo-live [data-screen-id="nIfHr"] .nifhr-screening-overlay,
body.nifhr-screen.screening-step-messenger [data-screen-id="nIfHr"] .nifhr-screening-drawer,
body.nifhr-screen.messenger-guide-active [data-screen-id="nIfHr"] .nifhr-screening-drawer,
body.nifhr-screen.messenger-demo-live [data-screen-id="nIfHr"] .nifhr-screening-drawer,
body.nifhr-screen.screening-step-messenger [data-screen-id="nIfHr"] .nifhr-compare-overlay,
body.nifhr-screen.messenger-guide-active [data-screen-id="nIfHr"] .nifhr-compare-overlay,
body.nifhr-screen.messenger-demo-live [data-screen-id="nIfHr"] .nifhr-compare-overlay,
body.nifhr-screen.screening-step-messenger [data-screen-id="nIfHr"] .nifhr-compare-modal,
body.nifhr-screen.messenger-guide-active [data-screen-id="nIfHr"] .nifhr-compare-modal,
body.nifhr-screen.messenger-demo-live [data-screen-id="nIfHr"] .nifhr-compare-modal,
body.nifhr-screen.screening-step-messenger [data-screen-id="nIfHr"] .compare-rank-coach,
body.nifhr-screen.messenger-guide-active [data-screen-id="nIfHr"] .compare-rank-coach,
body.nifhr-screen.messenger-demo-live [data-screen-id="nIfHr"] .compare-rank-coach,
body.nifhr-screen.screening-step-messenger [data-screen-id="nIfHr"] .candidate-select-layer,
body.nifhr-screen.messenger-guide-active [data-screen-id="nIfHr"] .candidate-select-layer,
body.nifhr-screen.messenger-demo-live [data-screen-id="nIfHr"] .candidate-select-layer,
body.nifhr-screen.screening-step-messenger [data-screen-id="nIfHr"] .nifhr-coach,
body.nifhr-screen.messenger-guide-active [data-screen-id="nIfHr"] .nifhr-coach,
body.nifhr-screen.messenger-demo-live [data-screen-id="nIfHr"] .nifhr-coach {
  display: none !important;
  visibility: hidden !important;
  opacity: 0 !important;
  pointer-events: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

body.nifhr-screen.messenger-guide-active [data-screen-id="nIfHr"] .messenger-content,
body.nifhr-screen.messenger-guide-active [data-screen-id="nIfHr"] .messenger-list,
body.nifhr-screen.messenger-guide-active [data-screen-id="nIfHr"] .messenger-chat {
  opacity: 1 !important;
  filter: none !important;
}

/* Positioning context for the camera scrim (.job-card-cam-scrim). (Originally added for
   the removed iPhone-preview TEST button, and once also for .job-notif-push — which was
   reparented to .job-card-intro-overlay on 2026-07-15 to anchor at the viewport top. The
   scrim still depends on this, so it must stay.) */
.job-card-help-modal { position: relative; }

/* "New application" push notification — REAL iOS banner chrome (entry #70): frosted
   material + squircle app-icon tile + grey app name / right-aligned "now" header row +
   bold title + body. Ported from production-ready-interviews' .notification (class-based,
   literal colours — the same port reference-check's .ref-push already made).
   (2026-07-15) It is now a direct child of .job-card-intro-overlay (position:fixed;inset:0),
   so position:absolute + top:16px pins it to the VIEWPORT TOP on every view/device — it
   slides down from above the screen and plays once after the 500 count settles. z14 keeps
   it above the modal/camera subtree at the overlay level. */
.job-notif-push{
  position:absolute; top:16px; left:0; right:0; margin-inline:auto; z-index:14;
  /* Measured against the VIEWPORT, not the host's padding box (2026-07-17). This is
     viewport-anchored by design (that is the whole point of the 2026-07-15 reparent), so a
     `calc(100% - 96px)` silently retuned itself when the overlay's padding went 32px -> 0 for
     the bottom sheet (230px -> 294px on a 390px phone). Pin the intent so it cannot drift again.
     z-index 14 is INSIDE .job-card-intro-overlay's stacking context (z2300), so it lands above
     the dim, the sheet and the deck with no number of its own to maintain. */
  /* ⚠️⚠️ (#351) SIZED FROM collaborator-invite.html's `.final-push`, HER NAMED REFERENCE, AND ITS
     NUMBERS ARE *NOT* THE ONES IN THAT FILE'S `.notification` BASE RULE. That base rule is
     byte-identical to what this block used to be (420 / gap 10 / padding 11px 13px 12px /
     radius 23 / icon 36 / 13px title), which is exactly the trap: reading the reference's base
     class would have concluded there was nothing to change. What renders in her screenshot is
     `.final-push` (collaborator-invite.html:700-753) overriding it, plus `.final-push.is-drop`.
     Copied: width 560, gap 16, padding 20px 22px 21px, radius 34, icon 62/15, head gap 4,
     app+time 17, title 22, body 20/1.32.
     ⚠️ NOT copied, deliberately: `.final-push`'s POSITION (left/top 50% with a -280px margin,
     anchoring it to the middle of that film's stage). This one stays viewport-anchored at
     `top:16px`, which is the whole point of the 2026-07-15 reparent documented above, and her
     ask was the size and layout of the message, not where it sits. Also not copied: that rule's
     `-webkit-line-clamp: 3`, which exists to tame a much longer string and here could only
     truncate copy she asked to keep. */
  max-width:min(560px, calc(100vw - 32px));
  display:grid; grid-template-columns:auto 1fr; align-items:start; gap:var(--space-md);
  padding:20px 22px 21px;
  border:0; border-radius:34px;
  /* SOLID WHITE (entry #99, user call) so the gag banner pops off the dark blurred deck. It was
     rgba(250,250,252,.9) + backdrop-filter:blur(18px) saturate(1.4) — a frosted material whose
     whole job was to pull the backdrop through, which behind this dim meant pulling DARK through
     and dulling the one element the viewer is meant to read. The backdrop-filter is deleted with
     it: it cannot show through an opaque fill, so it was pure GPU cost.
     ⚠️ Do NOT reach for `filter` here — mobile.spec.js asserts this element computes filter:none
     (the #94 contract that the notification stays crisp while the deck behind it is blurred). */
  background:var(--surface);
  box-shadow:0 8px 26px rgba(17,24,39,.16), 0 1px 3px rgba(17,24,39,.10);
  transform:translateY(-140%); opacity:0; pointer-events:none;
  transition:transform 440ms var(--ease-out, cubic-bezier(.16,1,.3,1)), opacity 240ms ease;
}
.job-notif-push.is-visible{ transform:translateY(0); opacity:1; }
.job-notif-icon{
  /* (#351) 62/15 from `.final-push .notif-icon`. The 68% background-size is proportional, so
     the logo scales with the tile and needs no second number. */
  width:62px; height:62px; border-radius:15px;
  background:var(--surface) url("mq50ezgz-hirelloLogo-BgVy4PP3.png") center / 68% no-repeat;
  box-shadow:0 1px 2px rgba(0,0,0,.10);
}
.job-notif-body{ min-width:0; padding-top:1px; }
/* (#351) Every size below is `.final-push`'s; every COLOUR and WEIGHT is this block's own and is
   unchanged, because her ask was size and layout while keeping this screen's own content. */
.job-notif-head{ display:flex; align-items:baseline; gap:6px; margin-bottom:var(--space-2xs); }
.job-notif-app{ font-size:17px; font-weight:var(--fw-650); letter-spacing:.01em; color:var(--text-secondary); }
.job-notif-time{ margin-left:auto; font-size:17px; font-weight:var(--fw-medium); color:var(--text-tertiary); }
.job-notif-title{ font-size:22px; font-weight:var(--fw-bold); line-height:1.25; letter-spacing:-.006em; color:#1F2937; overflow-wrap:anywhere; }
.job-notif-text{ margin-top:1px; font-size:20px; line-height:1.32; font-weight:450; color:var(--text-muted); text-wrap:pretty; overflow-wrap:anywhere; }
@media (prefers-reduced-motion: reduce){
  .job-notif-push{ transition:none; }
}

/* ══ (#399) MOBILE AND TABLET ONLY: a REALISTIC phone push banner ═════════════════════════════
   User call, from an annotated 390-wide Brave-on-Android frame: *"On mobile and tablet ONLY
   (*CRITICAL* leave desktop view!!) then make this notification into a realistic size and layout
   for the push notification at the moment this is comically large and works for the desktop but
   change of the mobile and tablet views is all"*.

   MEASURED BEFORE, headless chromium, `.is-visible` forced and settled past the 440ms transition:
   358 x 205.6 at 390x844, which is 24.4% of the screen, with the body wrapping to FOUR lines. A
   real iOS banner is nearer 11%. At 834x1112 and 1440x900 it is 560 x 152.8, and the 834 reading
   is why this tier stops at 900 rather than at a phone width: a tablet gets the same oversized
   card a phone does.

   ⚠️ THE DESKTOP RULE ABOVE IS UNTOUCHED, WHICH IS THE WHOLE ASK. Everything here is inside
   `max-width: 900px`, the project's own mobile-and-tablet tier (`min-width: 901px` is desktop
   throughout this sheet and is what `nifhr-watch-mode.js:67` gates on). Nothing at 901 and above
   sees a byte of this.
   ⚠️ ENTRY #351 PREDICTED THIS ASK IN ITS OWN WORDS and left it open: *"A narrow-width type
   step-down is one rule if she wants it."* #351 sized this banner from collaborator-invite's
   `.final-push`, which lives on a 1080 film stage, so its numbers were only ever right for a wide
   view. That call is NOT reversed here, it is scoped: desktop keeps it exactly.
   ⚠️ EVERY NUMBER BELOW IS LIFTED FROM AN EXISTING REALISTIC PUSH BANNER IN THIS CODEBASE, never
   invented: padding and radius from `.employer-push` (production-ready-interviews.html), icon size
   and radius and the 2px head gap from `.notif-icon`/`.notif-head` in the same file, which is the
   port this banner's own chrome came from at entry #70. The gap is the `--space-sm` token.
   ⚠️ NO `filter` HERE, DELIBERATELY: the guard "landing (phone): the deck behind the sheet is
   redacted" runs at 390 and asserts this element computes `filter:none` (the #94 contract that the
   banner stays crisp while the deck behind it blurs). A filter in this block would redden it.
   ⚠️ NO `-webkit-line-clamp` HERE EITHER, for #351's reason: it could only truncate copy she asked
   to keep, and the job title is config-driven so its length is not ours to assume.
   ⚠️ THE ICON NEEDS NO SECOND NUMBER: its logo is a `background` at `center / 68%`, which is
   proportional, so it follows the tile down. `references-mobile.html`'s twin uses an `<img>` with
   a padding instead and DOES need one; see the note in that file.
   ⚠️ `.wmode-push` (this file, the nifhr watch route) is NOT given this tier and that is correct,
   not an oversight: `nifhr-watch-mode.js` is gated `(min-width: 901px)`, so that banner only ever
   draws on desktop. The two stay in parity at every width either of them can be seen at.
   -> today-update.md #399 ══════════════════════════════════════════════════════════════════════ */
@media (max-width: 900px){
  .job-notif-push{
    /* 366 wide at 390, i.e. a 12px gutter each side, which is the iOS banner inset. The 400 cap
       keeps a tablet showing a phone-sized banner rather than a 560px slab. */
    max-width:min(400px, calc(100vw - 24px));
    gap:var(--space-sm);
    padding:12px 14px 13px;
    border-radius:24px;
  }
  .job-notif-icon{ width:36px; height:36px; border-radius:9px; }
  .job-notif-head{ margin-bottom:var(--space-3xs); }
  .job-notif-app{ font-size:13px; }
  .job-notif-time{ font-size:13px; }
  .job-notif-title{ font-size:15px; line-height:1.27; }
  .job-notif-text{ font-size:14px; line-height:1.34; }
}

/* Cinematic "camera" wrapper — JS sets translate()+scale() to push in on the
   notification, glide to the "500", then pull back (see the camera module in rj0wj.html).
   Transform lives here (a plain wrapper), never on the modal, whose jobCardIntroIn
   fill:both would otherwise re-apply scale(1) and override us. */
.job-card-cam{
  transform-origin: 0 0;            /* local (0,0) = modal top-left → simple framing math */
  transform: none;                  /* JS drives it per phase */
  transition: transform 900ms cubic-bezier(.22,.61,.36,1); /* JS overrides duration per phase */
  backface-visibility: hidden;
}
.job-card-cam.is-filming{ will-change: transform; }
@media (prefers-reduced-motion: reduce){
  .job-card-cam{ transform:none !important; transition:none !important; }
}

/* Grey-focus scrim — dims the card during the camera's notification beat so the lit
   push message (higher z-index) pops; fades back out as the camera lands on "501". */
.job-card-cam-scrim{
  position:absolute; inset:0; z-index:6; pointer-events:none;
  border-radius:var(--radius-sheet);
  background: rgba(17,24,39,.62);
  opacity:0;
  transition: opacity 620ms ease;
}
.job-card-cam-scrim.is-dimmed{ opacity:1; }
@media (prefers-reduced-motion: reduce){
  .job-card-cam-scrim{ display:none; }
}


/* ===== Mobile app-shell: burger + off-canvas nav drawer =====
   Injected controls (.nav-burger / .topbar-mobile-title / .nav-scrim) come from
   hirello-shell.js. At <=900px the real aside.sidebar becomes an off-canvas
   drawer; body.nav-open slides it in. Appended last so source order wins over
   the legacy 900px strip rules and the per-screen shell overrides. */
.nav-burger,
.topbar-mobile-title,
.nav-scrim { display: none; }

@media (max-width: 900px) {
  /* Single-column shell on every screen (unify the divergent per-screen paths). */
  .app-shell { grid-template-columns: 1fr; }
  body.x1riqt-screen .app-shell { grid-template-columns: 1fr; }
  body.nifhr-screen .app-shell { grid-template-columns: 1fr; }
  body.workspace-screen .app-shell { grid-template-columns: 1fr; }

  /* Burger + active-section title live in the topbar (normal flow). */
  .nav-burger {
    display: inline-grid;
    place-items: center;
    width: 40px;
    height: 40px;
    flex: 0 0 auto;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text-strong);
    cursor: pointer;
  }
  .nav-burger:hover,
  .nav-burger:focus-visible { background: var(--bg-subtle); outline: none; }
  .topbar-mobile-title {
    display: block;
    margin-right: auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text-primary);
    font-size: var(--fs-xl);
    font-weight: var(--fw-bold);
    letter-spacing: -0.2px;
  }
  .topbar { flex-direction: row; align-items: center; gap: var(--space-xs); }
  /* The inert topbar search would eat the row and crush the section title to an
     ellipsis ("J."). Standard mobile header = burger + section title + icons. */
  .topbar > .search-box { display: none; }

  /* The real sidebar becomes an off-canvas drawer. Explicit per-screen counters
     defeat the higher-specificity legacy grid/display rules. */
  .app-shell .sidebar,
  body.workspace-screen .sidebar,
  body.nifhr-screen .sidebar,
  body.x1riqt-screen .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    height: 100vh;
    height: 100dvh;
    width: min(280px, 84vw);
    min-height: 0;
    z-index: 1010;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    border-right: 1px solid var(--border);
    border-bottom: 0;
    transform: translateX(-100%);
    transition: transform var(--motion-drawer, 300ms) var(--ease-out, cubic-bezier(.16, 1, .3, 1));
  }
  body.nav-open .app-shell .sidebar,
  body.nav-open.workspace-screen .sidebar,
  body.nav-open.nifhr-screen .sidebar,
  body.nav-open.x1riqt-screen .sidebar { transform: translateX(0); }

  /* Restore the full drawer contents (undo the legacy horizontal-strip hides). */
  .sidebar .side-brand,
  .sidebar .workspace-switcher,
  .sidebar .support-link,
  body.x1riqt-screen .side-brand,
  body.nifhr-screen .side-brand,
  body.nifhr-screen .workspace-switcher,
  body.nifhr-screen .support-link { display: flex; }
  .nav-list,
  body.x1riqt-screen .nav-list { flex-direction: column; overflow: visible; padding-bottom: 0; }

  /* Scrim behind the drawer. */
  .nav-scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 1000;
    background: rgba(17, 24, 39, 0.46);
    opacity: 0;
    pointer-events: none;
    transition: opacity 200ms ease;
  }
  body.nav-open .nav-scrim { opacity: 1; pointer-events: auto; }
  body.nav-open { overflow: hidden; }

  /* rj0wj job-card modal blurs .app-shell, which would make the fixed drawer
     resolve against (and inherit the blur of) the shell. Neutralize on mobile —
     the scrim/drawer read as part of the same layer as the modal here. */
  body.job-card-modal-active .app-shell { filter: none; }

  /* Content: the comparison table can't shrink its 4 columns to a phone width
     without clipping the last candidate. Let it scroll inside its own box with
     readable min-column widths (swipe the table, not the page). */
  .comparison-table { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .comparison-row { grid-template-columns: 130px repeat(3, minmax(140px, 1fr)); }
}

@media (max-width: 900px) and (prefers-reduced-motion: reduce) {
  .app-shell .sidebar,
  body.workspace-screen .sidebar,
  body.nifhr-screen .sidebar,
  body.x1riqt-screen .sidebar,
  .nav-scrim { transition: none; }
}

/* nifhr is a viewport-LOCKED tutorial on desktop (overflow:hidden + fixed
   100dvh heights) so it never scrolls. On a phone the board is taller than the
   screen, so cards below the fold (Priya) are unreachable — you can't scroll.
   An earlier @media(980) tried to unlock it but was defeated by the later
   unconditional overflow:hidden rules. Appended last → wins source order.
   Override the WHOLE overflow chain so the page scrolls vertically, and stack
   the fixed-width board so unlocking never introduces horizontal scroll. */
@media (max-width: 900px) {
  body.nifhr-screen { overflow-x: hidden; overflow-y: auto; }
  body.nifhr-screen .app-shell { height: auto; min-height: 100dvh; }
  body.nifhr-screen .main { height: auto; min-height: 0; overflow: visible; }
  body.nifhr-screen .content,
  body.nifhr-screen .nifhr-content {
    height: auto;
    min-height: 0;
    overflow: visible;
  }
  body.nifhr-screen .nifhr-stage { height: auto; min-height: 0; overflow: visible; }
  body.nifhr-screen .nifhr-board-shell {
    width: 100%;
    height: auto;
    grid-template-columns: 1fr;
    padding-bottom: var(--space-lg);
  }
  /* Messenger step: let it grow + scroll too, and stack its 2-col grid. */
  body.nifhr-screen.screening-step-messenger .messenger-content {
    height: auto;
    min-height: 0;
    overflow: visible;
    grid-template-columns: 1fr;
  }
}

/* (2026-07-15, user call) Re-lock the GUIDED tutorial steps to the viewport so the dimmed
   background can't be scrolled around on phones/tablets — the counter-rule to the unlock block
   directly above. Phases 1-3 (base "View/message", candidate-move, compare/rank) carry NO
   `screening-step-*` body class; screening / references / scheduling / messenger each carry one,
   so `:not([class*="screening-step-"])` EXCLUDES them and they keep the scroll/grow behaviour they
   need. This selector is higher-specificity than the unlock's `body.nifhr-screen`, so it wins; the
   `overflow:hidden` locks the body (the viewport takes its scrollability from body when html is
   overflow:visible — same mechanism as body.nav-open / rj0wj's job-card-modal-active). The 900px
   width arm covers phones + portrait tablets; landscape tablets (>900px) already get the desktop
   locked layout. In each guided step the star content sits at the top and the compare modal is its
   own fixed layer with its own internal scroll, so nothing below-fold is needed here. NEVER prove
   this with window.scrollTo (Chrome honours a programmatic scroll under overflow:hidden) — use a
   real touch-drag. */
@media (max-width: 900px) {
  body.nifhr-screen:not([class*="screening-step-"]) {
    overflow: hidden;
    overscroll-behavior: none;
  }

  /* Compare/rank on phones/tablets: hide the floating "Rank your preferred candidates" coach — it
     read as a broken dark sheet floating in the dimmed area. Ranking is auto-armed in JS
     (setCompareModal → comparison-rank-active) so the stacked list is immediately draggable and
     "Submit my ranking" ungates; desktop keeps the coach + manual "Try it now". Specificity (0,4,1)
     beats the base show rule (0,3,1). */
  body.nifhr-screen.comparison-modal-open [data-screen-id="nIfHr"] .compare-rank-coach {
    opacity: 0;
    pointer-events: none;
    visibility: hidden;
  }
}

/* ============================================================================
   OPENING-FLOW + CHOOSER (2026-07-12) — appended last so source order wins.
   ============================================================================ */

/* ---- rj0wj boot veil: seamless handoff from opening.html's fade-to-black.
   Armed only when the cinematic sets the single-use sessionStorage flag (read in a
   sync <head> script that stamps html.boot-veil-on before first paint). The veil is
   born fully opaque (#05040a = opening's #finalBlack, so the navigation frame is
   invisible) then fades out; the intro modal + odometer run underneath. Direct
   loads/refreshes never carry the class → the veil stays hidden and inert. */
.boot-veil {
  position: fixed;
  inset: 0;
  z-index: 999999; /* above every real overlay (max in-app z 10020, intro overlay 2200) */
  background: #05040a;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
html.boot-veil-on .boot-veil {
  visibility: visible;
  animation: bootVeilReveal 0.7s cubic-bezier(0.4, 0, 0.2, 1) 0.15s both;
}
@keyframes bootVeilReveal {
  from { opacity: 1; }
  to { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  /* Opening's reduced-motion path never shows a black frame — don't fabricate one. */
  html.boot-veil-on .boot-veil { animation: none; opacity: 0; visibility: hidden; }
}

/* ---- Carousel "Jump in" pills: every intro-modal panel is a jump-point into the
   walkthrough. Text panels carry the pill IN-FLOW (bottom-right row) — the panels'
   mini-demo cursors are position:absolute anchored ABOVE the article, so the article
   must NOT gain position:relative (it would re-anchor them). The .jc-embed panels are
   already position:relative, so their pill overlays bottom-right above the iframe
   (whose pointer-events are none — the pill needs its own auto). */
.jc-jump-row {
  display: flex;
  justify-content: flex-end;
  margin-top: 10px;
}
.jc-jump {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px var(--space-sm);
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  box-shadow: 0 4px 10px rgba(17, 24, 39, 0.06);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--brand);
  text-decoration: none;
  cursor: pointer;
}
.jc-jump:hover,
.jc-jump:focus-visible {
  background: var(--brand-tint);
  border-color: var(--brand);
  outline: none;
}
.jc-track > article.jc-embed .jc-jump {
  position: absolute;
  right: 10px;
  bottom: 10px;
  z-index: 6;
  pointer-events: auto;
}
@media (hover: hover) {
  /* Mouse: keep the embed overlays quiet until the panel is hovered/focused. Text-panel
     pills stay always-visible (they're in-flow content and teach the affordance). */
  .jc-track > article.jc-embed .jc-jump {
    opacity: 0;
    transform: translateY(4px);
    transition: opacity var(--motion-fast) ease, transform var(--motion-fast) ease;
  }
  .jc-track > article.jc-embed:hover .jc-jump,
  .jc-track > article.jc-embed:focus-within .jc-jump {
    opacity: 1;
    transform: none;
  }
}

/* ---- The 7th deck card's universal-inbox preview: entry #103 replaced the static
   `.jc-inbox-*` rows with the animated `.jc-chat-*` machine. Its CSS lives in the
   #103 tail block at the END of this file (cascade-order law). */

/* ---- Persistent "Demo sections" launcher + panel (injected by hirello-shell.js).
   Lives on <body>, independent of .topbar/.sidebar, so it survives nifhr's
   messenger-guide-active / messenger-demo-live states (topbar gets
   pointer-events:none + filter:grayscale there — a child can't escape an ancestor
   filter). z 20000 clears every real overlay (max in-app z 10020) but stays far
   below the boot veil (999999) and the reserved debug range. */
.demo-sections-launcher {
  position: fixed;
  top: 76px; /* just under the topbar row; clear of .topbar-right and the burger */
  right: 20px;
  /* Above nifhr's messenger tutorial layers (.messenger-guide-mask 2147483001,
     .messenger-content 2147483002 in demo-live) so the escape hatch stays clickable
     during the messenger walkthrough — verified unclickable at z 20000. Kept below
     the iphone TEST overlay (2147483040) and the boot veil pattern. */
  z-index: 2147483030;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-primary);
  font: 700 12px/1 Inter, system-ui, sans-serif;
  box-shadow: var(--shadow-float);
  cursor: pointer;
}
.demo-sections-launcher:hover,
.demo-sections-launcher:focus-visible {
  border-color: var(--brand);
  color: var(--brand);
  outline: none;
}
.demo-sections-panel {
  position: fixed;
  top: 114px;
  right: 20px;
  z-index: 2147483030; /* pairs with the launcher — see its z-index comment */
  width: min(260px, calc(100vw - 40px));
  padding: 6px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--surface);
  box-shadow: var(--shadow-popover);
}
.demo-sections-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs);
  padding: 9px 10px;
  border-radius: var(--radius-sm);
  font-size: 13px;
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  text-decoration: none;
}
.demo-sections-item:hover,
.demo-sections-item:focus-visible {
  background: var(--bg-subtle);
  outline: none;
}
.demo-sections-item.is-current { color: var(--brand); }
/* first-of-type, NOT first-child: sheet mode inserts the grab handle before the
   items, which would silently un-match a :first-child divider. */
.demo-sections-item:first-of-type {
  border-bottom: 1px solid var(--border);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  margin-bottom: var(--space-2xs);
}
.demo-sections-tag {
  font-size: var(--fs-2xs);
  font-weight: var(--fw-extrabold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--brand);
}
@media (max-width: 900px) {
  /* Icon-only pill on phones (like the burger); keep it clear of the topbar row. */
  .demo-sections-launcher span { display: none; }
  .demo-sections-launcher { padding: 9px; top: 64px; right: 14px; }
  .demo-sections-panel { top: 102px; right: 14px; }
}

/* ---- nifhr ?scene= deep-link seeding: hide the first paint while the scene state is
   applied synchronously (prevents a flash of the default STEP-3 board). The class is
   stamped by a <head> script pre-paint and removed in seedStateForScene()'s finally.
   visibility (not display) keeps layout intact so the seeding functions' rect
   measurements stay correct. */
html.nifhr-scene-seeding body { visibility: hidden; }

/* ============================================================================
   PHASE 2 (2026-07-12): mobile / physical-device optimisation — appended last
   so source order wins over the legacy rules it corrects.
   ============================================================================ */

/* -- Step-aware kanban columns on phones. The tutorial focuses one move at a
   time; a phone shows ONLY the columns that step needs (audit: all three were
   squeezed to ~100-130px each at 390px). Tablet (>700px) keeps 3 columns. */
@media (max-width: 700px) {
  /* x1riqt: the resume-drop step only ever fills Applied — Maybe/Shortlist are
     desktop context. Show Applied alone, full width (same selector chain as the
     3-col override at :5689, appended later so it wins). */
  body.x1riqt-screen [data-screen-id="x1RIQT"] .import-board {
    grid-template-columns: 1fr;
  }
  body.x1riqt-screen [data-screen-id="x1RIQT"] .kanban-col.review-col,
  body.x1riqt-screen [data-screen-id="x1RIQT"] .kanban-col.shortlist-col {
    display: none;
  }

  /* nifhr: outside a move step, stack the columns vertically (the generic
     .kanban{1fr} mobile rule is defeated by the unconditional :8296 — this
     re-asserts it with a matching chain). height:auto pairs with the ≤900px
     scroll unlock. */
  body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board {
    grid-template-columns: 1fr;
    height: auto;
  }
  /* During a move step: ONLY source + target, side by side. */
  body.nifhr-screen.candidate-move-step[data-candidate-move-source] [data-screen-id="nIfHr"] .kanban.import-board {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  body.nifhr-screen.candidate-move-step[data-candidate-move-source="new"] [data-screen-id="nIfHr"] .kanban-col.shortlist-col,
  body.nifhr-screen.candidate-move-step[data-candidate-move-source="reviewing"] [data-screen-id="nIfHr"] .kanban-col.new-col {
    display: none;
  }
}

/* -- Coach card. The legacy @media(980) rule leaves a stray bottom:24px that,
   combined with the base top:174px, STRETCHES the coach to fill the stage
   (~997px tall at 390×844 — measured). Kill the stray bottom across that whole
   range, then give phones a proper fixed bottom-sheet. */
@media (max-width: 980px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-coach {
    bottom: auto;
  }
}
@media (max-width: 700px), (max-width: 900px) and (max-height: 500px) {
  /* Bottom sheet on phones — gated on width OR height so LANDSCAPE phones
     (e.g. 844×390: width past the 700px gate but only 390px tall, where the
     desktop-absolute top:537px placement is below the fold) get it too.
     Must ALSO match the higher-specificity move-step variants (:9329 sets
     width:min(430px,30vw) → 117px at 390px; :9402 lifts bottom by 128px on
     completion) — same chains here, appended last, so the bottom sheet wins
     in every tutorial state. */
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-coach,
  body.nifhr-screen.profile-drawer-open [data-screen-id="nIfHr"] .nifhr-coach,
  body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .nifhr-coach,
  body.nifhr-screen.candidate-move-step.candidate-move-complete [data-screen-id="nIfHr"] .nifhr-coach {
    position: fixed;
    top: auto;
    left: 14px;
    right: 14px;
    bottom: 14px;
    width: auto;
    min-height: 0;
    max-height: 44dvh;
    overflow-y: auto;
    padding: 18px;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-coach h2 {
    font-size: 20px;
    line-height: 24px;
  }
}

/* -- Profile drawer: the @media(1180) cap `min(33vw, 88vw)` is inverted (33vw
   always wins → 129px at 390px, text clipped mid-word). Real cap semantics: */
@media (max-width: 1180px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer {
    width: min(460px, 92vw);
    min-width: 0;
  }
}

/* -- Messenger document/location/PDF drawers: fixed `33vw + min-width:460px`
   hangs ~70px off BOTH edges of a phone. Full-width-minus-margin sheet: */
@media (max-width: 900px) {
  body.nifhr-screen .messenger-document-drawer,
  body.nifhr-screen .messenger-location-drawer,
  body.nifhr-screen .messenger-pdf-drawer {
    width: calc(100vw - 24px);
    min-width: 0;
  }
}

/* -- Screening-sequence modals + answers drawer. The whole family sizes
   against a hardcoded 33vw desktop side-drawer (width: calc(100vw - 64px -
   33vw) → ~197px at 390px; margin-right: calc(33vw + 24px) shoves boxes
   off-centre; the drawer itself is 460px min-width hanging off the phone).
   Phone pattern instead: full-width modal anchored top, answers drawer as a
   bottom sheet — nothing overlaps, every CTA stays reachable. */
@media (max-width: 900px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-screening-overlay {
    padding: 14px;
    place-items: start center;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-modal {
    width: min(724px, calc(100vw - 28px));
    margin-right: 0;
    grid-template-columns: 1fr;
    max-height: calc(100dvh - 28px);
    overflow-y: auto;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-modal,
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-live-modal,
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-out-modal,
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-follow-modal {
    width: min(736px, calc(100vw - 28px));
    min-height: 0;
    margin-right: 0;
    justify-self: center;
    max-height: calc(56dvh - 14px);
    overflow-y: auto;
    transform: none; /* kills the qr modal's desktop drawer-compensation translateX(-9vw) */
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-screening-drawer {
    inset: auto 0 0 0;
    width: 100vw;
    min-width: 0;
    height: 42dvh;
    border-left: none;
    border-top: 1px solid var(--border);
  }
  /* Feature/scheduling teaser modals were missed by the first pass: flat
     min-heights (482/452px) with an unscrollable fixed overlay made their CTA
     unreachable on a landscape phone (hard walkthrough dead-end — measured). */
  body.nifhr-screen [data-screen-id="nIfHr"] .scheduling-feature-modal,
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-feature-modal {
    width: min(736px, calc(100vw - 28px));
    min-height: 0;
    margin-right: 0;
    max-height: calc(100dvh - 28px);
    overflow-y: auto;
  }
}

/* -- rj0wj intro modal: the `calc(100dvh - 64px)` override that lived here is GONE (2026-07-17).
   It existed because the base cap was calc(100vh - 64px), and 100vh on Android Chrome is the
   LARGE viewport (measured 801px tall in a 785px visual viewport on a real Pixel 6: 8px clipped
   top AND bottom). The base is now a plain `88dvh` on a bottom-flush sheet, so the vh trap it
   patched cannot occur and the symmetric -64px margin maths means nothing for one edge. */

/* -- Move step on phones: the fixed coach bottom-sheet covered Ahmed's card
   almost entirely and Priya's completely on a real Pixel 6 (elementFromPoint →
   the coach; the page had no scroll room below the board). Reserve clearance
   below the board so every card can be scrolled above the sheet (the circles
   layer resyncs on scroll — nifhr JS). */
@media (max-width: 700px), (max-width: 900px) and (max-height: 500px) {
  body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board {
    padding-bottom: calc(44dvh + 24px);
  }
}

/* -- LANDSCAPE phones (short + wide, e.g. 844×390): the stacked
   modal-over-bottom-sheet pattern breaks down (56dvh ≈ 204px clips modal CTAs
   right where the drawer sits — measured). There's width to spare, so go
   side-by-side instead: modal left, answers drawer as a right panel. This
   block is AFTER the ≤900px one, so it re-overrides every stacked prop. */
@media (max-width: 900px) and (max-height: 500px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-modal,
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-live-modal,
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-out-modal,
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-follow-modal {
    width: min(520px, calc(100vw - 44vw - 42px));
    margin-right: 44vw;
    max-height: calc(100dvh - 28px);
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-screening-drawer {
    inset: 0 0 0 auto;
    width: 44vw;
    height: 100dvh;
    border-top: none;
    border-left: 1px solid var(--border);
  }
}

/* -- rj0wj workspace spotlight on mobile: the switcher lives inside the
   off-canvas drawer (z 1010 / scrim 1000); the ring overlay must paint above
   the OPEN drawer, and the desktop side-arrow geometry is meaningless when the
   tooltip sits BELOW the ring.
   (Entry #108, 2026-07-18, user call) That side arrow used to be `display:none`
   here, which left the phone's coach card floating with NOTHING pointing at the
   workspace switcher it teaches. It is now a sharp UP arrow on the card's top
   edge, aimed from `--ws-tail-x` (written live from the switcher's measured rect
   in rj0wj's placeWorkspaceTour(), same mechanism as the job coachmark's
   --tail-x at :15202). 14x14 is deliberately taller and narrower than the 20x12
   tails elsewhere: that is what makes it read as a keen point, not a blunt nub.
   Every base declaration at :2771 must be restated (top/left/width/height/
   clip-path) or the left-pointing desktop geometry leaks through. */
@media (max-width: 900px) {
  .workspace-coach-overlay { z-index: 1200; } /* above drawer 1010, below intro modal 2200 */
}

/* -- (Entry #71) The `.kanban-tap-move` "Move selected here" button and its styles are
   DELETED. It was a 2026-07-12 fallback for native HTML5 DnD being dead under an iOS
   finger; hirello-board.js is a POINTER-EVENTS engine now, and a real finger drag was
   proven to complete the move on the physical iPhone 13 before this was removed. It sat
   directly above the real drop target and stole the ghost-hand coach's meaning. */

/* ==========================================================================
   MOBILE CONTINUATION FIXES (2026-07-12, ledger entry #61) — appended last
   so source order wins.
   ========================================================================== */

/* Blocker A: at ≤700px the import board collapses to one full-bleed column,
   so the processing spotlight's z:25 painted the Applied card OVER the
   parsing tour modal (z:18) — the "black modal never shows" bug. The
   spotlight elevation is pointless when the sibling columns are hidden. */
@media (max-width: 700px) {
  body.x1riqt-screen [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] + .import-board .new-col {
    z-index: auto;
    box-shadow: none;
  }
}

/* Blocker B: fixed-width coaches with desktop-centred offsets resolve to
   an off-screen left edge on portrait phones — cap to the viewport. */
@media (max-width: 900px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-rank-coach {
    right: 14px;
    left: 14px;
    width: auto;
  }
  .company-basics-coach {
    right: 14px;
    left: 14px;
    width: auto;
  }
}

/* Blocker C: on short/landscape viewports the drag stack sat below a
   non-scrolling 100dvh clip — let only the content pane scroll. */
@media (max-height: 520px) {
  body.x1riqt-screen .content {
    overflow-y: auto;
  }
}

/* Kanban drag, skill CSS contract: touch-action:none was already present on
   the cards; user-select was the missing half (press-and-hold selection UI
   competes with the drag gesture on phones). */
.workspace-candidate,
.kanban.import-board .candidate-card {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* ==========================================================================
   rj0wj CHOOSER DECK (2026-07-12, entry #61) — Tinder-style pain-point
   chooser replacing the intro-modal carousel. The deck container reuses the
   .job-card-intro-benefits/.jc-track classes so all panel CONTENT styling
   (h3/p/previews) keeps matching; the layout rules below override the old
   track behaviour. Appended last so source order wins.
   ========================================================================== */
/* The deck and the intro overlay used to be MUTUALLY EXCLUSIVE, via a
   `body.job-card-chooser-active .job-card-intro-overlay { display:none !important }` lived here.
   Deleted 2026-07-17: the landing sheet now sits OVER the live deck, so that invariant is gone.
   The sheet gates on `jc-sheet-active` alone, which is why no !important is needed to beat this
   any more. (The jc-chooser-seed kill switch below is a different thing and stays.) */
.job-card-chooser-overlay {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 2200;
  align-items: center;
  justify-content: center;
  padding: var(--space-md);
  background: rgba(11, 15, 26, 0.72);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}
body.job-card-modal-active.job-card-chooser-active .job-card-chooser-overlay { display: flex; }
html.jc-chooser-seed .job-card-intro-overlay { display: none !important; }
html.jc-chooser-seed body.job-card-modal-active .job-card-chooser-overlay { display: flex; }
.job-card-chooser-cam { display: flex; width: 100%; justify-content: center; }
.job-card-chooser {
  width: min(520px, 100%);
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-height: calc(100dvh - 24px);
  /* (#350) THE FLOATING CARDS' WIDTH, AS ONE NUMBER. Her ask was that the footer CTA be "the same
     width as the floating cards on all views", and the deck's `min(400px, 100%)` was a literal that
     would then have existed in two places, free to drift. Declared HERE because `.jc-deck` and
     `.job-card-chooser-foot` are both direct children of this section, so the `100%` half resolves
     against the SAME containing block in both consumers: 520 at 1440 and 834, 358 at 390, 288 at
     320, all measured. Change this and both the deck and its CTA move together. */
  --jc-deck-w: min(400px, 100%);
}
.job-card-chooser-head { text-align: center; color: var(--text-inverse); }
/* Typeset to match opening.html's end-of-cinematic .app-title ("We found your job ad...",
   opening.html:752-757 → 900 / 1.02 / -0.03em), keeping THIS head's own white (entry #98,
   user call). The size stays a clamp rather than .app-title's literal 31px: opening.html
   scales a fixed design space as one texture, this header does not, so the number would
   mean something different here. The two rows are spans (see rj0wj.html) — text-wrap is
   gone with the subtitle because there is no longer anything for it to decide. */
/* ⚠️ The size is --jc-h2, declared with its grown twin in the entry #123 tail block. The two
   are read together by the coach-dismiss grow, whose whole correctness rests on the header's
   height being invariant, so they must stay one source. Do not re-inline the clamp here. */
.job-card-chooser-head h2 {
  font-size: var(--jc-h2);
  line-height: 1.02;
  letter-spacing: -0.03em;
  font-weight: var(--fw-black);
}
.job-card-chooser-head h2 > span { display: block; }
.jc-deck {
  position: relative;
  /* (#350) Was the literal `min(400px, 100%)`; identical value, now shared with the footer CTA
     through `--jc-deck-w` on `.job-card-chooser` so the two cannot drift apart. */
  width: var(--jc-deck-w);
  /* Floor raised 400→428 on 2026-07-13: the action-button row's removal freed
     ~58px of the chooser column (new overhead at 390×664: head 99 + pipsrow 30
     + foot 34 + 3×14 gaps = 205; 640 cap − 205 = 435 → 428 keeps slack). */
  height: clamp(428px, 58dvh, 600px);
  margin: 0 auto;
  touch-action: none;
}
.jc-deck.jc-track {
  display: block;
  overflow: visible;
  scroll-snap-type: none;
  padding: 0;
}
.jc-deck > article {
  position: absolute;
  inset: 0 0 40px 0; /* bottom strip reserved so the NEXT + THIRD cards visibly peek */
  margin: 0;
  width: auto;
  min-width: 0;
  max-width: none;
  min-height: 0;
  flex: none;
  display: none;
  border-radius: var(--radius-sheet);
  background: var(--surface);
  box-shadow: 0 30px 80px rgba(15, 23, 42, 0.45);
  overflow: hidden;
  cursor: grab;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  will-change: transform;
}
.jc-deck > article:active { cursor: grabbing; }
.jc-deck > article[data-deck-pos="top"] { display: block; z-index: 3; }
/* Tier poses: translate FIRST (unscaled) so GSAP's {y, scale} decomposition matches
   these values EXACTLY — the deck JS peek/promote tweens (POSE const in the Chooser
   DECK IIFE) must stay in lockstep with the y/scale numbers here. */
.jc-deck > article[data-deck-pos="next"] {
  display: block;
  z-index: 2;
  transform: translateY(20px) scale(0.96);
  filter: brightness(0.96);
  pointer-events: none;
}
.jc-deck > article[data-deck-pos="third"] {
  display: block;
  z-index: 1;
  transform: translateY(40px) scale(0.92);
  filter: brightness(0.9);
  pointer-events: none;
}
.jc-card-float {
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 10px;
  padding: 20px;
  min-height: 0;
}
.jc-deck > article.jc-embed {
  padding: 0;
  /* position MUST be restated here: the carousel-era `.jc-track > article.jc-embed
     { position: relative }` (earlier in this file) has equal specificity and would
     otherwise beat the deck base's absolute+inset sizing — the card then collapses
     to its abs-positioned content (≈2px sliver = the "blank iPhone panel" bug). */
  position: absolute;
}
.jc-deck > article.jc-embed .jc-card-float { padding: 0; display: block; }
.jc-deck .jc-embed-scaler { position: absolute; inset: 0; background: var(--bg-app); }
/* Embed loading shimmer — driven by setFrame() (src assigned → .jc-embed-loading until
   that iframe's load; src stripped → class removed). A src-less embed (third tier)
   shows the static scaler background + ribbon, never an infinite shimmer. */
.jc-deck > article.jc-embed.jc-embed-loading .jc-embed-scaler::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 32%, rgba(124, 58, 237, 0.09) 50%, transparent 68%);
  background-size: 220% 100%;
  animation: jcShimmer 1.15s linear infinite;
}
@keyframes jcShimmer {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}
.jc-deck hirello-cinematic,
.jc-deck [data-import-demo],
.jc-deck [data-rank-demo],
.jc-deck .jc-chat-preview { pointer-events: none; }
.jc-embed-ribbon {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 2;
  padding: 14px var(--space-md) 28px;
  background: linear-gradient(180deg, rgba(15, 23, 42, 0.6), rgba(15, 23, 42, 0));
  color: var(--text-inverse);
  font: 800 15px/1.25 Inter, system-ui, sans-serif;
  letter-spacing: -0.01em;
  pointer-events: none;
}
.jc-card-glow {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  z-index: 4;
}
.jc-card-glow-go { background: linear-gradient(115deg, rgba(16, 185, 129, 0.45), rgba(16, 185, 129, 0.1) 55%, rgba(16, 185, 129, 0)); }
.jc-card-glow-later { background: linear-gradient(245deg, rgba(239, 68, 68, 0.45), rgba(239, 68, 68, 0.1) 55%, rgba(239, 68, 68, 0)); }
.jc-card-chip {
  position: absolute;
  top: 18px;
  z-index: 5;
  opacity: 0;
  padding: var(--space-xs) var(--space-sm);
  border: 3px solid;
  border-radius: 10px;
  font: 800 16px/1 Inter, system-ui, sans-serif;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  pointer-events: none;
}
/* Solid red/green with WHITE type (entry #99, user call). The "progressively solid the more
   they swipe" half needs no code: setOverlays() drives opacity = clamp(|ratio|), and ratio is
   the drag distance over the COMMIT threshold, so the stamp reaches FULL solid exactly when
   releasing would commit the card. Filled, not tinted, so that read is unmissable.
   ⚠️ DOUBLE CLASS, and it is load-bearing. The chips are <span>s inside the deck, which still
   carries the legacy `job-card-intro-benefits` class, so `.job-card-intro-benefits span
   { color: var(--brand) }` (:1024) applies to them at (0,1,1) — which BEAT the old single-class
   (0,1,0) rules. Both chips were therefore rendering BRAND PURPLE text, never the green/red
   they were authored with (measured: rgb(124,58,237) on both, pre-#99). (0,2,0) wins. */
.jc-card-chip.jc-chip-go { left: 14px; transform: rotate(-12deg); background: var(--success); color: var(--text-inverse); border-color: var(--success); }
.jc-card-chip.jc-chip-later { right: 14px; transform: rotate(12deg); background: var(--danger); color: var(--text-inverse); border-color: var(--danger); }
/* Pips row (2026-07-13): the See-later/Show-me-now action row is REMOVED (user
   call — swipe/tap/arrow keys are the interactions; the freed height went into a
   taller .jc-deck). The ↺ rewind icon lived LEFT of the pips in a 1fr/auto/1fr grid;
   ⚠️ the BUTTON was REMOVED on 2026-07-17 (entry #99, user call), which SUPERSEDES entry
   #65's "it must never vanish (its only affordance)" law. The grid stays because it is
   what keeps the pips DEAD-CENTRE (user-specified) — column 1 is simply empty now. */
.jc-deck-pipsrow {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  min-height: 30px;
}
/* Progress pips — one per non-terminal card; filled as cards are dispatched
   ("how many left" conversion cue). Decorative: aria-hidden, the live region
   already announces the top card. */
.jc-deck-pips {
  grid-column: 2;
  display: flex;
  justify-content: center;
  gap: 7px;
  min-height: 8px;
}
.jc-deck-pips i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.28);
  transition: background 0.25s ease, transform 0.25s ease;
}
.jc-deck-pips i.is-current { background: var(--surface); transform: scale(1.3); }
.jc-deck-pips i.is-done { background: var(--success); }
/* Terminal CTA pulse — gated to the TOP tier so it never invites a tap while the
   terminal card sits pointer-events:none in the next/third peek slots. */
.jc-deck > article[data-deck-pos="top"] .jc-terminal-cta { animation: jcCtaPulse 2.2s ease-in-out infinite; }
@keyframes jcCtaPulse {
  0%, 100% { box-shadow: 0 8px 22px rgba(124, 58, 237, 0.28); }
  50% { box-shadow: 0 8px 34px rgba(124, 58, 237, 0.6); }
}
.job-card-chooser-foot { text-align: center; }
/* (#349) THE DECK'S FOOTER ACTION IS A STANDARD PURPLE CTA, ON EVERY VIEW. Her ask: "our standard
   purple colour token CTA style button with Show me everything label no arrow icon on the label
   though this is for all views". The paint now comes entirely from `.btn-primary` in the markup,
   which is the project's standard button and reads `var(--brand)` rather than any hex written here.
   ⚠️ THE OLD DECLARATIONS HAD TO GO, NOT JUST BE OVERRIDDEN. This rule sits ~14,000 lines BELOW
   `.btn-primary` at the same (0,1,0) specificity, so on a tie the later rule wins: leaving
   `background: none`, `color: rgba(255,255,255,.85)` and `text-decoration: underline` here would
   have quietly beaten the button styling and produced the old text link wearing a new class.
   Superseded values, kept so nobody restores them by accident:
       background: none; color: rgba(255,255,255,.85); font: 600 14px/1 Inter;
       text-decoration: underline; padding: 10px;
   `border: 0` and `cursor: pointer` stay because `.btn-primary` sets neither and this is a real
   <button>, which carries a UA border and the default arrow cursor without them. */
.jc-full-tour-link {
  border: 0;
  cursor: pointer;
  /* (#350) SIZED OFF THE TWO THINGS SHE POINTED AT, not off numbers of its own.
     WIDTH = the floating cards ("make width the same width as the floating cards on all views"),
     via `--jc-deck-w`, the same expression `.jc-deck` itself uses. `display: flex` + `margin: auto`
     rather than leaning on the foot's `text-align: center`, because a width on an inline-flex box
     centres by text alignment and would stop centring the moment that rule moved.
     HEIGHT + RADIUS = the dark coaching card's CTA. The radius already agreed (both `--radius-pill`
     -> 999px, measured), and the height agrees below 901px (both 41). The desktop tier is the one
     that differed and is set in the media query below. */
  display: flex;
  width: var(--jc-deck-w);
  margin: 0 auto;
  /* (#430) ⚠️ THE LABEL GREW AND 320 WENT TO A 1.9px CLEARANCE, WHICH IS NOT A PASS.
     "I've got coffee: Show me everything" measures 246.1px at this button's own 14px/700, against a
     content box of 248.0px at 320 once `.btn-primary`'s 20px side padding is taken off both ends.
     That is the sub-pixel margin this project has been bitten by before: it holds on the width I
     happened to emulate and has nothing left for a different font stack, a fallback face during
     load, or a 0.5px rounding. Trimming the side padding to `--space-sm` takes the content box to
     264.0px and the clearance to 17.9px at the tightest view, and it cannot move the label off
     centre because the width is fixed by `--jc-deck-w` and the label is centred inside it.
     ⚠️ THE PADDING IS THE ONLY THING THAT MAY GIVE HERE. The width is hers ("make width the same
     width as the floating cards on all views", #350) and the 14px/700 label is hers too ("use
     normal text label", #349), so neither is available to solve this with. */
  padding-inline: var(--space-sm);
}

/* (#350) ⚠️ 50px IS THE DARK COACHING CARD'S OWN DESKTOP HEIGHT, COPIED DELIBERATELY AND NOT
   INVENTED: `.company-coach-cta` inside `@media (min-width: 901px)` sets `height: 50px` (measured
   live at 1440: the coach CTA renders 50 tall at 999px radius, against this button's 41). The
   BREAKPOINT is that rule's own 901px, so the two change tier at the same width rather than at two
   nearby ones. ⚠️ If that rule's height moves, move this with it: they are one number in two
   places and CSS gives no way to reference the other rule.
   ⚠️ WHAT IS DELIBERATELY *NOT* COPIED: the coach CTA's `font-weight: 800` (850 below 901px) and
   its 16px size. Her ask was "use normal text label", so the label stays on `.btn-primary`'s own
   700 / 14px, which is this project's standard button label. */
@media (min-width: 901px) {
  .jc-full-tour-link { height: 50px; }
}
.jc-deck-live {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
/* `.job-card-cam.is-chooser-out` was deleted here 2026-07-17 with openChooser(), its only
   writer. .job-card-cam is the SHEET's wrapper, so this rule would now fade out the landing
   sheet rather than the old intro modal. Do not restore it. */
@media (max-height: 540px) {
  .job-card-chooser { gap: var(--space-xs); }
  .job-card-chooser-head h2 { font-size: var(--fs-2xl); }
  /* The gesture row's landscape hide CANNOT live here: .jc-gesture-coach's base rule is
     appended at the END of this file, so an equal-specificity rule up here loses on source
     order even though it is inside a media query. It sits with the rest of the #98 block. */
  /* Constant retuned 176→134 (2026-07-13): action row removed (pipsrow 28px+gap
     replaces it) and the chooser column re-measured — overhead at 844×390 is
     head 22 + pipsrow 28 + foot 34 + 3×8 gaps = 108, cap = 100dvh−24 → deck may
     take 100dvh−132; 134 keeps slack. Landscape deck-card previews were clipping
     ~136px before this (pre-existing, fixed with the tier below). */
  /* ⚠️⚠️ ENTRY #396j TRIED 134 -> 133 HERE, MEASURED IT INERT, AND PUT IT BACK. Recorded rather
     than silently dropped, because the reasoning looks correct and the next reader will have it too.
     THE THEORY: the landscape guard fails by -0.109375px, and the comment above derives the floor
     as 100dvh-132, so 134 keeps 2px where the content wants 2.11 and 133 should have bought it.
     WHY IT DID NOTHING, MEASURED at 844x390 on the served bytes: `.jc-deck` renders **236.281px**,
     not the 257 that `100dvh - 133` predicts, and the value is FRACTIONAL, so this height is being
     distributed by the flex parent rather than taken from this rule. The article is deck minus the
     28px inset either way, and the measured slack was byte-identical before and after: -0.109375,
     not improved by one pixel.
     ⚠️ THE OVERFLOWING ELEMENT IS AN `<i>` ICON of height 28 inside `.jc-card-float`, and the other
     six cards in the same sweep have +1.00 to +11.73 of slack. So this is a sub-pixel artefact of
     one glyph box in a flex-scaled card, not a height budget that can be bought back here.
     ⚠️ #286 AND #394 BOTH DELETED A RULE THAT MEASURED INERT rather than shipping it, for the same
     reason: a rule that changes nothing teaches the next reader it was needed. */
  .jc-deck { height: calc(100dvh - 134px); min-height: 220px; }
  /* Shallow peek on short viewports: the 40px two-tier strip would cost text cards
     14px of height they can't spare at min-height 220. The deck JS poseFor() reads
     the same media query — keep these y/scale values in lockstep with it. */
  .jc-deck > article { inset: 0 0 28px 0; }
  .jc-deck > article[data-deck-pos="next"] { transform: translateY(14px) scale(0.97); }
  .jc-deck > article[data-deck-pos="third"] { display: none; }
  /* Pips dots stay hidden in landscape (height-starved). The ROW used to survive because it
     hosted the ↺ rewind button ("must never vanish — its only affordance", entry #65); that
     button is GONE as of entry #99, so the row is empty here. Its 28px is deliberately KEPT
     anyway: the .jc-deck height below is `100dvh - 134`, derived from "head 22 + pipsrow 28 +
     foot 34 + 3x8 gaps = 108". Collapsing the row would free ~36px and force that constant to
     be re-derived, for a tier no real phone reaches (portrait-only is enforced on phone-class
     touchscreens — this is resized desktop windows only). Not worth the churn. */
  .jc-deck-pips { display: none; }
  .jc-deck-pipsrow { min-height: 28px; }
  /* Landscape preview compaction (2026-07-13): the deck card is still only
     400px wide here but just ~200-230px tall, so the card DESCRIPTION hides
     (same pattern as the chooser-head p above) and every preview runs a
     tighter value set than the ≤700px portrait tier (which cannot match at
     these widths — 740/844 > 700). Terminal card keeps its p (short + fits). */
  .jc-deck > article:not([data-deck-terminal]) .jc-card-float > p { display: none; }
  .jc-card-float { padding: var(--space-sm) 20px; gap: 6px; }
  .job-card-intro-benefits h3 { margin-top: 2px; font-size: var(--fs-lg); line-height: 1.12; }
  .job-card-import-preview,
  .job-card-rank-preview { margin-top: var(--space-2xs); padding: 6px; gap: var(--space-2xs); }
  .job-card-import-dropzone { min-height: 30px; padding: var(--space-2xs); }
  .job-card-import-row { min-height: 26px; padding: var(--space-2xs) 6px; }
  .job-card-rank-row { min-height: 27px; padding: var(--space-2xs) 6px var(--space-2xs) 7px; }
  /* ⚠️ #103: this tier's .jc-chat-* overrides CANNOT live here. The base rules are appended
     at the END of this file, so an equal-specificity rule in this block LOSES on source
     order (measured: the panel rendered 190px here instead of 156px). They live in the #103
     tail block instead. Same trap entry #98 law 7 records. */
}
@media (prefers-reduced-motion: reduce) {
  .jc-deck > article { will-change: auto; }
  .jc-deck > article.jc-embed.jc-embed-loading .jc-embed-scaler::after { animation: none; }
  .jc-deck > article[data-deck-pos="top"] .jc-terminal-cta { animation: none; }
  .jc-deck-pips i { transition: none; }
}

/* The landing sheet on portrait phones. The seven job-ad-hero compaction rules that lived here
   went with the hero itself (2026-07-17), and so did `.job-card-intro-copy h2 { font-size: 19px }`
   (19px existed ONLY to squeeze the headline in ABOVE the hero card, and with the sheet carrying
   just the headline and the CTA it read as badly under-scaled.) The headline now sizes from the
   container query above (~24.7px at 390px), which tracks the sheet rather than the viewport.
   ⚠️ Do not re-add an h2 font-size here: it would be (0,1,1), the same as the container rule, and
   would silently win on source order alone. */
@media (max-width: 700px) {
  .job-card-help-modal { padding: 22px 18px 26px; gap: var(--space-md); }
}

/* ==========================================================================
   Demo-sections BOTTOM SHEET (2026-07-12, entry #63) — on touch phones the
   "Demo sections" launcher presents its options as a native-style bottom
   sheet: dimmed scrim, slide-up panel with a grab handle, drag-to-dismiss.
   Mode is decided at OPEN time in hirello-shell.js via
   (pointer: coarse) and (max-width: 900px); desktop keeps the dropdown.
   ========================================================================== */
.demo-sheet-scrim {
  position: fixed;
  inset: 0;
  z-index: 2147483029; /* directly under the launcher/panel pair (2147483030) */
  background: rgba(11, 15, 26, 0.5);
  opacity: 0;
  transition: opacity 0.28s ease;
  touch-action: none;
}
.demo-sheet-scrim.is-open { opacity: 1; }
.demo-sheet-handle { display: none; }
.demo-sections-panel.is-sheet {
  top: auto;
  right: 0;
  bottom: 0;
  left: 0;
  width: auto;
  max-height: 78dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  border: 0;
  border-top: 1px solid var(--border);
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  padding: 0 10px calc(14px + env(safe-area-inset-bottom, 0px));
  box-shadow: 0 -18px 60px rgba(15, 23, 42, 0.35);
  transform: translateY(100%);
  transition: transform 0.32s cubic-bezier(0.32, 0.72, 0, 1); /* native sheet ease */
}
.demo-sections-panel.is-sheet.is-open { transform: translateY(0); }
.demo-sections-panel.is-sheet.is-dragging { transition: none; }
.demo-sections-panel.is-sheet .demo-sheet-handle {
  display: block;
  position: sticky;
  top: 0;
  z-index: 1;
  width: 100%;
  padding: 10px 0 var(--space-sm);
  background: var(--surface);
  cursor: grab;
  touch-action: none; /* the handle owns vertical drags; the list below scrolls */
}
.demo-sections-panel.is-sheet .demo-sheet-handle::before {
  content: "";
  display: block;
  width: 40px;
  height: 4.5px;
  margin: 0 auto;
  border-radius: 3px;
  background: var(--border);
}
.demo-sections-panel.is-sheet .demo-sections-item {
  min-height: 48px; /* native touch-row height */
  padding: var(--space-sm);
  font-size: var(--fs-lg);
  border-radius: var(--radius-md);
}
@media (prefers-reduced-motion: reduce) {
  .demo-sheet-scrim,
  .demo-sections-panel.is-sheet { transition: none; }
}

/* ============================================================================
   PHASE 3 (2026-07-13): compare/rank modal — collaborator ranking + phone
   stacked layout. Desktop TABLE geometry untouched; on phones the table is
   replaced by compact stacked rank cards (built by buildCompareMobileList()
   in nifhr.html) that reorder VERTICALLY through the same rankDrag machine.
   Keep the media query below identical to isMobileRankLayout() in nifhr.html.
   ========================================================================= */

/* Touch drag prerequisite (kanban precedent): without touch-action:none the
   browser steals a finger drag for scroll-pan and fires pointercancel. */
body.nifhr-screen [data-screen-id="nIfHr"] .compare-rank-handle {
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* Collaborator deadline chip (all views) */
body.nifhr-screen [data-screen-id="nIfHr"] .compare-deadline {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  margin-left: auto;
  margin-right: 14px;
  padding: 6px var(--space-sm);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--bg-subtle);
  white-space: nowrap;
}
body.nifhr-screen [data-screen-id="nIfHr"] .compare-deadline-clock {
  font-size: 13px;
  line-height: 1;
}
body.nifhr-screen [data-screen-id="nIfHr"] .compare-deadline-copy strong {
  display: block;
  color: var(--brand-dark, var(--brand));
  font-size: var(--fs-sm);
  line-height: 14px;
  font-weight: var(--fw-extrabold);
  font-variant-numeric: tabular-nums;
}
body.nifhr-screen [data-screen-id="nIfHr"] .compare-deadline-copy small {
  display: block;
  color: var(--text-secondary);
  font-size: 9.5px;
  line-height: 12px;
  font-weight: var(--fw-semibold);
}

/* "Submit my ranking" is disabled until ranking mode is active (the
   collaborator's explicit yes) — mirrors the compare-close disabled look. */
/* ENTRY #170: the MD3 inactive value is 0.39, the same one the rank gate's CTA, the drawer's
   "Message Marta" and rj0wj's landing CTA already use. It was 0.45 here alone. `opacity` on the
   button carries the label with it, so button and label dim together as asked. */
body.nifhr-screen [data-screen-id="nIfHr"] .compare-primary:disabled,
body.nifhr-screen [data-screen-id="nIfHr"] .compare-primary[aria-disabled="true"] {
  opacity: 0.39;
  cursor: default;
  pointer-events: none;
}

/* Phone-only elements stay dead on desktop */
body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-list { display: none; }
body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-drawer,
body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-drawer-scrim { display: none; }
.compare-mob-drawer[hidden],
.compare-mob-drawer-scrim[hidden] { display: none !important; }
body.nifhr-screen [data-screen-id="nIfHr"] .coach-copy-mobile { display: none; }

@media (max-width: 700px), (max-width: 900px) and (max-height: 500px) {
  /* swap layouts: hide the desktop column grid, show the stacked cards */
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-rank-labels,
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-candidate-heads,
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-table { display: none; }
  body.nifhr-screen [data-screen-id="nIfHr"] .coach-copy-desktop { display: none; }
  body.nifhr-screen [data-screen-id="nIfHr"] .coach-copy-mobile { display: inline; }

  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-compare-overlay { padding: 14px; }
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-compare-modal {
    width: min(1060px, calc(100vw - 24px));
    max-height: calc(100dvh - 28px); /* dvh: Android URL-bar-safe */
    grid-template-rows: auto minmax(0, 1fr) auto;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-modal-head {
    flex-wrap: wrap;
    gap: 10px;
    padding: 14px var(--space-md);
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-deadline {
    order: 4;
    flex: 1 1 100%;
    margin: 0;
    justify-content: flex-start;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: var(--space-sm) 14px 14px;
    overflow-y: auto;
    min-height: 0;
  }

  /* ENTRY #89 (user call). The traffic light on the STACKED layout. This view has no labels row
     at all (the desktop .compare-rank-labels is display:none up at :14040 and the two labels are
     ::before/::after content strings below), and the ranking axis here is VERTICAL, so the sweep
     runs top-to-bottom: green at the most-preferred end, red at the least.
     Gated on .comparison-rank-active to mirror the desktop row, which only reveals itself
     (height 0 → 46px) in that same state.
     ⚠️ This element is the scroll container. `background-attachment: scroll` (the default) pins the
     image to its border box rather than the scrolled content, so the wash stays put while the cards
     move over it, which is what we want. (`local` would drag the gradient along with them.) The
     cards are opaque var(--surface), so it reads in the gaps and behind the two labels. */
  body.nifhr-screen.comparison-rank-active [data-screen-id="nIfHr"] .compare-mob-list {
    background-image: linear-gradient(180deg, var(--rank-most), var(--rank-mid), var(--rank-least));
    background-repeat: no-repeat;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-list::before,
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-list::after {
    color: var(--text-tertiary);
    font-size: var(--fs-2xs);
    line-height: 12px;
    font-weight: var(--fw-extrabold);
    letter-spacing: 0.08em;
    text-transform: uppercase;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-list::before {
    content: "Most preferred";
    order: -1;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-list::after {
    content: "Least preferred";
    order: 99;
  }
  /* ENTRY #89. Once the wash is behind them, --text-tertiary (#9CA3AF) is a light grey sitting on
     a light pastel and stops reading. Each label takes the text token paired with the tint under
     it, matching the desktop row exactly. Scoped to .comparison-rank-active because that is the
     only state that paints the gradient. Without it the labels would go green/red on plain white. */
  body.nifhr-screen.comparison-rank-active [data-screen-id="nIfHr"] .compare-mob-list::before {
    color: var(--success-text);
  }
  body.nifhr-screen.comparison-rank-active [data-screen-id="nIfHr"] .compare-mob-list::after {
    color: var(--danger-text);
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-card {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 60px;
    padding: var(--space-xs) 10px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--surface);
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-card.is-rank-origin {
    opacity: 0.36; /* the desktop .is-rank-origin rule can't match this class */
  }
  /* the card handle is a 44px in-flow touch target (desktop keeps its 30px
     absolute pill); the opacity/pointer-events Try-it-now gate still applies */
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-card .compare-rank-handle {
    position: static;
    width: 44px;
    height: 44px;
    min-width: 44px;
    transform: none;
    font-size: var(--fs-md);
    border-radius: var(--radius-md);
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-card .compare-avatar {
    width: 36px;
    height: 36px;
    font-size: var(--fs-xs);
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-id {
    flex: 1;
    min-width: 0;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-id strong {
    display: block;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text-primary);
    font-size: 13px;
    line-height: 17px;
    font-weight: var(--fw-extrabold);
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-id small {
    display: block;
    color: var(--text-secondary);
    font-size: var(--fs-xs);
    line-height: 14px;
    font-weight: var(--fw-semibold);
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-badge {
    padding: var(--space-2xs) var(--space-xs);
    border-radius: var(--radius-pill);
    background: var(--bg-subtle);
    color: var(--text-secondary);
    font-size: var(--fs-2xs);
    line-height: 12px;
    font-weight: var(--fw-extrabold);
    white-space: nowrap;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-card[data-rank-position="1"] .compare-mob-badge {
    background: var(--brand-tint);
    color: var(--brand-dark, var(--brand));
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-card[data-rank-position="1"] .compare-mob-badge::before { content: "1st"; }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-card[data-rank-position="2"] .compare-mob-badge::before { content: "2nd"; }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-card[data-rank-position="3"] .compare-mob-badge::before { content: "3rd"; }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-toggle {
    width: 40px;
    height: 44px;
    display: grid;
    place-items: center;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--surface);
    color: var(--text-tertiary);
    font-size: 19px;
    line-height: 1;
  }

  /* footer: explainer stacks above a full-width submit */
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-modal-foot {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
    padding: var(--space-sm) 14px;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-modal-foot span {
    text-align: center;
    font-size: var(--fs-xs);
    line-height: 15px;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-modal-foot div { display: flex; }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-primary {
    flex: 1;
    min-height: 44px;
    font-size: 13px;
  }

  /* candidate details drawer (bottom sheet inside the compare overlay) */
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-drawer-scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 9640;
    background: rgba(17, 24, 39, 0.5);
    opacity: 0;
    transition: opacity var(--motion-ui) ease;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-drawer-scrim.is-open { opacity: 1; }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-drawer {
    display: flex;
    flex-direction: column;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 9700; /* above the rank coach (9600), below the drag ghost (10020) */
    max-height: 78dvh;
    background: var(--surface);
    border-radius: 18px 18px 0 0;
    box-shadow: 0 -18px 44px rgba(17, 24, 39, 0.28);
    transform: translateY(102%);
    transition: transform 260ms cubic-bezier(0.2, 0.9, 0.16, 1);
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-drawer.is-open { transform: translateY(0); }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-drawer-grip {
    padding: 10px 0 6px;
    display: grid;
    place-items: center;
    touch-action: none;
    cursor: grab;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-drawer-grip span {
    display: block;
    width: 40px;
    height: 4.5px;
    border-radius: 3px;
    background: var(--border);
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-drawer-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 2px var(--space-md) var(--space-sm);
    border-bottom: 1px solid var(--border);
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-drawer-head .compare-avatar {
    width: 36px;
    height: 36px;
    font-size: var(--fs-xs);
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-drawer-close {
    width: 36px;
    height: 36px;
    margin-left: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    background: var(--surface);
    color: var(--text-tertiary);
    font-size: 17px;
    line-height: 1;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-drawer-body {
    overflow-y: auto;
    min-height: 0;
    display: grid;
    gap: var(--space-sm);
    padding: 12px 16px calc(16px + env(safe-area-inset-bottom, 0px));
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-detail-row { display: grid; gap: 3px; }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-detail-label {
    color: var(--text-tertiary);
    font-size: var(--fs-2xs);
    line-height: 12px;
    font-weight: var(--fw-extrabold);
    letter-spacing: 0.08em;
    text-transform: uppercase;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-detail-value {
    color: var(--text-primary);
    font-size: 12.5px;
    line-height: 18px;
    font-weight: var(--fw-medium);
  }
  /* experience clones keep the desktop b/em/small hierarchy */
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-detail-value b,
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-detail-value em,
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-detail-value small {
    display: block;
    font-style: normal;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-detail-value b {
    color: var(--text-primary);
    font-size: var(--fs-sm);
    line-height: 16px;
    font-weight: var(--fw-extrabold);
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-detail-value em {
    color: var(--text-secondary);
    font-size: var(--fs-xs);
    line-height: 14px;
    font-weight: var(--fw-bold);
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-detail-value small {
    margin-bottom: 6px;
    color: var(--text-secondary);
    font-size: var(--fs-xs);
    line-height: 14px;
    font-weight: var(--fw-semibold);
  }

  /* the vertical insertion guide becomes a horizontal bar between cards */
  .compare-rank-insertion.is-horizontal { width: auto; height: 4px; }
}

/* short-landscape phones: tighten so 3 cards + head + foot fit unscrolled */
@media (max-width: 900px) and (max-height: 500px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-list { gap: 7px; padding: var(--space-xs) 14px 10px; }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-card { min-height: 50px; padding: var(--space-2xs) 10px; }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-modal-head { padding: 10px var(--space-md); }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-deadline { flex: 0 1 auto; order: 0; margin-left: auto; margin-right: var(--space-sm); }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-modal-foot { flex-direction: row; align-items: center; gap: var(--space-sm); padding: 10px 14px; }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-modal-foot span { flex: 1; text-align: left; }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-primary { flex: 0 0 auto; padding: 0 22px; }
}

/* ═══ PHASE 4 (2026-07-13) — chooser DECK card fit + button removal ═══════════
   Issues ③④ from the user's annotated iPhone frame: the import + rank preview
   bottoms clipped on iPhone (Safari AND Chrome) because the deck bottoms out at
   its clamp floor there (iOS chrome eats more dvh than Android) while the card
   content is fixed-px. Fix = the raised floor (in the .jc-deck base block) plus
   this width-gated compaction tier. NO max-height ceiling on the gate — a
   ceiling creates an overflow cliff at 390×776-800 where the compaction switches
   off one line before 58dvh overtakes the floor (measured). The min-height:541px
   floor is different and safe: below it the (max-height:540px) LANDSCAPE tier
   owns the compaction with tighter values — the two tiers partition exactly at
   the 540/541 boundary, so narrow-landscape phones (e.g. 667×375) get the tight
   set instead of this one overriding it from later in the cascade (measured
   −49px overflow without the floor). */
@media (max-width: 700px) and (min-height: 541px) {
  .jc-card-float { padding: 20px 20px var(--space-md); gap: var(--space-xs); }
  .job-card-intro-benefits h3 { margin-top: 6px; font-size: var(--fs-xl); line-height: 1.12; }
  .job-card-intro-benefits p { margin-top: 6px; font-size: 12.5px; line-height: 1.35; }
  .job-card-import-preview,
  .job-card-rank-preview { margin-top: var(--space-xs); padding: 7px; gap: 5px; }
  .job-card-import-dropzone { min-height: 46px; padding: 6px; }
  .job-card-import-row { min-height: 33px; padding: 6px; }
  .job-card-rank-row { min-height: 34px; padding: 6px 6px 6px 7px; }
}
/* The 541-620px-height PORTRAIT band (no real phone — resized windows only):
   the chooser column's max-height cap flex-squeezes the deck below its clamp
   floor there, so the moderate tier above overflows (measured −88px at 390×541, −8px at 390×621).
   This band gets the same tight set as the ≤540 landscape tier — but WITHOUT
   touching the 540 shallow-peek pose/deck mechanics (JS lockstep stays intact). */
@media (max-width: 700px) and (min-height: 541px) and (max-height: 656px) {
  .jc-deck > article:not([data-deck-terminal]) .jc-card-float > p { display: none; }
  .jc-card-float { padding: var(--space-sm) 20px; gap: 6px; }
  .job-card-intro-benefits h3 { margin-top: 2px; font-size: var(--fs-lg); }
  .job-card-import-preview,
  .job-card-rank-preview { margin-top: var(--space-2xs); padding: 6px; gap: var(--space-2xs); }
  .job-card-import-dropzone { min-height: 30px; padding: var(--space-2xs); }
  .job-card-import-row { min-height: 26px; padding: var(--space-2xs) 6px; }
  .job-card-rank-row { min-height: 27px; padding: var(--space-2xs) 6px var(--space-2xs) 7px; }
  /* ⚠️ #103: see the twin note in the (max-height:540px) tier above. The .jc-chat-* overrides
     for this tier live in the #103 tail block, or the end-of-file base rules beat them. */
}

/* ⑦ (2026-07-13, WIDENED 2026-07-27) The import deck card's "PDF or DOCX" sub-line.
   It originally went on physical iPhones only, via the UA-stamped html.jc-ios class
   (set in rj0wj's pre-paint head script, because CSS cannot tell iPhone from Android).
   The user then asked for it gone on MOBILE AND TABLET generally, so the gate is now
   the viewport, not the UA: <=900px is this project's mobile-and-tablet boundary, the
   same one the mobile cinematics and entry #184 use. Desktop keeps the sub-line.
   The UA rule is SUBSUMED, not merely redundant: an iPhone is never wider than 900px
   in portrait, and landscape phones get the rotate blocker, so it could not match
   anything this rule does not already cover.
   ⚠️ VERIFIED 2026-07-27: that rule was html.jc-ios's ONLY consumer, so the pre-paint
   UA stamp at rj0wj.html:32 is now DEAD CODE. It is left in place only because
   deleting it was outside the scope of this ask; it is a one-line removal and should
   go, rather than sit here accumulating a confident comment that implies it is live.
   ⚠️ Nothing else sets `display` on this element (the only other rules are the 8px
   type at :1125 and the armed/parse colour+opacity at :1307), so this wins cleanly. */
@media (max-width: 900px) {
  .jc-deck .job-card-import-dropzone small { display: none; }
}

/* ⑧ (2026-07-13) The applications count inside "Parsing 500 resumes…" rendered
   INVISIBLE: the generic `.job-card-intro-benefits span` rule (0,1,1) paints all
   descendant spans brand-purple 12px, and purple-on-the-purple-parsing-bar reads
   as a "big gap" between the words. The count span must inherit its strong's
   style — (0,1,2) outranks the generic rule. */
.job-card-import-dropzone strong span {
  color: inherit;
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
}

/* ============================================================================
   2026-07-13 config-harmonisation overflow guards (see CONFIG.md field limits):
   config-driven free text must wrap, never burst its container, when a tested
   value contains a long unbroken token. Scoped — no blanket overflow-wrap.
   ========================================================================== */
.nifhr-job-head h1,
.company-field strong {
  overflow-wrap: anywhere;
}

/* ============================================================================
   2026-07-13 PORTRAIT-ONLY rotate blocker (user decision — phones never show
   the landscape layout). Trigger = phone-class touchscreen in landscape ONLY:
   pointer:coarse + hover:none excludes all desktop; max-height:540px excludes
   iPads (smallest iPad landscape height 744px). True orientation lock is
   impossible for a plain web page (iOS has no lock(); Android needs
   fullscreen) — this overlay is the guarantee instead. Injected by
   hirello-shell.js on app screens; cinematics carry their own inline copy.
   z = 2147483647 (CSS ceiling; above every other overlay in the app).
   ========================================================================== */
.rotate-lock-overlay {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 2147483647;
  background: #0B0D14;
  color: var(--text-inverse);
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-md);
  text-align: center;
  padding: var(--space-lg);
  height: 100dvh;
}
.rotate-lock-overlay svg { width: 46px; height: 46px; opacity: 0.92; }
.rotate-lock-overlay p {
  margin: 0;
  font: 600 17px/1.45 Inter, system-ui, sans-serif;
  max-width: 340px;
}
@media (orientation: landscape) and (pointer: coarse) and (hover: none) and (max-height: 540px) {
  .rotate-lock-overlay { display: flex; }
}

/* ---------------------------------------------------------------------------
   x1riqt — ghost-hand coach loop + inactive (MD3-disabled) Browse-files CTA.
   Entry #68 (2026-07-13). Cache tag: x1riqt-mob-7.
   --------------------------------------------------------------------------- */

/* The coach layer. position:fixed inside .import-demo — the same trick
   .drag-focus-dim already uses — so it spans the viewport regardless of the
   pile's absolute/scaled mobile positioning. z12 puts it above the dim (z1),
   the drop zone (z2/z4) and the pile (z3/z8) inside .import-demo's stacking
   context, but well below the real drag's ghost (z10000) and helper (z10020),
   so an actual drag always paints over it. Never interactive. */
[data-screen-id="x1RIQT"] .drag-coach {
  position: fixed;
  inset: 0;
  z-index: 12;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--motion-ui) ease;
}
[data-screen-id="x1RIQT"] .drag-coach.is-visible { opacity: 1; }
[data-screen-id="x1RIQT"] .drag-coach-svg {
  position: absolute;
  left: 0;
  top: 0;
  overflow: visible;
  pointer-events: none;
}
[data-screen-id="x1RIQT"] .drag-coach-trail {
  fill: none;
  stroke-width: 5;
  stroke-linecap: round;
  filter: drop-shadow(0 2px 10px rgba(124, 58, 237, 0.35));
  opacity: 0;
}
[data-screen-id="x1RIQT"] .drag-coach-hand {
  position: absolute;
  left: 0;
  top: 0;
  width: clamp(54px, 6.2vw, 72px);
  color: var(--brand);
  transform-origin: 43.7% 4%;
  opacity: 0;
  will-change: transform, opacity;
}
[data-screen-id="x1RIQT"] .drag-coach-hand svg {
  display: block;
  width: 100%;
  height: auto;
  filter:
    drop-shadow(0 0 1.5px rgba(255, 255, 255, 0.95))
    drop-shadow(0 1px 2px rgba(255, 255, 255, 0.9))
    drop-shadow(0 12px 24px rgba(17, 24, 39, 0.28));
}
[data-screen-id="x1RIQT"] .drag-coach-ripple {
  position: absolute;
  left: 0;
  top: 0;
  width: 84px;
  height: 84px;
  border-radius: var(--radius-pill);
  border: 2px solid rgba(124, 58, 237, 0.75);
  background: rgba(124, 58, 237, 0.14);
  opacity: 0;
  will-change: transform, opacity;
}

/* Browse files (entry #68/#88) was the MD3-inactive CTA in the resume pile. ENTRY #113 DELETED
   the button on every view (user call), so these rules matched nothing and went with it. The
   generic `.drop-row` flex primitive at :2846 is left in place as a layout utility. */

@media (prefers-reduced-motion: reduce) {
  [data-screen-id="x1RIQT"] .drag-coach {
    transition: none !important;
  }
  [data-screen-id="x1RIQT"] .drag-coach-hand,
  [data-screen-id="x1RIQT"] .drag-coach-trail,
  [data-screen-id="x1RIQT"] .drag-coach-ripple {
    animation: none !important;
    transition: none !important;
  }
}

/* Entry #68 (user call, all views): the resume pile now floats on its own —
   drop the dashed panel that boxed it in (border/fill/shadow + both decorative
   pseudo-elements: the drag placeholder and the old dashed trail streak, which
   the ghost-hand coach replaces), and drop the black "Click and hold…" caption.
   The .manual-source box itself stays as the (invisible) positioning wrapper. */
[data-screen-id="x1RIQT"] .manual-source,
body.x1riqt-screen [data-screen-id="x1RIQT"] .manual-source {
  border: 0;
  background: transparent;
  box-shadow: none;
  /* padding kept — it is what holds the card at its current position */
}
[data-screen-id="x1RIQT"] .manual-source::before,
[data-screen-id="x1RIQT"] .manual-source::after,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .manual-source::after,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="grabbing"] .manual-source::before,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="dragging"] .manual-source::before,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="drop-active"] .manual-source::before,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="released"] .manual-source::before {
  content: none;
  display: none;
  animation: none;
}
[data-screen-id="x1RIQT"] .demo-caption,
body.x1riqt-screen .demo-caption,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .demo-caption {
  display: none;
}

/* ---------------------------------------------------------------------------
   nifhr — move-coach ghost-hand loop on the kanban. Entry #70 (2026-07-14).
   Cache tag: nifhr-mob-10.

   Same machine as x1riqt's .drag-coach (entry #68) but its OWN namespace, so the
   two can never collide. Scoped on `body.nifhr-screen` — NOT [data-screen-id] —
   because the layer is re-parented to <body> at init (.nifhr-stage is
   isolation:auto, so a fixed layer left inside it would stack in the wrong
   context). The tick-proxy layer above is scoped the same way for the same reason.

   z9100 puts it above the screen dim (z9000) and the tick proxies (z9065), and
   below the coach card (z9200) and the real drag's ghost (z10000) — so an actual
   drag always paints over it. Never interactive.
   --------------------------------------------------------------------------- */
body.nifhr-screen .move-coach {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 9100;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--motion-ui) ease;
}
body.nifhr-screen.candidate-move-step .move-coach { display: block; }
body.nifhr-screen .move-coach.is-visible { opacity: 1; }
body.nifhr-screen .move-coach-svg {
  position: absolute;
  left: 0;
  top: 0;
  overflow: visible;
  pointer-events: none;
}
body.nifhr-screen .move-coach-trail {
  fill: none;
  stroke-width: 5;
  stroke-linecap: round;
  filter: drop-shadow(0 2px 10px rgba(124, 58, 237, 0.35));
  opacity: 0;
}
body.nifhr-screen .move-coach-hand {
  position: absolute;
  left: 0;
  top: 0;
  width: clamp(54px, 6.2vw, 72px);
  color: var(--brand);
  transform-origin: 43.7% 4%;
  opacity: 0;
  pointer-events: none;
  will-change: transform, opacity;
}
body.nifhr-screen .move-coach-hand svg {
  display: block;
  width: 100%;
  height: auto;
  filter:
    drop-shadow(0 0 1.5px rgba(255, 255, 255, 0.95))
    drop-shadow(0 1px 2px rgba(255, 255, 255, 0.9))
    drop-shadow(0 12px 24px rgba(17, 24, 39, 0.28));
}
body.nifhr-screen .move-coach-ripple {
  position: absolute;
  left: 0;
  top: 0;
  width: 84px;
  height: 84px;
  border-radius: var(--radius-pill);
  border: 2px solid rgba(124, 58, 237, 0.75);
  background: rgba(124, 58, 237, 0.14);
  opacity: 0;
  pointer-events: none;
  will-change: transform, opacity;
}

/* ---------------------------------------------------------------------------
   nifhr — phone header stacking (entry #70, 2026-07-14).

   PRE-EXISTING BUG found on the physical iPhone 13 (390x699 visual viewport):
   .nifhr-job-head is `display:flex; flex-wrap:nowrap`, so the action buttons
   (Polish the job ad / Invite teammate / Filters) crushed the title column to
   98px. With the entry-#66 `overflow-wrap:anywhere` guard on the h1, "Senior
   Marketing Manager" then broke MID-WORD ("Senior / Marke / ting / Mana / ger")
   into 5 lines / 170px, and the strapline into 6 — shoving the kanban so far
   down that only 30px of it sat above the coach bottom-sheet. The board (and
   therefore the new move-coach arc) was effectively invisible on iPhone.

   Fix = let the header WRAP on phones: title takes the full width, the actions
   drop to their own row. Measured on the real iPhone: board visible 30px -> 218px,
   h1 5 lines/170px -> 2 lines/68px. Desktop is untouched (>=701px never matches).
   Keep `overflow-wrap:anywhere` — it is the config long-token guard, not the cause.
   --------------------------------------------------------------------------- */
/* ⚠️ ENTRY #284 DE-SCOPED THESE THREE from `body.nifhr-screen [data-screen-id="nIfHr"] ...`
   to the bare component. The block above calls this "THE standard for every app-shell screen",
   and it was true of the SELECTOR only on nifhr. collaborator-invite.html now renders the same
   .nifhr-job-head, and without these it did exactly what entry #70 describes: at 390 the title
   column was crushed by the action row and the strapline overlapped the board.
   This CANNOT change nifhr: the declarations are byte-identical, the specificity only drops,
   and nifhr was the sole other user of the class (grepped). `flex-wrap: wrap` is added to the
   actions row because this screen keeps three buttons on a phone where nifhr hides its own. */
@media (max-width: 700px) {
  .nifhr-job-head {
    flex-wrap: wrap;
    gap: var(--space-sm);
  }
  .nifhr-job-head > div:first-child {
    flex: 1 1 100%;
    min-width: 0;
  }
  .nifhr-head-actions {
    flex: 1 1 100%;
    flex-wrap: wrap;
  }
}

/* ---------------------------------------------------------------------------
   Workspace screens — phone header stacking (entry #70, 2026-07-14).
   USER DECISION: the stacked page header proven on nifhr is now THE standard for
   every app-shell screen of the walkthrough on physical phones (iPhone + Android).

   ⚠️⚠️ ENTRY #375/#375b: ALL FOUR SCREENS NAMED BELOW ARE ARCHIVED, AND THIS RULE NOW HAS NO
   CARRIER. `.workspace-head` and `.workspace-actions` existed only on ghnij / p9phv4 / rgikg /
   wzscb; the whole cluster was orphaned when nifhr's six links to ghnij were removed, and all four
   were moved to `archive/`. The `.workspace-head` half of this block is therefore DEAD CSS, and
   the guard that covered it (`STACKED_HEADER_SCREENS` in `mobile.spec.js`) lost all four of its
   `.workspace-head` rows in the same entry. The `.nifhr-job-head` half below is still live.
   ⚠️ NOT DELETED, deliberately: the measurements in this comment are the record of WHY the rule
   exists, and if one of those screens is ever brought back it comes back needing this. Deleting
   dead CSS is a separate decision with its own blast radius, not a side effect of a file move.

   ghnij / p9phv4 / rgikg / wzscb all share `.workspace-head` (`display:flex;
   flex-wrap:nowrap`), so their action buttons crushed the title column — measured
   at 390px: rgikg's title got 25% of the header, ghnij 41%, wzscb 46%, p9phv4 45%,
   each spilling the heading onto 3 lines. Worse, the action row was pushed PAST the
   viewport's right edge (wzscb "Back to board" by 130px, ghnij "Original resume" by
   91px) and `.content` is `overflow:hidden`, so those buttons were unreachable —
   dead controls, not just an ugly wrap.

   Same fix as nifhr: let the header WRAP on phones — title takes the full width,
   actions drop to their own row. Desktop is untouched (>=701px never matches).
   `.workspace-head` is used ONLY on these four screens (grepped), so one rule
   serves all of them. rj0wj already stacks via the generic `.page-head` <=900px
   rule; x1riqt's header has no actions row at all — neither needs this.
   --------------------------------------------------------------------------- */
@media (max-width: 700px) {
  .workspace-head {
    flex-wrap: wrap;
    gap: var(--space-sm);
  }
  .workspace-head > div:first-child {
    flex: 1 1 100%;
    min-width: 0;
  }
  .workspace-actions {
    flex: 1 1 100%;
    flex-wrap: wrap;
  }
}

/* ===========================================================================================
   ENTRY #71 (2026-07-14) — nifhr board: phone de-chroming, source-column highlight,
   MD3-inactive candidates. Appended last so source order wins.
   =========================================================================================== */

/* --- (1) PHONES ONLY: the job screen is a BOARD, not an import screen ----------------------
   At <=700px the header loses the candidate count, the action row (Polish the job ad /
   Invite teammate / Filters) and the "+ Drop more resumes" strip. Drag-and-drop for MORE
   resumes belongs solely to the initial mass-import screen (x1riqt) — showing a second drop
   affordance here competes with the ONE drop target the tutorial is teaching, and the three
   of them together cost ~100px of vertical budget on a phone, which is board the viewer
   cannot see. DESKTOP KEEPS ALL THREE (>=701px never matches) — asserted in the suite.
   NB the #70 LOCKED PHONE HEADER wrap rule stays exactly as it is: it is a user decision,
   it still governs .nifhr-job-head, and it simply has less to do now. */
@media (max-width: 700px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-head-actions,
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-drop-strip,
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-candidate-count {
    display: none;
  }
}

/* --- (2) The SOURCE column wears the highlighted state too --------------------------------
   Same tint + white ring + purple glow as .is-curated-target, and the same z-index lift over
   the walkthrough dim (body.nifhr-screen::before, z9000) — WITHOUT it the tint simply does
   not read. It keeps its NORMAL border (1px solid var(--border)); only the TARGET wears the
   dashed "drop here" border, so the two never look like the same affordance.
   opacity/filter are reset because in segment 2 the source IS .review-col, which the
   walkthrough dims to .42/grayscale by default. */
/* ⚠️ SPECIFICITY: the pre-existing source-column rules are keyed on the BODY attribute
   (`body…[data-candidate-move-source="new"] … .new-col`, (0,8,1)) and paint the source a pale
   white/lilac. To repaint it with the target's tint we must outrank that, so this selector
   carries the same body-attribute term and a `.kanban-col` qualifier → (0,9,1). Keep both;
   dropping either silently reverts the column to the pale gradient (the box-shadow lands, the
   background does not — which is exactly how this was caught). Border is deliberately NOT set:
   the source keeps its normal border, only the target wears the dashed one. */
body.nifhr-screen.candidate-move-step[data-candidate-move-source] [data-screen-id="nIfHr"] .kanban.import-board .kanban-col.is-curated-source {
  z-index: 9044;
  opacity: 1;
  filter: none;
  background: var(--col-applied-surface);
  box-shadow:
    0 0 0 4px rgba(255, 255, 255, 0.86),
    0 22px 54px rgba(124, 58, 237, 0.24);
  isolation: isolate;
  overflow: visible;
  transition:
    box-shadow 560ms ease,
    background 560ms ease,
    border-color 560ms ease;
}
body.nifhr-screen.candidate-move-step[data-candidate-move-source] [data-screen-id="nIfHr"] .kanban.import-board .kanban-col.is-curated-source .candidate-stream {
  opacity: 1;
  filter: none;
}

/* --- (3) MD3-inactive: a candidate who is not part of THIS move ---------------------------
   Driven off `data-move-eligible`, which resetCandidateBoardForSelection() already stamps on
   every card from the single source of truth isMoveEligibleId(). So it generalises with zero
   new state: segment 1 greys Clare/Jonas/Daniel; segment 2 greys Priya (who is deliberately
   `data-move-disabled` there — exactly what the coach copy already says about her).
   `pointer-events: none` is the "not tappable" half and matters: these cards are <a> links,
   so a stray tap mid-tutorial would NAVIGATE AWAY to the candidate profile.
   Deliberately CSS-only, gated on `body.candidate-move-step` — a persistent aria-disabled/
   tabindex attribute would survive the tutorial's global class reset and leave the cards
   disabled outside the move step.
   ⚠️ SPECIFICITY: `.is-curated-target *` / `.is-curated-source .candidate-stream` force
   opacity:1 on the columns' descendants. This selector is (0,7,1) — one class higher — so it
   wins on the CARD itself regardless of source order. Do not "simplify" the prefix away.  */
body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board .candidate-card[data-move-eligible="false"] {
  opacity: 0.39;
  filter: grayscale(0.35);
  pointer-events: none;
  cursor: not-allowed;
  box-shadow: none;
}

/* ==========================================================================
   ENTRY #72 (2026-07-14) — rj0wj phone dead-ends. Appended last so source
   order wins. Every rule here is scoped to an rj0wj state class or an
   rj0wj-only class, so no other screen can be touched.
   ========================================================================== */

/* --- (1) The workspace spotlight is a REAL modal step -----------------------------------
   `.workspace-coach-overlay` is pointer-events:none and its "dim" is only the ring's
   `box-shadow: 0 0 0 9999px` — and a box-shadow NEVER hit-tests. So nothing under the
   spotlight was ever blocked: on a phone a tap on the nav scrim closed the drawer the
   switcher lives in (stranding the coachmark on a target that no longer exists), and on
   BOTH views the highlighted job card's "Claim & Review" link stayed live, letting a
   viewer skip the whole company-profile step. Only two things may be tapped during this
   step: the workspace switcher, and the Demo-sections FAB (a <body> child at z2147483030,
   which no .app-shell rule can reach). The .nav-scrim deliberately keeps
   pointer-events:auto — it must still BLOCK the content beneath, it just no longer closes
   (that half is the navLocked guard in hirello-shell.js). */
body.workspace-tour-locked .main,
body.workspace-tour-locked .sidebar .nav-list,
body.workspace-tour-locked .sidebar .support-link,
body.workspace-tour-locked .sidebar .side-brand {
  pointer-events: none;
}

/* --- (2) A locked nav burger is MD3-inactive --------------------------------------------
   Same idiom as entry #71's ineligible candidate cards. `pointer-events:none` disables the
   FINGER, not the KEYBOARD — hirello-shell.js's setNavLocked() also stamps tabindex="-1" +
   aria-disabled, and refuses the burger/scrim/nav-item/Escape close paths outright. */
body.nav-locked .nav-burger {
  opacity: 0.39;
  pointer-events: none;
  cursor: default;
}

/* --- (3) The phone-only "Try it now" exit from the company-profile modal ------------------
   At <=900px `.company-basics-coach` becomes a fixed bottom sheet (left/right:14px;
   bottom:24px; z95) that covers BOTH the drawer foot's "Continue demo →" (z2 inside a z90
   drawer) AND the Company-basics toggle that un-disables it — so the phone had NO exit at
   all. Desktop keeps the foot button and never shows this one. `.coach-cta` is the house
   dark-panel CTA, byte-identical to nifhr's ranking coach. (It is NOT wrapped in a
   `.tooltip-foot` — entry #69 deleted that class and smoke.spec asserts it stays at count 0.) */
/* ⚠️ (Entry #121) THIS RULE USED TO BE `display: none` WITH A <=900px SHOW. The sheet has moved
   to the workspace step and desktop renders it as a centred modal, where this button is the only
   exit — hiding it above 900px would dead-end the tour. It is now shown at every width. */
.company-coach-cta {
  display: inline-flex;
  width: 100%;
  margin-top: 14px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   ENTRY #74 — the nifhr candidate profile drawer: a PHONE TIER (it never had
   one) + the scroll-collapsed identity row.

   Every .nhsq0-* rule above sits OUTSIDE any @media block. At 390px the drawer
   is min(460px, 92vw) = 359px; minus 32px padding each side = 295px of content,
   and the old hero grid (76px | 1fr | 72px, 18px gaps) ate 184px of it — leaving
   the 28px-bold name ~111px. That is why "Marta Kovač" broke to 2 lines and the
   role to 3 on the real iPhone. Same crush as the #70 LOCKED PHONE HEADER.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The collapsed identity row. Absolutely positioned UNDER the 76px head and
   OUTSIDE the scroller's flow — revealing it cannot resize the flex scroller,
   so there is zero layout shift and no shrink→scroll→shrink feedback loop.
   (A position:sticky child, or growing the head, would do exactly that.) */
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-sticky-id {
  position: absolute;
  /* Anchored to the head via a var — the head height and this offset are ONE
     number. Hardcoding both let them drift apart (head 76 / row 64 = a 20px overlap). */
  top: var(--nhsq0-head-h, 76px);
  left: 0;
  right: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 10px var(--space-xl);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  box-shadow: 0 14px 22px -20px rgba(15, 23, 42, 0.62);
  opacity: 0;
  transform: translateY(-8px);
  pointer-events: none;
  transition: opacity 180ms ease, transform 180ms ease;
}

/* Driven off aria-hidden, which the scroll handler flips. (A `.is-scrolled ~ …`
   sibling rule is impossible here — the row PRECEDES the scroller in the DOM and
   `~` only matches forward. Keeping the state on the row itself also means the
   a11y state and the visual state can never disagree.) */
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-sticky-id[aria-hidden="false"] {
  opacity: 1;
  transform: translateY(0);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-sticky-avatar {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--cand-marta);
  color: var(--text-inverse);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-sticky-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: var(--fs-lg);
  font-weight: var(--fw-extrabold);
  color: var(--text-primary);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-sticky-score {
  flex: 0 0 auto;
  padding: var(--space-2xs) 10px;
  border: 1px solid var(--success);
  border-radius: var(--radius-pill);
  background: var(--success-bg);
  color: var(--success-text);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
}

/* -- The phone tier the drawer never had. */
@media (max-width: 700px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .nhsq0-body {
    padding: 22px 20px 104px;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer {
    --nhsq0-head-h: 64px;
  }

  /* ⚠️ MUST carry .nifhr-profile-drawer: the desktop rule is (0,5,0), so a (0,4,0)
     phone rule silently LOSES even inside the media query — the head stayed 76px
     while the sticky row moved to 64px and overlapped it. */
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .nhsq0-head {
    height: 64px;
    min-height: 64px;
    padding: 0 20px;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-hero {
    grid-template-columns: 44px minmax(0, 1fr) auto;
    gap: var(--space-sm);
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-avatar {
    width: 44px;
    height: 44px;
    margin-top: 0;
    font-size: var(--fs-md);
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-identity h3 {
    font-size: 20px;
    line-height: 26px;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-identity p {
    font-size: var(--fs-md);
    line-height: 20px;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-score strong {
    width: 44px;
    height: 44px;
    font-size: var(--fs-sm);
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-score span {
    font-size: 9px;
    letter-spacing: 0.4px;
  }

  /* The sticky row tracks the shorter phone head. */
  body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-sticky-id {
    padding: var(--space-xs) 20px;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-actions {
    padding: 18px 20px;
  }
}

/* ENTRY #74 — nifhr's OPENING step is TAP-ONLY (the board carries
   data-free-drag="off"; see hirello-board.js canStartCuratedMove).
   The kanban card is unconditionally `touch-action: none; cursor: grab`, which
   (a) advertises a drag that is no longer available and (b) stops a finger from
   PANNING the column. Outside a curated move step, give the card back its tap
   affordances. The move steps re-assert touch-action:none themselves. */
body.nifhr-screen:not(.candidate-move-step) [data-screen-id="nIfHr"] .kanban.import-board .candidate-card {
  touch-action: manipulation;
  cursor: pointer;
}

/* ENTRY #74 — hide the Demo-sections FAB while a drawer is active, on PHYSICAL MOBILE.
   `body.drawer-active` is ORed from ~20 per-screen drawer signals in hirello-shell.js, which
   carves out the escape-hatch states (workspace-tour-locked veto; messenger overlays). */
@media (pointer: coarse) and (hover: none) and (max-width: 700px),
       (pointer: coarse) and (hover: none) and (max-height: 540px) {
  body.drawer-active .demo-sections-launcher {
    opacity: 0;
    pointer-events: none;
    visibility: hidden;
    transition: opacity 140ms ease, visibility 0s linear 140ms;
  }
}

/* 2026-07-17 (user call) — the FAB is hidden while the LANDING SHEET is up, on EVERY view, and
   returns once the sheet has gone. Deliberately NOT inside the #74 media query above: that rule
   is gated to physical phones, so on DESKTOP the FAB sat over the redacted deck through the whole
   ~8.4s gag (measured visible at 1440 before this rule; the phone was already hidden, because
   `job-card-modal-active`/`job-card-chooser-active` are both DRAWER_SIGNALS).

   Keyed on `jc-sheet-active` ALONE rather than by widening `drawer-active`: entry #88 already
   proved that broadening the desktop hide to all drawer-active states kills rj0wj's FAB at load
   (its intro sets job-card-modal-active -> drawer-active) and turns #75 red. One gate, one writer.
   Safe against the #72 escape hatch by construction: `jc-sheet-active` is dropped in
   dismissSheet() and `workspace-tour-locked` is only ever added later, in startWorkspaceSpotlight(),
   so the two classes can never coexist. And the sheet's CTA carries a watchdog, so a viewer can
   never be stranded behind a FAB-less sheet. */
body.jc-sheet-active .demo-sections-launcher {
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  transition: opacity 140ms ease, visibility 0s linear 140ms;
}

/* 2026-07-21 (user call) — the floating-cards CHOOSER DECK screen ("Where are you bleeding the most
   time?") shows NO Demo-sections FAB on any view. Mobile/tablet already hide it (the deck sets
   `job-card-chooser-active`, a DRAWER_SIGNAL caught by the #74 rule above); this matches that on
   DESKTOP too. Keyed on `job-card-chooser-active` ALONE, never `drawer-active` (#88 proved
   broadening to drawer-active kills the FAB at load via job-card-modal-active and reddens #75). The
   class is dropped before startWorkspaceSpotlight (rj0wj.html:3025), so the #72 escape-hatch FAB on
   the workspace step is unaffected; a card jump or "Just show me everything" both leave the deck and
   restore the FAB. The deck self-navigates (cards + footer link), so no one is stranded. */
body.job-card-chooser-active .demo-sections-launcher {
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
}

/* ENTRY #88 (user call) — on DESKTOP, the right-side CANDIDATE PROFILE drawer overlaps the
   top-right FAB, so the FAB hides while that drawer is open and returns when it closes. Scoped
   to `profile-drawer-open` ONLY: the rj0wj full-screen intro (job-card-modal-active) covers the
   FAB itself. ⚠️ This comment used to say the COMPANY-profile drawer is the #72 escape-hatch
   step where the FAB must STAY: that carve-out is now RETIRED ON DESKTOP by the rule below.
   Still true, and still the reason neither rule is keyed on `drawer-active`: broadening to all
   drawer states kills the FAB at load via `job-card-modal-active` and reddens #75. */
@media (min-width: 901px) {
  body.profile-drawer-open .demo-sections-launcher {
    opacity: 0;
    pointer-events: none;
    visibility: hidden;
    transition: opacity 140ms ease, visibility 0s linear 140ms;
  }
}

/* 2026-07-21 (user call) — on DESKTOP the left-launched COMPANY PROFILE drawer hides the
   Demo-sections FAB too, matching what mobile/tablet already do (the drawer sets
   `company-drawer-open`, which hirello-shell.js ORs into `drawer-active`, caught by the entry
   #74 coarse-pointer rule above). The FAB sat live in the top-right corner over the drawer's
   own header on every desktop width; measured visible/opacity 1/pointer-events auto at 1440x900
   with the drawer open, before this rule.
   Keyed on `company-drawer-open` ALONE, never `drawer-active` (#88's law: broadening the
   desktop hide to all drawer states kills rj0wj's FAB at load and turns #75 red).
   ⚠️ This deliberately retires the #72 escape-hatch carve-out FOR DESKTOP ONLY. Nobody is
   stranded: the drawer carries its own onward exits ("Open >" then the "Continue demo" button
   at the end of its scrolling column, #128). The ≤900px behaviour is untouched, so the phone
   escape hatch the #72 work built is exactly as it was. */
@media (min-width: 901px) {
  body.company-drawer-open .demo-sections-launcher {
    opacity: 0;
    pointer-events: none;
    visibility: hidden;
    transition: opacity 140ms ease, visibility 0s linear 140ms;
  }
}

/* ⚠️⚠️ USER CALL ON AN ANNOTATED FRAME, 2026-08-12: *"When any of the slideout drawers are showing
   we have a design rule that ensures the scene selector FAB doesn't show until the slideout drawer
   is closed, so check this entire animation segment as that rule has not been applied"*, arrow
   drawn from the open "Select files to attach" drawer to the FAB sitting live on top of it.

   ⚠️ ALL FIVE OF THE MESSENGER'S PANELS WERE UNCOVERED ON DESKTOP, and the two halves failed for
   DIFFERENT reasons, which is why naming them all is the fix rather than adding one class:
     - library, location and pdf-preview DO reach `hirello-shell.js`'s `drawer-active`, but the only
       consumer of `drawer-active` in this whole sheet is the entry #74 rule above, gated
       `(pointer: coarse) and (hover: none)`. A desktop mouse never matches it, at any width.
     - questionnaire and share are not in `DRAWER_SIGNALS` at all, so they never set
       `drawer-active` and were uncovered on the PHONE as well.
   Measured by selector search before writing this: zero hits pairing any of the five body classes
   with `.demo-sections-launcher` anywhere in the file.

   ⚠️ NAMED CLASSES, NEVER `drawer-active`, and that is #88's law rather than a preference: the
   comment above records that broadening the desktop hide to all drawer states kills rj0wj's FAB at
   load through `job-card-modal-active` and turns `#75` red. Adding two names to `DRAWER_SIGNALS`
   instead would also have meant a `nav18 -> nav19` bump across fourteen files for a rule that is
   pure CSS.

   ⚠️ NOT MEDIA-GATED, unlike its two neighbours, because her rule is stated for the drawers rather
   than for a tier, and this is the same treatment the phone already gets for three of the five. The
   #72 escape hatch is not harmed: every one of these panels closes itself seconds later inside the
   film, and the FAB returns with the same 140ms fade the other two rules use. */
body.messenger-document-library-open .demo-sections-launcher,
body.messenger-location-open .demo-sections-launcher,
body.messenger-questionnaire-open .demo-sections-launcher,
body.messenger-pdf-preview-open .demo-sections-launcher,
body.messenger-share-open .demo-sections-launcher {
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  transition: opacity 140ms ease, visibility 0s linear 140ms;
}

/* ============================================================================
   rj0wj — LOCK THE PAGE BEHIND THE JOB-CARD OVERLAYS (entry #77, 2026-07-15).
   The dimmed app-shell behind the intro modal AND the chooser deck ("What's slowing
   your hiring down most?") still scrolled under a wheel or a finger. Root cause: every
   viewport-lock rule in this file is scoped to body.x1riqt-screen / body.nifhr-screen,
   and rj0wj carries NEITHER — so html/body are overflow:visible there at every width.
   Measured: 121px of scrollable slack at 390x844, 33px at 430x932; a wheel/touch on the
   dim scrolled it. Desktop measures 0px (1440x900 / 1440x700 / 1280x800 / 1920x1080),
   so this is a provable no-op there — it only hardens the page and kills the iOS bounce.

   TWO mechanisms, both needed:
   (a) body { overflow:hidden } — the viewport takes its scrollability from BODY when
       html's overflow is `visible` (CSS overflow propagation), so a body-only rule locks
       the document on EVERY browser incl. iOS Safari. This is the same mechanism
       `body.nav-open { overflow:hidden }` (:12956) already relies on in this app.
       overscroll-behavior propagates identically → it kills the iOS rubber-band bounce.
   (b) touch-action:none on the DECK SCRIM — touch-action resolves up the ancestor chain,
       so ONE rule covers the scrim and every card under it, killing the finger-pan at
       source. Deliberately NOT on .job-card-intro-overlay: the intro modal is its own
       scroller (.job-card-intro-modal { overflow:auto }) and must still pan on a finger.

   Keyed purely on the class rj0wj already toggles — set in the markup at rj0wj.html:32
   (so the lock is live pre-paint, incl. the ?stage=chooser deep link) and removed in
   exactly ONE place, startWorkspaceSpotlight() (rj0wj.html:615, reached only via
   fullTour() → 'hf-full-tour'). So it releases the instant the workspace tour starts and
   can never strand a viewer. No new JS. The class is rj0wj-only — no other screen sets it.
   The Demo-sections FAB's bottom sheet is position:fixed with its OWN
   overflow-y:auto + overscroll-behavior:contain and is NOT a descendant of the scrim,
   so it keeps scrolling normally while the lock is on.
   ========================================================================== */
body.job-card-modal-active {
  overflow: hidden;
  overscroll-behavior: none;
}
.job-card-chooser-overlay {
  touch-action: none;
  overscroll-behavior: contain;
}
.job-card-intro-modal { overscroll-behavior: contain; }

/* ==========================================================================
   (Task 1, 2026-07-16) rj0wj company-profile drawer on phone/tablet.
   The drawer X advances to the next screen (handler gated to <=900px in rj0wj.html) and the
   coach "Try it now" dismisses the explainer sheet so the body scrolls; the disabled desktop
   foot button is hidden so the X is the only advance. Desktop (>900px) is byte-identical.
   ========================================================================== */
@media (max-width: 900px) {
  .company-drawer-close.company-drawer-close-inert { pointer-events: auto; cursor: pointer; }
  .company-drawer-foot { display: none; }
}
/* "Try it now" dismisses the coach so the drawer scrolls. Matches the show rule's (0,3,0)
   specificity (body.company-drawer-open .company-basics-expandable.is-collapsed .company-basics-coach)
   and wins by source order. The dismiss class is only ever set on phone/tablet (the CTA is phone-only). */
body.company-coach-dismissed .company-basics-expandable.is-collapsed .company-basics-coach { display: none; }

/* ==========================================================================
   (Task 4, 2026-07-16) nifhr Compare/ranking modal:
   4a: DD:HH:MM:SS odometer countdown pill (replaces the "Xd Yh Zm" chip).
   4b: light "Drag to reorder" hint on the top-ranked (Marta) handle.
   4c: in-modal success state after "Submit my ranking".
   All scoped to nifhr's compare modal; desktop + mobile share these rules.
   ========================================================================== */
body.nifhr-screen [data-screen-id="nIfHr"] .compare-deadline.compare-deadline-odo {
  flex-direction: column;
  align-items: center;
  gap: var(--space-2xs);
  /* equal top/bottom breathing room for the pill + caption inside the section border */
  padding: 10px var(--space-sm);
  background: transparent;
  border-radius: 0;
}
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-timer {
  display: grid;
  grid-template-columns: repeat(7, auto);
  align-items: center;
  justify-items: center;
  column-gap: 5px;
  row-gap: 1px;
  padding: 7px 15px;
  border-radius: 99px;
  background: #111827;
}
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-timer .cdo-reels { grid-row: 1; }
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-timer .cdo-colon { grid-row: 1; }
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-timer .cdo-label { grid-row: 2; }
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-timer [data-odo-unit="d"],
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-timer [data-odo-label="d"] { grid-column: 1; }
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-timer [data-odo-unit="h"],
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-timer [data-odo-label="h"] { grid-column: 3; }
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-timer [data-odo-unit="m"],
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-timer [data-odo-label="m"] { grid-column: 5; }
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-timer [data-odo-unit="s"],
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-timer [data-odo-label="s"] { grid-column: 7; }
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-reels {
  display: inline-flex;
}
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-digit {
  display: inline-block;
  width: 0.64em;
  height: 1lh;
  overflow: hidden;
  vertical-align: top;
  font-size: 19px;
  line-height: 1;
  font-weight: var(--fw-extrabold);
  color: var(--text-inverse);
  font-variant-numeric: tabular-nums;
}
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-reel {
  display: block;
  transform: translateY(calc(var(--o, 0) * -1lh));
  transition: transform 0.45s cubic-bezier(0.4, 0, 0.2, 1);
  will-change: transform;
}
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-reel > span {
  display: block;
  height: 1lh;
  text-align: center;
}
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-colon {
  font-size: 19px;
  line-height: 1;
  font-weight: var(--fw-extrabold);
  color: var(--text-inverse);
}
body.nifhr-screen [data-screen-id="nIfHr"] .cdo-label {
  font-size: 8px;
  line-height: 1;
  font-weight: var(--fw-bold);
  letter-spacing: 0.05em;
  color: var(--text-inverse);
  opacity: 0.72;
}
body.nifhr-screen [data-screen-id="nIfHr"] .compare-deadline-caption {
  font-size: var(--fs-xs);
  color: var(--text-tertiary);
}

/* 4b: Marta drag hint (top-ranked handle), while ranking is armed and untouched. */
/* ENTRY #136: the BOB MOVED OFF THIS BUTTON AND ONTO THE WHOLE ROW (see the tail block). The
   handle keeps its ring/border/colour, which is what marks it as the grab point, but it no longer
   carries `animation`: a floating button inside a still row read as the button being the only
   draggable thing. `cmpDragHintBob` is gone; `cmpDragHintFloat` on the row replaces it. */
body.nifhr-screen.comparison-rank-active:not(.comparison-rank-touched) [data-screen-id="nIfHr"] .compare-marta .compare-rank-handle,
body.nifhr-screen.comparison-rank-active:not(.comparison-rank-touched) [data-screen-id="nIfHr"] .compare-mob-card[data-compare-candidate="marta"] .compare-rank-handle {
  box-shadow: 0 0 0 4px rgba(124, 58, 237, 0.18);
  border-color: var(--brand);
  color: var(--brand-dark);
}
body.nifhr-screen.comparison-rank-active:not(.comparison-rank-touched) [data-screen-id="nIfHr"] .compare-marta .compare-rank-handle::after,
body.nifhr-screen.comparison-rank-active:not(.comparison-rank-touched) [data-screen-id="nIfHr"] .compare-mob-card[data-compare-candidate="marta"] .compare-rank-handle::after {
  content: "Drag to reorder";
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  transform: none;
  white-space: nowrap;
  background: var(--brand-dark);
  color: var(--text-inverse);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: 0.01em;
  padding: 5px 9px;
  border-radius: var(--radius-sm);
  pointer-events: none;
  box-shadow: 0 6px 16px rgba(17, 24, 39, 0.22);
  z-index: 4;
}
/* Mobile handle sits at the card's LEFT, so let its tooltip extend rightward into the open row. */
body.nifhr-screen.comparison-rank-active:not(.comparison-rank-touched) [data-screen-id="nIfHr"] .compare-mob-card[data-compare-candidate="marta"] .compare-rank-handle::after {
  right: auto;
  left: 0;
}

/* ==========================================================================
   ENTRY #200 (user call). The STACKED (mobile/tablet) compare layout only: the "Drag to reorder"
   chip takes the DARK TOOLTIP TREATMENT WITH A TAIL, the same look as #178's "Tap to open" card on
   Marta's kanban row (hirello-app.css:20233 and below). Same tokens, so the two read as one
   component: --text-primary fill, --text-inverse ink, --radius-md, --shadow-popover, 14px/20px at
   weight 800. The COPY is unchanged; the user asked for #178's style carrying this text.

   ⚠️ WHY THE DESKTOP COLUMN LAYOUT IS DELIBERATELY EXCLUDED, and it is not an oversight. The
   annotation was of the stacked phone modal. Applying the dark treatment to `.compare-marta` too
   was BUILT AND RENDERED FIRST, and at 1440 the bigger bubble sits directly over Marta's own NAME
   in the column header (measured: the bubble spans the name row, which the old 11px chip cleared),
   so shipping it there would have traded a legibility bug for a style match nobody asked for.
   Desktop keeps the small brand-dark chip above. If desktop should match, it needs its own anchor,
   not this rule copied.

   ⚠️ THE TAIL CANNOT LIVE ON THE CHIP: a pseudo-element cannot own another pseudo-element. It is a
   SEPARATE `::before` on the same handle, which was free (verified, nothing else styles it). Both
   are positioned from the handle's box, so they track it together with no JS, and both sit inside
   the floating row so #136's `cmpDragHintFloat` carries them for free.

   ⚠️ `pointer-events: none` is KEPT, unlike #178 which needed `auto`. #178 overlaps a live sibling
   card that would swallow the tap. Here the whole hint is torn down by the capture-phase
   `pointerdown` that adds `comparison-rank-touched` (nifhr.html:3085), which runs BEFORE
   `startRankDrag` binds in the bubble phase, so a tap "through" the chip cannot reach a drag the
   chip is still covering. Giving it `auto` would route taps to the handle and change behaviour;
   the ask was a restyle.
   ========================================================================== */
/* ⚠️ ANCHOR THE PSEUDOS TO THE HANDLE. `.compare-rank-handle` ships `position: static`, so an
   absolutely positioned pseudo on it resolves its percentages against the nearest POSITIONED
   ancestor, which is `.compare-mob-card` (measured: offsetParent === compare-mob-card). `top: 100%`
   therefore landed 8px BELOW the handle's bottom rather than on it, leaving the tail's tip floating
   clear of the thing it points at, while #178's tip touches its target's border. Making the handle
   a containing block puts 100% back on the handle's own bottom edge.
   ⚠️ STACKED TWIN ONLY. `.compare-rank-handle` is shared with the desktop column layout, where the
   chip is anchored `right: 0` against the CELL; re-anchoring it to the handle there would move the
   desktop chip, and desktop is deliberately out of scope for #200. */
/* ⚠️⚠️ ENTRY #234: `inset: auto` IS LOAD-BEARING, NOT TIDYING. The base rule at :9825 is
   `position: absolute; top: 12px; right: 12px`, and the phone tier at :14149 neutralises it with
   `position: static`, which leaves those two offsets DECLARED but INERT. Switching this one handle
   to `relative` for the containing block above brought them BACK TO LIFE, and only on Marta:
   measured identically at 390x844, 360x800 and 412x915, her handle sat 12px down and 12px left of
   where Ahmed's and Priya's sit (insetLeft -1 vs 11, insetTop 21 vs 9, centreDeltaY 12 vs 0) and
   overflowed the card's bottom edge by 3px. It snapped back the instant `comparison-rank-touched`
   landed and this rule stopped matching, so it read as "misaligned until you drag it".
   THE LAW: changing an element from `static` to `relative` REACTIVATES every box-offset an
   ancestor tier had silenced. Whenever you make something a containing block only for a pseudo,
   clear its own offsets in the same rule. */
body.nifhr-screen.comparison-rank-active:not(.comparison-rank-touched) [data-screen-id="nIfHr"] .compare-mob-card[data-compare-candidate="marta"] .compare-rank-handle {
  position: relative;
  inset: auto;
}
body.nifhr-screen.comparison-rank-active:not(.comparison-rank-touched) [data-screen-id="nIfHr"] .compare-mob-card[data-compare-candidate="marta"] .compare-rank-handle::after {
  background: var(--text-primary);
  color: var(--text-inverse);
  font-size: var(--fs-md);
  line-height: 20px;
  font-weight: var(--fw-extrabold);
  letter-spacing: -0.01em;
  padding: 9px 14px;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-popover);
}
/* The tail points UP and is 8px tall against the 8px gap above it, so its tip touches the handle's
   bottom edge exactly the way #178's touches Marta's border. 14px in from the bubble's left edge,
   one padding-width, which lands it under the first word. */
body.nifhr-screen.comparison-rank-active:not(.comparison-rank-touched) [data-screen-id="nIfHr"] .compare-mob-card[data-compare-candidate="marta"] .compare-rank-handle::before {
  content: "";
  position: absolute;
  top: 100%;
  left: 14px;
  right: auto;
  width: 16px;
  height: 8px;
  background: var(--text-primary);
  clip-path: polygon(0 100%, 50% 0, 100% 100%);
  pointer-events: none;
  z-index: 5;
}

@media (prefers-reduced-motion: reduce) {
  /* ENTRY #136: the float is on the ROW now, so this must name the row or it silences nothing. */
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-marta,
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-mob-card[data-compare-candidate="marta"] {
    animation: none;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .cdo-reel { transition: none; }
}

/* 4c: in-modal success state. */
body.nifhr-screen [data-screen-id="nIfHr"] .compare-success {
  position: absolute;
  inset: 0;
  z-index: 6;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: var(--space-xl);
  text-align: center;
  background: var(--surface);
}
body.nifhr-screen.comparison-submitted [data-screen-id="nIfHr"] .compare-success {
  display: flex;
}
body.nifhr-screen [data-screen-id="nIfHr"] .compare-success-check {
  width: 56px;
  height: 56px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: #DCFCE7;
  color: #16A34A;
}
body.nifhr-screen [data-screen-id="nIfHr"] .compare-success h2 {
  margin: 0;
  font-size: 22px;
  font-weight: var(--fw-extrabold);
  color: var(--text-primary);
}
body.nifhr-screen [data-screen-id="nIfHr"] .compare-success p {
  margin: 0;
  max-width: 420px;
  font-size: var(--fs-md);
  line-height: var(--lh-normal);
  color: var(--text-secondary);
}
body.nifhr-screen [data-screen-id="nIfHr"] .compare-success .compare-primary {
  margin-top: var(--space-xs);
  /* The mobile footer rule sets .compare-primary { flex: 1 } for its flex ROW; here the success
     panel is a flex COLUMN, so flex:1 would stretch the button into a tall oval. Pin it to its
     natural size and give it a sensible width. */
  flex: 0 0 auto;
  align-self: center;
  width: auto;
  min-width: 220px;
  max-width: 100%;
}

/* ==========================================================================
   (2026-07-16, user call) Ghost-hand coach LABEL PILL — riding the animated hand on both kanban
   coaches (x1riqt import +
   ⚠️ ENTRY #200 MADE THIS COMMENT'S OLD CLAIM FALSE. It used to read "same look as the compare
   modal's 'Drag to reorder' hint". That hint is now the DARK tooltip with a tail (#178's treatment,
   hirello-app.css:15227); this pill is still the small brand-dark chip and was deliberately NOT
   changed, because the user's annotation named the compare modal only. The two are no longer one
   look: do not "restore consistency" by copying either onto the other without asking.
   nifhr curated move). Text is "Click and drag" (desktop) / "Tap and drag" (touch). The pill
   is a SIBLING of the hand, positioned upright + constant-size in placeHand() (no rotate/scale).
   ========================================================================== */
[data-screen-id="x1RIQT"] .drag-coach-pill,
body.nifhr-screen .move-coach-pill {
  position: absolute;
  left: 0;
  top: 0;
  opacity: 0;
  pointer-events: none;
  white-space: nowrap;
  background: var(--brand-dark);
  color: var(--text-inverse);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: 0.01em;
  padding: 5px 9px;
  border-radius: var(--radius-sm);
  box-shadow: 0 6px 16px rgba(17, 24, 39, 0.22);
  will-change: transform, opacity;
}

/* ---- Coachmark tail aim (2026-07-16, entry #87, user call) ----
   Desktop/widescreen only: the "Start here" tooltip tail follows --tail-x, set live
   from the "Claim & Review" button in rj0wj's placeTour(), so it points AT the button
   instead of the empty gap. ≤900px keeps the base .tooltip::before{left:40px} (css:637)
   because the card stacks and the button drops below the body there. */
@media (min-width: 901px) {
  .tooltip::before { left: var(--tail-x, 40px); }
}

/* ---- Anonymous app-user avatar (2026-07-16, entry #87, user call) ----
   Shown when the contact has no first name, greeting, or second name. The grey
   head-and-shoulders SVG is injected by hirello-config-apply.js; here we recolour the
   avatar grey and size the glyph. Covers the top-right .user-dot on all shell screens
   AND the rj0wj reviewer chip (<i data-viewer-initial>). */
.user-dot.is-anonymous {
  background: #E5E7EB;
  border-color: #D1D5DB;
  color: var(--text-tertiary);
}
.job-card-rank-avatars i.is-anonymous {
  background: #E5E7EB;
  color: var(--text-tertiary);
}
[data-viewer-initial].is-anonymous > svg {
  display: block;
  width: 68%;
  height: 68%;
}

/* ---- x1riqt import/parse screen tweaks (2026-07-16, entry #88, user call) ---- */
/* C: the "00:07" parse timer is removed on every view (not needed). */
[data-screen-id="x1RIQT"] .parse-timer { display: none; }
/* D: while parsing AND when finished, the status-bar outline is SOLID, not dashed
   (the ::after draws the outline during the processing state; the idle drop zone
   keeps its dashed affordance, unchanged). */
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .import-drop::after {
  border-style: solid;
}
/* DESKTOP-ONLY (body.x1riqt-screen is present at every width, so scope by min-width). */
@media (min-width: 701px) {
  /* A: (entry #88) styled the pile's "Browse files" CTA. ENTRY #113 DELETED that button on every
     view, so these rules could only ever match nothing. Removed with the feature. */
  /* B: the Maybe + Shortlist "Drag candidates here" placeholders are removed on desktop
     (this screen never receives a curated drag — that happens on nifhr — so the prompt
     was misleading). Scoped to x1riqt so nifhr's own placeholders are untouched. */
  [data-screen-id="x1RIQT"] .review-col .col-drag-placeholder,
  [data-screen-id="x1RIQT"] .shortlist-col .col-drag-placeholder {
    display: none;
  }
}

/* ---- nifhr board tweaks (2026-07-16, entry #88 + desktop follow-ups, user call) ---- */
/* G: the "+ Drop more resumes" strip is removed on EVERY view. Dropping more resumes
   belongs to the x1riqt mass-import screen; by the time the candidate lands here the
   import is done. (Supersedes entry #71's "desktop keeps it".) */
body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-drop-strip { display: none; }

/* Header actions ("Polish the job ad" / "Invite teammate" / "Filters") removed on EVERY view. */
body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-head-actions { display: none; }

/* During the curated move (desktop), inset the board so the highlighted source/target columns'
   outward glow is not clipped by the stage's overflow (the leftmost source column's glow was
   flush cut on the left). */
@media (min-width: 901px) {
  body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board {
    padding-inline: 14px;
  }
}

/* The Maybe empty drop prompt (the "Drag to Maybe" box) is removed from the resting board /
   View-message view. It comes back ONLY during the curated-move step, where the Maybe zone is
   the live drag target. (The "Your top picks" shortlist ghost was deleted from the markup.) */
body.nifhr-screen:not(.candidate-move-step) [data-screen-id="nIfHr"] .review-drop-target {
  display: none;
}

/* F: a left-pointing tail on the base "View/message the candidate" coach, aimed squarely at
   the Marta row. The coach is nudged UP so the tail rides the mid-upper edge (clear of the
   rounded corner) and points at Marta's vertical centre. The coach tracks Marta horizontally
   (left = first-card-right + gap). `.nifhr-coach` is a SINGLE reused element repositioned per
   step, so both the nudge and the tail apply at the BASE step only (state rules out-specify the
   nudge; the tail is explicitly hidden wherever the coach moves). Desktop. */
@media (min-width: 901px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-coach {
    top: 116px;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-coach::before {
    content: "";
    position: absolute;
    left: -13px;
    top: 41px;
    width: 15px;
    height: 18px;
    background: #0f1726;
    clip-path: polygon(100% 0, 0 50%, 100% 100%);
    pointer-events: none;
  }
  body.nifhr-screen.profile-drawer-open [data-screen-id="nIfHr"] .nifhr-coach::before,
  body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .nifhr-coach::before,
  body.nifhr-screen.screening-flow-active [data-screen-id="nIfHr"] .nifhr-coach::before,
  body.nifhr-screen.screening-step-messenger [data-screen-id="nIfHr"] .nifhr-coach::before,
  body.nifhr-screen.messenger-guide-active [data-screen-id="nIfHr"] .nifhr-coach::before,
  body.nifhr-screen.messenger-demo-live [data-screen-id="nIfHr"] .nifhr-coach::before {
    display: none;
  }
}

/* =========================================================================================
   ENTRY #89 (user call). The DONE Applied column: token colour, MD3-inactive at 39%.
   -----------------------------------------------------------------------------------------
   ⚠️ SPECIFICITY (this is why the obvious edit does nothing): the column KEEPS its
   `.is-curated-source` class after the move completes, and that rule (entry #71, :14702) is
   (0,9,1) and hard-sets `opacity: 1`. It has to, to lift the column out of the walkthrough dim
   during the move. The move-complete rule up at :9373 is only (0,7,0), so an `opacity: .39`
   written THERE is silently dead: the column renders at full strength and only the cards look
   dim (they carry their own 39%). The tell is that the BACKGROUND still looks right, because
   both rules now resolve to the same token: a coincidence that hides the loss.
   This restates the SAME (0,9,1) chain plus `.candidate-move-complete`, and sits at the END of
   the file so it wins on specificity AND source order. Do not "simplify" the prefix away.
   Verified by driving a REAL mouse drag to completion. Forcing the body classes reports the
   wrong numbers on this board (entry #88b).
   ========================================================================================= */
body.nifhr-screen.candidate-move-step.candidate-move-complete[data-candidate-move-source] [data-screen-id="nIfHr"] .kanban.import-board .kanban-col.is-curated-source {
  background: var(--col-applied-surface);
  opacity: 0.39;
  box-shadow: none;
  transition: opacity 520ms ease, background 520ms ease, box-shadow 520ms ease;
}

/* ─────────────────────────────────────────────────────────────────────────────
   CHOOSER-DECK GESTURE COACH (entry #98, 2026-07-17, user call)
   Replaces the deck header's old "Swipe right to jump straight in, left to see the next
   one - or tap any card." subtitle: a tapping hand in the centre, a swipe hand drifting
   left, and a mirrored swipe hand drifting right. It SHOWS the gesture instead of
   describing it.

   The art is the user's own two gifs, keyed off their white background and flipped to
   white line art for this dark header. THE SPRITE IS GENERATED, NOT HAND-MADE:
   `python3 harness/build-gesture-sprite.py` reads harness/gesture-src/{slide,click}.gif
   and writes assets/gesture-hands.png. That script PRINTS the four constants below as its
   "CSS contract" - re-run it and re-read them rather than editing them by hand.

   ⚠ THE THREE NUMBERS THAT ARE NOT ARBITRARY:
   (1) 102.0408% is not a typo for 100%. background-position-x is a percentage of
       (container - image), so cell k shows at k/(cells-1) = k/49. A steps(50) run must
       therefore end at 50/49 = 102.0408%; ending at 100% skews every frame and never
       reaches the last cell.
   (2) 1.67s is the gifs' real loop, resampled onto a constant 30fps grid because the
       source frame delays are variable (30/40ms) and steps() assumes they are not.
   (3) 0.835s is the TRAVEL period, and it is 1.67/2 because each gif runs TWO gesture
       cycles per loop (measured: the hand leans left at 200-500ms and again at
       1035-1335ms). Give the travel the full 1.67s and it disagrees with the hand's own
       stroke on every second swipe.
   Both animations sit on the same element, so they start in the same frame and stay
   locked; 1.67 is an exact multiple of 0.835, so they cannot drift apart.
   ───────────────────────────────────────────────────────────────────────────── */
.jc-gesture-coach {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: clamp(2px, 1.2vw, var(--space-xs));
  /* Fixed height: the row must never be able to reflow the header, whose measured height
     is baked into .jc-deck's own height constants. The clamp lives in the entry #123 tail
     block as --jc-coach-h, because the dismiss grow subtracts from exactly this number. */
  height: var(--jc-coach-h);
  margin-top: var(--space-xs);
  /* The hands are decoration over the deck's live drag surface - never let them take a
     press that was meant for a card. */
  pointer-events: none;
}
.jc-gesture {
  width: clamp(34px, 10vw, 46px);
  height: clamp(34px, 10vw, 46px);
  background-image: url("assets/gesture-hands.png");
  background-repeat: no-repeat;
  background-size: 5000% 200%;   /* 50 cells across, 2 rows down */
  will-change: transform, opacity;
}
/* Row 0 = the swipe hand, row 1 = the tap hand. */
.jc-gesture-left,
.jc-gesture-right { background-position-y: 0%; }
.jc-gesture-tap { background-position-y: 100%; }

.jc-gesture-tap { animation: jcGestureFilm 1.67s steps(50) infinite; }
.jc-gesture-left { animation: jcGestureFilm 1.67s steps(50) infinite, jcGestureDriftL 0.835s linear infinite; }
.jc-gesture-right { animation: jcGestureFilm 1.67s steps(50) infinite, jcGestureDriftR 0.835s linear infinite; }

@keyframes jcGestureFilm {
  from { background-position-x: 0%; }
  to { background-position-x: 102.0408%; }
}
/* The drift tracks the hand's OWN stroke, measured off the sprite: wind-up to 24%, the
   stroke 24-60%, held at the far end 60-72% while the gif's arrow is faded out, then it
   fades and returns to the start WHILE INVISIBLE. That invisible return is what satisfies
   "loops back without sudden position jarring" - the alternative, sliding back in view,
   reads as a swipe in the wrong direction. */
@keyframes jcGestureDriftL {
  0% { transform: translateX(0); opacity: 0; }
  8% { transform: translateX(0); opacity: 1; }
  24% { transform: translateX(0); opacity: 1; animation-timing-function: cubic-bezier(.4, 0, .2, 1); }
  60% { transform: translateX(calc(var(--jc-gesture-drift) * -1)); opacity: 1; }
  72% { transform: translateX(calc(var(--jc-gesture-drift) * -1)); opacity: 1; }
  94% { transform: translateX(calc(var(--jc-gesture-drift) * -1)); opacity: 0; }
  100% { transform: translateX(0); opacity: 0; }
}
/* scaleX(-1) mirrors the art so the arrow points RIGHT (the gifs only ship a left swipe).
   It lives INSIDE the keyframes on purpose: a static transform on the element would be
   overwritten by the animation's own transform, silently un-mirroring the hand. */
@keyframes jcGestureDriftR {
  0% { transform: translateX(0) scaleX(-1); opacity: 0; }
  8% { transform: translateX(0) scaleX(-1); opacity: 1; }
  24% { transform: translateX(0) scaleX(-1); opacity: 1; animation-timing-function: cubic-bezier(.4, 0, .2, 1); }
  60% { transform: translateX(var(--jc-gesture-drift)) scaleX(-1); opacity: 1; }
  72% { transform: translateX(var(--jc-gesture-drift)) scaleX(-1); opacity: 1; }
  94% { transform: translateX(var(--jc-gesture-drift)) scaleX(-1); opacity: 0; }
  100% { transform: translateX(0) scaleX(-1); opacity: 0; }
}
.jc-gesture-coach { --jc-gesture-drift: clamp(16px, 5vw, 24px); }

/* Same fate as the subtitle this replaced (which was display:none here): the landscape tier
   budgets the WHOLE head at 22px, which a hand row cannot live inside. This override must
   live HERE, not up with the tier's other rules at the .job-card-chooser-head h2 rule - the
   base .jc-gesture-coach rule above is later in the file, so an equal-specificity rule up
   there loses on source order despite being inside the media query. */
@media (max-height: 540px) {
  .jc-gesture-coach { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  /* Freeze to one representative cell of each gesture (mid-stroke for the swipe, ripple
     peak for the tap) and hold the drift at rest: the row still reads as "swipe / tap /
     swipe", with no motion at all. The right hand keeps its mirror. */
  .jc-gesture { animation: none !important; }
  .jc-gesture-left,
  .jc-gesture-right { background-position-x: 24.4898%; }  /* cell 12, mid-stroke */
  .jc-gesture-right { transform: scaleX(-1); }
  .jc-gesture-tap { background-position-x: 28.5714%; }    /* cell 14, ripple peak */
}

/* ─────────────────────────────────────────────────────────────────────────────
   CHOOSER DECK, PASS 2 (entry #99, 2026-07-17, user call)
   Everything here is tail-block ON PURPOSE: each rule below corrects an earlier one of
   EQUAL specificity, so source order is the only thing that makes it win. Moving any of it
   up-file silently reverts it. (#98 proved this the hard way with the landscape hide.)
   ───────────────────────────────────────────────────────────────────────────── */

/* (A1) The gesture coach shows NOTHING until the landing sheet is gone. The deck header sits
   ABOVE the bottom sheet and is only blurred, so the hands were visibly swiping behind it.
   `animation-play-state: paused` from first paint means the animation never ADVANCES, so on
   reveal it starts from FRAME ONE. That is the same guarantee #95 gives the deck, and the reason
   this is not just `visibility`. body.jc-sheet-active drops 380ms after the CTA, i.e. once
   the sheet has actually left; `?stage=chooser` never carries it, so that deep link is
   correct by construction.
   Hidden as well as frozen because frame 0 is not a presentable pose: the two side hands
   REST at opacity 0 (their loop has to start invisible so the return leg can be invisible),
   so a merely-frozen row would show one lone tap hand. */
body.jc-sheet-active .jc-gesture-coach { visibility: hidden; }
body.jc-sheet-active .jc-gesture { animation-play-state: paused; }

/* (A2) The two SWIPE hands run 25% slower (user call). ⚠️ Both numbers move together and the
   2:1 ratio is LOAD-BEARING: each gif runs TWO gesture cycles per film loop (#98 law 4), so
   the drift period must stay exactly half the film or the hand fights its own stroke on every
   second swipe. 1.67 -> 2.09s film, 0.835 -> 1.045s drift. The centre TAP hand is untouched
   at 1.67s. The user slowed the swipe, not the row. */
.jc-gesture-left { animation: jcGestureFilm 2.09s steps(50) infinite, jcGestureDriftL 1.045s linear infinite; }
.jc-gesture-right { animation: jcGestureFilm 2.09s steps(50) infinite, jcGestureDriftR 1.045s linear infinite; }

/* (A3) First grab or tap and the coaching is done, for good (user call). Set by
   markInteracted(), which never un-sets. Fades rather than snaps so it does not pop out from
   under the finger that just landed. ⚠️ visibility, NEVER display: display cannot transition,
   and the box is now on a timed shrink (below).
   ⚠️ The row no longer keeps its whole box. Entry #123 hands PART of it to the headline, which
   grows into the vacated space. The height here and --jc-h2-grown in the #123 block are one
   calculation and must move together: see that block for why the leftover is what it is.
   ⚠️ `transition` is a SHORTHAND. Anything added to this rule must be appended to THIS list.
   Declaring a second `transition` for the height in a later rule replaces the whole thing and
   silently kills the 150ms opacity fade. */
body.jc-coach-done .jc-gesture-coach {
  opacity: 0;
  visibility: hidden;
  height: calc(var(--jc-coach-h) - 2.04 * (var(--jc-h2-grown) - var(--jc-h2)));
  transition: opacity 150ms ease, visibility 0s linear 150ms,
              height 420ms cubic-bezier(.4, 0, .2, 1);
}

/* (D) The END CARD (user call, annotated screenshot): left-aligned, copy from the top, CTA
   full-width on the floor of the card.
   ⚠️ .jc-terminal-head MUST be here at the tail. The terminal h3 is NOT unstyled: the deck
   container still carries the legacy .job-card-intro-benefits class, so that rule sizes it to
   18px AND three media tiers retune it (15/16/15px). Every one of those is (0,2,0), exactly
   this rule's specificity, so ONLY source order wins. */
/* ⚠️ THE TYPE IS SIZED IN dvh, NOT vw, AND THAT IS THE WHOLE TRICK. This copy is ~4x longer
   than the "Want the full picture?" card it replaces, and the deck card is capped at 400px WIDE
   on every view (.jc-deck width: min(400px,100%)), so width tells you nothing about whether it
   fits. What it has to fit INTO is the deck's height, which is itself `clamp(428px, 58dvh,
   600px)`. Sizing off dvh therefore tracks the box the copy actually lives in.
   Measured before this: the CTA was shoved clean out of the card by +115px at 1280x720, +21 at
   1440x900 and +11 at 320x640 (margin-top:auto cannot push back against overflow). Every
   viewport on the ladder now lands at 0. Re-measure this ladder if the copy ever changes. */
.jc-card-terminal .jc-card-float {
  align-items: stretch;
  text-align: left;
  justify-content: flex-start;
  gap: clamp(var(--space-xs), 1.25dvh, 10px);
}
/* ⚠️ min(dvh, vw): BOTH terms are load-bearing, and each was measured, not guessed:
   • dvh: the copy has to fit the card's HEIGHT, which is the deck's `clamp(428px, 58dvh, 600px)`.
   • vw:  "That's all we need." has to fit ONE ROW of the card's WIDTH, and the card tracks the
     viewport below ~432px. Height alone gave 26.9px at 320px wide, where the span wrapped and
     the headline silently became THREE rows, losing the two-row lock that is the whole point.
   Measured across 320/360/390/430/1280/1440/1920: two rows everywhere, the full 31px on every
   desktop size, 65-214px of slack. Re-measure that ladder if this copy changes. */
.jc-card-terminal .jc-terminal-head {
  margin-top: 0;
  margin-bottom: 2px;
  font-size: clamp(21px, min(4.6dvh, 7.2vw), 31px);
  line-height: 1.02;
  letter-spacing: -0.03em;
  font-weight: var(--fw-black);
  color: var(--text-primary);
}
/* ⚠️ Every one of these four is load-bearing, not defensive noise. The deck still carries the
   legacy `job-card-intro-benefits` class, whose eyebrow-label rule
   `.job-card-intro-benefits span { color: var(--brand); font-size: 12px; font-weight: 900;
   letter-spacing: .08em }` (:1024) is (0,1,1), so it HIJACKS any <span> added to a deck card
   and rendered this headline as a 12px purple eyebrow. Same root cause as the chips above.
   (0,3,0) beats it; `inherit` hands each property back to the h3. */
.jc-card-terminal .jc-terminal-head > span {
  display: block;
  color: inherit;
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
}
.jc-card-terminal .jc-card-float > p {
  margin-top: 0;
  /* ⚠️⚠️ (#352) 20px IS collaborator-invite.html's `.final-push span`, HER NAMED REFERENCE for this
     subtext ("Use the size of this subtext font for the screenshot (1) subtext"). COPIED, NOT
     INVENTED, and `:root` was grepped first: it carries no type scale at all, and its only 20px
     values are `--shadow-popover`'s offset and `--radius-xl`, neither of which is a font size.
     ⚠️⚠️ THIS REPLACED `clamp(12px, 1.6dvh, 13px)`, A VIEWPORT-HEIGHT CLAMP, AND THE COPY HAD TO BE
     SHORTENED FOR IT TO FIT. This card lives in a height-capped deck
     (`.jc-deck { height: clamp(428px, 58dvh, 600px) }`), so the old clamp shrank the text on short
     screens instead of overflowing. A flat size cannot do that: at 20px the original 29-word
     paragraph ran UNDER the CTA by 53.7px at 1280x720, 41.4 at 1024x768, 42.7 at 381x800 and 23.4
     at 390x844. The user's call was to shorten the paragraph rather than vary the size or ship the
     clipping. It is now 21 words and every tier measures 0.
     ⚠️ SO THE COPY LENGTH IS NOW LOad BEARING FOR THE LAYOUT. Adding a line back to that paragraph
     re-breaks the four tiers above; guard `#352A` measures it and names them.
     ⚠️ ONLY THE SIZE IS TAKEN FROM THE REFERENCE. `line-height` stays this card's own 1.42, not the
     reference's 1.32: her words were "the size of this subtext font". */
  font-size: 20px;
  line-height: 1.42;
  color: var(--text-secondary);
  text-wrap: pretty;
  /* ⚠️ THE ONE THAT ACTUALLY MATTERED. `@media (min-width:861px) { .job-card-intro-benefits p
     { min-height: calc(1.45em * 5) } }` (:1051) is a CAROUSEL-ERA rule that reserves five lines
     for every description so the old 3-column layout's preview panels started on the same line.
     This card has THREE <p>s, so on desktop it was reserving 15 lines: "You're busy." measured
     83px tall, and the CTA was pushed +115px out of the card at 1280x720. Phones (<861px) never
     saw it, which is exactly why this looked fine on the viewport I checked first.
     Scoped to the terminal card only. The other cards' single descriptions still want it. */
  min-height: 0;
}
/* ⚠️⚠️ (#352) THE 320px TIER KEEPS THE OLD SIZE, AND THIS IS THE USER'S EXPLICIT CALL between
   three measured options: "20px everywhere except 320px", chosen over cutting either the list of
   automated steps or the "Fridays are yours" payoff out of the copy.
   ⚠️ THE BOUNDARY IS MEASURED, NOT PICKED. With the shortened paragraph at 20px, entry #99's
   existing `slack > 15` requirement ("the card content must fit, not just barely") reads:
       1920 201 · 1440 123 · 1280 33 · 1024 48 · 900 201 · 834 201 · 768 195 · 700 127
       430 146 · 390 72 · 381 47 · 375 55 · 360 18 · 320 3
   Exactly one tier fails, so exactly one tier falls back. ⚠️ 360 PASSES BY 3px, which is thin: if
   this card's copy or type ever moves again, re-run that sweep rather than trusting this comment.
   ⚠️ 320px IS NOT AN INVENTED NUMBER: it is this project's own narrowest tested tier (the suite
   runs 320x640, and CLAUDE.md's move-step note reasons about "90px at 320"). The wider
   `max-width: 380px` already in this file was REJECTED because it would also catch 375 and 360,
   which the user explicitly chose to keep at 20px.
   ⚠️ THE VALUE IS THIS CARD'S OWN PREVIOUS DECLARATION, restored verbatim rather than a new
   compromise size, so nothing here is invented either. */
@media (max-width: 320px) {
  .jc-card-terminal .jc-card-float > p { font-size: clamp(var(--fs-sm), 1.6dvh, 13px); }
}

/* margin-top:auto floors the CTA whatever the copy's length; width:100% overrides
   .btn-primary's inline-flex shrink-to-content. */
.jc-card-terminal .jc-terminal-cta {
  width: 100%;
  margin-top: auto;
}

/* (F) End card reached => the pips and the footer link go. `.jc-terminal-top` is stamped by
   render() on `[data-job-chooser]`, which is the OUTER .job-card-chooser-overlay, not the
   inner .job-card-chooser section. Naming the wrong one here is a silent no-op.
   ⚠️ visibility, NEVER display:none. The chooser column is a flex stack whose .jc-deck height
   is a fixed clamp, so collapsing these two boxes AND their 14px gaps would re-centre the
   column and make the card visibly JUMP the moment the viewer lands on the end card. Keeping
   the boxes also keeps both .jc-deck height constants honest: they are derived from
   "head 99 + pipsrow 30 + foot 34 + 3x14 gaps". */
.job-card-chooser-overlay.jc-terminal-top .jc-deck-pipsrow,
.job-card-chooser-overlay.jc-terminal-top .job-card-chooser-foot { visibility: hidden; }

@media (prefers-reduced-motion: reduce) {
  .jc-card-terminal .jc-terminal-cta { animation: none; }
  body.jc-coach-done .jc-gesture-coach { transition: none; }
}

/* ============================================================================
   ENTRY #102 (2026-07-17, user call, annotated screenshot): the chooser deck's
   per-card "TIME LEFT" bar.

   Every deck card previews a LOOPING animation, but nothing told the viewer how long
   the loop ran or when it would restart. This is a thin bar along the bottom of the TOP
   card that DRAINS: it starts FULL and empties to 0 as the sequence runs out, so its
   width IS the time REMAINING (user's words: "the time left of the animation sequence").
   Left-anchored, so the fill's right edge recedes leftward.

   Colour is var(--cand-priya). The user asked for "the Priya avatar colour" by name.
   Entry #74's ONE CANDIDATE PALETTE law: read the token, NEVER the hex.

   ⚠️ THE MARKUP IS <div>/<i>, NEVER <span>, AND THAT IS LOAD-BEARING (entry #99's trap).
   The deck still carries the carousel-era `job-card-intro-benefits` class, so
   `.job-card-intro-benefits span` (:1024) is an EYEBROW style at (0,1,1) that BEATS a
   single-class (0,1,0) rule. It silently rendered both swipe chips brand-purple for YEARS.
   The only element selectors under that class are span/h3/p/li/ul/article, so div and i
   are clean. Do not "tidy" these into spans.

   ⚠️ ZERO LAYOUT IMPACT IS MANDATORY. This is an absolute OVERLAY and must never reserve
   space. hirello-cinematic law #9: an embed host's viewport units and @media resolve at
   BUILD time, so resizing a host needs a REBUILD, not a re-fit. Entry #99 also asserts the
   deck box stays byte-identical (a changed box re-centres the flex column = the card JUMPS).

   7 of the 8 cards carry one: only the terminal end card has no sequence to count.
   (Entry #103 gave the once-static `messenger` card a real sequence and it joined with NO
   change to this bar or its rAF driver: it just registers a clock in the same WeakMap, exactly
   as #102 predicted. The markup is the marker.) See today-update.md #102 and #103.
   ============================================================================ */
.jc-card-timer {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 14px;
  height: 6px;
  border-radius: var(--radius-pill);
  overflow: hidden;
  /* z3 is deliberate. The article is position:absolute with a z-index from [data-deck-pos]
     (top = 3), so it ESTABLISHES A STACKING CONTEXT and these numbers are article-local; they
     cannot leak into the deck. 3 puts the bar ABOVE the embed scaler (z auto) and the ribbon
     (z2) so a grafted scene cannot bury it, but BELOW .jc-card-glow (z4) and .jc-card-chip
     (z5) on purpose: mid-swipe the green/red commit wash and stamp must dominate. The timer
     is scene furniture, not chrome. */
  z-index: 3;
  pointer-events: none;
  opacity: 0;
  transition: opacity 180ms linear;
}
/* Only the TOP card runs, and the next/third cards' BOTTOMS deliberately peek below it
   (`.jc-deck > article { inset: 0 0 40px 0 }`), which is exactly where this bar sits. Ungated,
   a stack of frozen bars would peek through under the live one. `opacity`, never `display`:
   the box must keep its geometry on every tier so the article box never changes. */
.jc-deck > article[data-deck-pos="top"] .jc-card-timer { opacity: 1; }
/* The unfilled RAIL is the SAME token at low alpha, via ::before because `opacity` on the
   track would fade the fill too. A flat neutral grey is the obvious alternative and is worse:
   this bar sits over BOTH var(--surface) text cards AND the grafted cinematics' var(--bg-app)
   scaler, and no single neutral reads on every scene the four cinematics travel through. */
.jc-card-timer::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--cand-priya);
  opacity: 0.2;
}
.jc-card-timer > i {
  position: relative; /* over the ::before rail */
  display: block;
  width: 100%;        /* seed = FULL. The driver only ever shrinks it. */
  height: 100%;
  border-radius: inherit;
  background: var(--cand-priya);
  /* ⚠️ NO transition. The parsebar (:1320) needs one because it is written every 85ms in
     coarse 11% steps; this bar is written every frame, so a transition would lag the drive
     and fight the drain. */
}

/* ⚠️ Reduced motion HIDES the bar rather than parking it, and that is evidence-backed, not
   taste. Nothing animates under reduced motion: both inline machines bail before scheduling a
   single cycle (rj0wj.html :1314 import, :1544 rank) and applyEmbedDesire returns early
   (:1776) so the grafted pages self-park at a static frame. A timer counting down a sequence
   that will never advance would be LYING to the viewer. */
@media (prefers-reduced-motion: reduce) {
  .jc-card-timer { display: none; }
}

/* ============================================================================
   ENTRY #103 — the messenger deck card's UNIVERSAL-INBOX CINEMATIC.

   Replaces the static `.jc-inbox-*` rows (deleted at :13151). Eight messages fall
   and slide in from both sides, the stack scrolls up under them, then the whole
   thing fades outwards to light and loops. Marta / Ahmed / Priya each message in
   and the employer replies, so the card SHOWS "numerous candidates, one inbox"
   rather than only asserting it in the heading.

   ⚠️⚠️ THE #99 AMBUSH GOVERNS EVERY NODE IN HERE. The deck still carries the
   carousel-era `job-card-intro-benefits` class, so its ELEMENT rules outrank any
   single-class rule of ours:
     `.job-card-intro-benefits span` (:1024, (0,1,1)) = a purple 12px/900 EYEBROW.
     `.job-card-intro-benefits p`    (:1051)         = min-height 5 LINES >=861px.
   The old markup used <span> avatars and they rendered brand purple rgb(124,58,237)
   on Marta's blue rgb(29,78,216) at 12px/900 instead of white 10px/800, on every
   viewport, for as long as this card has existed. That bug is why this file uses
   <div>/<i>/<b>/<time> and NOTHING else. Never add a <span> or a <p> to this card.
   (Only article/span/h3/p/li/ul have element rules under that class.)

   ⚠️ The viewport HEIGHT is pinned (190px, 156px in the two tight tiers) to the
   exact box the static rows occupied. The binding constraint is 1280x720, which
   leaves the panel only 219px. Hold this box and the card's layout is unchanged
   and every tier tuning is inherited; grow it and 1280x720 bursts first.
   ============================================================================ */
.jc-chat-preview {
  position: relative;
  margin-top: var(--space-sm);
  width: 100%;
  min-width: 0;
  height: 190px;
  /* ⚠️ LOAD-BEARING. This is a flex item of the centred column .jc-card-float, so an explicit
     height is SHRINKABLE (flex-shrink defaults to 1) and its min-height:auto resolves against
     min-CONTENT, which is ~0 here because the messages start display:none. Measured without
     this: 149px instead of 190. The old static grid held 190 for free only because its height
     came FROM its three always-present rows. */
  flex: 0 0 auto;
  padding: 9px;
  border: 1px solid var(--border);
  border-radius: 13px;
  background: var(--bg-subtle);
  overflow: hidden; /* the scroll viewport: the stack is translated, never scrolled */
}
.jc-chat-stack {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  /* Tail room so a reaction hanging off the LAST message's bottom edge is not eaten by the
     viewport's overflow:hidden (it was, measurably, at the moment msg 6 landed last).
     scrollToBottom() reads stack.offsetHeight live, so the scroll target absorbs this. */
  padding-bottom: 10px;
  /* Translated by the machine to scroll. transform, NOT scrollTop: a programmatic
     scroll is honoured even under overflow:hidden, so it is unprovable (#77's law). */
  transform: translateY(0);
  transition: transform 520ms cubic-bezier(0.4, 0, 0.2, 1);
  will-change: transform;
}
.jc-chat-msg {
  display: none; /* a message must not occupy the stack before it lands, or the
                    scroll target is wrong from frame one */
  /* flex-START, not flex-end: the body is bubble + meta, so bottom-aligning hung the
     avatar off the bubble and level with the timestamp instead (seen in a render). */
  align-items: flex-start;
  gap: 7px;
  margin-bottom: 7px;
  opacity: 0;
  /* Fall + slide in from the sending side. Restrained overshoot: bouncy, corporate. */
  transform: translate(-18px, -10px) scale(0.94);
  transition: transform 420ms cubic-bezier(0.34, 1.56, 0.64, 1), opacity 240ms ease-out;
}
.jc-chat-msg.is-out { flex-direction: row-reverse; transform: translate(18px, -10px) scale(0.94); }
.jc-chat-msg.is-shown { display: flex; }
.jc-chat-msg.is-shown.is-in { opacity: 1; transform: none; }
.jc-chat-msg:last-child { margin-bottom: 0; }

.jc-chat-avatar {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: var(--fs-2xs);
  font-weight: var(--fw-extrabold);
  font-style: normal;
  letter-spacing: 0;
  /* Per-entity colour identity (#74): the --cand-* TOKEN via each row's own --jc-av,
     never a hardcoded hex, and white initials, the demo-wide avatar treatment. */
  color: var(--text-inverse);
  background: var(--jc-av, var(--brand));
}
.jc-chat-body { min-width: 0; max-width: 78%; }
.jc-chat-text {
  padding: 6px 9px;
  border: 1px solid var(--border);
  border-radius: 11px;
  background: var(--surface);
  color: var(--text-primary);
  font-size: 11.5px;
  font-weight: var(--fw-semibold);
  line-height: 1.3;
}
.jc-chat-msg.is-out .jc-chat-text {
  border-color: var(--brand);
  background: var(--brand);
  color: var(--text-inverse);
}
.jc-chat-meta {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  margin-top: 2px;
  color: var(--text-tertiary);
  font-size: 9px;
  font-weight: var(--fw-bold);
}
.jc-chat-msg.is-out .jc-chat-meta { justify-content: flex-end; }
.jc-chat-meta time { font-variant-numeric: tabular-nums; }
.jc-chat-status { font-weight: var(--fw-bold); font-style: normal; }

/* The double-tick read receipt. ⚠️ Authored HERE rather than reusing nifhr's `.read-ticks`:
   every one of those rules is scoped `body.nifhr-screen` (:6130) and rj0wj's body does not
   carry that class, so the reused class matched nothing and rendered a 0px-wide empty <i>
   (measured, not theorised). Sized for this 9px meta row rather than nifhr's 13px one.
   `flex: 0 0 auto` is load-bearing: it is a flex item with no content, so it would otherwise
   shrink away. Grey = Delivered, brand blue = Read, mirroring the nifhr idiom's meaning. */
.jc-chat-ticks {
  position: relative;
  flex: 0 0 auto;
  width: 13px;
  height: 8px;
}
.jc-chat-ticks::before,
.jc-chat-ticks::after {
  content: "";
  position: absolute;
  top: 0;
  width: 4px;
  height: 7px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
}
.jc-chat-ticks::before { left: 1px; }
.jc-chat-ticks::after { left: 5px; }
.jc-chat-msg.is-out .jc-chat-ticks.is-opened { color: var(--info-text); }

/* The EMAIL landing in the instant chat: the beat that proves "universal inbox".
   Mirrors nifhr's real `.message-bubble.email` idiom (:6051) so the preview and the
   screen it deep-links to agree. */
.jc-chat-msg.is-email .jc-chat-body { max-width: 84%; }
.jc-chat-msg.is-email .jc-chat-text {
  padding: 7px 9px var(--space-xs);
  border-color: color-mix(in srgb, var(--brand) 18%, var(--border));
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: 0 6px 14px rgba(17, 24, 39, 0.08);
}
.jc-chat-email-head {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  margin-bottom: var(--space-2xs);
  color: var(--brand);
  font-size: 9px;
  font-weight: var(--fw-extrabold);
  letter-spacing: .04em;
  text-transform: uppercase;
}
.jc-chat-email-head svg { width: 11px; height: 11px; flex: 0 0 auto; }

/* The thumbs-up REACTION. An inline SVG, never an emoji glyph (standing rule): the
   ask was for a message to be "thumbs up emoji'd", and the SVG delivers exactly that
   effect. Bounces on with an overshoot + a pop ring, the TV-ad VFX beat.
   ⚠️ It hangs off the BUBBLE's bottom-LEFT, not the body's bottom-right: the body
   includes the meta row, and anchoring it there covered the "Read" status it is
   supposed to sit beside (seen in a render, not reasoned about). The meta is
   right-aligned on an outgoing message, so the left corner is the one free spot. */
.jc-chat-react {
  position: absolute;
  left: -7px;
  bottom: -8px;
  width: 19px;
  height: 19px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1.5px solid var(--surface);
  background: var(--brand-tint);
  color: var(--brand);
  transform: scale(0);
  transition: transform 420ms cubic-bezier(0.34, 1.9, 0.64, 1);
}
.jc-chat-react svg { width: 10px; height: 10px; }
.jc-chat-msg.is-reacted .jc-chat-react { transform: scale(1); }
.jc-chat-react::before {
  content: "";
  position: absolute;
  inset: -3px;
  border-radius: 50%;
  border: 2px solid var(--brand);
  opacity: 0;
}
.jc-chat-msg.is-reacted .jc-chat-react::before { animation: jcReactPop 620ms ease-out; }
@keyframes jcReactPop {
  0%   { opacity: .55; transform: scale(0.6); }
  100% { opacity: 0;   transform: scale(1.9); }
}
/* The reaction hangs off the bubble, so the BUBBLE is its containing block. */
.jc-chat-text { position: relative; }

/* The loop exit: "fades outwards to light". The stack grows toward the viewer while a
   white veil takes the panel, then the cycle resets while it is invisible. Same idiom
   as the rank machine's scale(1.06)+fade and the cinematics' #loopFade. */
/* ⚠️ The SCALE is NOT here, and must not be moved here. The machine writes the stack's transform
   INLINE (it is the live scroll offset), and an inline style BEATS a stylesheet rule, so a
   `transform: ... scale(1.08)` on this rule is silently DEAD: measured, the scale stayed 1.000
   through the entire white-out and the "grows outwards" half of the beat never ran, while the
   veil's fade made it look correct. This rule owns the TIMING only; the exit beat re-states the
   transform in JS, re-stating its own lastY. Keep the 900ms in lockstep with the veil below. */
.jc-chat-preview[data-chat-state="exit"] .jc-chat-stack {
  transition: transform 900ms ease-in;
}
.jc-chat-veil {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--surface);
  opacity: 0;
  pointer-events: none;
  transition: opacity 900ms ease-in;
}
.jc-chat-preview[data-chat-state="exit"] .jc-chat-veil { opacity: 1; }

/* ⚠️⚠️ THE TWO TIGHT TIERS MUST BE RESTATED HERE, NOT IN THEIR ORIGINAL BLOCKS.
   The deck's compaction partition tunes the card at (max-height:540px) [:13745] and in the
   541-656 squeeze band [:14282], and that is where the old `.jc-inbox-*` overrides lived. But
   THIS block is appended at the END of the file, so those rules are equal-specificity (0,1,0)
   and EARLIER: the base `height:190px` above beat them and the panel rendered 190 where it
   must render 156 (measured on a real render at 360x640 and 740x500, both tiers). Entry #98
   law 7 records this exact trap. Keep these two blocks AFTER the base rules, forever.
   156px is the box the three static rows occupied in these tiers, so the card is unchanged. */
@media (max-height: 540px) {
  .jc-chat-preview { margin-top: var(--space-2xs); padding: 6px; height: 156px; }
  .jc-chat-msg { margin-bottom: 5px; }
  .jc-chat-avatar { width: 20px; height: 20px; font-size: 9px; }
  .jc-chat-text { font-size: var(--fs-xs); padding: 5px var(--space-xs); }
}
@media (max-width: 700px) and (min-height: 541px) and (max-height: 656px) {
  .jc-chat-preview { margin-top: var(--space-2xs); padding: 6px; height: 156px; }
  .jc-chat-msg { margin-bottom: 5px; }
  .jc-chat-avatar { width: 20px; height: 20px; font-size: 9px; }
  .jc-chat-text { font-size: var(--fs-xs); padding: 5px var(--space-xs); }
}

/* ⚠️ Reduced motion: the machine bails before its first cycle and hand-seeds the END
   state (all 8 shown, scrolled to the bottom), so every transition here must be off or
   the seed would animate on arrival. The timer bar is already display:none (:15665). */
@media (prefers-reduced-motion: reduce) {
  .jc-chat-stack,
  .jc-chat-msg,
  .jc-chat-react,
  .jc-chat-veil { transition: none; }
  .jc-chat-msg.is-reacted .jc-chat-react::before { animation: none; }
}

/* ======================================================================================
   MOBILE / TABLET MESSENGER (universal inbox), entry #105
   Additive, mobile-only (<=900px) reimagining of the messenger segment. Everything here is
   display:none by default and only appears at <=900px while body.screening-step-messenger is
   set, so DESKTOP (>900px) is byte-identical. Candidate colours use the canonical --cand-*
   tokens (#74). Built config-driven + token-based so it can be promoted to desktop later.
   ====================================================================================== */
.messenger-mobile,
.messenger-mobile-chat,
.mm-coach,
.mm-intro,
.mm-sheet,
.mm-sheet-scrim { display: none; }

@media (max-width: 900px) {
  /* Hide the desktop messenger + its black coach overlay; show the mobile app instead. */
  body.nifhr-screen.screening-step-messenger .messenger-content { display: none !important; }
  body.nifhr-screen .messenger-guide-overlay { display: none !important; }
  body.nifhr-screen.mm-active { overflow: hidden; overscroll-behavior: none; }
  /* Suppress the shared board-focus dim (body.nifhr-screen::before, z9000) while the mobile
     messenger app is up: it is a full-screen opaque app, not a tutorial-over-board, so the dim
     only washes it grey. mm-active is set on mobile only, so desktop keeps its dim untouched. */
  body.nifhr-screen.mm-active::before { content: none; }

  body.nifhr-screen.screening-step-messenger .messenger-mobile {
    display: flex;
    flex-direction: column;
    position: fixed;
    top: var(--mm-top, 56px);
    left: 0; right: 0; bottom: 0;
    z-index: 40;
    background: var(--bg-app);
    overflow: hidden;
  }

  /* ---- Inbox: job header (grouped against the config job, LinkedIn-Recruiter style) ---- */
  .mm-job-head {
    display: grid;
    grid-template-columns: 44px minmax(0, 1fr);
    align-items: center;
    gap: var(--space-sm);
    padding: 14px var(--space-md);
    background: linear-gradient(180deg, color-mix(in srgb, var(--brand) 8%, var(--surface)), var(--surface));
    border-bottom: 1px solid var(--border);
  }
  /* Company logo badge: same token + treatment as the demo's .company-avatar. */
  .mm-job-avatar {
    width: 44px; height: 44px;
    display: grid; place-items: center;
    border-radius: var(--radius-pill);
    background: var(--text-primary);
    color: var(--text-inverse);
    font-weight: var(--fw-extrabold); font-size: var(--fs-lg); letter-spacing: .3px;
  }
  .mm-job-meta { min-width: 0; }
  .mm-job-meta strong {
    display: block;
    font-size: var(--fs-xl); font-weight: var(--fw-extrabold); color: var(--text-primary);
    letter-spacing: -0.2px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .mm-job-meta span {
    display: block;
    font-size: var(--fs-sm); color: var(--text-secondary);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .mm-job-meta b { color: var(--brand); font-weight: var(--fw-extrabold); }

  /* Search row: FOR SHOW ONLY (not tappable), lowering cognitive load in the demo. */
  .mm-search {
    display: flex; align-items: center; gap: 9px;
    margin: var(--space-sm) var(--space-md) 0;
    height: 40px; padding: 0 14px;
    border-radius: var(--radius-pill);
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--text-tertiary);
    flex: 0 0 auto;
    pointer-events: none;
    transition: box-shadow 180ms var(--ease-out, ease);
  }
  .mm-search svg { width: 17px; height: 17px; flex: 0 0 auto; }
  .mm-search-ph { font: 500 14px/1.3 var(--font-ui, inherit); color: var(--text-tertiary); }
  /* Elevation once the flat list scrolls under the (now inactive) search row. */
  body.nifhr-screen.screening-step-messenger .messenger-mobile.is-scrolled .mm-search { box-shadow: var(--shadow-float); }

  /* ---- Inbox: candidate rows ---- */
  .mm-thread-list { flex: 1 1 auto; overflow-y: auto; padding: 6px var(--space-xs) 96px; -webkit-overflow-scrolling: touch; }
  .mm-thread {
    display: grid;
    grid-template-columns: 52px minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--space-sm);
    width: 100%;
    padding: var(--space-sm) var(--space-sm);
    border: none; border-radius: var(--radius-lg);
    background: none; text-align: left; cursor: pointer;
    position: relative;
    transition: background var(--motion-fast, 140ms) var(--ease-out, ease);
  }
  .mm-thread:active { background: var(--bg-row-highlight); }
  /* Only Marta opens in the demo; the others keep full colour but lose tap ability (user call). */
  .mm-thread.mm-locked { pointer-events: none; cursor: default; }
  .mm-thread-avatar {
    width: 52px; height: 52px;
    display: grid; place-items: center;
    border-radius: 50%;
    background: var(--mm-c, var(--brand));
    color: var(--text-inverse);
    font-weight: var(--fw-extrabold); font-size: var(--fs-xl);
  }
  .mm-thread-main { min-width: 0; }
  .mm-thread-main .mm-thread-name { display: block; font-size: var(--fs-lg); font-weight: var(--fw-bold); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .mm-thread-main .mm-thread-prev { display: block; font-size: 13px; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 2px; }
  .mm-thread.is-unread .mm-thread-prev { color: var(--text-primary); font-weight: var(--fw-semibold); }
  .mm-thread-meta { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
  /* Received time = the same text token; ONLY the weight differs by read state. */
  .mm-thread-meta time { font-size: var(--fs-xs); color: var(--text-secondary); font-weight: var(--fw-regular); white-space: nowrap; }
  .mm-thread.is-unread .mm-thread-meta time { font-weight: var(--fw-bold); }
  .mm-thread-badge {
    min-width: 20px; height: 20px; padding: 0 6px;
    display: grid; place-items: center;
    border-radius: var(--radius-pill);
    background: var(--danger); color: var(--text-inverse);
    font-size: var(--fs-xs); font-weight: var(--fw-extrabold);
  }
  .mm-thread-channel { position: absolute; left: 44px; bottom: 8px; width: 18px; height: 18px; border-radius: 50%; display: grid; place-items: center; background: var(--surface); box-shadow: 0 0 0 2px var(--surface); }
  .mm-thread-channel svg { width: 11px; height: 11px; }

  /* Marta coach: the WHOLE row gently pulses (scale + tint), plus the purple ring. */
  /* ⚠️ SUPERSEDED BY #204, KEPT BECAUSE ITS WARNING IS STILL TRUE. This used to read "pulse the
     whole row via background + soft glow (NOT transform:scale, which moves the box and makes the
     row un-tappable under Playwright's stability check / can jitter)". #204 added the transform
     anyway, by user instruction, and the warning's consequence duly arrived: EVERY plain
     `page.tap` on this row now times out at 30s, because Playwright's actionability gate waits for
     two still frames and an infinite animation never gives it one. Ten tests failed on exactly
     that. The resolution is `{ force: true }` on taps of THIS row, not removing the motion:
     measured, a genuine CDP touch with no actionability gate opens the chat, and `force: true`
     succeeds in 24ms, so a real finger is unaffected. If you add a new test that taps this row,
     it needs `force: true` too. */
  /* mmRowPulse / mmCoachRing are declared at BASE tier (see the entry #147 block at the end of
     this file), because @keyframes declared inside a media query do not exist outside it and the
     desktop messenger reuses these exact two animations. */
  /* ENTRY #204 (user call): the coached row PULSES IN SIZE and FLOATS, and the #203 "Tap to open"
     hint moves with it. The hint is a CHILD of the row, so it inherits the row's transform and the
     two are synced with NO JS and no second animation to keep in phase.

     ⚠️ BOTH ANIMATIONS ARE DECLARED IN ONE SHORTHAND ON PURPOSE. They start together, so the colour
     pulse and the lift stay in phase for the life of the element, and the existing reduced-motion
     rule (`.mm-thread.is-coached { animation: none }` further down this file) silences BOTH with no
     second rule to remember. Splitting them into two declarations would break both properties.

     ⚠️ THE AMPLITUDE IS BOUNDED BY A CLIP, not by taste. `.mm-thread-list` is `overflow-y: auto`,
     and a computed `overflow-y` that is not `visible` forces `overflow-x` to `auto` too, so the
     list clips on BOTH axes. Marta is the FIRST row and sits only 6px below the list's top padding
     edge. A 3px lift plus half of the 2% vertical growth puts her top edge ~3.7px up, inside that
     6px. A larger lift would be silently cut off at the top, which is the #175 trap: a rect cannot
     detect a clip.

     ⚠️ `z-index` IS NOT DECORATION HERE. A transform makes this row a STACKING CONTEXT, which traps
     the hint's own z-index inside it; the later sibling rows would then paint OVER the hint that
     hangs below Marta. The row must therefore outrank its siblings itself. */
  .mm-thread.is-coached {
    animation:
      mmRowPulse 1.7s var(--ease-standard, ease-in-out) infinite,
      mmRowLift 1.7s var(--ease-standard, ease-in-out) infinite;
    z-index: 2;
  }
  .mm-thread.is-coached::after {
    content: ""; position: absolute; inset: var(--space-2xs);
    border-radius: var(--radius-lg);
    box-shadow: 0 0 0 2px var(--brand);
    animation: mmCoachRing 1.7s var(--ease-standard, ease-in-out) infinite;
    pointer-events: none;
  }

  /* ================= Chat overlay (full-screen native pattern) ================= */
  body.nifhr-screen.screening-step-messenger .messenger-mobile-chat {
    display: none;
    flex-direction: column;
    position: fixed; inset: 0;
    z-index: 1002;
    background: var(--bg-app);
  }
  body.nifhr-screen.screening-step-messenger.mm-chat-open .messenger-mobile-chat { display: flex; animation: mmChatIn 240ms var(--ease-out, cubic-bezier(.16,1,.3,1)); }
  /* While the chat is open, hide the inbox behind it so it can never peek through the gap the
     keyboard leaves at the bottom on iOS. */
  body.nifhr-screen.screening-step-messenger.mm-chat-open .messenger-mobile { visibility: hidden; }
  @keyframes mmChatIn { from { transform: translateX(14px); opacity: 0; } to { transform: none; opacity: 1; } }

  .mm-chat-head {
    display: grid;
    grid-template-columns: 34px 40px minmax(0, 1fr) auto;
    align-items: center;
    gap: 10px;
    padding: 10px 14px calc(10px);
    padding-top: max(10px, env(safe-area-inset-top));
    background: var(--surface);
    border-bottom: 1px solid var(--border);
  }
  .mm-back { width: 34px; height: 34px; display: grid; place-items: center; border: none; background: none; color: var(--text-primary); cursor: pointer; margin-left: -6px; }
  .mm-back svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
  .mm-chat-avatar { width: 40px; height: 40px; display: grid; place-items: center; border-radius: 50%; background: var(--mm-c, var(--brand)); color: var(--text-inverse); font-weight: var(--fw-extrabold); font-size: var(--fs-md); }
  .mm-chat-id { min-width: 0; }
  .mm-chat-id strong { display: block; font-size: var(--fs-lg); font-weight: var(--fw-bold); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .mm-chat-id > span { display: block; font-size: var(--fs-sm); color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .mm-presence { display: inline-block; vertical-align: middle; width: 7px; height: 7px; border-radius: 50%; background: var(--success); margin-right: 3px; }
  .mm-chat-actions { display: flex; gap: var(--space-2xs); }
  .mm-chat-actions button { width: 36px; height: 36px; display: grid; place-items: center; border: none; border-radius: 10px; background: none; color: var(--text-secondary); cursor: pointer; }
  .mm-chat-actions svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

  /* ---- Message list + bubbles ---- */
  .mm-chat-body { flex: 1 1 auto; overflow-y: auto; padding: var(--space-md) 14px 20px; display: flex; flex-direction: column; gap: 3px; -webkit-overflow-scrolling: touch; }
  .mm-day { align-self: center; margin: 6px 0 10px; padding: var(--space-2xs) var(--space-sm); border-radius: var(--radius-pill); background: var(--surface); border: 1px solid var(--border); color: var(--text-tertiary); font-size: var(--fs-xs); font-weight: var(--fw-semibold); }
  .mm-msg { max-width: 82%; display: flex; flex-direction: column; margin-top: var(--space-xs); }
  .mm-msg.in { align-self: flex-start; align-items: flex-start; }
  .mm-msg.out { align-self: flex-end; align-items: flex-end; }
  .mm-bubble { padding: 10px 13px; border-radius: 18px; font-size: var(--fs-md); line-height: 1.38; }
  .mm-msg.in .mm-bubble { background: var(--surface); border: 1px solid var(--border); color: var(--text-primary); border-bottom-left-radius: 6px; }
  .mm-msg.out .mm-bubble { background: var(--brand); color: var(--text-inverse); border-bottom-right-radius: 6px; }
  .mm-meta { display: flex; align-items: center; gap: 5px; margin-top: var(--space-2xs); font-size: var(--fs-xs); color: var(--text-tertiary); }
  .mm-channel-chip { display: inline-flex; align-items: center; gap: var(--space-2xs); font-size: var(--fs-2xs); font-weight: var(--fw-bold); color: var(--text-secondary); text-transform: uppercase; letter-spacing: .4px; }
  .mm-channel-chip svg { width: 11px; height: 11px; fill: none; stroke: currentColor; stroke-width: 2; }
  .mm-ticks { display: inline-flex; }
  .mm-ticks svg { width: 15px; height: 10px; fill: none; stroke: var(--text-tertiary); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
  .mm-ticks.is-read svg { stroke: var(--info-text, #1D4ED8); }

  /* Email card in the same thread (universal inbox / Spike-style) */
  .mm-msg.is-email { max-width: 90%; }
  .mm-email {
    border-radius: var(--radius-lg);
    border: 1px solid color-mix(in srgb, var(--brand) 22%, var(--border));
    background: var(--surface);
    box-shadow: var(--shadow-float);
    overflow: hidden;
  }
  .mm-email-head { display: flex; align-items: center; gap: 7px; padding: 9px 13px 7px; color: var(--brand); border-bottom: 1px solid color-mix(in srgb, var(--brand) 12%, var(--border)); }
  .mm-email-head svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.9; }
  .mm-email-head strong { font-size: var(--fs-xs); font-weight: var(--fw-extrabold); text-transform: uppercase; letter-spacing: .6px; }
  .mm-email-body { padding: 9px 13px 11px; font-size: var(--fs-md); line-height: var(--lh-snug); color: var(--text-primary); }

  /* Attachment cards (document / photo / location / questionnaire) */
  .mm-att-card { border-radius: var(--radius-lg); overflow: hidden; max-width: 260px; }
  .mm-msg.out .mm-att-card { box-shadow: var(--shadow-brand); }
  .mm-doc { display: flex; align-items: center; gap: 11px; padding: var(--space-sm) 14px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); width: 240px; }
  .mm-doc-ico { width: 40px; height: 40px; flex: 0 0 auto; display: grid; place-items: center; border-radius: 10px; background: var(--danger-bg); color: var(--danger-text); font-size: var(--fs-2xs); font-weight: var(--fw-extrabold); }
  .mm-doc-copy { min-width: 0; }
  .mm-doc-copy strong { display: block; font-size: 13px; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .mm-doc-copy small { font-size: var(--fs-xs); color: var(--text-tertiary); }
  .mm-photo { display: block; }
  .mm-photo img { display: block; width: 240px; max-width: 100%; max-height: 320px; object-fit: cover; border-radius: var(--radius-lg); border: 1px solid var(--border); }

  .mm-map { width: 258px; border-radius: var(--radius-lg); overflow: hidden; border: 1px solid var(--border); background: var(--surface); }
  .mm-map-frame { width: 100%; height: 150px; border: 0; display: block; background: var(--bg-subtle); }
  .mm-map-cap { display: flex; align-items: center; gap: var(--space-xs); padding: 9px var(--space-sm); }
  .mm-map-cap svg { width: 16px; height: 16px; flex: 0 0 auto; fill: none; stroke: var(--brand); stroke-width: 1.9; }
  .mm-map-cap .mm-map-txt { min-width: 0; }
  .mm-map-cap strong { display: block; font-size: var(--fs-sm); color: var(--text-primary); }
  .mm-map-cap small { font-size: var(--fs-xs); color: var(--text-tertiary); }
  .mm-map-open { margin-left: auto; flex: 0 0 auto; color: var(--brand); font-size: var(--fs-xs); font-weight: var(--fw-bold); text-decoration: none; }

  .mm-quest { width: 250px; padding: 13px 14px; background: var(--surface); border: 1px solid color-mix(in srgb, var(--brand) 20%, var(--border)); border-radius: var(--radius-lg); }
  .mm-quest-head { display: flex; align-items: center; gap: var(--space-xs); color: var(--brand); }
  .mm-quest-head svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.9; }
  .mm-quest-head strong { font-size: 13px; font-weight: var(--fw-extrabold); }
  .mm-quest ul { list-style: none; margin: 9px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
  .mm-quest li { font-size: var(--fs-sm); color: var(--text-secondary); padding-left: var(--space-md); position: relative; }
  .mm-quest li::before { content: ""; position: absolute; left: 2px; top: 6px; width: 5px; height: 5px; border-radius: 50%; background: var(--brand); }
  .mm-quest-ans li { color: var(--text-primary); font-weight: var(--fw-semibold); }
  .mm-quest-ans li::before { background: var(--success); }
  .mm-quest-pass { display: inline-flex; align-items: center; gap: 5px; margin-top: 10px; padding: var(--space-2xs) 10px; border-radius: var(--radius-pill); background: color-mix(in srgb, var(--success) 16%, var(--surface)); color: var(--success-text); font-size: var(--fs-xs); font-weight: var(--fw-extrabold); white-space: nowrap; }
  .mm-quest-pass svg { width: 13px; height: 13px; flex: 0 0 auto; fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }

  /* Typing indicator (candidate composing an auto-reply) */
  .mm-typing { align-self: flex-start; margin-top: var(--space-xs); display: inline-flex; gap: var(--space-2xs); padding: var(--space-sm) 14px; background: var(--surface); border: 1px solid var(--border); border-radius: 18px; border-bottom-left-radius: 6px; }
  .mm-typing i { width: 7px; height: 7px; border-radius: 50%; background: var(--text-tertiary); animation: mmTyping 1.1s ease-in-out infinite; }
  .mm-typing i:nth-child(2) { animation-delay: .18s; }
  .mm-typing i:nth-child(3) { animation-delay: .36s; }
  @keyframes mmTyping { 0%, 60%, 100% { opacity: .3; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }
  .mm-typing-row { align-self: flex-start; display: inline-flex; align-items: center; gap: var(--space-xs); margin-top: var(--space-xs); }
  .mm-typing-row .mm-typing { margin-top: 0; }
  .mm-typing-name { font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--text-secondary); }

  /* Attached questionnaire drop-in: bezier bounce, purple border fading to rest, shadow tracked to the bounce */
  .mm-quest.mm-quest-drop { animation: mmQuestDrop 720ms cubic-bezier(.34,1.56,.64,1) both; }
  @keyframes mmQuestDrop {
    0%   { transform: translateY(-16px) scale(.96); opacity: 0; border-color: var(--brand);
           box-shadow: 0 16px 24px -12px color-mix(in srgb, var(--brand) 60%, transparent); }
    45%  { transform: translateY(0) scale(1); opacity: 1; border-color: var(--brand);
           box-shadow: 0 6px 14px -6px color-mix(in srgb, var(--brand) 45%, transparent); }
    70%  { transform: translateY(-4px); border-color: color-mix(in srgb, var(--brand) 55%, var(--border));
           box-shadow: 0 8px 16px -8px color-mix(in srgb, var(--brand) 28%, transparent); }
    100% { transform: none; opacity: 1;
           border-color: color-mix(in srgb, var(--brand) 20%, var(--border)); box-shadow: none; }
  }

  /* Message entrance */
  .mm-msg.mm-enter { animation: mmMsgIn 260ms var(--ease-out, cubic-bezier(.16,1,.3,1)); }
  @keyframes mmMsgIn { from { transform: translateY(8px); opacity: 0; } to { transform: none; opacity: 1; } }

  /* ================= Composer (full-width, attach inside, MD3-dim send) ================= */
  .mm-composer {
    flex: 0 0 auto;
    display: flex; align-items: flex-end; gap: var(--space-xs);
    padding: var(--space-xs) var(--space-sm);
    padding-bottom: max(var(--space-xs), env(safe-area-inset-bottom));
    background: var(--surface);
    border-top: 1px solid var(--border);
    box-shadow: none;                                    /* no elevation on the input area (user call) */
  }
  .mm-input-pill {
    flex: 1 1 auto; min-width: 0;
    display: flex; align-items: center; gap: 6px;
    min-height: 44px; padding: var(--space-2xs) var(--space-xs) var(--space-2xs) 6px;
    border-radius: 22px;
    background: var(--bg-app);
    border: 1px solid var(--border);
  }
  .mm-attach {
    width: 34px; height: 34px; flex: 0 0 auto;
    display: grid; place-items: center;
    border: none; background: none; color: var(--text-secondary);
    cursor: pointer; border-radius: 50%;
  }
  .mm-attach svg { width: 21px; height: 21px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
  .mm-attach { color: var(--brand); transition: transform 180ms var(--ease-out, ease); }
  body.mm-attach-open .mm-attach { transform: rotate(45deg); }
  .mm-input {
    flex: 1 1 auto; min-width: 0;
    border: none; background: none; outline: none;
    font: 500 15px/1.3 var(--font-ui, inherit); color: var(--text-primary);
    padding: 6px 0;
  }
  .mm-input::placeholder { color: var(--text-tertiary); }
  .mm-send {
    width: 44px; height: 44px; flex: 0 0 auto;
    display: grid; place-items: center;
    border: none; border-radius: 50%;
    background: var(--brand); color: var(--text-inverse);
    cursor: pointer;
    opacity: 0.39;                                   /* MD3 inactive until content */
    transition: opacity 160ms var(--ease-out, ease), transform 160ms var(--ease-out, ease);
  }
  .mm-composer.has-content .mm-send { opacity: 1; }
  .mm-composer.has-content .mm-send:active { transform: scale(0.92); }
  .mm-send svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

  /* ================= Bottom sheets (attachment / questionnaire / library) ================= */
  body.nifhr-screen.screening-step-messenger .mm-sheet-scrim {
    display: block; position: fixed; inset: 0; z-index: 1003;
    background: color-mix(in srgb, var(--text-primary) 34%, transparent);
    opacity: 0; pointer-events: none;
    transition: opacity 200ms var(--ease-out, ease);
  }
  body.nifhr-screen.screening-step-messenger.mm-sheet-open .mm-sheet-scrim { opacity: 1; pointer-events: auto; }
  body.nifhr-screen.screening-step-messenger .mm-sheet {
    display: block; position: fixed; left: 0; right: 0; bottom: 0; z-index: 1004;
    background: var(--surface);
    border-radius: 22px 22px 0 0;
    box-shadow: var(--shadow-popover);
    padding: 8px 12px calc(16px + env(safe-area-inset-bottom));
    transform: translateY(102%);
    transition: transform 260ms var(--ease-out, cubic-bezier(.16,1,.3,1));
    max-height: 76vh; overflow-y: auto;
  }
  body.nifhr-screen.screening-step-messenger .mm-sheet.is-open { transform: translateY(0); }
  .mm-sheet-grip { width: 40px; height: 4px; border-radius: 2px; background: var(--border); margin: var(--space-2xs) auto 10px; }
  .mm-sheet-title { margin: 2px var(--space-xs) var(--space-xs); font-size: 13px; font-weight: var(--fw-extrabold); color: var(--text-secondary); text-transform: uppercase; letter-spacing: .5px; }
  .mm-sheet-item {
    display: grid; grid-template-columns: 46px minmax(0, 1fr); align-items: center; gap: var(--space-sm);
    width: 100%; padding: 11px 10px;
    border: none; background: none; text-align: left; cursor: pointer; border-radius: 14px;
  }
  .mm-sheet-item:active { background: var(--bg-row-highlight); }
  .mm-sheet-ico { width: 46px; height: 46px; display: grid; place-items: center; border-radius: 14px; background: var(--brand-tint); color: var(--brand); }
  .mm-sheet-ico svg { width: 23px; height: 23px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
  .mm-sheet-ico.pdf { background: var(--danger-bg); color: var(--danger-text); font-size: var(--fs-xs); font-weight: var(--fw-extrabold); }
  .mm-sheet-lbl { min-width: 0; }
  .mm-sheet-lbl strong { display: block; font-size: var(--fs-lg); font-weight: var(--fw-bold); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .mm-sheet-lbl small { font-size: var(--fs-sm); color: var(--text-tertiary); }
  .mm-sheet-item[disabled] { opacity: .4; pointer-events: none; }

  /* ================= Branded dark WELCOME modal (first landing) ================= */
  /* The two intro dialogs are black BOTTOM SHEETS: they slide up on show and slide down on
     the purple CTA tap (native speed), instead of a centred modal card. */
  body.nifhr-screen.screening-step-messenger .mm-intro {
    display: grid; place-items: end center;
    position: fixed; inset: 0; z-index: 1005;
    animation: mmIntroIn 260ms var(--ease-out, cubic-bezier(.16,1,.3,1));
  }
  body.nifhr-screen.screening-step-messenger .mm-intro[hidden] { display: none; }
  @keyframes mmIntroIn { from { opacity: 0; } to { opacity: 1; } }
  .mm-intro-scrim {
    position: absolute; inset: 0;
    background: color-mix(in srgb, var(--text-primary) 62%, transparent);
    -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  }
  .mm-intro-card {
    position: relative; width: 100%; max-width: 560px;
    padding: 30px 26px calc(28px + env(safe-area-inset-bottom, 0px));
    border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
    background: var(--text-primary); color: var(--text-inverse);
    box-shadow: var(--shadow-popover); text-align: center;
    animation: mmIntroCard 340ms var(--ease-out, cubic-bezier(.16,1,.3,1));
    will-change: transform;
  }
  .mm-intro-card::before {
    content: ""; position: absolute; top: 9px; left: 50%; transform: translateX(-50%);
    width: 36px; height: 4px; border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--text-inverse) 26%, transparent);
  }
  @keyframes mmIntroCard { from { transform: translateY(100%); } to { transform: translateY(0); } }
  /* CTA tap slides the sheet back down (native ~300ms ease-in) before it is hidden;
     the closing sheet stops blocking so the app behind is usable during the slide. */
  .mm-intro.mm-sheet-closing { pointer-events: none; }
  .mm-intro.mm-sheet-closing .mm-intro-card { animation: mmIntroCardOut 300ms var(--ease-in, cubic-bezier(.4,0,1,1)) forwards; }
  .mm-intro.mm-sheet-closing .mm-intro-scrim { animation: mmIntroScrimOut 300ms ease forwards; }
  @keyframes mmIntroCardOut { from { transform: translateY(0); } to { transform: translateY(100%); } }
  @keyframes mmIntroScrimOut { from { opacity: 1; } to { opacity: 0; } }
  .mm-intro-title { font-size: 22px; font-weight: var(--fw-extrabold); line-height: 1.24; letter-spacing: -0.4px; }
  .mm-intro-body { margin-top: var(--space-sm); font-size: var(--fs-md); line-height: var(--lh-normal); color: color-mix(in srgb, var(--text-inverse) 76%, transparent); }
  .mm-intro-cta {
    margin-top: var(--space-lg); width: 100%; height: 50px;
    border: none; border-radius: var(--radius-pill);
    background: var(--brand); color: var(--text-inverse);
    font-size: var(--fs-lg); font-weight: var(--fw-bold); cursor: pointer;
    box-shadow: var(--shadow-brand);
    transition: transform 140ms var(--ease-out, ease);
  }
  .mm-intro-cta:active { transform: scale(.97); }

  /* ================= Coach pill (gentle, non-blocking) ================= */
  body.nifhr-screen.screening-step-messenger .mm-coach {
    display: flex; align-items: center; gap: 9px;
    position: fixed; left: 16px; right: 16px; bottom: calc(24px + env(safe-area-inset-bottom));
    z-index: 1005;
    padding: 11px 15px;
    border-radius: 15px;
    background: var(--brand);
    color: var(--text-inverse);
    box-shadow: var(--shadow-brand);
    font-size: 13px; font-weight: var(--fw-semibold); line-height: 1.35;
    pointer-events: none;
    opacity: 0; transform: translateY(8px);
    transition: opacity 240ms var(--ease-out, ease), transform 240ms var(--ease-out, ease);
  }
  body.nifhr-screen.screening-step-messenger .mm-coach.is-shown { opacity: 1; transform: none; }
  body.nifhr-screen.screening-step-messenger.mm-chat-open .mm-coach { bottom: calc(80px + env(safe-area-inset-bottom)); }
  .mm-coach-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--text-inverse); flex: 0 0 auto; animation: mmCoachPulse 1.5s ease-in-out infinite; }
  @keyframes mmCoachPulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

  /* ---- Quoted reply inside a bubble (normal messaging-app reply) ---- */
  .mm-quote {
    display: block; margin-bottom: 6px; padding: 6px 9px;
    border-left: 3px solid var(--brand);
    border-radius: 7px;
    background: color-mix(in srgb, var(--text-primary) 5%, transparent);
    font-size: 12.5px; line-height: 1.3; color: var(--text-secondary);
  }
  .mm-quote b { display: block; font-weight: var(--fw-bold); font-size: 11.5px; margin-bottom: 1px; color: var(--brand); }
  .mm-msg.out .mm-bubble .mm-quote { background: color-mix(in srgb, var(--text-inverse) 18%, transparent); border-left-color: var(--text-inverse); color: var(--text-inverse); }
  .mm-msg.out .mm-bubble .mm-quote b { color: var(--text-inverse); opacity: .9; }

  /* ---- Reaction chip attached to a message ---- */
  .mm-msg { position: relative; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
  .mm-msg.has-react { margin-bottom: var(--space-2xs); }
  /* The reaction is an IN-FLOW flex item between the bubble and the meta row: it overlaps the
     bubble's bottom corner (negative margin) and reserves space so the timestamp/Read row drops
     below it, never under it. No absolute positioning = no clipping by a card's overflow:hidden. */
  .mm-msg-react {
    position: relative; z-index: 1; margin-top: -12px;
    display: inline-flex; align-items: center; place-content: center;
    width: 26px; height: 26px; border-radius: var(--radius-pill);
    background: var(--surface); border: 1px solid var(--border);
    box-shadow: var(--shadow-float);
  }
  .mm-msg.in .mm-msg-react { margin-left: 6px; }
  .mm-msg.out .mm-msg-react { margin-right: 6px; }
  .mm-msg-react svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
  .mm-msg-react[data-r="like"], .mm-react-btn[data-mm-react="like"] { color: var(--brand); }
  .mm-msg-react[data-r="love"], .mm-react-btn[data-mm-react="love"] { color: var(--danger); }
  .mm-msg-react[data-r="haha"], .mm-msg-react[data-r="wow"], .mm-msg-react[data-r="sad"],
  .mm-react-btn[data-mm-react="haha"], .mm-react-btn[data-mm-react="wow"], .mm-react-btn[data-mm-react="sad"] { color: var(--text-secondary); }
  .mm-msg-react[data-r="like"] svg, .mm-msg-react[data-r="love"] svg { fill: currentColor; stroke: none; }
  .mm-msg-react { animation: mmReactPop 260ms var(--ease-out, cubic-bezier(.16,1,.3,1)); }
  @keyframes mmReactPop { from { transform: scale(0); } 60% { transform: scale(1.25); } to { transform: none; } }

  /* ---- Long-press quick-reaction bar (WhatsApp style) ---- */
  body.nifhr-screen.screening-step-messenger .mm-react-bar {
    display: flex; align-items: center; gap: 1px;
    position: fixed; z-index: 1006;
    padding: 5px 7px;
    border-radius: var(--radius-pill);
    background: var(--surface); border: 1px solid var(--border);
    box-shadow: var(--shadow-popover);
    opacity: 0; transform: scale(.82) translateY(8px); transform-origin: bottom center;
    transition: opacity 160ms var(--ease-out, ease), transform 180ms var(--ease-out, cubic-bezier(.16,1,.3,1));
    pointer-events: none;
  }
  body.nifhr-screen.screening-step-messenger .mm-react-bar.is-open { opacity: 1; transform: none; pointer-events: auto; }
  .mm-react-btn, .mm-react-reply {
    width: 40px; height: 40px; flex: 0 0 auto;
    display: grid; place-items: center;
    border: none; background: none; cursor: pointer; border-radius: 50%;
    color: var(--text-secondary);
    transition: transform 140ms var(--ease-out, ease);
  }
  .mm-react-btn:active, .mm-react-reply:active { transform: scale(1.22); }
  .mm-react-btn svg, .mm-react-reply svg { width: 25px; height: 25px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
  .mm-react-btn[data-mm-react="like"] svg, .mm-react-btn[data-mm-react="love"] svg { fill: currentColor; stroke: none; }
  .mm-react-reply { color: var(--brand); }
  .mm-react-div { width: 1px; height: 24px; background: var(--border); margin: 0 var(--space-2xs); flex: 0 0 auto; }

  /* ---- Reply preview above the composer ---- */
  .mm-reply-preview {
    display: flex; align-items: center; gap: 10px;
    padding: var(--space-xs) var(--space-sm) var(--space-xs) 10px;
    background: var(--bg-app);
    border-top: 1px solid var(--border);
  }
  .mm-reply-preview[hidden] { display: none; }
  .mm-reply-accent { width: 3px; align-self: stretch; min-height: 30px; border-radius: 2px; background: var(--brand); flex: 0 0 auto; }
  .mm-reply-copy { min-width: 0; flex: 1 1 auto; }
  .mm-reply-copy strong { display: block; font-size: var(--fs-sm); font-weight: var(--fw-bold); color: var(--brand); }
  .mm-reply-copy span { display: block; font-size: 13px; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .mm-reply-x { width: 30px; height: 30px; flex: 0 0 auto; display: grid; place-items: center; border: none; background: none; color: var(--text-tertiary); cursor: pointer; border-radius: 50%; }
  .mm-reply-x svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

  /* Tablet: give the map/photo/cards a touch more room. */
  @media (min-width: 620px) {
    .mm-msg { max-width: 68%; }
    .mm-map, .mm-photo img, .mm-doc, .mm-quest { width: 300px; }
  }
}

@media (max-width: 900px) and (prefers-reduced-motion: reduce) {
  .mm-thread.is-coached,
  .mm-thread.is-coached::after,
  .mm-coach-dot,
  .mm-typing i,
  .mm-msg-react { animation: none; }
  body.nifhr-screen.screening-step-messenger .messenger-mobile-chat,
  body.nifhr-screen.screening-step-messenger .mm-intro,
  .mm-intro-card,
  .mm-quest.mm-quest-drop,
  .mm-msg.mm-enter { animation: none; }
  body.nifhr-screen.screening-step-messenger .mm-sheet,
  body.nifhr-screen.screening-step-messenger .mm-react-bar,
  body.nifhr-screen.screening-step-messenger .mm-coach { transition: none; }
}

/* ==========================================================================
   ENTRY #109 (2026-07-18) AS REWRITTEN BY ENTRY #121 (2026-07-20, user call,
   annotated screenshots) — the rj0wj company-profile drawer.

   WHAT #121 CHANGED HERE, and why the old comments would now mislead:

   (A) THE COLLAPSE IS NO LONGER <=900px ONLY, AND NO LONGER SNAPS. The user reported the
       big "10 of 16" panel and the employer panel "almost snap instantly from large to
       small version and needs to be a lot smoother on the transition for all views". Only
       `padding` (220ms) and `box-shadow` (180ms) ever animated; the text hide was
       `display:none`, and the badge/avatar/name size changes had no `transition` declared
       at all, so everything that dominates the visual footprint was instant. All of it is
       animated now, at ONE 320ms ease-in-out, at every width. ⚠️ The curve matters as much as
       the duration: the first attempt used this file's house `--ease-out`
       (cubic-bezier(.16,1,.3,1)), and measured mid-flight it was already 93% collapsed at 42% of
       its duration — still a snap, just a slower-starting one. `ease-in-out` measures ~40% at the
       midpoint, which is what actually reads as a move.
       ⚠️ STILL A ONE-WAY LATCH, AND IT MUST STAY ONE WAY. Re-measured at 390x844 after the
       move: the body has 183px of scroll and the stack is 406.2px tall, so collapsing
       removes more flow height than there is scroll, maxScroll goes negative, the browser
       clamps scrollTop to 0, and a two-way toggle would collapse / clamp / re-expand
       forever. Animating the properties does not change that arithmetic. `setDrawer(true)`
       releases the latch, and that is the only reset.
       ⚠️ Lifting the <=900px gate is safe above it precisely BECAUSE of the scroll
       threshold: at 1440x900 maxScroll is 66px so it can fire, and where the body does not
       overflow at all it simply never fires (#109 already measured that for 820x1180).
   (B) THE BADGE AND AVATAR STAY TOP-ALIGNED IN BOTH STATES. #109 flipped `align-items`
       flex-start -> center on collapse. `align-items` is a discrete property: it cannot be
       transitioned, so it was one of the snaps being complained about. Collapsed, both
       panels are a single row, so flex-start and center resolve to the same place anyway.
   (C) THE COMPANY-BASICS COACH IS NO LONGER HERE. #109's bottom sheet, its dim, and #112's
       `right:0; width:100vw` containing-block workaround all moved to the workspace step at
       body level (see the entry #121 block near `.company-basics-coach`). What stands in
       the drawer now is `.company-basics-gate`, below.
   ========================================================================== */
/* ⚠️ BASE HIDES, DELIBERATELY OUTSIDE ANY MEDIA QUERY. `.celebration-compact-title` is
   revealed by state (`is-collapsed-head`), never by viewport — #109 learned the hard way
   that a mobile-only hide leaves desktop on the UA default, and since #121 runs the collapse
   at every width a viewport-gated show would have been wrong in the other direction too. */
.celebration-compact-title { display: none; }

/* -- (A) the collapse, at every width -------------------------------------------------- */
.company-drawer-sticky-stack { transition: box-shadow var(--motion-drawer) ease-in-out, padding var(--motion-drawer) ease-in-out; }
.company-celebration,
.company-hero-card { transition: padding var(--motion-drawer) ease-in-out; }

/* The four panels that go to nothing. `--collapse-h` is stamped per element by
   updateDrawerHeadCollapse() in rj0wj.html from a live getBoundingClientRect() — see the
   comment there for why a fixed value cannot work. The fallback only ever applies if that
   stamp is missing. */
@keyframes companyHeadPanelShrink {
  from { max-height: var(--collapse-h, 120px); opacity: 1; }
  to { max-height: 0; opacity: 0; margin-top: 0; }
}
@keyframes companyHeadCompactIn {
  from { max-height: 0; opacity: 0; }
  to { max-height: 60px; opacity: 1; }
}
.company-drawer-body.is-collapsed-head .company-celebration > div > h3,
.company-drawer-body.is-collapsed-head .company-celebration > div > p,
.company-drawer-body.is-collapsed-head .company-contact-list,
.company-drawer-body.is-collapsed-head .company-chip-row {
  overflow: hidden;
  animation: companyHeadPanelShrink var(--motion-drawer) ease-in-out forwards;
}

.company-drawer-body.is-collapsed-head .celebration-compact-title {
  display: block;
  overflow: hidden;
  font-size: 17px;
  font-weight: var(--fw-extrabold);
  line-height: 1.2;
  color: var(--success-text, #166534);
  /* An ANIMATION, not a transition: this element goes display:none -> display:block in the
     same frame, and a transition never runs on the frame an element becomes displayed. */
  animation: companyHeadCompactIn var(--motion-drawer) ease-in-out both;
}

.company-drawer-body.is-collapsed-head .company-celebration { padding: 10px 14px; }
.company-drawer-body.is-collapsed-head .company-hero-card { padding: 10px 14px; }

/* The badge and the avatar shrink with the panels: at full size they alone are taller than
   the whole collapsed row is meant to be. Kept identical to each other so the two rows read
   as one stack. #121 adds the transitions: these four properties changed with none declared
   anywhere, which is why they snapped. */
.celebration-icon,
.company-avatar {
  transition: width var(--motion-drawer) ease-in-out, height var(--motion-drawer) ease-in-out, min-width var(--motion-drawer) ease-in-out, font-size var(--motion-drawer) ease-in-out;
}
.company-name-row h3 { transition: font-size var(--motion-drawer) ease-in-out; }
.company-drawer-body.is-collapsed-head .celebration-icon {
  width: 44px;
  height: 44px;
  min-width: 44px;
  font-size: 13px;
}
.company-drawer-body.is-collapsed-head .company-avatar {
  width: 44px;
  height: 44px;
  min-width: 44px;
  font-size: var(--fs-lg);
}
.company-drawer-body.is-collapsed-head .company-name-row h3 { font-size: var(--fs-xl); }

/* "a drop shadow on the company name panel when scrolling". The collapsed stack lifts
   off the content sliding under it. Same shadow value as the drawer's existing
   is-scrolled treatment and the #74 sticky identity row, so the three cannot drift. */
.company-drawer-body.is-collapsed-head .company-drawer-sticky-stack {
  box-shadow: 0 14px 22px -20px rgba(15, 23, 42, 0.62);
}

@media (max-width: 900px) {
  /* -- (B) top-align the badge / avatar with the first row of their own title. Applies in
        BOTH states since #121; see (B) in the block comment. -- */
  .company-celebration,
  .company-hero-main { align-items: flex-start; }

  /* -- (C) the coach is a BOTTOM SHEET at <=900px. It is body-level now, so `left:0; right:0`
        simply works: #112's `left:auto; right:0; width:100vw` existed only because the
        drawer's transform made the DRAWER the containing block, and that is no longer true.
        The four-side border of the desktop card still has to lose its two VERTICAL sides or
        they come to rest exactly on the viewport edges and paint the thin light seam entry
        #116 was raised for. `border-top` stays on purpose: it is a real hairline along the
        sheet's top edge and was never part of that defect. -- */
  .company-basics-coach {
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    width: auto;
    max-height: calc(100dvh - 64px);
    transform: none;
    padding: 26px 22px calc(22px + env(safe-area-inset-bottom, 0px));
    border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
    border-left: none;
    border-right: none;
    border-bottom: none;
    animation: companyCoachSheetIn 340ms var(--ease-out, cubic-bezier(.16, 1, .3, 1));
  }
  /* The grab handle. The desktop pointer wedge is long gone (#121 deleted it), so there is
     nothing left to suppress here. */
  .company-basics-coach::before {
    content: "";
    position: absolute;
    top: 9px;
    left: 50%;
    transform: translateX(-50%);
    width: 36px;
    height: 4px;
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--text-inverse) 26%, transparent);
  }

  @keyframes companyCoachSheetIn { from { transform: translateY(100%); } to { transform: translateY(0); } }
  @keyframes companyCoachSheetOut { from { transform: translateY(0); } to { transform: translateY(100%); } }

  /* The CTA slides the sheet down before it is hidden, and the closing sheet stops
     blocking so the step is usable during the slide. ⚠️ This must restate the animation so
     it beats the centred-modal `companyCoachModalOut` declared for desktop. */
  .company-basics-coach.is-closing {
    pointer-events: none;
    animation: companyCoachSheetOut 300ms var(--ease-in, cubic-bezier(.4, 0, 1, 1)) forwards;
  }
}

/* -- (C) THE "OPEN" GATE, EVERY WIDTH ---------------------------------------------------
   With the explainer gone from the drawer, the user's ask is that "Open >" be the only
   highlighted tappable option. This dim covers the drawer's own content and hit-tests, so
   the fields, the four inert sections, the foot button and the X are all dead until the
   section is opened; `.company-basics-expandable` is raised above it so the one live row
   reads undimmed. The page behind the drawer is already covered by `.company-drawer-scrim`. */
.company-basics-gate { display: none; }
body.company-drawer-open .company-basics-gate {
  display: block;
  position: absolute;
  /* 2026-07-21 (user call) — the LEFT INSET IS -1px, NOT 0, and that is the whole fix for the
     bright vertical seam the user reported down the drawer's left edge on desktop.
     Mechanism: `.company-profile-drawer` carries `border-left: 1px solid var(--border)`
     (:2000). An absolutely positioned child resolves `inset` against its containing block's
     PADDING box, so `inset: 0` stopped 1px short and left that border strip UNDIMMED —
     #E5E7EB at full brightness, with this 52% dim on its right and the page scrim's dark on
     its left, which reads as a white hairline running the full viewport height.
     ⚠️ Do NOT "fix" this by clearing `border-left-color`: measured, that changes nothing,
     because a background paints under the border box, so the drawer's own white background
     fills the same 1px. Either dim the strip (this) or remove the border's WIDTH.
     Kept width-agnostic on purpose: the seam was measured at 390x844 (coarse pointer) too,
     drawer left 32 / gate left 33 — the same 1px, just less obvious against a phone gutter. */
  inset: 0 0 0 -1px;
  z-index: 94;
  background: color-mix(in srgb, var(--text-primary) 52%, transparent);
  touch-action: none;
}
body.company-basics-has-opened .company-basics-gate { display: none; }
body.company-drawer-open:not(.company-basics-has-opened) .company-basics-expandable {
  position: relative;
  z-index: 95;
}
/* The highlight. `basicsCuePulse` was defined years-of-entries ago and never bound to
   anything; this is its first and only consumer. The `animation: none` rule that used to sit
   on this exact selector was DELETED (see the note at its old site) — it would have won on
   source order and the cue would have sat there silently static. */
body.company-drawer-open:not(.company-basics-has-opened) .company-basics-expandable.is-collapsed .section-expand-cue {
  animation: basicsCuePulse 1800ms ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .company-drawer-sticky-stack,
  .company-celebration,
  .company-hero-card,
  .celebration-icon,
  .company-avatar,
  .company-name-row h3 { transition: none; }
  .company-drawer-body.is-collapsed-head .company-celebration > div > h3,
  .company-drawer-body.is-collapsed-head .company-celebration > div > p,
  .company-drawer-body.is-collapsed-head .company-contact-list,
  .company-drawer-body.is-collapsed-head .company-chip-row { animation: none; display: none; }
  .company-drawer-body.is-collapsed-head .celebration-compact-title { animation: none; }
  .company-basics-coach,
  .company-basics-coach.is-closing { animation: none; }
  body.company-drawer-open:not(.company-basics-has-opened) .company-basics-expandable.is-collapsed .section-expand-cue { animation: none; }
}

/* ==========================================================================
   ENTRY #110 (2026-07-18, user call). The page behind the company-profile
   drawer MUST NOT SCROLL. The drawer is the focus; the dimmed page behind it
   was still taking a finger and drifting under it.

   Mechanism, and why it is a body rule (the entry #77 law, same page): rj0wj
   carries NEITHER `body.x1riqt-screen` NOR `body.nifhr-screen`, so not one of
   the stylesheet's viewport-lock rules reaches it, and both `html` and `body`
   compute `overflow: visible` here. **The viewport takes its scrollability
   from `body` when `html` is `visible`**, so a body rule locks it on iOS
   Safari too, and `overscroll-behavior` propagates the same way and kills the
   rubber-band. The drawer is a SIBLING of the page scrim, not a descendant, so
   its own `.company-drawer-body` scroller is untouched.
   ⚠️ `.company-drawer-scrim` ALREADY carries `touch-action: none` +
   `overscroll-behavior: none` from entry #79 (:1970). Do NOT add a second
   copy: the missing half here was only ever the body `overflow`.

   Deliberately NOT inside a media query: the slack with the drawer open is
   real on phone-height viewports and **0 at 820x1180 and 0 at 1440x900**, so on
   tablet and desktop this rule is a provable no-op.
   ⚠️⚠️ ENTRY #396j RE-MEASURED THIS AND THE OLD FIGURES WERE WRONG BY THE TIME
   ANYONE READ THEM. The superseded sentence quoted two per-viewport slack
   numbers, one of which had become impossible. Measured 24 Aug 2026 on the real
   drawer route: the page behind the drawer is about **914px tall whatever the
   viewport**, so the slack is simply `914 - clientHeight`. That gives 114 at
   360x800, 70 at 390x844, 70 at 412x844, 18 at 414x896 and **0 at 412x915**,
   where the viewport is TALLER than the content and nothing can overflow at all.
   The old text asserted 50px at exactly that size, which sent a guard to fail on
   arithmetic rather than on a defect. Numbers here are DERIVED from one content
   height now, so a future reader can compute any viewport instead of trusting a
   list that rots. -> today-update.md #396j
   ⚠️ NEVER prove this with `window.scrollTo()`: Chrome honours a PROGRAMMATIC
   scroll under `overflow:hidden`, so a scrollTo-based guard goes red on a
   correct fix. Drive a real touch drag, and assert the slack is > 0 FIRST or
   the guard passes vacuously.
   ========================================================================== */
body.company-drawer-open {
  overflow: hidden;
  overscroll-behavior: none;
}

/* ==========================================================================
   ENTRY #111 (2026-07-18, user call). x1riqt's "Application overload" WELCOME
   MODAL BECOMES A BOTTOM SHEET on phones and portrait tablets.

   It was a centred card on every viewport (there was NO @media rule for
   `.welcome-modal` / `.modal-scrim` / `.modal-header` / `.modal-body` anywhere
   in this file), so on a phone it floated mid-screen and read as a desktop
   dialog dropped onto a phone. It now speaks the same sheet language as
   `.company-basics-coach` (#109) and `.mm-intro-card` (#106), and uses the
   SAME max-width:900px those two use.

   ⚠️ CSS-ONLY, NO MARKUP CHANGE, ON PURPOSE. Eleven tests drive this modal
   through `document.querySelector('.welcome-modal [data-start-demo]')`, and
   x1riqt is a LOCKED reference screen (additive changes only). Renaming or
   re-parenting anything here would break both at once.

   ⚠️ The sheet does NOT scroll and must not be given one. Measured before
   writing this block: the card is 422px tall at 390x844 and 460px at its
   worst case 360x640, against a 640-844px viewport, and going full-width
   makes it SHORTER still. A `max-height` + `overflow-y:auto` would only
   carry the absolutely-positioned grab handle out of view for no gain.
   (#76: measure the overflow, never assume there is one.)

   ⚠️ `place-items: end stretch` is what does the real work: `end` bottom-
   anchors the card and `stretch` full-widths it. `.welcome-modal`'s
   `width: min(520px, 100%)` must be released to `auto` or stretch cannot win.

   Desktop is untouched: everything below is inside the media query, and the
   base rules at :5416 keep the centred card above 900px.
   ========================================================================== */
@media (max-width: 900px) {
  .modal-scrim {
    place-items: end stretch;
    padding: 0;
  }
  .welcome-modal {
    /* ⚠️ LOAD-BEARING. The base rule leaves this `position: static`, so without it the
       absolute grab handle below resolves against `.modal-scrim` (the nearest positioned
       ancestor, `position: fixed`) and lands 9px from the top of the VIEWPORT instead of
       9px from the top of the sheet. */
    position: relative;
    width: auto;
    border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
    /* ⚠️ Entry #116, the SAME seam as `.company-basics-coach`: the base rule (:5463) borders all
       four sides for the desktop CARD, but #111 stretched this to a full-width sheet, so the
       vertical borders came to rest ON the viewport edges as a thin light line. #111 neutralized
       only `border-bottom`. `border-top` stays: it is a real hairline on the sheet's top edge. */
    border-left: none;
    border-right: none;
    border-bottom: none;
    animation: welcomeSheetIn 340ms var(--ease-out, cubic-bezier(.16, 1, .3, 1));
  }
  /* The grab handle. A plain CSS bar, never a glyph. `.welcome-modal` is
     `overflow: hidden`, which clips it to the rounded top exactly as intended. */
  .welcome-modal::before {
    content: "";
    position: absolute;
    top: 9px;
    left: 50%;
    transform: translateX(-50%);
    width: 36px;
    height: 4px;
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--text-inverse) 26%, transparent);
  }
  /* Desktop padding is too generous once the card is edge-to-edge, and the
     bottom must clear the home indicator. */
  .modal-header {
    padding: 26px 22px 10px;
  }
  .modal-body {
    padding: 14px 22px calc(22px + env(safe-area-inset-bottom, 0px));
  }
  @keyframes welcomeSheetIn {
    from { transform: translateY(100%); }
    to { transform: translateY(0); }
  }
}

@media (max-width: 900px) and (prefers-reduced-motion: reduce) {
  .welcome-modal { animation: none; }
}

/* ==========================================================================
   ENTRY #114 (2026-07-18, user call). THE HANDOFF FROM x1riqt TO nifhr BECOMES
   ONE CONTINUOUS CHAIN OF BOTTOM SHEETS.

   (A) x1riqt's parsing tour card. It was a floating dark rectangle pinned to the
   bottom-right corner (:4111 `right:11.8vw; bottom:13.5vh`, nudged to 24/24 at
   :5630) with no dim, no handle and no animation, appearing the INSTANT parsing
   began. It is now the same sheet language as `.welcome-modal` (#111),
   `.mm-intro-card` (#106) and `.company-basics-coach` (#109/#112), and the JS
   reveals it only once the cards AND the bar are both done.

   ⚠️ APPENDED AT THE END ON PURPOSE. The base `.tour-step` (:4111) and the
   existing `@media (max-width:900px)` override (:5630) are both (0,1,0), the same
   specificity as the rules below, so SOURCE ORDER is the only thing that makes
   these win. This is the #98 law 7 trap: writing them next to :5630 instead
   would silently lose. Same for `.parsing-tour.is-visible` (0,2,0) at :4099.

   Desktop is untouched: everything is inside the media query, and above 900px
   the base rules keep the bottom-right floating card exactly as it was.
   ========================================================================== */
@media (max-width: 900px) {
  /* It is a real modal sheet now, so it takes the taps and re-arms the dim that
     :4096 deliberately removed while it was a passive corner card. Safe because
     the JS no longer shows it during parsing, so it can no longer sit on top of
     the resume drag-stack and swallow the drag (the reason for the :4102 note). */
  .parsing-tour.is-visible {
    pointer-events: auto;
  }
  .parsing-tour::before {
    background: var(--scrim-dim);
  }
  .tour-step {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    width: auto;
    max-width: none;
    border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
    /* ⚠️ Entry #116, the full-width-sheet seam: the base rule (:4132) borders all four sides for
       the desktop card, so once this spans the viewport the vertical borders sit ON its edges as a
       thin light line. `border-top` stays (a real hairline on the sheet's top edge). */
    border-left: none;
    border-right: none;
    padding: 30px 22px calc(28px + env(safe-area-inset-bottom, 0px));
    animation: parsingTourSheetIn 340ms var(--ease-out, cubic-bezier(.16, 1, .3, 1));
  }
  /* The grab handle. A plain CSS bar, never a glyph. */
  .tour-step::before {
    content: "";
    position: absolute;
    top: 9px;
    left: 50%;
    transform: translateX(-50%);
    width: 36px;
    height: 4px;
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--text-inverse) 26%, transparent);
  }
  /* A small left-aligned pill reads as an afterthought once the card is
     edge-to-edge. Full-width, matching `.mm-intro-cta`. */
  .tour-step-actions {
    align-items: stretch;
    flex-direction: column;
  }
  .tour-step .tour-primary {
    width: 100%;
    height: 50px;
  }
  @keyframes parsingTourSheetIn {
    from { transform: translateY(100%); }
    to { transform: translateY(0); }
  }
}

@media (max-width: 900px) and (prefers-reduced-motion: reduce) {
  .tour-step { animation: none; }
}

/* ==========================================================================
   ENTRY #114 (B). nifhr: the base coach becomes a sheet, and the profile drawer
   gets its "Message Marta" exit sheet.

   ⚠️ SCOPED TO THE BASE STEP ONLY. The `@media` block at :13318 turns `.nifhr-coach`
   into a 14px-inset floating card for the base step AND both move-step variants.
   Only the BASE chain is converted to a flush sheet here: the move-step coach sits
   over a live board the viewer has to drag on, and the #70 measurement (a taller
   coach buries the board and the arc) still governs there. Do not widen these
   selectors to `.candidate-move-step`.

   ⚠️ Same source-order law as block (A): :13318 and these are the same specificity
   family, so being at the END of the file is what makes them win.
   ========================================================================== */
@media (max-width: 900px) {
  /* (A) the base-step coach as a flush bottom sheet, matching `.mm-intro-card`. */
  body.nifhr-screen:not(.candidate-move-step) [data-screen-id="nIfHr"] .nifhr-coach,
  body.nifhr-screen.profile-drawer-open:not(.candidate-move-step) [data-screen-id="nIfHr"] .nifhr-coach {
    left: 0;
    right: 0;
    bottom: 0;
    border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
    /* ⚠️ Entry #116, the full-width-sheet seam: the base rule (:5117) borders all four sides, so
       once this is a flush sheet the vertical borders land ON the viewport edges as a thin light
       line. ⚠️ SCOPED TO THIS BASE-STEP CHAIN ON PURPOSE: do NOT widen it to a bare `.nifhr-coach`
       or move it into the ≤700px block at :13341, which also governs the `candidate-move-step`
       variant. That one stays a genuinely inset 14px-margin floating card whose border is correct
       and must keep all four sides. `border-top` stays here too. */
    border-left: none;
    border-right: none;
    padding: 30px 22px calc(28px + env(safe-area-inset-bottom, 0px));
    max-height: 60dvh;
    animation: nifhrCoachSheetIn 340ms var(--ease-out, cubic-bezier(.16, 1, .3, 1));
  }
  body.nifhr-screen:not(.candidate-move-step) [data-screen-id="nIfHr"] .nifhr-coach::before {
    content: "";
    position: absolute;
    top: 9px;
    left: 50%;
    transform: translateX(-50%);
    width: 36px;
    height: 4px;
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--text-inverse) 26%, transparent);
  }
  /* Full-width pill CTA, the same shape as `.mm-intro-cta` and the x1riqt sheet the
     viewer just came from. Base step only: the move-step coach keeps its compact CTA. */
  body.nifhr-screen:not(.candidate-move-step) [data-screen-id="nIfHr"] .nifhr-coach .coach-cta {
    width: 100%;
    height: 50px;
    border-radius: var(--radius-pill);
  }
  @keyframes nifhrCoachSheetIn {
    from { transform: translateY(100%); }
    to { transform: translateY(0); }
  }

  /* (C) Marta is not tappable until that CTA is tapped (mobile/tablet only, user call).
     The JS guard blocks the tap; this is what makes the block VISIBLE rather than a
     dead affordance. Both are keyed off the same class. */
  body.nifhr-screen:not(.base-coach-dismissed):not(.candidate-move-step) [data-screen-id="nIfHr"] .candidate-card[data-open-profile] {
    opacity: 0.39;
    pointer-events: none;
  }
}

/* (B) Once "See it now" is tapped the coach is gone, on EVERY view. Desktop needs this
   as much as the phone does: previously "Got it" advanced immediately and the same reused
   element re-skinned itself for the move step, so it was never seen stale. Now the drawer
   opens instead, and without this the desktop coach sat beside the open drawer still
   reading "Groundbreaking? No. Useful? Absolutely." with a CTA that only re-opened what
   was already open. The `:not(.candidate-move-step)` releases it again the moment
   startSelectionStep() runs, so the move-step coach is untouched. */
body.nifhr-screen.base-coach-dismissed:not(.candidate-move-step) [data-screen-id="nIfHr"] .nifhr-coach {
  display: none;
}

/* The "Message Marta" sheet. Scoped to the DRAWER's box on every view, so it is
   `position: absolute` (the drawer already carries a transform, making it the
   containing block) where `.mm-intro` is `fixed`. Desktop gets it too: the user
   asked for this exit on ALL VIEWS. */
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-sheet {
  display: grid;
  place-items: end center;
  position: absolute;
  inset: 0;
  z-index: 30;
  animation: nhsq0MsgIn 260ms var(--ease-out, cubic-bezier(.16, 1, .3, 1));
}
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-sheet[hidden] { display: none; }
@keyframes nhsq0MsgIn { from { opacity: 0; } to { opacity: 1; } }

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-scrim {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--text-primary) 62%, transparent);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-card {
  position: relative;
  width: 100%;
  padding: 30px 26px calc(28px + env(safe-area-inset-bottom, 0px));
  border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
  background: var(--text-primary);
  color: var(--text-inverse);
  box-shadow: var(--shadow-popover);
  text-align: center;
  animation: nhsq0MsgCard 340ms var(--ease-out, cubic-bezier(.16, 1, .3, 1));
  will-change: transform;
}
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-card::before {
  content: "";
  position: absolute;
  top: 9px;
  left: 50%;
  transform: translateX(-50%);
  width: 36px;
  height: 4px;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--text-inverse) 26%, transparent);
}
@keyframes nhsq0MsgCard { from { transform: translateY(100%); } to { transform: translateY(0); } }

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-sheet.is-closing { pointer-events: none; }
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-sheet.is-closing .nhsq0-msg-card {
  animation: nhsq0MsgCardOut 300ms var(--ease-in, cubic-bezier(.4, 0, 1, 1)) forwards;
}
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-sheet.is-closing .nhsq0-msg-scrim {
  animation: nhsq0MsgScrimOut 300ms ease forwards;
}
@keyframes nhsq0MsgCardOut { from { transform: translateY(0); } to { transform: translateY(100%); } }
@keyframes nhsq0MsgScrimOut { from { opacity: 1; } to { opacity: 0; } }

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-card h2 {
  font-size: 22px;
  font-weight: var(--fw-extrabold);
  line-height: 1.24;
  letter-spacing: -0.4px;
  color: var(--text-inverse);
}
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-card p {
  margin-top: var(--space-sm);
  font-size: var(--fs-md);
  line-height: var(--lh-normal);
  color: color-mix(in srgb, var(--text-inverse) 76%, transparent);
}
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-cta {
  margin-top: var(--space-lg);
  width: 100%;
  height: 50px;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: var(--text-inverse);
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  cursor: pointer;
  box-shadow: var(--shadow-brand);
  transition: transform 140ms var(--ease-out, ease);
}
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-cta:active { transform: scale(.97); }

@media (prefers-reduced-motion: reduce) {
  body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-sheet,
  body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-card,
  body.nifhr-screen:not(.candidate-move-step) [data-screen-id="nIfHr"] .nifhr-coach { animation: none; }
}

/* ==========================================================================
   ENTRY #116 (2026-07-19, user call) — the full-width-sheet SEAM, last instance.
   `.job-card-help-modal` (rj0wj's landing sheet) borders all four sides at :730 for its
   desktop card pose. ⚠️ ITS WIDTH IS `min(560px, 100vw)`, NOT a media-query stretch, so it
   only becomes edge-to-edge BELOW 560px. Measured: at 600x900 it is left=20 / width=560 (a
   legitimately floating card with margins either side, border correct); at 390x844 it is
   left=0 / width=390 with the border ON the viewport edge. So this needs its OWN 560px gate:
   reusing the ≤700px block at :13869 would strip the border across 561-700px where the card
   still floats. `border-top` stays: a real hairline on the sheet's top edge.
   Appended at the END of the file on purpose — equal specificity (0,1,0), source order wins.
   ========================================================================== */
@media (max-width: 560px) {
  .job-card-help-modal {
    border-left: none;
    border-right: none;
  }
}

/* ============================================================================
   ENTRY #118 (2026-07-19, user call, annotated screenshot) THE rj0wj JOB-TOUR
   COACH: a real blurred scrim, a genuinely modal dim, and a tooltip that can
   never cross the fold.

   Three asks, one scene: darken the dim and blur it "to our normal blur value
   token"; stop the viewer dismissing it by tapping anything but the button that
   opens the next scene; keep the coach bubble above the fold at every size.

   ⚠️ THE DIM COULD NOT SIMPLY BE RESTYLED. It was `box-shadow: 0 0 0 9999px
   var(--scrim-dim)` on .coach-ring (:646). A spreading shadow:
     - cannot be BLURRED. backdrop-filter samples the element's OWN box, and the
       ring's box is the size of the card, not the screen.
     - never HIT-TESTS. It looks modal and gates nothing (a documented law of
       this codebase). Every "tap the dim" guard written against it was inert.
   So the dim becomes a REAL element, .coach-scrim, with a clip-path hole over
   the card. The ring keeps its purple border and drop shadow and LOSES the
   9999px layer, or the two would double-dim.
   ============================================================================ */

:root {
  /* ⚠️ THE FIRST NAMED BLUR IN THIS PROJECT. There was no --blur anything: every
     one of the 19 blur sites hardcoded its value. 2px is not a new look, it is
     the de-facto standard already in use at 9 of them (the job-card modal shell,
     the nifhr compare + screening overlays, the messenger scrims), so this
     codifies what exists rather than inventing a value. User picked it over
     3.6px (opening.html's redaction blur) and 10px (the frosted drawer footer).
     New backdrop blurs should consume this token, not another literal. */
  --blur-scrim: 2px;
  /* ⚠️⚠️ (ENTRY #416) HER "MEDIUM BLURRING TOKEN", AND IT DID NOT EXIST UNTIL THIS ENTRY. She asked
     for it by name; the honest answer is that this project has ONE named blur and no small/medium/
     large scale, so rather than inventing a bare number this is DERIVED from the token that does
     exist and declared beside it. The multiplier lands it at the midpoint of the two extremes the
     `--blur-scrim` comment above already records as being in use: its own 2px and the frosted
     drawer footer's 10px. So "medium" is the codebase's own middle, not a taste call.
     ⚠️ DERIVED, NOT RETYPED: if `--blur-scrim` ever moves, this moves with it and the scale stays
     a scale. */
  --blur-scrim-md: calc(var(--blur-scrim) * 3);
  /* Scoped DELIBERATELY to this coach, not folded into --scrim-dim. The shared
     token stays 0.35 and its other four consumers (the workspace coach, the
     nifhr/x1riqt spotlight, x1riqt's welcome-modal scrim and its parsing tour)
     are untouched: the user chose "this coach only" over a lockstep change. */
  --scrim-dim-coach: rgba(0, 0, 0, 0.55);
  /* (2026-07-21, entry #145) How far the workspace spotlight's clip-path hole is grown past the
     switcher's measured rect. It must clear BOTH the ring's 4px purple halo AND the outer edge of
     `wsRingPulse`'s scale(1.045), or the ring breathes out onto dimmed page mid-pulse. Measured at
     1440: the switcher is 200x40, so the pulse pushes its edge 4.5px wider / 0.9px taller, i.e.
     2.25px and 0.45px per side. 6px clears the larger of those plus the halo. */
  /* ⚠️ (#357) 0, HER CALL: the hole is the ELEMENT, not a box around it. At 6px the desktop dim
     was cut as a sharp-cornered rectangle 6px proud of a 207x42 rounded pill on every side, which
     is exactly the "harsh square highlight around the pulsing company highlight element" she asked
     to remove. Superseded value: `--ws-hole-pad: 6px`. Kept as a variable rather than deleted so
     the four calc()s below still read as one intent and a future step can re-pad if it needs to. */
  --ws-hole-pad: 0px;
  /* (2026-07-22, entry #160) How much bigger an emphasised word inside a coach
     headline runs than the headline around it. Deliberately RELATIVE (em), not a
     px literal: that same headline is 28px at >=901px and 20px at <=700px, so a
     hardcoded size would be "slightly larger" in one view and oversized in the
     other. One token, both views, correct by construction. */
  --coach-emph-size: 1.14em;
}

.coach-scrim {
  position: fixed;
  inset: 0;
  /* Article-local: .coachmark-overlay is z-index 10 and establishes the stacking
     context, so 0 puts this UNDER the ring and the tooltip and above nothing
     else. It does not need to out-rank anything globally, the hole does the work. */
  z-index: 0;
  background: var(--scrim-dim-coach);
  -webkit-backdrop-filter: blur(var(--blur-scrim));
  backdrop-filter: blur(var(--blur-scrim));
  /* The parent .coachmark-overlay is pointer-events:none (:626). Re-enabling it
     HERE is the entire point: this is what finally makes the dim modal. */
  pointer-events: auto;
  opacity: 0;
  transition: opacity 200ms linear;
  /* ⚠️ NO `touch-action: none` and NO scroll lock. At 320x568 the "Claim &
     Review" button itself sits below the fold, so a locked page would strand the
     viewer with the one control they are told to press out of reach. Taps are
     swallowed; scrolling is deliberately still allowed. */
}
.coachmark-overlay.tour-ready .coach-scrim { opacity: 1; }
/* The hole. Single polygon, evenodd: outer loop is the viewport, inner loop is
   the card, and evenodd subtracts the inner one. Coords are the card's LIVE
   viewport rect from placeTour() (--tour-v*), because this element is fixed.
   The card's corner radius leaves four ~10px slivers of undimmed page inside the
   square hole; the purple ring is drawn over exactly that band, so they are not
   visible. Fallback values keep the scrim a plain full-screen dim if placeTour()
   has not run yet, rather than a mispositioned hole. */
.coach-scrim {
  clip-path: polygon(evenodd,
    0 0, 100% 0, 100% 100%, 0 100%, 0 0,
    var(--tour-vx, 0px) var(--tour-vy, 0px),
    var(--tour-vx, 0px) var(--tour-vb, 0px),
    var(--tour-vr, 0px) var(--tour-vb, 0px),
    var(--tour-vr, 0px) var(--tour-vy, 0px),
    var(--tour-vx, 0px) var(--tour-vy, 0px)
  );
}

/* .coach-ring keeps the purple border + lift, loses the 9999px dim layer that
   .coach-scrim now owns. Appended here rather than edited at :646 so the
   original entry #111 comment stays readable as history. */
.coach-ring {
  box-shadow:
    0 0 0 4px rgba(124, 58, 237, 0.12),
    0 12px 28px rgba(76, 29, 149, 0.18);
}

/* ONLY the button that opens the next scene is tappable. .coach-scrim handles
   everything OUTSIDE the card; this handles everything INSIDE it, which the hole
   necessarily leaves reachable.
   ⚠️ Gated on a BODY class, not on .coachmark-overlay's own state: the overlay
   sits AFTER .jobs-list in the DOM, so no sibling combinator can reach backwards
   from it to the card. `job-tour-live` is added by showJobTour() and removed the
   moment the coach is dismissed, so the card returns to fully live. */
body.job-tour-live .tour-target { pointer-events: none; }
body.job-tour-live .tour-target .row-claim { pointer-events: auto; }

/* The fold clamp. --tip-top is written by placeTour() on every load, resize and
   scroll; the fallback is the historical below-the-card position so a stylesheet
   loaded without the JS still renders sensibly. */
.tooltip { top: var(--tip-top, calc(var(--tour-y) + var(--tour-h) + 6px)); }
/* Flipped above the card: the tail must point DOWN from the tooltip's underside.
   Pinned to the viewport bottom: the tooltip is no longer adjacent to the card at
   all, so a tail pointing anywhere would be a lie. Hide it. */
.coachmark-overlay.tip-above .tooltip::before {
  top: auto;
  bottom: -11px;
  clip-path: polygon(50% 100%, 0 0, 100% 0);
}
.coachmark-overlay.tip-pinned .tooltip::before { display: none; }

/* ---- ENTRY #150 (2026-07-21, user call, annotated screenshot) SUPERSEDES #119 ----
   The coach card is BACK on mobile and tablet, with its own copy ("Tap your latest job ad"),
   and desktop is untouched. The #119 block below is kept verbatim as the record of why it went
   away and what still holds (the ring, the scrim and the "only Claim & Review is tappable"
   gating never changed); only its `display: none` is reversed here.

   Three asks, in the annotation's own order:
     1. It exists on phone and tablet again.
     2. It may NEVER hang off the bottom of the viewport. That is #118's fold clamp in
        placeTour(), which #119's own comment deliberately kept alive for exactly this moment:
        below the card -> flipped above it -> pinned to the fold. It was dormant only because a
        `display:none` element has a null `offsetParent` and was skipped.
     3. The tail points at "Claim & Review". `--tail-x` is measured live off that button by
        placeTour() and was gated to `>=901px` by #87; the gate is lifted below.
   ⚠️ (Entry #152) The copy is NO LONGER width-split. #150 shipped a second node pair so desktop
   could keep "Start here"; the user then asked desktop to read the same as mobile, so there is ONE
   pair again and `.tooltip-mob` and its show/hide rules are DELETED. Only the card's GEOMETRY is
   still width-split below. The title/body fonts come from `.tooltip-head` and the tooltip's own
   `p`, which are this card family's standard ones. */

/* (Entry #154, user call) The coach's VERB is per-view: "Tap" on phones and tablets, "Click" on
   desktop. Base hide + desktop show, the documented direction. Everything else in this card stayed
   shared by #152 and must stay shared.
   ⚠️ THESE MUST BE SCOPED TO `.tooltip-head`. The legacy `.tooltip-head span` rule (:1976) is
   (0,1,1) and sets `display: inline-flex`; a bare `.coach-verb-click` is (0,1,0) and LOSES to it,
   so both verbs render. Measured before this scoping: the headline read "Tap Click your latest job
   ad" at every width. */
.tooltip-head .coach-verb-click { display: none; }

@media (min-width: 901px) {
  .tooltip-head .coach-verb-tap { display: none; }
  .tooltip-head .coach-verb-click { display: inline-flex; }
}

@media (max-width: 900px) {
  .coachmark-overlay .tooltip { display: block; }
  /* ask 3: the same live-measured aim desktop has had since #87 */
  .tooltip::before { left: var(--tail-x, 40px); }

  /* ⚠️ ask 3 needs the card MOVED as well as the tail aimed. The base `.tooltip` is
     `right: 24px` and 380px wide: on a TABLET that parks it on the right of the viewport while
     "Claim & Review" sits at the left of the job card, so `--tail-x` clamps and the tail points
     at nothing. MEASURED at 820x1180 before this rule: tail x 438, button centre 111, i.e. 327px
     adrift. Anchoring the card to the highlighted card's own left edge puts the button inside the
     card's span at every width, which is also how the annotation draws it. */
  .tooltip {
    left: var(--tour-x);
    right: auto;
    width: min(380px, var(--tour-w));
  }
}

/* ---- ENTRY #119 (2026-07-19, user call, annotated screenshot) ----
   ⚠️ REVERSED BY #150 ABOVE (the bubble is back on phones, with mobile copy). Kept for the
   reasoning, which still holds for everything except the hide itself.
   The "Start here" tooltip is REMOVED on mobile and tablet. Desktop keeps it.
   ≤900px is this project's established mobile/tablet tier (the shared burger shell,
   the step-aware kanban, the coach viewport caps all key off it), so this needs no new
   breakpoint and desktop stays byte-identical.
   The ring, the blurred scrim and the "only Claim & Review is tappable" gating all stay:
   only the bubble goes.
   ⚠️ `display:none`, not `opacity:0`. An opacity:0 overlay still eats taps (a documented
   law of this codebase) and this element is `pointer-events:auto`.
   ⚠️ Consequence worth knowing: #118's fold clamp (the below/above/pinned flip in
   placeTour()) only ever mattered on phones, since desktop never overflowed. It is now
   effectively a desktop-only safety net. It is deliberately KEPT rather than reverted, so
   that raising this breakpoint or bringing the bubble back cannot silently reintroduce a
   coach hanging past the fold. */
/* ⚠️ #119's rule was `@media (max-width: 900px) { .coachmark-overlay .tooltip { display: none } }`
   and it is DELETED, not overridden. It sat AFTER the #150 block above with the same specificity,
   so leaving it in place would have beaten the reveal on source order and #150 would have looked
   like it did nothing. */

/* ---- ENTRY #121 (2026-07-20, user call, annotated screenshots) ----
   THE WORKSPACE SPOTLIGHT IS TWO PHASES. Phase A dims everything, the switcher included,
   behind the "NO WE DIDN'T HACK YOU" explainer; phase B spotlights the switcher and pulses it.
   The user's words: "start this screen with the workspace switcher also dimmed and remove this
   coach modal and once the bottomsheet dismissed then show the workspace switcher as
   highlighted as normal but with a slightly pulsing size".

   ⚠️ THE RING IS THE ONLY THING THAT PAINTS THE OLD DIM. `.workspace-coach-ring` carries
   `box-shadow: 0 0 0 9999px var(--scrim-dim)`, so the ring IS the dim: showing it in phase A
   would un-dim the switcher (a box-shadow never covers its own element's box), which is exactly
   the state being complained about. Hence phase A hides the ring outright and lets the real
   `.company-coach-scrim` element do the dimming, over everything, evenly.
   The tail block is the right home for these: a rule appended at the END of this file beats an
   equal-specificity rule inside a tier block, and `.workspace-coach-overlay.tour-ready
   .workspace-coach-ring { opacity: 1 }` at :2782 is exactly such a rule. */
body.workspace-tour-active:not(.company-coach-dismissed) .workspace-coach-ring {
  opacity: 0;
}

/* The pulse. ⚠️ IT IS ON THE RING, NEVER ON THE SWITCHER. The ring is `pointer-events:none` and
   purely cosmetic, so scaling it cannot move or destabilise a tap target — the same reasoning
   that made the messenger row-coach avoid `transform:scale` on a tappable row (:16101). The
   9999px shadow scales with it, which still covers the viewport; what the eye sees breathing is
   the hole around the switcher, which is the requested effect. */
@keyframes wsRingPulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.045); }
}
body.workspace-tour-active.company-coach-dismissed .workspace-coach-ring {
  transform-origin: center;
  animation: wsRingPulse 1600ms ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  body.workspace-tour-active.company-coach-dismissed .workspace-coach-ring { animation: none; }
}

/* ============================================================================
   ENTRY #123 (2026-07-20, user call, annotated screenshot): when the gesture
   coach dismisses, the headline GROWS into the space the hands leave behind.

   The hands taught the swipe; once markInteracted() has fired they are dead weight, and
   the user asked for the headline to "smoothly animate into being larger font and into
   new place" as they go. Both halves are driven by the SAME body.jc-coach-done class the
   hands already fade on, so there is one state, not two that could disagree.

   ⚠️ THE HEADER'S HEIGHT MUST NOT CHANGE, AND THAT IS WHAT SIZES THESE NUMBERS.
   .job-card-chooser is a centre-justified flex column and .jc-deck's height is a fixed
   clamp, so a header that grows or shrinks by N moves the CARD by N/2. The file already
   warns about this twice (:13604 bakes "head 99 + pipsrow 30 + foot 34" into the deck
   height; :15590 keeps the end-card's pips/foot at visibility for the same reason).
   Measured at rest, the header is EXACTLY h2 + 8px + coach, with no margins in play
   (320px: 44.88 + 8 + 38 = 90.88; 1440px: 61.19 + 8 + 52 = 121.19).
   The h2 is two <span> rows at line-height 1.02, so growing it by G costs 2.04 x G of
   height. The coach row therefore gives back exactly 2.04 x (grown - base) and the sum is
   invariant by construction, at every width, with no magic literal. If you retune either
   size, the other follows automatically; if you add a THIRD row to the header, this whole
   cancellation is void.

   ⚠️ --jc-h2-grown IS A WIDTH-AWARE CLAMP, NOT A MULTIPLIER, and it has to be.
   The headline's two rows are structural (see rj0wj.html) and a third row would break both
   the cancellation above and the design. Measured single-line headroom for the longer row
   ("Where are you bleeding") before it wraps: 320px has only 1.17x, while 430px has 1.62x
   and desktop 1.55x. One flat multiplier big enough to read as a change at 390 wraps at
   320. The clamp below lands at 23.0 / 25.9 / 28.1 / 31.0 / 44px for 320 / 360 / 390 / 430
   / desktop, each with 10 to 13% of width still spare.
   ============================================================================ */
.job-card-chooser-head {
  --jc-h2: clamp(22px, 4.6vw, 30px);
  --jc-h2-grown: clamp(23px, 7.2vw, 44px);
  --jc-coach-h: clamp(38px, 11vw, 52px);
}
/* Matches the coach's own 420ms shrink so the two read as one movement: the hands go, the
   words take the room. The ease is the file's existing cubic-bezier(.4, 0, .2, 1). */
.job-card-chooser-head h2 {
  transition: font-size 420ms cubic-bezier(.4, 0, .2, 1);
}
body.jc-coach-done .job-card-chooser-head h2 {
  font-size: var(--jc-h2-grown);
}

/* Carve-out 1: the LANDSCAPE tier never showed the hands (:15467 hides the row outright),
   so there is no vacated space to grow into and nothing for the height to cancel against.
   Growing here would be a pure header expansion, i.e. the card jump this block exists to
   prevent. The headline stays at the tier's 18px. Specificity matches the grow rule above
   and it is later in source, which is the only reason it wins. */
@media (max-height: 540px) {
  body.jc-coach-done .job-card-chooser-head h2 { font-size: var(--fs-2xl); }
}
@media (prefers-reduced-motion: reduce) {
  .job-card-chooser-head h2 { transition: none; }
}

/* Entry #123b (2026-07-20, user call): the END CARD reached => the headline goes too, on every
   view. It asked "where are you bleeding the most time?"; once the viewer is on the destination
   card that question has been answered and the card's own copy is the only thing left to read.

   ⚠️ visibility, NEVER display, for the same reason as the (F) rule above at :15598: the header
   carries the h2 + coach box that .jc-deck's height constants are derived from, and
   .job-card-chooser is a centre-justified flex column, so collapsing this box would re-centre the
   column and make the END CARD visibly JUMP the moment the viewer lands on it. Keeping the box
   also keeps #123's height cancellation intact.
   ⚠️ .jc-terminal-top is stamped by render() on [data-job-chooser], which is the OUTER
   .job-card-chooser-overlay, not the inner .job-card-chooser section. Naming the wrong one is a
   silent no-op (the (F) rule's comment learned this the hard way).

   Fades rather than snaps, using the same opacity + delayed-visibility idiom the gesture coach
   dismisses with: the headline has just finished growing, so cutting it dead would read as a
   glitch. */
.job-card-chooser-overlay.jc-terminal-top .job-card-chooser-head h2 {
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--motion-ui) ease, visibility 0s linear var(--motion-ui);
}
@media (prefers-reduced-motion: reduce) {
  .job-card-chooser-overlay.jc-terminal-top .job-card-chooser-head h2 { transition: none; }
}

/* ============================================================================
   ENTRY #124 (2026-07-20, user call, annotated screenshot): the FAB COACH step.

   The golden Demo-sections launcher is the viewer's way back into any part of the demo, and
   nothing had ever pointed at it. Between #121's phase A (the "NO WE DIDN'T HACK YOU" sheet)
   and phase B (the switcher spotlight) the launcher is now introduced: highlighted but inert,
   then grown 50% and pulsing under an explainer card, then normal size and finally live.

   ⚠️ THE GROWTH IS `transform-origin: top right`, NOT centre, AND IT IS LOAD-BEARING.
   The launcher sits at `right: 20px` / `right: 14px`. MEASURED with a centre origin at 1440:
   scale(1.5) puts its right edge 13.5px PAST the viewport edge and the 1.62 pulse peak 21.6px
   past, clipping the button and adding horizontal scroll (the project's standing zero-hscroll
   bar). Anchoring the top-right corner grows it DOWN AND LEFT, so it can never leave the screen
   at any width, and it makes the coach card's tail target exact arithmetic rather than a live
   measurement of a moving box (see placeFabCoach() in rj0wj.html).

   ⚠️ FAB_PULSE_MAX in rj0wj.html and the 1.62 in @keyframes fabCoachPulse ARE ONE NUMBER.
   The JS derives the card's position from the largest pulsed state so the two never overlap
   (the user's "so no overlapping"). Change one without the other and the tail drifts off the
   button or the card slides under it.
   ============================================================================ */

/* ARMED: highlighted but inert. Deliberately NOT the project's MD3-inactive treatment
   (opacity .39 + pointer-events:none), because the user asked for it HIGHLIGHTED: it keeps full
   opacity and gains a brand ring instead. Inert but loud. `pointer-events:none` is the third
   mechanism, alongside the real `disabled` attribute and `aria-disabled` stamped by the JS:
   a <button disabled> already refuses clicks and keyboard, this just makes it refuse hit-testing
   too so nothing underneath can be reached through it either. */
body.fab-coach-armed .demo-sections-launcher {
  pointer-events: none;
  cursor: default;
  box-shadow:
    0 0 0 3px rgba(124, 58, 237, 0.55),
    0 2px 6px rgba(0, 0, 0, .18),
    0 8px 22px rgba(0, 0, 0, .28);
}

/* ACTIVE: grown 50% and pulsing a little bigger again. */
@keyframes fabCoachPulse {
  0%, 100% { transform: scale(1.5); }
  50% { transform: scale(1.62); }
}
body.fab-coach-active .demo-sections-launcher {
  transform-origin: top right;
  animation: fabCoachPulse 1500ms ease-in-out infinite;
}
/* Reduced motion still GROWS (the size change is information, not decoration) but does not pulse.
   It rests at the pulse MAXIMUM, not at 1.5, so the card placed for the largest state still sits
   clear of it rather than leaving a visible gap. */
@media (prefers-reduced-motion: reduce) {
  body.fab-coach-active .demo-sections-launcher {
    animation: none;
    transform: scale(1.62);
  }
}

/* The card. Body level and position:fixed, because the launcher it points at is a <body> child
   at position:fixed, OUTSIDE `.content`. The existing `.tooltip` job coach cannot be reused as is:
   it is position:absolute inside `.content` and its --tour-x/y are offsets from that box. Only the
   TAIL idiom is borrowed from it. left/top are written by placeFabCoach(). */
.fab-coach,
.switcher-coach {
  position: fixed;
  z-index: 2147483031; /* one above the launcher (2147483030) so the card is never behind it */
  width: min(300px, calc(100vw - 24px));
  box-sizing: border-box;
  padding: 14px var(--space-md) var(--space-md);
  border-radius: 14px;
  background: #1F2937;
  color: var(--text-inverse);
  box-shadow: 0 18px 44px rgba(0, 0, 0, .38);
}
.fab-coach[hidden],
.switcher-coach[hidden] { display: none; }
.fab-coach p,
.switcher-coach p {
  margin: 0 0 var(--space-sm);
  font: 500 13px/1.45 Inter, system-ui, sans-serif;
  color: #E5E7EB;
}
/* Same clip-path triangle idiom as .tooltip::before (:667), pointing UP at the launcher. Its x is
   written by placeFabCoach() as --fab-tail-x and is clamped inside the card. */
.fab-coach::before,
.switcher-coach::before {
  content: "";
  position: absolute;
  top: -11px;
  left: var(--fab-tail-x, 40px);
  width: 18px;
  height: 12px;
  background: #1F2937;
  clip-path: polygon(50% 0, 0 100%, 100% 100%);
}
/* .coach-cta carries the house pill styling; this only does layout, exactly as
   .company-coach-cta does for the sheet. */
.fab-coach-cta {
  display: inline-flex;
  width: 100%;
}

/* ⚠️ (Entry #124) THE SHEET'S OWN `.is-dismissed` CLASS CANNOT HIDE IT, and the code comment
   calling it "belt-and-braces" is wrong. `.company-basics-coach.is-dismissed` (:2360) is (0,2,0);
   the show rule `body.workspace-tour-active .company-basics-coach` (:2356) is (0,2,1) and BEATS it.
   Only `body.company-coach-dismissed .company-basics-coach` (:2359, also (0,2,1) but later in
   source) has ever hidden this sheet. #124 needs it hidden ONE STEP EARLIER than that class is
   set, so this rule does it: same (0,2,1) shape, and it is in the tail, so source order carries it.
   MEASURED before this existed: the sheet sat at display:block, top 510 of an 844 viewport, fully
   visible behind the FAB coach card. The #109 guard is what caught it. */
body.fab-coach-active .company-basics-coach { display: none; }

/* ============================================================================
   ENTRY #125 (2026-07-20, user call, annotated screenshot): the SWITCHER coach.

   #121 spotlights and pulses the workspace switcher but DELETED the card that said why, so
   phase B was a glowing target with no sentence. This card says it. Its CTA dismisses the card
   only, so the switcher stays the one live control and the pulse still means "tap me".

   It shares .fab-coach's visual base (grouped selectors above, one source) and adds the
   annotation's two asks: LESS HIGH and text CENTRED.
   ⚠️ Aimed at the ring's LARGEST pulsed state. wsRingPulse scales 1 -> 1.045 about the CENTRE,
   so the centre is invariant (the tail aims there) but the bottom is not: it is
   `top + h * (1 + 1.045) / 2`. placeSwitcherCoach() places the card below THAT, so the pulse can
   never reach under the card. RING_PULSE_MAX in rj0wj.html and the 1.045 in @keyframes
   wsRingPulse are ONE number.
   ============================================================================ */
.switcher-coach {
  /* less high: the copy is one short line, so it does not need the FAB card's 3-line padding */
  padding: var(--space-sm) 14px 14px;
  text-align: center;
}
.switcher-coach p {
  margin: 0 0 10px;
  text-align: center;
}

/* ============================================================================
   ENTRY #128 (2026-07-20, user call, annotated screenshot): "Continue demo" leaves the
   sticky foot and becomes the last thing in the scrolling column, on EVERY view.

   Why it moved:
   - `.company-drawer-foot` (:2641) is `position: sticky; bottom: 0` in its own grid row, so the
     button was always on screen. The user wants it DISCOVERED: "not as a sticky so the user
     discovers it as they vertically scroll to find it".
   - That foot is `display: none` at <=900px (:15034), so a PHONE had no Continue button at all.
     Only the drawer X advanced there. Moving the button into the body gives every view the same
     affordance, which is the "All views" half of the ask.

   ⚠️ The `.company-drawer-foot` rules at :2641 and its <=900px `display:none` at :15034 are now
   DEAD (no element carries the class any more). They are deliberately left in place rather than
   deleted: they are harmless, and removing rules from this stylesheet is how unrelated screens
   have broken before. If you re-introduce a drawer foot, know that the <=900px hide is still there
   waiting for it.

   ⚠️ The button keeps `data-drawer-tour-next`. Its click handler binds with querySelectorAll and
   its enable-on-open gating writes to the same selector, so this move needed NO JavaScript change.
   ============================================================================ */
.drawer-primary-btn-inflow {
  /* 16px matches .company-accordion's own padding-top, the established rhythm for starting a new
     block after the accordion. Without it the CTA sits flush against the last section row. */
  margin-top: var(--space-md);
}
/* The body reserved 120px at the bottom purely as clearance UNDER the sticky foot. With the foot
   gone that is a dead gap the viewer has to scroll past after the CTA, which works against
   "scroll to find it". 28px leaves the button breathing room as the genuine end of the column. */
.company-drawer-body {
  padding-bottom: 28px;
}

/* ==========================================================================
   ENTRY #130. x1riqt (phones/tablets only): the Applied column is NOT dimmed
   while the resumes parse.

   The full-viewport `.drag-focus-dim` was armed for every drag state INCLUDING
   `processing`, so once parsing began the viewer watched the progress card over
   a dimmed board for the whole ~8s parse. The dim's job is to focus the DRAG;
   once the drop has happened there is nothing left to focus, and the board
   underneath is exactly what the viewer should be reading.

   The sheet phase is already covered and needs no JS: at <=900px
   `.parsing-tour::before` carries `var(--scrim-dim)` (the #114 tail block,
   :16846) and `.tour-modal`'s own 240ms opacity transition fades it in
   alongside the 340ms `parsingTourSheetIn` slide, then back out when the sheet
   is dismissed. So the sequence the user asked for falls out of the two layers:
   parse = undimmed, sheet up = dimmed, sheet gone = undimmed.

   ⚠️ PHONE-ONLY ON PURPOSE. Above 900px `.parsing-tour::before` is
   `transparent` (the tour is a passive bottom-right corner card there, not a
   modal), so dropping this dim on desktop would leave the tour with NO scrim at
   all. Desktop is byte-identical.

   ⚠️ APPENDED AT THE END ON PURPOSE. The rule it corrects (:2953) is (0,4,0),
   identical to this one, so SOURCE ORDER is the only thing that makes this win.
   Writing it next to :2953 would silently lose to the :2956 block below it.
   ========================================================================== */
@media (max-width: 900px) {
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .drag-focus-dim {
    opacity: 0;
  }
}

/* ==========================================================================
   ENTRY #131. nifhr base step: "See it now" dismisses its sheet and hands the
   next move to the VIEWER, by pulsing Marta's card until they tap it.

   Before this, the CTA called setDrawer(true) itself, so the profile drawer
   just appeared and the viewer never performed the tap the scene exists to
   teach. The CTA now only adds `base-coach-dismissed` (which hides the sheet
   and un-blocks the card), and this cue points at what to do next.

   ⚠️ KEYED ENTIRELY OFF EXISTING STATE, no new class. The cue is on exactly
   while the sheet is gone AND the drawer is shut AND the move step has not
   started, so it self-clears on every exit: opening the drawer adds
   `profile-drawer-open`, and startSelectionStep() adds `candidate-move-step`.
   A dedicated class would need a matching removal on each of those paths, and
   missing one leaves a card pulsing underneath an open drawer.

   ⚠️ SPECIFICITY. Marta's card ALREADY carries a static green success ring from
   `.kanban.import-board .source-selected` (:8463), which is (0,5,1). This rule
   is (0,7,1) so it wins, and the animation overrides that box-shadow for as
   long as it runs, then hands it straight back. The green ring is why the card
   already looks faintly picked out; this is the difference between "this is the
   best one" and "tap this".

   ⚠️ NO TRANSFORM, DELIBERATELY. A scale pulse would move the card's EDGES every
   frame (a centre-scaled box's centre is invariant, its edges are not), which
   makes any rect assertion against it luck-dependent and risks nudging layout
   inside the column. Pulsing only the box-shadow spread leaves geometry frozen,
   so the guards can assert a real rect.
   ========================================================================== */
body.nifhr-screen.base-coach-dismissed:not(.candidate-move-step):not(.profile-drawer-open)
  [data-screen-id="nIfHr"] .candidate-card[data-open-profile] {
  border-color: var(--brand);
  animation: nifhrMartaTapCue 1800ms cubic-bezier(.4, 0, .2, 1) infinite;
}
@keyframes nifhrMartaTapCue {
  0% {
    box-shadow:
      0 0 0 0 rgba(124, 58, 237, 0.45),
      0 18px 42px rgba(17, 24, 39, 0.20);
  }
  70% {
    box-shadow:
      0 0 0 12px rgba(124, 58, 237, 0),
      0 18px 42px rgba(17, 24, 39, 0.20);
  }
  100% {
    box-shadow:
      0 0 0 0 rgba(124, 58, 237, 0),
      0 18px 42px rgba(17, 24, 39, 0.20);
  }
}
/* Reduced motion still needs the card called out, just without the ripple. */
@media (prefers-reduced-motion: reduce) {
  body.nifhr-screen.base-coach-dismissed:not(.candidate-move-step):not(.profile-drawer-open)
    [data-screen-id="nIfHr"] .candidate-card[data-open-profile] {
    animation: none;
    box-shadow:
      0 0 0 3px rgba(124, 58, 237, 0.45),
      0 18px 42px rgba(17, 24, 39, 0.20);
  }
}

/* ==========================================================================
   ENTRY #132. The curated-move drop zone reads "Drag candidates" on every view,
   and that label must NEVER wrap to a second row (user call).

   It genuinely did. Measured at 15px the string is 121.4px wide, and during the
   move step the two columns split the viewport, so the zone's CONTENT box is
   only 90px at 320 and 110px at 360. Both wrapped to 2 lines. ("Drag to Maybe"
   was shorter and squeaked through, which is why this only appeared now.)

   Two mechanisms, because either alone is insufficient:
   (a) `white-space: nowrap` makes a second row impossible at any width. On its
       own it would just overflow the zone instead, which is worse, so:
   (b) a fluid size that keeps the string INSIDE that box.

   The zone's content width is linear in the viewport across the whole phone
   range: measured 90 at 320, 110 at 360, 125 at 390, 145 at 430, 280 at 700,
   i.e. exactly `0.5 * vw - 70`. The string is 8.093px wide per px of font-size.
   Solving `8.093 * f <= 0.5W - 70` for f as a vw fraction, the binding case is
   the narrowest (320px), which allows up to 3.475vw. 3.2vw is that with ~8%
   headroom, and the 15px cap means anything at or above 469px gets exactly the
   size it had before, so tablet and desktop are unchanged.

   ⚠️ `line-height` stays 20px, so shrinking the type does NOT change the label's
   height and nothing below it moves.

   ⚠️ DO NOT "verify" this with a probe `<span>`: `.col-drag-placeholder span`
   (:4065) is styled `width: 32px`, so a span probe reports 32px at every font
   size and the check silently cannot fail. It burned me once here. Use an `<i>`
   with `width:auto; display:inline`.

   Appended at the end: this has the same selector chain as the rule it corrects
   (:9214), so source order is what makes it win.
   ========================================================================== */
body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .review-drop-target strong {
  white-space: nowrap;
  font-size: clamp(var(--fs-2xs), 3.2vw, var(--fs-lg));
  line-height: 20px;
}



/* ==========================================================================
   ENTRY #134. The two coach-hand TRAILS are teal, matching the chooser deck's
   swipe-right colour (`--success` #10B981, the same value `.jc-card-glow-go`
   :13717 and `.jc-chip-go` :13741 use). x1riqt's drop-resumes coach and nifhr's
   Applied-to-Maybe move coach.

   The trail's COLOUR lives in an SVG `<linearGradient>` (three `stop-color`s per
   screen, x1riqt.html:140 and nifhr.html:156), not in CSS, because it fades along
   its own length. Those stops are changed in the markup. What is left here is the
   drop-shadow under the stroke, which was still purple and would have haloed a
   teal line with a purple glow.

   ⚠️ The HAND itself stays `var(--brand)` purple. Only the trail was asked for.
   ========================================================================== */
[data-screen-id="x1RIQT"] .drag-coach-trail,
body.nifhr-screen .move-coach-trail {
  filter: drop-shadow(0 2px 10px rgba(16, 185, 129, 0.35));
}

/* ==========================================================================
   ENTRY #135. The move step's coach card is HIDDEN while the viewer still has to
   make the drag, on EVERY view, and comes back only once the drop has landed.

   The waiting half needs no card: the ghost hand traces the gesture, the pill
   says "Tap and drag" and the zone says "Drag candidates". A dark sheet
   restating it just buries the board, and on a phone it is a fixed bottom sheet
   whose height was already the constraint that forced the copy to stay under ~71
   characters (measured: 177 chars left only 75px of board visible).

   ⚠️ KEYED OFF EXISTING STATE, no new class. `.candidate-move-step` is on while
   the step runs and `.candidate-move-complete` lands the moment the drop
   succeeds, so this hides and un-hides itself with no JS and nothing to clear.
   The `:not()` is what makes the card reappear for the post-drop message.

   ⚠️ This does NOT break the coach hand's arc. `measure()` (nifhr.html:3975)
   already guards its bottom-sheet clamp with `if (cc.height && ...)`, and a
   `display:none` card has height 0, so the clamp simply drops out and the hand
   gets the whole zone. Verified, not assumed.

   Appended at the end: `.nifhr-coach` is repositioned per step by several rules
   of the same specificity, so source order is what makes this win.
   ========================================================================== */
/* ⚠️ ENTRY #162 NARROWED THIS (user call). #135 hid the card for the WHOLE waiting half. It is
   now shown FIRST as a gate, and hidden only once its CTA has been pressed, which is what
   `move-coach-dismissed` marks. #135's actual argument is untouched: during the DRAG the ghost
   hand, the "Click and drag" pill and the "Drag candidates" zone are still the only things
   speaking. The `:not(.move-coach-dismissed)` is the entire difference. */
body.nifhr-screen.candidate-move-step.move-coach-dismissed:not(.candidate-move-complete) [data-screen-id="nIfHr"] .nifhr-coach,
body.nifhr-screen.candidate-move-step.move-coach-dismissed:not(.candidate-move-complete) .nifhr-coach {
  display: none;
}


/* ==========================================================================
   ENTRY #136. The rank hint floats the WHOLE Marta ROW, not just its handle, and
   stops the moment the viewer arms any drag panel. Plus: the compare modal hides
   the dark coach card sitting behind it.

   (A) THE FLOAT MOVES TO THE ROW. Previously `cmpDragHintBob` was on
   `.compare-rank-handle`, so a button bobbed inside a motionless row and read as
   "this button is the draggable thing" rather than "this row is". The animation
   is now on the row itself on both layouts, and the handle keeps only its
   ring/border/colour as the grab marker.

   The "Drag to reorder" chip needs NO change to follow: it is a `::after` on the
   handle, which is a descendant of the row, so a transform on the row carries it
   along exactly. Nothing tracks anything by hand.

   ⚠️ WHY THIS CANNOT BREAK THE DRAG, and it is a gate not a hope. Both rules are
   scoped `:not(.comparison-rank-touched)`, and that class is added by a
   CAPTURE-phase `pointerdown` listener on the modal (nifhr.html:3085) while
   `startRankDrag` is bound in the BUBBLE phase on the handle (:3286). Capture
   always runs first, so the class lands, the animation is removed, and the row's
   rect is static BEFORE the drag machine reads a single coordinate. Were it the
   other way round, `getRankInsertIndex()` would measure rows mid-bob and drop
   into the wrong slot. The ordering is asserted by a guard, not assumed.

   ⚠️ `translateY` only, never `scale`. A scaling row changes its own edges every
   frame, and `getRankInsertIndex` compares pointer position against row rects; a
   4px vertical offset that is GONE before any drag starts is safe, a live scale
   would not be.

   (B) THE COACH BEHIND THE MODAL. #135's "Keep them moving" card is what opens
   this modal, and it stayed lit behind it. `comparison-modal-open` is already on
   `<body>` for the whole time the modal is up, so this needs no new state.
   ========================================================================== */
body.nifhr-screen.comparison-rank-active:not(.comparison-rank-touched) [data-screen-id="nIfHr"] .compare-marta,
body.nifhr-screen.comparison-rank-active:not(.comparison-rank-touched) [data-screen-id="nIfHr"] .compare-mob-card[data-compare-candidate="marta"] {
  animation: cmpDragHintFloat 1.6s ease-in-out infinite;
}

@keyframes cmpDragHintFloat {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-4px); }
}

/* (B) The dark coach card is hidden for as long as the compare modal is up, on every view. */
body.nifhr-screen.comparison-modal-open [data-screen-id="nIfHr"] .nifhr-coach,
body.nifhr-screen.comparison-modal-open .nifhr-coach {
  display: none;
}


/* ============================================================================
   ENTRY #145 (2026-07-21, user call, annotated screenshots): the THREE remaining
   workspace-tour states get the SAME dim + blur as the company drawer (#144) and the
   job coach (#118) — `var(--scrim-dim-coach)` + `blur(var(--blur-scrim))`.
   DESKTOP ONLY (>= 901px). Mobile and tablet are deliberately untouched (user's words:
   "mobile and tablet views are fine leave them").

   The tail block is the right home: a rule appended at the END of this file beats an
   equal-specificity rule inside a tier block, and BOTH base rules overridden below
   (`.company-coach-scrim` :2391 and `.workspace-coach-ring` :2833) are exactly that.
   ============================================================================ */

/* ---- STATES 1 + 2: the "NO WE DIDN'T HACK YOU" sheet AND the FAB coach ----------------
   ONE element covers both screens. The scrim deliberately STAYS up through the FAB coach
   step (rj0wj.html:1337: "The SCRIM deliberately STAYS ... It leaves with phase B"), so a
   single rule repaints them both. Was `color-mix(in srgb, var(--text-primary) 52%,
   transparent)` with no blur. */
@media (min-width: 901px) {
  .company-coach-scrim {
    background: var(--scrim-dim-coach);
    -webkit-backdrop-filter: blur(var(--blur-scrim));
    backdrop-filter: blur(var(--blur-scrim));
  }
}

/* ---- STATE 3: the switcher spotlight (phase B) ----------------------------------------
   ⚠️ THIS STATE HAS NO SCRIM TO RESTYLE. Its dim is `box-shadow: 0 0 0 9999px
   var(--scrim-dim)` on `.workspace-coach-ring`, i.e. THE RING IS THE DIM. A
   backdrop-filter samples the element's OWN box and a spreading shadow is not that box, so
   this dim CANNOT be blurred. It is the same wall #118 hit on `.coach-ring`, and it takes
   the same answer: a real element does the dimming.

   That real element is a PSEUDO on `.workspace-coach-overlay` (:2812), which is already
   `position: fixed; inset: 0; z-index: 70; pointer-events: none` with NO transform, so:
     - a fixed pseudo resolves against the VIEWPORT (no transformed ancestor to trap it,
       the trap that bit #109 and #112), and
     - it INHERITS `pointer-events: none`, so hit-testing is byte-identical to today.
       That is load-bearing: the box-shadow dim it replaces never hit-tested either, and
       #72's guards describe what is tappable mid-spotlight. Nothing newly blocks.
   Gated on `.company-coach-dismissed` so it exists ONLY in phase B — phase A is dimmed by
   `.company-coach-scrim` above and the two must never stack.
   The FAB (z 2147483030) and the switcher coach card (2147483031) sit far above z 70, so
   they stay crisp exactly as they do now. */
@media (min-width: 901px) {
  body.workspace-tour-active.company-coach-dismissed .workspace-coach-overlay::before {
    content: "";
    position: fixed;
    inset: 0;
    background: var(--scrim-dim-coach);
    -webkit-backdrop-filter: blur(var(--blur-scrim));
    backdrop-filter: blur(var(--blur-scrim));
    /* The hole. Same evenodd idiom as `.coach-scrim` (:17219): the outer loop is the
       viewport, the inner loop is the switcher, and evenodd subtracts it. Coords are the
       switcher's LIVE viewport rect (`--workspace-*`), which placeWorkspaceTour()
       re-measures on resize, on orientation change and after the mobile nav's 320ms slide.
       ⚠️ Those four vars are written to `.workspace-coach-overlay`, NOT to the ring:
       inheritance runs DOWN, so a pseudo of the overlay could never read them off its own
       child. The ring still gets them by inheritance (rj0wj.html:506-508).
       Fallbacks keep this a plain full-screen dim if placeWorkspaceTour() has not run yet,
       rather than a mispositioned hole. */
    --ws-hole-l: calc(var(--workspace-x, 0px) - var(--ws-hole-pad));
    --ws-hole-t: calc(var(--workspace-y, 0px) - var(--ws-hole-pad));
    --ws-hole-r: calc(var(--workspace-x, 0px) + var(--workspace-w, 0px) + var(--ws-hole-pad));
    --ws-hole-b: calc(var(--workspace-y, 0px) + var(--workspace-h, 0px) + var(--ws-hole-pad));
    clip-path: polygon(evenodd,
      0 0, 100% 0, 100% 100%, 0 100%, 0 0,
      var(--ws-hole-l) var(--ws-hole-t),
      var(--ws-hole-l) var(--ws-hole-b),
      var(--ws-hole-r) var(--ws-hole-b),
      var(--ws-hole-r) var(--ws-hole-t),
      var(--ws-hole-l) var(--ws-hole-t)
    );
  }
  /* ⚠️⚠️ (#357) THE RING DRAWS NOTHING ON DESKTOP NOW. It still LOSES the 9999px layer the pseudo
     owns (or the two double-dim), and it has also lost the purple halo and the lift that were the
     "harsh square highlight" she asked to remove. It remains in the DOM because it is what pulses:
     `wsRingPulse` scales this element, and with the hole below tracking the same
     `--workspace-*` box, what the viewer sees pulsing is the un-dimmed element itself.
     Superseded declarations:
         box-shadow: 0 0 0 4px rgba(124, 58, 237, 0.12), 0 12px 28px rgba(76, 29, 149, 0.18); */
  body.workspace-tour-active.company-coach-dismissed .workspace-coach-ring {
    box-shadow: none;
  }
}

/* ---- ENTRY #146 (2026-07-21, user call, annotated screenshot): the FAB is DIMMED through
   phase A, and only lights up when the FAB coach step introduces it. DESKTOP ONLY.
   User's words: "also dim this button here until the next screen state" (arrow on the launcher
   during the "NO WE DIDN'T HACK YOU" sheet, pointing at the FAB-coach screen where it stays lit).

   ⚠️ THIS PARTLY SUPERSEDES #124. That entry made the launcher "HIGHLIGHTED BUT INERT from the
   moment this step opens" (rj0wj.html:933). Inert is unchanged; highlighted-from-the-start is not
   what is wanted any more. It now reads as part of the dimmed page until its own step.

   Mechanism: NOT an opacity fade. The launcher is a <body> child at z 2147483030, far above
   `.company-coach-scrim` (z 1290), which is the only reason it stayed crisp while the whole page
   behind it went dark. Dropping it to 1289 for this one state puts it UNDER that scrim, so it
   receives the SAME dim and the SAME blur as everything else rather than a second, differently
   -tuned dim of its own. One dim, one tuning.
   The state is `.fab-coach-armed:not(.fab-coach-active)`: `fab-coach-armed` is added when the
   workspace step opens (rj0wj.html:932) and `fab-coach-active` is added by the FAB coach step
   (:1337), so this covers phase A and stops exactly at "the next screen state".
   No hit-testing change: `body.fab-coach-armed .demo-sections-launcher` is already
   `pointer-events: none` plus a real `disabled` attribute (#124), so the launcher was unclickable
   in this state before and after. */
/* ⚠️ 2026-07-27 (#194) SUPERSEDES the desktop z-index dim that used to live here, on the user's
   call: "ALL VIEWS: hide this until its turn on the coaching card". #146's rule buried the FAB
   under the scrim at `@media (min-width: 901px)` so it dimmed WITH the page, which left it plainly
   VISIBLE on mobile and tablet, where no scrim dim reaches it. Burying an element you have already
   made invisible is a no-op, so that rule is REPLACED rather than kept alongside this one.
   Same state, every view now: `fab-coach-armed` opens with the workspace step and `fab-coach-active`
   is added by the FAB coach step, so this covers phase A and stops exactly at the FAB's own turn.
   ⚠️ opacity + visibility + pointer-events, NOT `display: none`: that is the idiom every other FAB
   hide in this file uses (:14997 drawer-active, :15020 jc-sheet-active, :15035 job-card-chooser-
   active, :15049 profile-drawer-open, :15070 company-drawer-open), and matching it keeps one
   behaviour for "the FAB is away" instead of two. */
body.fab-coach-armed:not(.fab-coach-active) .demo-sections-launcher {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* ⚠️ 2026-07-27 (#194) THE DISABLED WINDOW, every view. After the FAB's coaching card is dismissed
   the launcher stays on screen but must NOT be tappable until the viewer is past this first primer
   segment and into the drawer scene, where it is reactivated. The JS half is that
   `setFabInert(false)` no longer fires on the coach-card dismiss (it now fires from setDrawer(true)
   and showJobTour()); this is the hit-testing half, because `body.fab-coach-armed`'s
   `pointer-events: none` at :17520 stops applying the moment that class is removed.
   ⚠️ The gate is `workspace-tour-active.fab-coach-done`, which is deliberately keyed on EXISTING
   state rather than a new class: BOTH exits from the primer (setDrawer(true) at rj0wj.html:1073 and
   showJobTour() at :1023) already remove `workspace-tour-active`, so this window cannot be left
   stale by a path that forgets to clean up after itself. Same law as #131's self-clearing cue. */
body.workspace-tour-active.fab-coach-done .demo-sections-launcher {
  pointer-events: none;
  cursor: default;
}

/* =====================================================================================
   ENTRY #147 - MESSENGER RETHINK: the desktop learns from the mobile inbox
   =====================================================================================
   The old black .messenger-guide-* overlay (an SVG-mask spotlight dim plus dark modal
   callouts) is gone from nifhr.html. Viewers already know what a messenger looks like,
   so the coaching now walks the FEATURES with light anchored cards that dim nothing and
   block nothing. Appended at the TAIL so these rules beat the equal-specificity ones
   inside the tier blocks above.
   ===================================================================================== */

/* --- The two coach-cue keyframes, HOISTED to base tier ------------------------------
   They used to live inside @media (max-width:900px), where the desktop could not reach
   them. @keyframes declared in a media query simply do not exist outside it. One
   definition now serves .mm-thread.is-coached (mobile) and .messenger-thread.is-coached
   (desktop). Deliberately NOT transform:scale: scaling moves the box, which makes the
   row un-tappable under Playwright's stability check and can jitter. */
@keyframes mmRowPulse {
  0%, 100% { background-color: transparent; box-shadow: 0 0 0 0 color-mix(in srgb, var(--brand) 0%, transparent); }
  50% { background-color: var(--bg-row-highlight); box-shadow: 0 6px 20px color-mix(in srgb, var(--brand) 20%, transparent); }
}
/* ENTRY #204: the size pulse and float for the coached inbox row. Same 1.7s period as mmRowPulse
   so the colour and the motion breathe together. Scale and translate live in ONE transform because
   two animations cannot both drive `transform`; the amplitude is bounded by the list's clip (see
   the rule). */
@keyframes mmRowLift {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50% { transform: translate3d(0, -3px, 0) scale(1.02); }
}
@keyframes mmCoachRing {
  0%, 100% { box-shadow: 0 0 0 2px color-mix(in srgb, var(--brand) 55%, transparent); }
  50% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand) 22%, transparent); }
}

/* --- Desktop conversation rows: the Marta cue and the lock -------------------------- */
body.nifhr-screen .messenger-thread {
  position: relative;
  cursor: pointer;
}
/* Only Marta opens in the demo. The others keep full colour but lose tap ability, the
   same call the mobile inbox made (.mm-thread.mm-locked). This is guard one of two; the
   other is that only Marta's row carries [data-messenger-open] for the delegated
   closest() listener to find, so a bypassed pointer-events still does nothing. */
body.nifhr-screen .messenger-thread.is-locked {
  pointer-events: none;
  cursor: default;
}
body.nifhr-screen .messenger-thread.is-coached {
  animation: mmRowPulse 1.7s var(--ease-standard, ease-in-out) infinite;
}
body.nifhr-screen .messenger-thread.is-coached::after {
  content: "";
  position: absolute;
  inset: var(--space-2xs);
  border-radius: var(--radius-md);
  box-shadow: 0 0 0 2px var(--brand);
  animation: mmCoachRing 1.7s var(--ease-standard, ease-in-out) infinite;
  pointer-events: none;
}

/* --- THE MESSENGER SEGMENT DIMS NOTHING AT ALL --------------------------------------
   ⚠️⚠️ THE USER SAW THIS AND I HAD MEASURED IT AS "NOT DIMMED", WHICH WAS WRONG. Their words:
   *"dont dim out the app shell youre still incorrectly dimming the left main menu and the top
   navbar area dont dim them"*. Removing this segment's own scrim was not enough, because
   `body.nifhr-screen::before` is a SEPARATE, UNCONDITIONAL full-viewport
   `rgba(17, 24, 39, 0.58)` at z-index 9000 that this whole screen carries all the time (the
   curated-walkthrough focus layer, hirello-app.css:5226). The messenger panel is lifted above it
   and therefore looked fine; the sidebar and the topbar sit UNDER it and are visibly dark. My
   sidebar reading of 120.8 was the value WITH that dim applied, and I compared it against the
   dimmed-twice value instead of against white, which is how a plainly dark nav read as "clean".

   The messenger step has no walkthrough spotlight for that layer to serve, so it is switched off
   for the step outright rather than fought element by element. Scoped to
   `screening-step-messenger`, so every other step keeps it exactly as it was.

   ⚠️ `content: none` and not `opacity: 0` or `display: none`: a pseudo with no content is not
   generated at all, so there is no box left to be revived by a later rule, and nothing to leave
   behind on teardown. */
body.nifhr-screen.screening-step-messenger::before {
  content: none;
}

/* --- The messenger WATCH ------------------------------------------------------------
   ⚠️⚠️ THE z-index LIFT THAT USED TO LIVE HERE IS DELETED, AND ITS ABSENCE IS THE POINT. It
   existed to raise `.messenger-content` above `body.nifhr-screen::before`'s 0.58 page dim while
   staying below `.wmode-layer` so the watch's own scrim could still cut a hole in it. Both halves
   of that reasoning are gone: the page dim is switched off for this step by the rule above, and
   this segment paints no scrim at all. A z-index kept "just in case" is a stacking context nobody
   can account for later, so it goes. */


/* --- The guided-tour CUE: purple outline plus a size pulse, and NO dim ----------------
   ⚠️⚠️ THIS REPLACED A SCRIM, ON THE USER'S EXPLICIT CALL: *"dont dim anything on this screen
   though use purple outline and pulsing size and our black tails coach cards methods rather than
   dimming throughout this segment"*. The dim, its `clip-path` hole, its placement loop and its
   element are all DELETED rather than switched off.

   ⚠️ IT IS THE PROJECT'S OWN COACHED-ROW TREATMENT, NOT A NEW LOOK. `mmCoachRing` is the same
   keyframe `.messenger-thread.is-coached` and the mobile inbox's `.mm-thread.is-coached` already
   wear, at the same 1.7s period. Reusing it is what stops the desktop tour reading as a third
   species of highlight.

   ⚠️⚠️ THE PULSE IS ON THE PSEUDO-ELEMENT AND THE CONTROL ITSELF NEVER MOVES. A `transform: scale`
   on the control would also satisfy "pulsing size", and this file already records why not, four
   rules above: scaling the desktop row "moves the box, which makes the row un-tappable under
   Playwright's stability check and can jitter". These beats ASK the viewer to click the cued
   control, so its box has to be still. The ring grows instead, and it is `pointer-events: none`
   so it can never intercept the click it is advertising.

   ⚠️ `inset: -6px` puts the outline OUTSIDE the control rather than inside it, so it cannot be
   mistaken for a focus ring on a form field and cannot cover the control's own border.

   ⚠️ Gated `min-width: 901px`, belt and braces with the JS: the mobile inbox has its own coach and
   a leaked body class must never paint desktop coaching over a phone. */
@media (min-width: 901px) {
  /* ⚠️⚠️ ENTRY #272c: `:where()` HERE IS LOAD-BEARING AND IT FIXES A REAL, VISIBLE BUG. This rule
     exists only so the `::after` ring has a positioned box to sit in, which a STATIC control needs
     and an already-positioned one does not. Written as `body.nifhr-screen [data-tour-cue]` it is
     (0,2,1), which TIES with `body.nifhr-screen .messenger-document-drawer`'s `position: fixed`
     (also (0,2,1)) and, being written LATER in the file, WON: cueing the document library dropped
     it out of fixed positioning into normal flow inside `<main>`, so the right-hand slide-out
     drawer rendered as a panel at x=240 over the messages list. The user's words: *"Why the fuck is
     this attachment library not the fucking slide out drawer from right"*.
     ⚠️ THE LOCATION DRAWER HAD THE IDENTICAL BUG and nobody had looked at that beat yet. The
     QUESTIONNAIRE drawer did NOT, purely because its own rule happens to be written after this one.
     Same tie, three different outcomes decided by source order: exactly the trap this project
     already records as "count the classes, ties go to the later rule".
     `:where()` contributes ZERO specificity, so this drops to (0,1,0): it still positions a static
     control, and it can no longer beat any element that positions itself. */
  :where(body.nifhr-screen) [data-tour-cue] {
    position: relative;
  }
  body.nifhr-screen [data-tour-cue]::after {
    content: "";
    position: absolute;
    inset: -6px;
    border-radius: 14px;
    box-shadow: 0 0 0 2px var(--brand);
    animation:
      mmCoachRing 1.7s var(--ease-standard, ease-in-out) infinite,
      msgCueBreathe 1.7s var(--ease-standard, ease-in-out) infinite;
    pointer-events: none;
    z-index: 3;
  }
}

/* The size half of the cue. Same 1.7s period as the ring so the outline and the growth breathe
   together, exactly as `mmRowPulse` and `mmRowLift` do on the phone. */
@keyframes msgCueBreathe {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.045); }
}

@media (prefers-reduced-motion: reduce) {
  body.nifhr-screen [data-tour-cue]::after { animation: none; }
}

/* --- ENTRY #272g: the questionnaire CARD breathes instead of wearing a ring -----------
   ⚠️ USER CALL ON AN ANNOTATED FRAME: *"Pulse size the entire screening questionnaire element and
   remove the purple border pulse effect"*, with the arrow on the sent card in the thread. For THIS
   cue target only, the `::after` outline is switched off and the growth moves onto the card itself.

   ⚠️ THE ATTRIBUTE IS STILL STAMPED, and that is deliberate. `msgCueFor` keeps naming
   `.message-quest-stack`, because that ONE selector feeds BOTH the ring and the coach card's
   anchor (`showCoach(step, cue.sel)` hands it straight to `placeFeatureCoach`), so nulling it to
   kill the outline would have taken the anchor with it and parked the card somewhere else. This is
   a restyle of the cue, not a removal of it, and `#271D` still asserts the card is cued.

   ⚠️⚠️ THE SCALE IS ON THE INNER `.message-quest`, NOT ON `.message-quest-stack`, AND THAT IS THE
   WHOLE REASON THE COACH CARD DOES NOT WOBBLE. `placeFeatureCoach` re-measures the CUED element's
   `getBoundingClientRect()` every frame (entry #272b made the placement live so the compose card
   could track the caret). An element's rect is its own layout box and is NOT widened by a
   transformed DESCENDANT, so scaling the child leaves the anchor perfectly still. A scale on the
   stack itself would swing that anchor by 15px at 1.045 and put #272b's own jitter defect back on
   the page from a new direction.

   ⚠️ The standing law that a cue pulse stays on the pseudo (the `mmRowPulse` block above) is about
   CONTROLS THE VIEWER IS ASKED TO CLICK: a moving box is un-tappable under Playwright's stability
   gate. This target is a SENT MESSAGE inside a watch nobody operates, so nothing here is ever
   clicked and that half of the law does not bite. The law itself stands for every other cue.

   ⚠️ BOTH HALVES OF THE EXCHANGE, not only the one in the screenshot: the beat cues
   `.message-quest-stack` and then `.message-quest-stack.incoming`, the identical treatment on
   Marta's answers card, so fixing only the sent one would have left the same purple outline for
   the very next frame to show.

   ⚠️ `msgCueBreathe` is REUSED rather than re-declared, at the same 1.7s period the ring ran, so
   this is the project's existing pulse moved onto a different box rather than a second species of
   it. Measured at 1920: the card is 344px wide with its right edge 24px off the thread's own
   padding, and 1.045 grows it 7.7px each way, so the thread's `overflow: hidden` never clips it.
   The stack is a fixed 344px at every width, so that clearance is width-independent. */
@media (min-width: 901px) {
  body.nifhr-screen .message-quest-stack[data-tour-cue]::after { content: none; }
  body.nifhr-screen .message-quest-stack[data-tour-cue] .message-quest {
    animation: msgCueBreathe 1.7s var(--ease-standard, ease-in-out) infinite;
  }
}
@media (prefers-reduced-motion: reduce) {
  body.nifhr-screen .message-quest-stack[data-tour-cue] .message-quest { animation: none; }
}

/* --- Welcome coach card ------------------------------------------------------------
   Anchored over the conversation-list column, beside the row that is about to pulse.
   No scrim: the welcome card runs before the walk starts, so the dim is not up yet. */
.messenger-desk-coach {
  position: fixed;
  z-index: 2147483012;
  /* left is written by placeDeskCoach(), measured off the conversation list's own box, so the
     card never covers the rows it is telling the viewer to click. */
  left: 96px;
  top: 50%;
  transform: translateY(-50%);
  width: 316px;
  max-width: calc(100vw - 32px);
  padding: 20px;
  border-radius: var(--radius-lg, 18px);
  border: 1px solid var(--border);
  /* ⚠️ THE DARK TAILED CARD, and the surface is `.nifhr-coach`'s value for value (the purple
     radial over `#0f1726`), not a new dark. That card is the walkthrough's coaching card on this
     same screen, so a second black would read as a second component. */
  background:
    radial-gradient(circle at 18% 14%, rgba(124, 58, 237, 0.28), rgba(124, 58, 237, 0) 34%),
    #0f1726;
  border-color: rgba(148, 163, 184, 0.14);
  color: #f8fafc;
  box-shadow:
    0 28px 72px rgba(15, 23, 42, 0.42),
    0 0 0 1px rgba(124, 58, 237, 0.16);
  /* ⚠️ THE SAME HARDENING AS `.messenger-feature-coach` BELOW, AND IT IS THE SAME MECHANISM RATHER
     THAN A SECOND ONE: identical `position: fixed`, identical z 2147483012, and it likewise declared
     no `pointer-events`, so it could swallow a click on anything it happened to be placed over.
     Fixing only the twin that HAPPENED to misbehave would leave the identical footgun beside it.
     Its CTA needs no separate line: `.messenger-desk-coach-cta` shares the `pointer-events: auto`
     rule with the feature card's CTA. Nothing clicks this CARD (only `[data-desk-coach-cta]`, via
     `deskStartWalk` and two other guards), so there is no consumer to rewrite. */
  pointer-events: none;
}
.messenger-desk-coach[hidden] { display: none; }
.messenger-desk-coach-title {
  margin: 0;
  color: #f8fafc;
  font-size: 19px;
  font-weight: var(--fw-extrabold);
  line-height: 1.24;
  text-wrap: balance;
}
.messenger-desk-coach-body {
  margin: 10px 0 0;
  color: rgba(248, 250, 252, 0.78);
  font-size: 13.5px;
  font-weight: var(--fw-medium);
  line-height: 1.45;
}
.messenger-desk-coach-cta,
.messenger-feature-coach-cta {
  /* ⚠️ RE-ENABLES the click that each card's own `pointer-events: none` turns off, for BOTH cards:
     this selector is shared, which is why neither needed its own line. `pointer-events` INHERITS,
     so a card's `none` reaches every descendant and the CTA is the one that must opt back in.
     LOAD-BEARING ON BOTH SIDES: `[data-feature-coach-next]` is the terminal beat's "See my next
     steps" (`featureCoachNext.hidden = !terminal`) that `#207c` clicks to open the Next-steps
     drawer, and `[data-desk-coach-cta]` is "Show me", which `deskStartWalk` clicks to begin the
     walk at all. Drop this line and BOTH arms die silently while the placement guards stay green. */
  pointer-events: auto;
  margin-top: var(--space-md);
  padding: 10px 20px;
  border: 0;
  border-radius: var(--radius-pill, 999px);
  background: var(--brand);
  color: var(--text-inverse);
  font-family: inherit;
  font-size: 13.5px;
  font-weight: var(--fw-bold);
  cursor: pointer;
}
.messenger-desk-coach-cta:hover,
.messenger-feature-coach-cta:hover { background: var(--brand-strong, #6D28D9); }

/* --- Feature coach card ------------------------------------------------------------
   left/top are written by placeFeatureCoach(), which measures the target plus every open
   menu and drawer and picks a placement that intersects none of them. Never a guessed
   offset, which is what would let the coaching cover the control it points at. */
.messenger-feature-coach {
  position: fixed;
  z-index: 2147483012;
  left: 0;
  top: 0;
  width: 320px;
  max-width: calc(100vw - 24px);
  padding: var(--space-md) 18px 18px;
  border-radius: var(--radius-lg, 18px);
  border: 1px solid rgba(148, 163, 184, 0.14);
  background:
    radial-gradient(circle at 18% 14%, rgba(124, 58, 237, 0.28), rgba(124, 58, 237, 0) 34%),
    #0f1726;
  /* ⚠️ SET ON THE CARD, not only on its h3 and p. The children each carry their own colour today,
     so this changes nothing visible right now, and that is exactly why it belongs here: the next
     element added to this card would otherwise inherit `--text-primary` and render near-black on a
     near-black surface. */
  color: #f8fafc;
  box-shadow:
    0 24px 60px rgba(15, 23, 42, 0.42),
    0 0 0 1px rgba(124, 58, 237, 0.16);
  /* ⚠️⚠️ THE CARD MUST NEVER BE ABLE TO TAKE A CLICK. It is `position: fixed` at z 2147483012, two
     above every drawer (2147483010), and it is PLACED BY MEASUREMENT every frame. #275 proved what
     that combination costs: the placer read the attachment menu mid-transition, left a 6px lip over
     the questionnaire drawer, and those 6px SWALLOWED the click on the row underneath. Four guards
     hung at 30s and a real viewer at 1440 could not tick that row.
     #275 fixed the PLACEMENT. This removes the CAPABILITY, so the next mis-placement is cosmetic
     rather than blocking. A coaching card is something you READ; it is not a control.
     ⚠️ THE CTA MUST GET `auto` BACK AND THAT IS LOAD-BEARING, NOT DEFENSIVE:
     `featureCoachNext.hidden = !terminal` (nifhr.html), so the button exists on the terminal beat
     reading "See my next steps", and `#207c` clicks it to open the Next-steps drawer. Drop that
     line and the walk's last step becomes unreachable while every other guard stays green. */
  pointer-events: none;
}
/* ⚠️⚠️ ENTRY #272t, SAME USER CALL: *"and then the tool tip JANKILY QUICKLY MOVING above the
   modal"*. `placeFeatureCoach` writes `left`/`top` every frame against the union of the control and
   whatever that control just OPENED, so the frame the menu unhides, the union grows by 284x220 and
   the card TELEPORTS to a new slot. Nothing was wrong with the placement; what was missing was any
   time for the eye to follow it.
   ⚠️ A TRANSITION, NOT A JS TWEEN, because the placer already re-writes these two properties on
   every frame of the watch and a tween would be fighting it. A transition that is re-issued the
   same value does not restart, so the caret-tracking beat (#272b's "60fps smooth", which moves a
   few tenths of a pixel per frame) is unaffected, while a 284px jump gets eased over 220ms. */
/* ⚠️⚠️ ENTRY #272v: GATED ON THE ATTRIBUTE, and the ungated version was a real regression that two
   guards caught. `placeFeatureCoach` re-places this card EVERY FRAME against a caret that advances
   a few tenths of a pixel (#272b's "60fps smooth") and against a card that BREATHES between scale 1
   and 1.045 (#272J asserts the coach does not wobble with it). An unconditional transition turns
   both of those into a lag: measured 79 of 304 frames below a quarter of the mean caret speed, i.e.
   stalled. The gate is the STEP id, not the size of the move: `placeFeatureCoach` removes the
   attribute for `compose` and sets it for everything else.
   ⚠️ TWO EARLIER COMMENTS HERE WERE STALE AND WERE DELETED AT #277, not because they were untidy
   but because both described mechanisms that are not in the file. One said the attribute is stamped
   "only when the move is big enough to need it", which was a 40px size threshold superseded by the
   step gate. The other, headed "THIRD CUT", described the move as a FLIP on `transform` with the
   placer inverting the delta; that build was made, measured WORSE (worst single frame 240px -> 560px
   at 1920, because the hitch was never layout cost) and reverted, and nothing in this codebase has
   ever written a `transform` on this element. A comment that describes an abandoned design as if it
   were the shipped one is worse than no comment: the next reader builds on it. */
/* ⚠️⚠️ ENTRY #277: THE TWO AXES CARRY DIFFERENT CURVES, AND THAT IS THE WHOLE MECHANISM BEHIND THE
   USER'S *"happy to use curves ... at the moment it follows purely up down left right"*. Two
   properties easing over the SAME duration with the SAME curve get the same time-remap, and no
   time-remap can bend a straight line: the card was always going to travel the chord between two
   points. Giving `left` and `top` different curves makes them hold different progress fractions at
   the same instant, and the trajectory bows. `left` front-loads and resolves early; `top` keeps
   easing through the whole span, so the card reads as gliding sideways and then settling down onto
   its mark rather than sliding along a diagonal.
   ⚠️ `--coach-move` IS ITS OWN TOKEN AND MUST NOT BE `--motion-ui`. 550ms is a deliberate motion
   choice for this one card (user call: hold each pose 1.2s, travel about half a second, "as there
   is a lot to process"), while `--motion-ui` is the app-wide 220ms that the menus and drawers
   themselves animate on AND that `FEATURE_COACH_SETTLE_MS` reads to stay AHEAD of. Point this at
   `--motion-ui` and the settle pass would be racing the card it is meant to correct. */
.messenger-feature-coach[data-coach-ease] {
  transition:
    left var(--coach-move) cubic-bezier(0.33, 1, 0.2, 1),
    top var(--coach-move) cubic-bezier(0.65, 0, 0.35, 1);
}
/* ⚠️⚠️ ENTRY #338f: THE ARRIVAL, FOR THE TWO ACT-FOUR CARDS ONLY. Her frames asked for these to
   "fade and animate in from the right a little" instead of tracking and transforming out of the
   previous card's position, which is what `[data-coach-ease]` above does.
   ⚠️⚠️ `backwards`, NEVER `both`, AND THIS IS LOAD BEARING. `both` retains the final keyframe for
   ever, which would pin `opacity: 1` on this node and SILENTLY KILL the 200ms leave fade that
   `[data-coach-leaving]` owns twenty lines below. `backwards` applies the from-state before the
   animation starts and retains NOTHING afterwards, which is safe here only because the base rule
   declares no `opacity` of its own: the natural resting state already IS the to-state. Checked
   against the base rule rather than assumed.
   ⚠️ A `transform` IS SAFE ON THIS NODE even though placement is not. `placeFeatureCoach` writes
   `left`/`top` only, and the comment above records that a transform-based placement was tried,
   measured worse and reverted, so nothing else writes `transform` here for this to fight.
   ⚠️ THE OFFSET IS 14px BECAUSE `mmChatIn` IN THIS SAME FILE ALREADY USES 14px for exactly this
   motion. Never invent a spacing number when the project has already answered.
   ⚠️ THIS RUNS ON THE WALL CLOCK, NOT THE FILM'S TIMELINE, like every CSS animation. A guard that
   seeks straight onto the beat can therefore catch the card mid-slide; the two beats it applies to
   both have well over a second before anything depends on the card being settled, and the guards
   sample well clear of 240ms. Do not shorten either window without re-reading that. */
.messenger-feature-coach[data-coach-entrance] {
  animation: msgCoachEnter var(--coach-enter) var(--ease-out, cubic-bezier(.16, 1, .3, 1)) backwards;
}
@keyframes msgCoachEnter {
  from { transform: translateX(14px); opacity: 0; }
  to { transform: none; opacity: 1; }
}
/* ⚠️ ENTRY #278: the one scene that dims the shell raises this card over the scrim. Stamped by
   `placeHole()` for `[PHONE_IN, PHONE_T0 + fullAt)` and removed after it, so every other beat draws
   the card exactly where it always did. One above `.wmode-layer`'s messenger-only 2147483020, and
   still below the Demo-sections launcher at 2147483030. */
.messenger-feature-coach.wmode-lit { z-index: 2147483021; }
.messenger-feature-coach[hidden] { display: none; }
/* ⚠️⚠️ USER CALL ON TWO ANNOTATED FRAMES, 2026-08-12: *"fade out the coach card at that point of the
   click"* and *"200ms fade out before drawer is closed"*. THE DEPARTURE WAS A HARD CUT at every one
   of this card's boundaries: `hideCoach()` writes `hidden = true` and the rule above is
   `display: none`, which no transition can ease.
   ⚠️ AN OPACITY-ONLY LEAVE, NEVER `left`/`top`. The card's only other transition is the #277 two-curve
   move, and a departure that also moved would read as the card fleeing sideways, which is the exact
   complaint on the third frame of this batch (the questionnaire card sliding left as the phone faded).
   ⚠️ `pointer-events: none` while leaving, because the card is `pointer-events: none` in the film
   anyway and a fading element must never take a click it would not have taken a frame earlier.
   ⚠️ THE ATTRIBUTE IS REMOVED BY THE SAME TIMER THAT SETS `hidden`, in nifhr.html, and cancelled by
   any show that arrives inside the window: a leave left stamped on a re-shown card would fade the
   next pose in from nothing. */
.messenger-feature-coach[data-coach-leaving] {
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--coach-leave) var(--ease-standard);
}
/* ⚠️⚠️ ENTRY #338g: THE ONE CARD THAT LEAVES TO THE RIGHT. Her frame, on the new real-job card:
   *"fades out as it animates going right and has disappeared before the list of filtered candidates
   highlights"*. It arrives FROM the right and departs TO the right, which reads as the same object
   passing through rather than two unrelated moves.
   ⚠️ THE TRANSITION LIST IS RESTATED, NOT EXTENDED. The rule above sets `transition: opacity ...`
   as a whole shorthand, so adding `transform` here means writing both again; a second declaration
   naming only `transform` would REPLACE the opacity one and the card would slide without fading.
   ⚠️ 14px MATCHES THE ARRIVAL, so the distance out is the distance in. Same reason the arrival's own
   offset is not a new number: `mmChatIn` already answered it for this codebase.
   ⚠️ THIS ONLY WORKS BECAUSE THE ARRIVAL USES `backwards` FILL AND SO RETAINS NOTHING once it has
   finished. Had it been `both`, the finished animation would still be pinning `transform: none` and
   `opacity: 1`, and neither half of this would move. */
.messenger-feature-coach[data-coach-leaving][data-coach-exit="right"] {
  transform: translateX(14px);
  transition:
    opacity var(--coach-leave) var(--ease-standard),
    transform var(--coach-leave) var(--ease-standard);
}

/* ⚠️ ENTRY #321, USER CALL ON AN ANNOTATED FRAME: *"Once the bottomsheet starts animation to appear
   fade the coaching card out completely by time bottomsheet fully visible"*. The card sits at
   z 2147483012, ABOVE the #319 end dim (2147483009), so it stayed fully lit beside the rising END
   sheet. `finish()` stamps `body.wmode-end-open` in the same tick it unhides the sheet, so this
   fade and the sheet's 420ms entrance start on the same style recalc; 360ms with the standard ease
   lands the card at 0 before the sheet finishes.
   ⚠️ AN ANIMATION, NOT A TRANSITION, ON PURPOSE: the card's transition list is pinned by a guard
   (left/top, two curves, one duration) and the emailproof pose has `data-coach-ease` armed at END,
   whose transition block would override any opacity leg added to the base rule. `forwards` holds
   the faded frame for as long as the stamp stays; Watch again removes the stamp and the card
   returns instantly with the replay, which is the film restarting, not a visible pop. */
body.wmode-end-open .messenger-feature-coach {
  animation: wmodeCoachEndFade 360ms var(--ease-standard) forwards;
}
@keyframes wmodeCoachEndFade {
  from { opacity: 1; }
  to { opacity: 0; }
}

/* ⚠️ THE TAIL, aimed by MEASUREMENT rather than by a coordinate table, the same way
   `.nifhr-coach`'s move-step tail is. `placeFeatureCoach()` already scores six candidate
   placements and keeps the least-covering one; it now also records WHICH one won, as
   `data-tail`, and writes the tail's offset along the card's own edge as `--msg-tail-x/y`. The
   colour is `#0f1726` flat: the card's purple radial is anchored at its top-left, so a tail
   sampling the gradient would band against the corner it touches.
   Four directions, because all six placements collapse to "the anchor is above / below / left of
   / right of the card". A placement with no clear side (the clamped last-resort one) gets no
   attribute and therefore no tail, which is correct: a tail pointing nowhere is worse than none. */
.messenger-feature-coach::before,
.messenger-desk-coach::before {
  content: none;
}
.messenger-feature-coach[data-tail]::before {
  content: "";
  display: block;
  position: absolute;
  background: #0f1726;
  pointer-events: none;
}
.messenger-feature-coach[data-tail="down"]::before {
  bottom: -13px;
  left: var(--msg-tail-x, 40px);
  width: 18px;
  height: 15px;
  clip-path: polygon(0 0, 100% 0, 50% 100%);
}
.messenger-feature-coach[data-tail="up"]::before {
  top: -13px;
  left: var(--msg-tail-x, 40px);
  width: 18px;
  height: 15px;
  clip-path: polygon(0 100%, 50% 0, 100% 100%);
}
.messenger-feature-coach[data-tail="left"]::before {
  left: -13px;
  top: var(--msg-tail-y, 40px);
  width: 15px;
  height: 18px;
  clip-path: polygon(100% 0, 0 50%, 100% 100%);
}
.messenger-feature-coach[data-tail="right"]::before {
  right: -13px;
  top: var(--msg-tail-y, 40px);
  width: 15px;
  height: 18px;
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}
/* The welcome card is parked beside the conversation list by `placeDeskCoach()`, always to its
   right, so its tail only ever points left. */
.messenger-desk-coach[data-tail="left"]::before {
  content: "";
  display: block;
  position: absolute;
  left: -13px;
  top: var(--msg-tail-y, 46px);
  width: 15px;
  height: 18px;
  background: #0f1726;
  clip-path: polygon(100% 0, 0 50%, 100% 100%);
  pointer-events: none;
}
.messenger-feature-coach-kicker {
  margin: 0;
  /* Lifted off `--brand` for the dark surface: #7C3AED on #0f1726 is under 3:1. */
  color: #C4B5FD;
  font-size: var(--fs-xs);
  font-weight: var(--fw-extrabold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
/* ⚠️⚠️ ENTRY #338h: A SINGLE WORD LIFTED INSIDE A KICKER. Her frame: *"use our super bold token for
   the word NOT"*. `--sheet-title-weight` IS that token, 900, and this file already records that
   there is nothing heavier to reach for.
   ⚠️ THE EXPLICIT WEIGHT IS LOAD BEARING, NOT DECORATION. `<strong>` defaults to 700, which is
   LIGHTER than the kicker's own 800, so without this the emphasised word would render fainter than
   the words around it: the exact opposite of what she asked for. Never rely on the element's default
   weight when the surrounding rule is already heavier than it. */
.messenger-feature-coach-kicker strong { font-weight: var(--sheet-title-weight); }
/* Its sibling: `<em>` inside a kicker, added at #338g for "now THIS is a real job". Kept beside this
   one so the two emphasis paths are read together rather than found separately. */
.messenger-feature-coach-kicker em { font-style: italic; }
.messenger-feature-coach h3 {
  margin: 7px 0 0;
  color: #f8fafc;
  font-size: var(--fs-xl);
  font-weight: var(--fw-extrabold);
  line-height: 1.28;
}
.messenger-feature-coach p:not(.messenger-feature-coach-kicker) {
  margin: 7px 0 0;
  color: rgba(248, 250, 252, 0.78);
  font-size: 13px;
  font-weight: var(--fw-medium);
  line-height: 1.45;
}
.messenger-feature-coach-cta { margin-top: 14px; }

/* --- Incoming email stack ----------------------------------------------------------
   .message-email-stack is margin-left:auto (outgoing). Marta's inbound email needs the
   mirror, or her own message renders right-aligned as if we had sent it. */
body.nifhr-screen .message-email-stack.incoming {
  margin-left: 0;
  margin-right: auto;
}

/* --- Questionnaire drawer + the questionnaire message cards ------------------------- */
/* ⚠️⚠️ ENTRY #272m: THIS DRAWER NOW HAS A FOOTER, so it is a three-track grid like the location
   drawer next door, not a flex column. Three children, three tracks: head, body, foot. The body is
   `.location-library-body`, which already carries #272l's `align-content: start`, so handing it the
   free space cannot stretch its three rows to fill it.
   ⚠️ AND IT NOW LEAVES THE SCREEN THE SAME WAY THE OTHER TWO DO. The old block hid it with
   `visibility: hidden` keyed on `[aria-hidden="true"]`, which `setQuestionnaireDrawer(false)`
   stamps in the SAME synchronous tick that it drops the body class. `visibility` is not
   interpolable, so it landed on the very first frame of the 260ms slide: measured per rAF under
   real playback at 1920, the transform travelled 0 -> 469px with the drawer painting ZERO frames.
   That is the user's *"they just instantly close"*, exactly, and #272g's "drawer clear = +0.26" was
   measured off that transform, which nobody could see. Visibility now rides a 0s transition DELAYED
   to the end of the slide, so the drawer is painted for the whole of it and hidden only once it is
   off screen.
   ⚠️ THE `[aria-hidden]` RULE BELOW KEEPS ONLY `pointer-events`, and that is load-bearing, not
   tidying: it ties with the open-state rule on specificity (three classes each) and is written
   LATER, so anything it declares beats the open state. A `visibility: visible` in the open rule
   would silently lose to it. */
body.nifhr-screen .messenger-questionnaire-drawer {
  position: fixed;
  inset: 0 0 0 auto;
  z-index: 2147483010;
  width: 33vw;
  min-width: 460px;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  background: var(--surface);
  border-left: 1px solid var(--border);
  box-shadow: -18px 0 48px rgba(17, 24, 39, 0.16);
  transform: translateX(104%);
  visibility: hidden;
  transition:
    transform var(--motion-drawer) var(--ease-in, cubic-bezier(.4, 0, 1, 1)),
    visibility 0s linear var(--motion-drawer);
}
body.nifhr-screen.messenger-questionnaire-open .messenger-questionnaire-drawer {
  transform: translateX(0);
  visibility: visible;
  transition:
    transform var(--motion-drawer) var(--ease-out),
    visibility 0s linear 0s;
}
body.nifhr-screen .messenger-questionnaire-drawer[aria-hidden="true"] {
  pointer-events: none;
}
/* #318: THE ONE SHE POINTED AT (the answered-questionnaire card whose last row is the green
   "Meets requirements" tick). One of the five thread gaps, see the block above `.message-bubble`. */
body.nifhr-screen .message-quest-stack {
  width: 344px;
  max-width: 344px;
  margin-left: auto;
  margin-bottom: var(--space-md);
}
body.nifhr-screen .message-quest-stack.incoming {
  margin-left: 0;
  margin-right: auto;
}
body.nifhr-screen .message-quest {
  padding: var(--space-sm) 14px;
  border: 1px solid color-mix(in srgb, var(--brand) 20%, var(--border));
  border-radius: 14px;
  background: var(--surface);
  box-shadow: 0 8px 18px rgba(17, 24, 39, 0.06);
}
body.nifhr-screen .message-quest-head {
  display: flex;
  align-items: center;
  gap: 7px;
  color: var(--brand);
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
}
body.nifhr-screen .message-quest-head svg {
  width: 15px;
  height: 15px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
body.nifhr-screen .message-quest ul {
  margin: 9px 0 0;
  padding-left: 17px;
  color: var(--text-secondary);
  font-size: 12.5px;
  font-weight: var(--fw-medium);
  line-height: var(--lh-normal);
}
body.nifhr-screen .message-quest ul.message-quest-ans { color: var(--text-primary); font-weight: var(--fw-semibold); }
body.nifhr-screen .message-quest-pass {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 10px;
  padding: var(--space-2xs) 10px;
  border-radius: var(--radius-pill, 999px);
  background: color-mix(in srgb, var(--success, #10B981) 14%, transparent);
  color: var(--success, #10B981);
  font-size: var(--fs-xs);
  font-weight: var(--fw-extrabold);
}
body.nifhr-screen .message-quest-pass svg {
  width: 12px;
  height: 12px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* #318: one of the five thread gaps, see the block above `.message-bubble`. */
body.nifhr-screen .message-typing-row {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  margin-bottom: var(--space-md);
  color: var(--text-tertiary);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
}
body.nifhr-screen .message-typing {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
}
body.nifhr-screen .message-typing i {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--text-tertiary);
  animation: messengerTypingDot 1.1s ease-in-out infinite;
}
body.nifhr-screen .message-typing i:nth-child(2) { animation-delay: 0.16s; }
body.nifhr-screen .message-typing i:nth-child(3) { animation-delay: 0.32s; }
@keyframes messengerTypingDot {
  0%, 60%, 100% { opacity: 0.35; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-2px); }
}
body.nifhr-screen .message-document-bubble.is-static { cursor: default; }
body.nifhr-screen .message-document-copy small {
  display: block;
  margin-top: 2px;
  color: var(--text-tertiary);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
}

/* The desktop coaching is desktop-only: the mobile inbox has its own. */
@media (max-width: 900px) {
  .messenger-desk-coach,
  .messenger-feature-coach { display: none !important; }
  body.nifhr-screen .messenger-questionnaire-drawer { width: calc(100vw - 24px); min-width: 0; }
}

@media (prefers-reduced-motion: reduce) {
  body.nifhr-screen .messenger-thread.is-coached,
  body.nifhr-screen .messenger-thread.is-coached::after,
  body.nifhr-screen .message-typing i { animation: none; }
  body.nifhr-screen .messenger-questionnaire-drawer { transition: none; }
}

/* --- Retire the demo-live nav dim (entry #147) --------------------------------------
   messenger-demo-live used to arrive only at the END of the old spotlight walk, so
   greying the sidebar and topbar was a brief flourish. It now arrives the moment the
   viewer clicks Marta and stays up for the whole feature walk, which would leave the
   whole nav grey behind coaching that is supposed to dim nothing. Same selector as the
   rule at :7565, later in source, so it wins. Kept pointer-events:none: the demo still
   does not want the nav clicked mid-walk, it just should not LOOK disabled. */
body.nifhr-screen.messenger-demo-live .sidebar,
body.nifhr-screen.messenger-demo-live .topbar {
  opacity: 1;
  filter: none;
  pointer-events: none;
}

/* --- Mobile feature-walk pill: the Next button (entry #147) -------------------------
   The pill stays pointer-events:none so it can never eat a tap meant for the control it
   is describing. Only this button opts back in. */
.mm-coach-next {
  margin-left: auto;
  flex: 0 0 auto;
  padding: 6px 14px;
  border: 0;
  border-radius: var(--radius-pill, 999px);
  background: var(--surface);
  color: var(--brand);
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-extrabold);
  pointer-events: auto;
  cursor: pointer;
}
.mm-coach-next[hidden] { display: none; }

/* =====================================================================================
   ENTRY #148 - the company drawer's advance becomes a real STICKY BAR
   ENTRY #149 - and that behaviour now runs on EVERY viewport, phone and tablet included
   =====================================================================================
   #148 shipped this at >=901px only, leaving phones and tablets on entry #128's in-flow
   button. #149 (user call) removes that split: the mobile and tablet views mirror the
   desktop display and logic rules exactly, so the CTA is no longer shown as soon as the
   viewer reaches this stage.

   The CTA is wrapped in `.drawer-sticky-bar`. THE BAR is the sticky surface, the CTA pill
   sits inside it. An earlier pass made the BUTTON itself sticky, which floated it like a
   FAB instead of pinning a bar across the bottom; the fix is the wrapper, not dropping
   the bar.

   Three behaviours, in the order the viewer meets them:
     1. The bar is NOT RENDERED until the viewer has opened Company basics.
     2. From then on it is pinned across the bottom of the drawer, full-bleed, with a top
        hairline, and the column scrolls underneath it.
     3. Its drop shadow appears only once the content has been scrolled, reusing the same
        `.company-drawer-body.is-scrolled` signal that drives the sticky-stack's shadow.

   The scroller's horizontal padding is 24px at EVERY width (:2099, no media override), so
   the bar's `-24px` side margins go full-bleed on a 360px phone exactly as they do at 1920.
   ===================================================================================== */

/* Only reached while the drawer is CLOSED, where the bar has nothing to pin to: no layout
   box, so the markup adds nothing between the accordion and the CTA. */
.drawer-sticky-bar {
  display: contents;
}

/* 1. Hidden until Company basics has been opened. `display:none` and not just opacity, so
      it takes no space and cannot be tabbed to while the gate is still up. */
body.company-drawer-open .company-drawer-body > .drawer-sticky-bar {
  display: none;
}

/* 2. The bar. Full-bleed via negative side margins that cancel the scroller's 24px padding,
      and `bottom: 0` pins it to the bottom of the scrollport.
      ⚠️ The scroller's own bottom padding (:2099, retuned to 28px at :17563) MUST be
      cancelled here, or at maximum scroll the bar's natural position sits that far up and it
      UNSTICKS off the bottom edge on the last scroll step. The bar supplies the
      end-of-column spacing instead.
      `env(safe-area-inset-bottom)` in the padding is what keeps the pill clear of an iPhone
      home indicator now that this runs on phones. */
body.company-drawer-open.company-basics-has-opened .company-drawer-body {
  padding-bottom: 0;
}
body.company-drawer-open.company-basics-has-opened .company-drawer-body > .drawer-sticky-bar {
  display: block;
  position: sticky;
  bottom: 0;
  z-index: 6;
  margin: 20px -24px 0;
  padding: 14px 24px calc(14px + env(safe-area-inset-bottom));
  background: var(--surface);
  border-top: 1px solid var(--border);
  transition: box-shadow 180ms ease;
}

/* 3. The shadow, ONLY once the content has actually been scrolled. Cast UPWARDS (negative y)
      because the bar sits at the bottom: the content it overlaps is above it. Reuses the
      existing `.company-drawer-body.is-scrolled` signal (rj0wj.html updateDrawerScrollDepth,
      scrollTop > 2) that already drives the top sticky-stack's shadow. */
body.company-drawer-open.company-basics-has-opened .company-drawer-body.is-scrolled > .drawer-sticky-bar {
  box-shadow: 0 -14px 26px -12px rgba(15, 23, 42, 0.45);
}

/* The CTA keeps its pill inside the bar: no sticky, no shadow of its own to fight the bar's. */
body.company-drawer-open.company-basics-has-opened .drawer-sticky-bar > [data-drawer-tour-next] {
  position: static;
  box-shadow: none;
}

@media (min-width: 901px) {
  /* The X goes live in lockstep with the bar appearing. The base rule at :2089 keeps
     `.company-drawer-close-inert` pointer-events:none, so this is what releases it, and the
     JS gate (drawerCloseLive()) is the second, independent guard.
     ⚠️ DESKTOP ONLY, deliberately: at <=900px the X has been live from the moment the drawer
     opens since #85 (:15092), which is the phone's own advance. #149 harmonises the CTA, not
     that. */
  body.company-basics-has-opened .company-drawer-close.company-drawer-close-inert {
    pointer-events: auto;
    cursor: pointer;
  }
}

@media (prefers-reduced-motion: reduce) {
  body.company-drawer-open.company-basics-has-opened .company-drawer-body > .drawer-sticky-bar {
    transition: none;
  }
}


/* ============================================================================
   ENTRY #153 (2026-07-21, user call, annotated screenshot) - x1riqt, DESKTOP ONLY (>=901px)
   ============================================================================
   "I want this coaching card to be moved to this new position and its position synced to the
   floating animation of the card pile element and don't dim the card pile so put it at highest
   z layer on desktop view only" + "Add subtext to desktop only view".

   Phones and tablets keep entry #111's bottom sheet, byte-for-byte: every rule here is inside
   `min-width: 901px`, and the subtext carries a BASE hide with a desktop-only show.

   ⚠️ WHY THE PILE COULD NOT SIMPLY BE GIVEN A HIGHER z-index. `.manual-source` (the pile's box)
   lives inside `[data-screen-id="x1RIQT"] .import-demo`, which is `position: relative;
   isolation: isolate; z-index: 15`. That is a stacking context, so NO z-index on a descendant can
   lift it past `.modal-scrim`'s z-index 20 at the root. The context itself has to go: at >=901px,
   in the `selected` state ONLY (the pre-drag state, which is exactly when the welcome card is up),
   `.import-demo` drops `isolation` and its `z-index`, and the PILE alone is raised above the dim.
   The drop zone (`.import-drop`, z-index 2) and the drag dim (`.drag-focus-dim`, z-index 1) join
   the root context below the scrim's 20, so they stay dimmed exactly as before. The moment the
   drag starts the state changes and #61's original stacking returns untouched.
   ============================================================================ */

/* ENTRY #173 (user call): the subtext now runs on EVERY view. #153 shipped it desktop-only, so
   the phone and tablet bottom sheet went straight from the headline to the CTA. The declarations
   move from the >=901px block to BASE tier and the desktop block keeps nothing of its own, so
   there is one definition rather than a base hide fighting a desktop show.
   ⚠️ `margin: 0` is load-bearing: `.modal-body` is a flex column with `gap: 16px`, which supplies
   the space above the CTA, but the UA's default <p> margin would stack on top of that gap.
   ⚠️ It stays in `.modal-body`, NOT `.modal-header`: #113's guard asserts `.modal-header p` is 0
   at every viewport and that assertion is still correct. */
.welcome-subtext {
  display: block;
  margin: 0;
  color: #CBD5E1;
  font-size: var(--fs-md);
  line-height: 21px;
  font-weight: var(--fw-medium);
}

@media (min-width: 901px) {
  /* 1. The card is placed against the pile's live rect by the #153 IIFE in x1riqt.html.
        ⚠️ `.modal-scrim` is `position: fixed; inset: 0`, so an absolutely positioned child
        resolves against its padding box = the viewport. The scrim keeps `place-items: center`
        (nothing else in it needs moving) and this card is simply taken out of that grid flow.
        The left/top FALLBACKS reproduce the old centred pose, so if the script never runs the
        card is still on screen and usable rather than pinned to the corner. */
  .modal-scrim > .welcome-modal {
    position: absolute;
    left: 50%;
    top: 50%;
    margin: 0;
  }
  .modal-scrim > .welcome-modal:not([data-wm-pose]) {
    transform: translate(-50%, -50%);
  }

  /* 2. The float, in lockstep with the pile. Same keyframes, same duration, same delay, same
        timing function as `[data-demo-state="selected"] .drag-stack` (:3377). Identical timing is
        necessary but NOT sufficient: the two animations start at different moments, so the IIFE
        copies the pile's `startTime` onto this one. Keep the two shorthands identical. */
  .modal-scrim > .welcome-modal[data-wm-pose] {
    animation: x1riqt-stack-invite 2200ms linear 140ms infinite both;
  }

  /* 2b. THE TAIL (follow-up ask). Same clip-path triangle idiom as `.tooltip::before` and
        `.fab-coach::before`, rotated to point at whatever the card is anchored to: LEFT at the
        pile in the `beside` pose, DOWN at it in the `above` pose. Its position along the edge is
        `--wm-tail-y` / `--wm-tail-x`, written live by the #153 IIFE from the pile's own centre,
        because the card's centre and the pile's stop agreeing the moment the viewport clamp
        engages (measured: 22px apart at 1440x900, 69px at 1280x800).
        ⚠️ `.welcome-modal` is `overflow: hidden` at base tier (:5477), which would CLIP a tail
        drawn outside the card's box. Desktop releases it. The mobile grab handle still relies on
        that clip and is untouched, because this rule never applies below 901px. */
  .modal-scrim > .welcome-modal[data-wm-pose] {
    overflow: visible;
  }
  .modal-scrim > .welcome-modal[data-wm-pose]::before {
    content: "";
    position: absolute;
    background: #111827; /* the card's own surface (:5481); a token would be better if one existed */
    pointer-events: none;
  }
  .modal-scrim > .welcome-modal[data-wm-pose="beside"]::before {
    /* points LEFT, at the pile beside it. -11px of a 12px triangle overlaps the card's 1px border
       by a pixel, so no hairline seam shows between the two. */
    left: -11px;
    top: var(--wm-tail-y, 50%);
    width: 12px;
    height: 18px;
    transform: translateY(-50%);
    clip-path: polygon(0 50%, 100% 0, 100% 100%);
  }
  .modal-scrim > .welcome-modal[data-wm-pose="above"]::before {
    /* points DOWN, at the pile below it. */
    bottom: -11px;
    left: var(--wm-tail-x, 40px);
    width: 18px;
    height: 12px;
    transform: translateX(-50%);
    clip-path: polygon(50% 100%, 0 0, 100% 0);
  }

  /* 3b. ⚠️ (Entry #155) THE GHOST-HAND COACH MUST STAY THE TOP LAYER. Raising the pile to 21 had a
        consequence #153 did not see: `.drag-coach` is `position: fixed; z-index: 12` (:14445) and
        was ABOVE the pile only because `.import-demo`'s `isolation: isolate` kept them both in a
        LOCAL stacking context, where the pile was 3. Dropping that isolation moved both into the
        root context, where 21 beats 12 and the hand drew behind the card it is pointing at.
        The coach is a SIBLING of `.manual-source` inside `.import-demo`, so one rule fixes it.
        ⚠️ #153's own guards could never have caught this: they run while the welcome card is up,
        and the coach's `eligible()` gate refuses to start until that card is dismissed, so the two
        states were never on screen together. The #155 guard drives the CTA first. */
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .drag-coach {
    z-index: 22; /* one above the raised pile */
  }

  /* 3. The pile is NOT dimmed. See the stacking-context note above: the context has to be given
        up for the raise to reach the root, and only in the state where the card is up. */
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] {
    isolation: auto;
    z-index: auto;
  }
  [data-screen-id="x1RIQT"] .import-demo[data-demo-state="selected"] .manual-source {
    z-index: 21; /* one above .modal-scrim's 20 */
  }
}

@media (min-width: 901px) and (prefers-reduced-motion: reduce) {
  /* The pile's own float is already `animation: none !important` here (:3697), so the card must
     not keep bobbing against a static pile. */
  .modal-scrim > .welcome-modal[data-wm-pose] { animation: none; }
}

/* ==========================================================================
   ENTRY #156 (2026-07-21, user call, annotated screenshot) - DESKTOP ONLY (>=901px).
   "DESKTOP VIEW ONLY: Move from here to here and float animation the coaching
   panel too in this new position" + "add a shadow locked to the floating
   animation too".

   The parsing-tour card ("N Candidates imported. ONE Hirello to manage them.")
   sat in the bottom-right corner of the viewport (:4142 `right: 11.8vw;
   bottom: 13.5vh`, measured at 2000x1080: left 1294, bottom 934) which is over
   the empty Maybe/Shortlist half of the board. It now sits at the BOTTOM OF THE
   APPLIED COLUMN, over the cards it is talking about.

   ⚠️ THE POSITION IS MEASURED LIVE, not written in CSS. `[data-new-col]`'s left
   edge and width depend on the sidebar, `.content` padding and the 1fr/.82fr/.82fr
   board grid, so no static vw/px pair tracks it (measured Applied width: 565 at
   2000, 379 at 1440, 325 at 1280). The #156 IIFE in x1riqt.html writes
   `left`/`top`/`--ts-w` and stamps `data-ts-pose`; these rules only neutralise the
   base corner anchor and carry the motion.

   ⚠️ ONE ANIMATION CARRIES BOTH THE FLOAT AND THE SHADOW. That is what "locked to
   the floating animation" means mechanically: two animations are two clocks and
   drift (#153 measured 17.2ms apart on the SAME keyframes), whereas a single
   `@keyframes` interpolating `transform` AND `box-shadow` together cannot. The
   shadow's offset/blur/alpha are a linear function of the lift, so the card reads
   as rising off the column rather than sliding on it. The transform table is the
   pile's `x1riqt-stack-invite` curve verbatim, so the two motions feel the same.

   ⚠️ APPENDED AT THE END ON PURPOSE. The base `.tour-step` (:4142) is (0,1,0) and
   also carries the resting `box-shadow`; these rules are higher specificity AND
   later, which is what makes the animation's shadow win.
   ========================================================================== */
@media (min-width: 901px) {
  /* ⚠️ THE CARD WAS INVISIBLE UNTIL THIS EXISTED. `.tour-modal` is z-index 18 (:4107) while the
     processing spotlight raises `.kanban-col.new-col` to 25, so the moment the card moved OVER the
     Applied column the column painted on top of it and the probe still reported a perfectly
     correct rect. Measured with `elementFromPoint` at the card's own centre: the hit was
     `kanban-col new-col`. 26 is one above the column and still far below the Demo-sections FAB.
     Safe for hit-testing: `.parsing-tour.is-visible` is `pointer-events: none` on desktop and only
     `.tour-step` takes `auto` (:4139), so the raised layer swallows nothing.
     ⚠️ THE RULE ITSELF MOVED TO THE ENTRY #156b BLOCK AT THE TAIL OF THIS FILE and now applies at
     every width: the tablet band had the same burial, with the sheet 199px under the column at
     834x1112. Do not re-add it here. */
  .parsing-tour .tour-step[data-ts-pose] {
    right: auto;
    bottom: auto;
    width: var(--ts-w, min(470px, calc(100vw - 48px)));
    animation: x1riqt-tour-float 2200ms linear infinite both;
  }
}

@media (min-width: 901px) and (prefers-reduced-motion: reduce) {
  .parsing-tour .tour-step[data-ts-pose] { animation: none; }
}

@keyframes x1riqt-tour-float {
  0%, 100% {
    transform: translate3d(0, 0px, 0) rotate(0deg) scale(1);
    box-shadow:
      0 28.0px 72.0px rgba(15, 23, 42, 0.420),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  5% {
    transform: translate3d(0, -0.8px, 0) rotate(-0.03deg) scale(1.0008);
    box-shadow:
      0 28.8px 73.1px rgba(15, 23, 42, 0.425),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  10% {
    transform: translate3d(0, -2.5px, 0) rotate(-0.09deg) scale(1.0022);
    box-shadow:
      0 30.5px 75.4px rgba(15, 23, 42, 0.437),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  15% {
    transform: translate3d(0, -5px, 0) rotate(-0.17deg) scale(1.0041);
    box-shadow:
      0 32.9px 78.9px rgba(15, 23, 42, 0.454),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  20% {
    transform: translate3d(0, -8px, 0) rotate(-0.27deg) scale(1.0064);
    box-shadow:
      0 35.8px 83.0px rgba(15, 23, 42, 0.475),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  25% {
    transform: translate3d(0, -11.3px, 0) rotate(-0.37deg) scale(1.0087);
    box-shadow:
      0 39.1px 87.5px rgba(15, 23, 42, 0.498),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  30% {
    transform: translate3d(0, -14.5px, 0) rotate(-0.45deg) scale(1.0108);
    box-shadow:
      0 42.2px 91.9px rgba(15, 23, 42, 0.520),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  35% {
    transform: translate3d(0, -17.1px, 0) rotate(-0.5deg) scale(1.0124);
    box-shadow:
      0 44.8px 95.5px rgba(15, 23, 42, 0.537),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  40% {
    transform: translate3d(0, -19px, 0) rotate(-0.5deg) scale(1.0134);
    box-shadow:
      0 46.6px 98.1px rgba(15, 23, 42, 0.550),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  45% {
    transform: translate3d(0, -20.1px, 0) rotate(-0.45deg) scale(1.0138);
    box-shadow:
      0 47.7px 99.6px rgba(15, 23, 42, 0.558),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  50% {
    transform: translate3d(0, -20.4px, 0) rotate(-0.34deg) scale(1.0136);
    box-shadow:
      0 48.0px 100.0px rgba(15, 23, 42, 0.560),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  55% {
    transform: translate3d(0, -19.6px, 0) rotate(-0.19deg) scale(1.0126);
    box-shadow:
      0 47.2px 98.9px rgba(15, 23, 42, 0.555),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  60% {
    transform: translate3d(0, -17.7px, 0) rotate(-0.02deg) scale(1.0111);
    box-shadow:
      0 45.4px 96.3px rgba(15, 23, 42, 0.541),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  65% {
    transform: translate3d(0, -14.9px, 0) rotate(0.14deg) scale(1.0091);
    box-shadow:
      0 42.6px 92.5px rgba(15, 23, 42, 0.522),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  70% {
    transform: translate3d(0, -11.5px, 0) rotate(0.25deg) scale(1.0069);
    box-shadow:
      0 39.3px 87.8px rgba(15, 23, 42, 0.499),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  75% {
    transform: translate3d(0, -8px, 0) rotate(0.3deg) scale(1.0048);
    box-shadow:
      0 35.8px 83.0px rgba(15, 23, 42, 0.475),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  80% {
    transform: translate3d(0, -4.9px, 0) rotate(0.27deg) scale(1.003);
    box-shadow:
      0 32.8px 78.7px rgba(15, 23, 42, 0.454),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  85% {
    transform: translate3d(0, -2.6px, 0) rotate(0.19deg) scale(1.0017);
    box-shadow:
      0 30.5px 75.6px rgba(15, 23, 42, 0.438),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  90% {
    transform: translate3d(0, -1.1px, 0) rotate(0.1deg) scale(1.0007);
    box-shadow:
      0 29.1px 73.5px rgba(15, 23, 42, 0.428),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
  95% {
    transform: translate3d(0, -0.3px, 0) rotate(0.03deg) scale(1.0002);
    box-shadow:
      0 28.3px 72.4px rgba(15, 23, 42, 0.422),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }
}


/* ==========================================================================
   ENTRY #156b (2026-07-21, user call, annotated screenshot). THE REST OF THE PILE.

   "we said there was 500 imported but showing 6 here and we need approximations of the other 494
    purely representative in the remaining column space and nothing goes past the bottom of the
    current height bottom anyway ... for a visual continuity mental model for the viewer."

   Six resumes are genuinely parsed on screen; the demo's own copy says 500 were imported. The
   Applied column now continues past the six with inert scenery rows so the two agree visually.
   ALL VIEWS: phone, tablet and desktop, because the mismatch is on all three. The bottom sheet
   still triggers on the sixth REAL candidate; nothing here touches that.

   ⚠️ "NOTHING GOES PAST THE BOTTOM" IS ALREADY STRUCTURAL, and that is why the JS can append a
   fixed 20-row pool without measuring anything. `.import-board .kanban-col` is
   `height: 100%; max-height: 100%; overflow: hidden` (:3717) and `.candidate-stream` is
   `overflow: hidden` (:3733), so the column's own box cannot grow. What this block adds is the
   bottom FADE, so the row the clip cuts through reads as "and more below" instead of as a card
   with its head chopped off.

   ⚠️ THE FADE HEIGHT IS IN THE OVERLAY ONLY, never `padding-bottom`: padding on a clipped flex
   column does not reserve space at the bottom of an overflowing child list, it just moves the
   clip, so the last row would still be cut hard.
   ========================================================================== */

/* ⚠️ PRE-EXISTING BUG THE FILLER PILE EXPOSED, fixed here: at 701..900px (tablet) the parsing-tour
   BOTTOM SHEET was buried under the Applied column. The processing spotlight puts the column at
   `z-index: 25` (:3749) while `.tour-modal` is 18 (:4107). Measured at 834x1112: the column
   (top 269, bottom 1069) and the sheet (top 870) overlap by 199px, and `elementFromPoint` in the
   middle of that overlap returned `candidate-stream`, not the sheet. It was already true with an
   empty column, where the overlap was just blank lilac; once the column is full of rows it reads
   as the sheet being torn in half. Entry #156 raised the tour to 26 for `>=901px` only; the same
   raise is correct at EVERY width. On a phone the column's z is `auto` (measured at 390x844), so
   this is a no-op there rather than a phone behaviour change. */
.tour-modal.parsing-tour {
  z-index: 26;
}
.candidate-ghost {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  /* Scenery: it can never be tabbed to, hovered into a state, or hit-tested by a coach guard. */
  pointer-events: none;
}
/* Sits a step behind the six real cards without becoming grey mush: the real rows keep the full
   white surface and shadow, these lose the shadow and take the column's own tint. */
body.x1riqt-screen [data-screen-id="x1RIQT"] .candidate-ghost {
  border-color: color-mix(in srgb, var(--border) 70%, transparent);
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  box-shadow: none;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .candidate-ghost .candidate-avatar {
  opacity: .72;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .candidate-ghost .match-score {
  background: color-mix(in srgb, var(--bg-subtle) 82%, transparent);
  color: var(--text-tertiary);
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .candidate-ghost .candidate-name-lines strong {
  color: var(--text-secondary);
}
/* The whole pile arrives as one fade, so it reads as the column settling rather than as six more
   individual imports competing with the six that just landed. */
body.x1riqt-screen [data-screen-id="x1RIQT"] .new-col .candidate-ghost {
  opacity: 0;
  transition: opacity 420ms ease;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .new-col.has-filler-pile .candidate-ghost {
  opacity: 1;
}
/* The fade at the clip line.
   ⚠️ IT CANNOT BE A `mask-image` ON `.candidate-stream`. The stream OVERFLOWS the column, so its
   own box is roughly 1200px tall while only the top ~370px is visible; a `calc(100% - 96px)` mask
   stop would land far below the clip and fade nothing that anyone can see. The fade has to be
   positioned against the box that actually does the clipping, which is the COLUMN.
   ⚠️ AND IT CANNOT BE A MASK ON THE COLUMN EITHER: that would dissolve the column's own lilac
   surface and its bottom edge along with the cards. So it is an overlay pseudo painted IN the
   column's colour, which fades the rows into the surface and leaves the surface itself intact. */
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-board .new-col.has-filler-pile {
  position: relative;
}
body.x1riqt-screen [data-screen-id="x1RIQT"] .import-board .new-col.has-filler-pile::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 104px;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    color-mix(in srgb, var(--col-applied-tint-bottom), transparent 100%),
    var(--col-applied-tint-bottom) 78%,
    var(--col-applied-tint-bottom)
  );
}

@media (prefers-reduced-motion: reduce) {
  body.x1riqt-screen [data-screen-id="x1RIQT"] .new-col .candidate-ghost { transition: none; }
}

/* ==========================================================================
   ENTRY #157 (2026-07-21, user call, annotated 3-screenshot strip). ONE SIDEBAR MENU.

   "Apply this left hand main menu in the app shell to all the other desktop views as they deviate
    from this one in screenshot 2 and screenshot 3 and then check all other desktop views after
    this scenes" ... "desktop views only ... mobile and tablet views appear fine so dont touch
    them".

   Screenshot 1 (x1riqt) is the REFERENCE: Dashboard, Messenger, Jobs, Collaborators, Favourite
   lists. Screenshots 2 and 3 are both nifhr, and nifhr is the ONLY screen that deviates: it ships
   SIX items in a different order (Dashboard, Jobs, Candidates, Messenger, Collaborators, Favourite
   lists). Audited all seven app-shell screens: rj0wj, ghnij, wzscb, rgikg and p9phv4 already carry
   the reference five in the reference order, so nothing there needed changing.
   ⚠️ ENTRY #375b: THE AUDIT ABOVE COVERED SEVEN SCREENS AND THERE ARE NOW THREE. `ghnij`, `wzscb`,
   `rgikg` and `p9phv4` are archived, so the surviving app-shell set is `rj0wj`, `x1riqt` and
   `nifhr`, and nifhr is still the only deviation. The finding is unchanged; the sample it was
   drawn from is less than half the size, which matters if anyone re-derives the "reference five"
   from what is on disk today.

   ⚠️ DELIBERATELY CSS-ONLY AND GATED AT >=901px, NOT A MARKUP EDIT. The sidebar is ONE DOM subtree
   that also feeds the <=900px off-canvas drawer, so deleting and reordering the markup would
   change the phone and tablet drawer too. The user's instruction was explicit and repeated, so the
   phone/tablet drawer keeps all six items in their existing order and is byte-identical.
   That is the trade this makes: below 901px the drawer still lists Candidates.

   ⚠️ REORDERING IS SAFE ONLY BECAUSE `.nav-list` IS `display: flex; flex-direction: column`
   (:227). `order` on a block container is a silent no-op.

   ⚠️ `data-nav-id` IS LOAD-BEARING and stays. nifhr's own script toggles the active item between
   `jobs` and `messenger` for the messenger scene (nifhr.html:2447) and :5779 restyles the pair off
   the same attribute. Hiding the `candidates` item does not touch either path, and the active-item
   swap is a behaviour of the scene, not a deviation in the menu's composition.
   ========================================================================== */
@media (min-width: 901px) {
  body.nifhr-screen .nav-item[data-nav-id="candidates"] {
    display: none;
  }
  body.nifhr-screen .nav-item[data-nav-id="dashboard"] { order: 1; }
  body.nifhr-screen .nav-item[data-nav-id="messenger"] { order: 2; }
  body.nifhr-screen .nav-item[data-nav-id="jobs"] { order: 3; }
  body.nifhr-screen .nav-item[data-nav-id="collaborators"] { order: 4; }
  body.nifhr-screen .nav-item[data-nav-id="favourites"] { order: 5; }
}

/* ==========================================================================
   ENTRY #158 (2026-07-21, user call, annotated screenshot). TWO FIXES ON NIFHR'S
   CANDIDATE PROFILE DRAWER, ALL VIEWS.

   (A) "Here's that annoying light vertical line appearing again but here in this drawer now to
       remove."

   Identical mechanism to entry #143's seam on rj0wj's COMPANY drawer, on a different drawer, and
   the reason it survived that pass is that #143 fixed one element by name rather than the class of
   bug. `.nifhr-profile-drawer` carries `border-left: 1px solid var(--border)` (#E5E7EB, :8538 and
   :8574) and carries a `transform`, so it is the containing block for its absolutely positioned
   descendants. `.nhsq0-msg-sheet` is `position: absolute; inset: 0` inside it (:17046) and its
   `.nhsq0-msg-scrim` child is `inset: 0` again (:17055), and an absolutely positioned child
   resolves `inset` against its containing block's PADDING box. So the 62% dim stopped 1px short
   and left that border strip at full brightness with dark on both sides.

   MEASURED, at the vertical centre of the drawer, one pixel column each:
     1440x900  drawer.left 964.81, sheet.left 965.81 -> luminance 103.7 | 230.9 | 111.8
     1920x1080 drawer.left 1286.41, sheet.left 1287.41 -> 103.7 | 230.9 | 111.8
     390x844   drawer.left 31.2, sheet.left 32.2 -> 106.7 | 230.9 | 111.8
   One bright column, every view, which is why the fix carries no media query.

   ⚠️ THE -1px GOES ON THE SCRIM, NOT ON `.nhsq0-msg-sheet`. The sheet is the grid container that
   positions the card (`place-items: end center`, card at `width: 100%`), so extending the SHEET
   would drag the card 1px left and make it 1px wider. Extending only the dim leaves the card's
   geometry untouched, and the scrim's containing block is the sheet, whose left edge is already
   the drawer's padding edge, so -1px lands exactly on the border strip.

   ⚠️ DO NOT "FIX" THIS BY CLEARING `border-left-color`. #143 measured that as a NO-OP: a
   background paints under the border box, so the drawer's own white background fills the same 1px.
   Either dim the strip (this) or remove the border's WIDTH.

   (B) "ALL VIEWS: Left align title and subtext."

   `.nhsq0-msg-card` is `text-align: center` (:17069) and the h2/p inherit it. The card keeps that
   declaration so the CTA's own label stays centred inside its full-width pill; only the two text
   nodes the annotation points at are re-aligned.
   ========================================================================== */
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-scrim {
  inset: 0 0 0 -1px;
}
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-card h2,
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-card p {
  text-align: left;
}

/* ==========================================================================
   ENTRY #159 (2026-07-21, user call, annotated screenshot) - DESKTOP ONLY (>=901px).
   THE BASE COACH AND MARTA'S CARD FLOAT TOGETHER, AND EITHER ONE OPENS THE DRAWER.

   "Add the floating animation and sync the Marta card with the coaching card but if they click
    the Marta card then trigger the drawer open and instantly hide this coaching card. At the
    moment they can click the coaching card See it now which hides the coaching card and leaves
    the Marta card highlighted which is the wrong action. The correct action is to let the user
    click either the Marta card or see it now CTA which both hide this coaching card and opens
    the drawer."

   ⚠️ THIS SUPERSEDES ENTRY #131 ON DESKTOP ONLY. #131 deliberately made "See it now" hand the
   gesture to the viewer: dismiss the sheet, pulse Marta, let them tap her. That is UNCHANGED at
   <=900px, where the pulse is the phone's only signpost and Marta is blocked until the CTA. Above
   901px the two routes are now equivalent and both open the drawer.

   ⚠️ NEITHER ELEMENT FLOATED BEFORE THIS. The coach's only animation anywhere was a one-shot
   340ms `nifhrCoachSheetIn` entrance scoped to <=900px; above 901px it had no `animation` at all.
   Marta's only animation was #131's `nifhrMartaTapCue`, which by construction runs only AFTER the
   coach is dismissed. So there was nothing to "join" and both get the float here.

   ⚠️ AMPLITUDE IS 7px, NOT THE HOUSE 20.4px, AND THERE IS NO SCALE. Marta's card is IN the
   column's flex flow with a 10px gap to Ahmed's card above it; the x1riqt pile's -20.4px curve
   would have her overlapping her neighbour, and a scale would make the row visibly breathe wider
   than the cards it sits between. Same easing table, scaled by 7/20.4.

   ⚠️ SAME KEYFRAMES IS NOT SAME PHASE. Each animation starts when its own element first matches
   its rule, and the two elements match at different moments (measured 17.2ms apart on exactly this
   mistake at entry #153). The IIFE in nifhr.html copies the coach's `startTime` onto Marta's,
   looked up BY NAME, which is what actually locks them together.

   ⚠️ KEYED OFF EXISTING STATE, so it self-clears with no JS to remember (#131's law): the float is
   on only while the coach is up, the drawer is shut and the move step has not started. The move
   step therefore never sees an animated drag target, which is what keeps the drag machine's
   measurements honest.
   ========================================================================== */
@media (min-width: 901px) {
  body.nifhr-screen:not(.base-coach-dismissed):not(.candidate-move-step):not(.profile-drawer-open)
    [data-screen-id="nIfHr"] .nifhr-coach,
  body.nifhr-screen:not(.base-coach-dismissed):not(.candidate-move-step):not(.profile-drawer-open)
    [data-screen-id="nIfHr"] .candidate-card[data-open-profile] {
    animation: nifhrBaseFloat 2200ms linear infinite both;
  }
}

@media (min-width: 901px) and (prefers-reduced-motion: reduce) {
  body.nifhr-screen:not(.base-coach-dismissed):not(.candidate-move-step):not(.profile-drawer-open)
    [data-screen-id="nIfHr"] .nifhr-coach,
  body.nifhr-screen:not(.base-coach-dismissed):not(.candidate-move-step):not(.profile-drawer-open)
    [data-screen-id="nIfHr"] .candidate-card[data-open-profile] {
    animation: none;
  }
}
@keyframes nifhrBaseFloat {
  0%, 100% { transform: translate3d(0, 0.00px, 0); }
  5% { transform: translate3d(0, -0.27px, 0); }
  10% { transform: translate3d(0, -0.86px, 0); }
  15% { transform: translate3d(0, -1.72px, 0); }
  20% { transform: translate3d(0, -2.75px, 0); }
  25% { transform: translate3d(0, -3.88px, 0); }
  30% { transform: translate3d(0, -4.98px, 0); }
  35% { transform: translate3d(0, -5.87px, 0); }
  40% { transform: translate3d(0, -6.52px, 0); }
  45% { transform: translate3d(0, -6.90px, 0); }
  50% { transform: translate3d(0, -7.00px, 0); }
  55% { transform: translate3d(0, -6.73px, 0); }
  60% { transform: translate3d(0, -6.07px, 0); }
  65% { transform: translate3d(0, -5.11px, 0); }
  70% { transform: translate3d(0, -3.95px, 0); }
  75% { transform: translate3d(0, -2.75px, 0); }
  80% { transform: translate3d(0, -1.68px, 0); }
  85% { transform: translate3d(0, -0.89px, 0); }
  90% { transform: translate3d(0, -0.38px, 0); }
  95% { transform: translate3d(0, -0.10px, 0); }
}


/* ============================================================================
   ENTRY #160 (2026-07-22) — the emphasised word in the nifhr base-step coach
   headline ("Useful? *Absolutely*."), on BOTH VIEWS.

   Size comes from --coach-emph-size, an `em` token, so it tracks whichever
   headline size the current view already sets (28px desktop / 20px phone).
   `em` in a custom property is resolved AT THE POINT OF USE, against the
   parent's font-size, which is exactly the h2 — so there is nothing to keep
   in sync when either headline size moves.

   ⚠️ THERE IS DELIBERATELY NO `font-style: italic` HERE. It was written first
   and a control proved it DEAD: disabling it left the word still italic,
   because the UA sheet italicises `<em>` and this project ships no reset that
   touches it (measured, both views). The italic is the reason the element is an
   `<em>` and not a `<span>` — change the tag and you silently lose it.
   ============================================================================ */
.nifhr-coach h2 .coach-emph {
  font-size: var(--coach-emph-size);
  text-decoration: underline;
  text-decoration-thickness: 0.055em;
  text-underline-offset: 0.14em;
}

/* ============================================================================
   ENTRY #161 (2026-07-22) — the floating Marta card is no longer CLIPPED at its
   top edge. DESKTOP ONLY (>=901px), which is where #159's float lives.

   The clipper was never the column: it is
   `body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board
    .candidate-stream { overflow: hidden }` (hirello-app.css:8310), the card's
   DIRECT parent. Measured identically at 1440/1920/1280/1024/901: Marta's card
   is flush with that box on ALL FOUR EDGES at rest (left/right/top all equal to
   the stream's), so its 3px green ring and its 18px/42px drop shadow are being
   cut on every side even before the float, and #159's 7px lift then pushes the
   card's top exactly 7px past the clip line.

   ⚠️ UNCLIPPING IS SAFE HERE BECAUSE THE STREAM DOES NOT OVERFLOW, AND THAT WAS
   MEASURED, NOT ASSUMED: `scrollHeight === clientHeight` (359/359) and
   `scrollWidth === clientWidth` at all five widths, so nothing spills out of the
   bottom when the clip goes. There is also precedent in this very stylesheet:
   the curated-move step already flips the same element to `overflow: visible`
   (:9030, :9036, :9320). And the stream paints nothing of its own (no
   background, border, shadow or ::before/::after), so a taller clip box cannot
   reveal a stray surface.

   ⚠️ DO NOT "FIX" THIS WITH `overflow-x: hidden; overflow-y: visible`. That is a
   silent no-op trap: when either axis is not `visible`, the other computes to
   `auto`, so the top would still be clipped while the rule LOOKS correct.

   The state chain is #159's own, verbatim, so the unclip lives exactly as long
   as the float does: dismiss the coach, open the drawer or enter the move step
   and the normal clip returns with no JS. Specificity is (0,8,1) against
   :8310's (0,5,0), and it is later in source.
   ============================================================================ */
@media (min-width: 901px) {
  body.nifhr-screen:not(.base-coach-dismissed):not(.candidate-move-step):not(.profile-drawer-open)
    [data-screen-id="nIfHr"] .kanban.import-board .candidate-stream {
    overflow: visible;
  }
}

/* ============================================================================
   ENTRY #162 (2026-07-22) — the curated-move coach, both halves, both views.

   User ask, in their words: "mobile will need this on a bottomsheet, desktop views use this usual
   dark coaching card with point direction in the [green] manually annotated area", plus for the
   post-drop card "DESKTOP: Move to this new position and add tail in the green manually annotated
   area" and "MOBILE: Turn the current coach card for this in mobile currently into a bottomsheet".

   TWO STATES, ONE NODE (`.nifhr-coach` is reused per step, see :15382):
   - GATING (`.candidate-move-step:not(.move-coach-dismissed):not(.candidate-move-complete)`) -
     the new "Initial screening: done." card. Anchored beside the SOURCE column, tail on the
     ticked candidates.
   - COMPLETE (`.candidate-move-complete`) - "Keep them moving". Anchored beside the TARGET
     column, tail on the cards that were just moved. This is the reposition the user asked for:
     it used to sit at `right: 42px; bottom: 44px` (:9282) and MEASURABLY COVERED the Maybe
     column's cards (measured at 1440x900: coach left edge 940 against the column's right edge
     1023, an 83px overlap that hid two candidates' scores).

   ⚠️ THE POSITION IS MEASURED LIVE, NOT A COORDINATE TABLE. The columns are fluid
   (`.kanban-col` widths change with the viewport and with how many columns are shown), so a
   hardcoded offset is only ever right at one width. The IIFE at the end of nifhr.html writes
   `--nifhr-move-coach-x/y` and `--nifhr-move-coach-tail-y` from real rects.
   ⚠️ `position: fixed` is what makes those viewport-relative rects usable as `left`/`top`
   unchanged. Safe here because no ancestor of `.nifhr-coach` carries a transform (verified: the
   whole chain up to `body` reports `transform: none`); if one ever gains one, a fixed child
   resolves against IT and this silently mis-positions.
   ============================================================================ */
@media (min-width: 901px) {
  body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .nifhr-coach {
    position: fixed;
    left: var(--nifhr-move-coach-x, auto);
    top: var(--nifhr-move-coach-y, auto);
    right: auto;
    bottom: auto;
    /* Width is measured too: there is only 198px beside the column at 901px wide and 378px at
       1440, against a 430px design width, so a fixed width would put the card back over the
       board at every width below ~1500. */
    width: var(--nifhr-move-coach-w, min(430px, 30vw));
    transition: left 420ms cubic-bezier(.2, .9, .16, 1), top 420ms cubic-bezier(.2, .9, .16, 1),
      box-shadow 360ms ease;
  }
  /* The lift at :9355 moved `bottom`, which this block no longer uses. Neutralised explicitly so
     it cannot fight the measured `top`. */
  body.nifhr-screen.candidate-move-step.candidate-move-complete [data-screen-id="nIfHr"] .nifhr-coach {
    bottom: auto;
  }
  /* THE TAIL. :15401 kills `.nifhr-coach::before` for the whole move step (it is the base step's
     tail). Bring it back for these two states only, aimed by measurement. Same triangle idiom and
     the same surface colour as the base tail at :15389, so the two read as one component. */
  body.nifhr-screen.candidate-move-step[data-move-coach-tail="left"] [data-screen-id="nIfHr"] .nifhr-coach::before {
    content: "";
    display: block;
    position: absolute;
    left: -13px;
    top: var(--nifhr-move-coach-tail-y, 41px);
    width: 15px;
    height: 18px;
    background: #0f1726;
    clip-path: polygon(100% 0, 0 50%, 100% 100%);
    pointer-events: none;
  }
  /* The BELOW placement's tail points UP instead. Same surface colour, rotated triangle. */
  body.nifhr-screen.candidate-move-step[data-move-coach-tail="up"] [data-screen-id="nIfHr"] .nifhr-coach::before {
    content: "";
    display: block;
    position: absolute;
    top: -13px;
    left: var(--nifhr-move-coach-tail-x, 41px);
    width: 18px;
    height: 15px;
    background: #0f1726;
    clip-path: polygon(0 100%, 50% 0, 100% 100%);
    pointer-events: none;
  }
}

/* ---- PHONE / TABLET (<=900px): both halves become a flush bottom sheet ----
   ⚠️ THIS DELIBERATELY SUPERSEDES ENTRY #114's "Do not widen these selectors to
   `.candidate-move-step`". #114 kept the move-step coach as a 14px-inset floating card for ONE
   stated reason: "the move-step coach sits over a live board the viewer has to drag on, and the
   #70 measurement (a taller coach buries the board and the arc) still governs there." That reason
   is now void in BOTH halves: the gating card is dismissed BEFORE the board is draggable (the
   #162 capture gate), and the complete card appears only AFTER the drag is over. Neither state
   has a drag to bury. If the gate is ever removed, restore #114's split.
   Values copied from #114's base-step sheet so the two sheets are identical, not merely similar. */
@media (max-width: 900px) {
  body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .nifhr-coach,
  body.nifhr-screen.candidate-move-step.candidate-move-complete [data-screen-id="nIfHr"] .nifhr-coach {
    position: fixed;
    left: 0;
    right: 0;
    top: auto;
    bottom: 0;
    width: auto;
    border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
    /* #116's full-width-sheet seam: the base rule borders all four sides, so at full bleed the
       vertical borders land ON the viewport edges as a thin light line. */
    border-left: none;
    border-right: none;
    padding: 30px 22px calc(28px + env(safe-area-inset-bottom, 0px));
    max-height: 60dvh;
    overflow-y: auto;
    animation: nifhrCoachSheetIn 340ms var(--ease-out, cubic-bezier(.16, 1, .3, 1));
  }
  /* The grabber pill. Same pseudo-element as the desktop TAIL above, doing a different job at a
     different breakpoint - exactly how the base coach already splits it (:15389 vs :16996). */
  body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .nifhr-coach::before {
    content: "";
    display: block;
    position: absolute;
    top: 9px;
    left: 50%;
    transform: translateX(-50%);
    width: 36px;
    height: 4px;
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--text-inverse) 26%, transparent);
    clip-path: none;
  }
  body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .nifhr-coach .coach-cta {
    width: 100%;
  }
}

/* ============================================================================
   ENTRY #163 (2026-07-22).

   (1) BOTH VIEWS: the base coach CTA reads "Open candidate profile" instead of "See it now".
       Static markup only (nifhr.html:173) - no CSS involved, noted here so the change is findable
       from the stylesheet's history too.

   (2) DESKTOP (>=901px): once the profile drawer is open, MARTA'S CARD IS DIMMED.
       User's words: "when the drawer is visible dim this panel as not to be clicked again anyway".

   ⚠️ THERE IS NO PAGE SCRIM BEHIND THIS DRAWER, AND THAT WAS MEASURED BEFORE ANY EDIT. It LOOKS
   like the board is dimmed next to the white drawer, but a computed-style diff of the whole
   ancestor chain (app-shell, main, content, nifhr-stage, kanban, the column, the sidebar) across
   the drawer opening reported ZERO changes to filter/opacity/background, and an overlay scan for
   any fixed/absolute box covering >=60% of the viewport found NOTHING. The grey is simply
   `--bg-app` beside a white panel. So "dim it like the rest" could not be done by lowering the
   card under a scrim: there is no scrim to lower it under. It has to be dimmed on its own.

   What actually makes it stand out is the SPOTLIGHT at :8484 -
   `.candidate-final:first-child, .source-selected` - which gives it `background: #fff`, a 3px
   green ring, a 42px shadow and `z-index: 9034`. This rule cancels that and applies the MD3
   inactive treatment ALREADY used for out-of-selection cards at :14749, so the two read as one
   idea rather than two different greys.

   ⚠️ `pointer-events: none` is included because the ask's reason was "as not to be clicked again
   anyway" - a dimmed control that still responds is the worst of both. It is safe: the card is an
   `<a>`, and #159's route to the drawer is this same click, which by then has already happened.
   ⚠️ SPECIFICITY: the spotlight at :8484 is (0,5,1). This is (0,6,1) and later in source, so it
   wins on both counts. Do not simplify the `.kanban.import-board` prefix away.
   ⚠️ DESKTOP ONLY. On phones the drawer is full-screen, so the card behind it is not visible and
   dimming it would be a change nobody can see, with the #74 phone drawer tier to re-verify.
   ============================================================================ */
@media (min-width: 901px) {
  body.nifhr-screen.profile-drawer-open [data-screen-id="nIfHr"] .kanban.import-board .candidate-card[data-open-profile] {
    opacity: 0.39;
    filter: grayscale(0.35);
    pointer-events: none;
    cursor: default;
    background: var(--surface);
    border-color: var(--border);
    box-shadow: none;
    z-index: auto;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   ENTRY #164: the nifhr candidate drawer's Profile / Cover letter switch.

   Appended at the TAIL deliberately: these rules override the drawer block at
   :8563+ and the ≤700px tier at :14888+, and an equal-specificity rule inside a
   tier block would lose to whichever came later in source. TOKENS ONLY. Every
   colour, radius, shadow, duration and easing below resolves to a :root custom
   property. There is no hex, no rgb()/rgba() and no bare colour keyword here.
   ═══════════════════════════════════════════════════════════════════════════ */

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer {
  --nhsq0-tabs-h: 44px;
}

/* ⚠️ The collapsed identity row (:14826) is absolutely positioned under the head.
   The tab bar now sits between them, so its offset is head + tabs. Two vars, two
   sources of truth, never one hardcoded sum: that is what #74's comment warns about. */
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-sticky-id {
  top: calc(var(--nhsq0-head-h, 76px) + var(--nhsq0-tabs-h, 0px));
}

/* The switch. `flex: 0 0 auto` so it never absorbs the scroller's free space, and it is
   a sibling of the scroller rather than a child of it, so it can never scroll away. */
body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .nhsq0-tabs {
  flex: 0 0 auto;
  height: var(--nhsq0-tabs-h);
  box-sizing: border-box;
  margin: 0 var(--space-xl) var(--space-2xs);
  padding: var(--space-2xs);
  display: flex;
  align-items: stretch;
  gap: var(--space-2xs);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--bg-subtle);
}

/* Both segments are equal width and always labelled: the affordance IS the discoverability,
   so there is no icon-only state and no disclosure to find. */
body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .nhsq0-tab {
  flex: 1 1 0;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--space-sm);
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-secondary);
  font-family: inherit;
  font-size: 13px;
  line-height: 1;
  font-weight: var(--fw-extrabold);
  letter-spacing: 0;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background var(--motion-fast) var(--ease-standard),
    color var(--motion-fast) var(--ease-standard);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .nhsq0-tab:hover {
  color: var(--text-primary);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .nhsq0-tab.is-active {
  background: var(--brand);
  color: var(--text-inverse);
  box-shadow: var(--shadow-brand);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .nhsq0-tab:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

/* ── the letter document ─────────────────────────────────────────────────── */

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter {
  color: var(--text-strong);
  font-size: var(--fs-md);
  line-height: 1.62;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter-head {
  display: grid;
  gap: 2px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter-head strong {
  color: var(--text-primary);
  font-size: 20px;
  line-height: 26px;
  font-weight: var(--fw-extrabold);
  letter-spacing: -0.01em;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter-head span {
  color: var(--text-tertiary);
  font-size: 13px;
  line-height: 19px;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter-date {
  margin: 0 0 18px;
  color: var(--text-tertiary);
  font-size: 13px;
  line-height: 19px;
  font-weight: var(--fw-bold);
}

/* The recipient block, tinted so the letter's two addressed parties read as a header
   pair rather than as body copy. `font-style: normal` because <address> italicises. */
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter-to {
  display: grid;
  gap: 1px;
  margin: 0 0 22px;
  padding: var(--space-sm) 14px;
  border-radius: var(--radius-md);
  background: var(--bg-subtle);
  font-style: normal;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter-to span {
  color: var(--text-strong);
  font-size: 13px;
  line-height: 19px;
  font-weight: var(--fw-bold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter-to span:first-child {
  color: var(--text-tertiary);
  font-weight: var(--fw-extrabold);
  letter-spacing: 0.4px;
  text-transform: uppercase;
  font-size: var(--fs-xs);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter-body p {
  margin: 0 0 14px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter-salutation {
  color: var(--text-primary);
  font-weight: var(--fw-bold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter-quote {
  color: var(--text-primary);
  font-style: italic;
}

/* Bullets are drawn, never a glyph: a list-marker character would be one more
   pictograph-adjacent literal to police, and this one takes the brand token. */
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter-points {
  margin: 0 0 var(--space-md);
  padding: 0;
  list-style: none;
  display: grid;
  gap: 10px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter-points li {
  position: relative;
  padding-left: 18px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter-points li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 8px;
  width: 6px;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--brand);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter-points b {
  color: var(--text-primary);
  font-weight: var(--fw-extrabold);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter-signoff {
  margin-top: 20px;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter-signoff b {
  display: block;
  margin-top: 6px;
  color: var(--text-primary);
  font-size: var(--fs-lg);
  font-weight: var(--fw-extrabold);
}

/* ── phone / small tablet ────────────────────────────────────────────────── */

@media (max-width: 700px) {
  /* A comfortable touch target, and the gutters track the head's 20px phone padding. */
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer {
    --nhsq0-tabs-h: 48px;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .nhsq0-tabs {
    margin: 0 20px var(--space-2xs);
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter-head strong {
    font-size: var(--fs-2xl);
    line-height: 24px;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-letter {
    font-size: 13px;
  }
}

@media (prefers-reduced-motion: reduce) {
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .nhsq0-tab {
    transition: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   ENTRY #165: the candidate drawer's guided read-then-message sequence.

   TOKENS ONLY, same as the #164 block above (the #164 token guard slices from its
   marker to the END of the file, so it scans this block too). No hex, no rgb(),
   no bare colour keyword below.
   ═══════════════════════════════════════════════════════════════════════════ */

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer {
  --nhsq0-actions-h: 90px;
  --nhsq0-guide-gap: 12px;
}

/* The MD3-inactive gate on the drawer's only exit. `disabled` is what makes it inert;
   these are styling only, and the hover hold stops a pointer lighting a dead control. */
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-primary {
  transition: opacity 240ms var(--ease-standard);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-primary[disabled],
body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-primary[data-inactive-cta] {
  opacity: 0.39;
  cursor: default;
  box-shadow: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-primary[data-inactive-cta]:hover {
  background: var(--brand);
}

/* The moment it goes live is worth seeing. */
body.nifhr-screen.drawer-guide-ready [data-screen-id="nIfHr"] .nhsq0-primary {
  animation: jcCtaPulse 1500ms var(--ease-standard) 2;
}

/* ── the stepping card ───────────────────────────────────────────────────── */

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-guide {
  position: absolute;
  left: 20px;
  right: 20px;
  bottom: calc(var(--nhsq0-actions-h, 90px) + var(--nhsq0-guide-gap));
  z-index: 24;
  margin: 0;
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-md);
  background: var(--text-primary);
  color: var(--text-inverse);
  box-shadow: var(--shadow-popover);
  /* ⚠️ INERT BY DESIGN. It advertises the next action and never receives one, so it cannot
     eat a tap meant for a tab or the CTA. Guards must compare rects, never elementFromPoint. */
  pointer-events: none;
  animation: nifhr-actions-ready 260ms var(--ease-out);
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-guide[hidden] {
  display: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-guide-title {
  margin: 0;
  font-size: var(--fs-md);
  line-height: 20px;
  font-weight: var(--fw-extrabold);
  letter-spacing: -0.01em;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-guide-sub {
  margin: 2px 0 0;
  color: var(--text-tertiary);
  font-size: 13px;
  line-height: 19px;
  font-weight: var(--fw-semibold);
}

/* When the card points at a TAB it moves to the top of the drawer. Its top edge is
   head + tabs + a gap, so it is always strictly BELOW the tab bar: that is the single
   invariant protecting #164's tab hit-test guard. */
body.nifhr-screen[data-drawer-guide-step="to-letter"] [data-screen-id="nIfHr"] .nhsq0-guide,
body.nifhr-screen[data-drawer-guide-step="to-profile"] [data-screen-id="nIfHr"] .nhsq0-guide {
  top: calc(var(--nhsq0-head-h, 76px) + var(--nhsq0-tabs-h, 44px) + var(--nhsq0-guide-gap));
  bottom: auto;
}

/* The tail, aimed by measurement at the tab the card is naming. */
body.nifhr-screen[data-drawer-guide-step="to-letter"] [data-screen-id="nIfHr"] .nhsq0-guide::before,
body.nifhr-screen[data-drawer-guide-step="to-profile"] [data-screen-id="nIfHr"] .nhsq0-guide::before {
  content: "";
  position: absolute;
  top: -7px;
  left: var(--nhsq0-guide-tail-x, 50%);
  width: 16px;
  height: 8px;
  margin-left: -8px;
  background: var(--text-primary);
  clip-path: polygon(0 100%, 50% 0, 100% 100%);
}

/* Pointing DOWN at the now-live CTA. */
body.nifhr-screen[data-drawer-guide-step="ready"] [data-screen-id="nIfHr"] .nhsq0-guide::after {
  content: "";
  position: absolute;
  bottom: -7px;
  left: 50%;
  width: 16px;
  height: 8px;
  margin-left: -8px;
  background: var(--text-primary);
  clip-path: polygon(0 0, 50% 100%, 100% 0);
}

/* ── the "there is more below" cue ───────────────────────────────────────── */

/* ⚠️ ON THE DRAWER, NOT ON .nhsq0-body. A pseudo inside an overflow-y:auto box scrolls away
   with its content; the fade has to be painted by the box that CLIPS. Same law as x1riqt
   #156b at :18766. It sits above the footer and below the guide card. */
body.nifhr-screen.drawer-guide-scroll [data-screen-id="nIfHr"] .nifhr-profile-drawer::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--nhsq0-actions-h, 90px);
  height: 72px;
  z-index: 22;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    color-mix(in srgb, var(--surface), transparent 100%),
    var(--surface) 92%
  );
}

/* ── the tab the viewer is being sent to ─────────────────────────────────── */

body.nifhr-screen[data-drawer-guide-step="to-letter"] [data-screen-id="nIfHr"] .nhsq0-tab[data-profile-tab="letter"],
body.nifhr-screen[data-drawer-guide-step="to-profile"] [data-screen-id="nIfHr"] .nhsq0-tab[data-profile-tab="profile"] {
  /* mmCoachRing is declared at TOP LEVEL (:17993), so it is legal to reference here.
     mmCoachPulse (:16445) is inside a media query and would be a silent no-op. */
  animation: mmCoachRing 1400ms var(--ease-standard) infinite;
}

/* Reserve the card's measured height inside the scroller so it never covers the copy it is
   pointing at. Top-anchored steps pad the top, bottom-anchored steps pad the bottom. */
body.nifhr-screen[data-drawer-guide-step="to-letter"] [data-screen-id="nIfHr"] .nhsq0-body,
body.nifhr-screen[data-drawer-guide-step="to-profile"] [data-screen-id="nIfHr"] .nhsq0-body {
  padding-top: calc(28px + var(--nhsq0-guide-h, 0px) + var(--nhsq0-guide-gap));
}

body.nifhr-screen.profile-drawer-open[data-drawer-guide-step="scroll"] [data-screen-id="nIfHr"] .nhsq0-body,
body.nifhr-screen.profile-drawer-open[data-drawer-guide-step="dwell"] [data-screen-id="nIfHr"] .nhsq0-body,
body.nifhr-screen.profile-drawer-open[data-drawer-guide-step="ready"] [data-screen-id="nIfHr"] .nhsq0-body {
  padding-bottom: calc(var(--nhsq0-actions-h, 90px) + var(--nhsq0-guide-h, 0px) + 34px);
}

@media (max-width: 700px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer {
    --nhsq0-actions-h: 82px;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-guide {
    left: 14px;
    right: 14px;
  }
}

@media (prefers-reduced-motion: reduce) {
  body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-guide,
  body.nifhr-screen.drawer-guide-ready [data-screen-id="nIfHr"] .nhsq0-primary,
  body.nifhr-screen[data-drawer-guide-step="to-letter"] [data-screen-id="nIfHr"] .nhsq0-tab[data-profile-tab="letter"],
  body.nifhr-screen[data-drawer-guide-step="to-profile"] [data-screen-id="nIfHr"] .nhsq0-tab[data-profile-tab="profile"] {
    animation: none;
  }
}

/* ==========================================================================
   ENTRY #166b (2026-07-22, user call, re-raised): the "Message Marta" sheet
   spans the WHOLE VIEWPORT at <= 900px, not the drawer it lives in.
   ==========================================================================
   The user asked for this before and it did not land: on mobile and tablet the
   sheet must read as a viewport bottom sheet even though its owning drawer is a
   narrower panel (measured: drawer 359 of 390, 397 of 432, 460 of 834).

   ⚠️ `position: fixed` CANNOT DO THIS. `.nifhr-profile-drawer` carries a
   transform (measured `matrix(1, 0, 0, 1, 0, 0)` at rest, and an IDENTITY matrix
   still establishes a containing block), so a fixed child resolves against the
   drawer's box, not the viewport. That transform IS the slide, so it cannot be
   removed. Same trap as #109 and #112.

   Instead: the drawer is flush with the RIGHT edge of the viewport on every view
   (it slides in from there), so anchoring the sheet to `right: 0` and giving it
   `width: 100vw` reaches the left edge exactly, with no reparenting and no JS.
   Nothing between the sheet and the viewport clips it: the drawer is
   `position: fixed` with `overflow: visible`, so `.app-shell`'s `overflow: hidden`
   is not in its containing-block chain (measured).

   `.nhsq0-msg-card` is `width: 100%` of the sheet, so it follows for free and
   keeps its top corner radius. It carries NO border, so the #116 full-width seam
   does not apply here. Desktop (>= 901px) is deliberately untouched: the sheet
   stays inside the drawer there. */
@media (max-width: 900px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .nhsq0-msg-sheet {
    left: auto;
    right: 0;
    width: 100vw;
  }
}

/* ==========================================================================
   ENTRY #166d (2026-07-22, user call): the board is DIMMED until the move-step
   CTA is tapped, and that CTA is no longer dimmed itself.
   ==========================================================================
   #162 already GATES the drag behind "OK, let me move them now" (a capture-phase
   listener on `document`), but the board still looked live: ticked cards, a
   pulsing drop zone, nothing saying "not yet". This is the missing visual half of
   that gate, on every view, since both run the identical gating state (measured:
   the same body classes and the same DOM at 390 and at 1440).

   ⚠️⚠️ THE DIM GOES ON THE COLUMNS, NEVER ON `.kanban.import-board`. Putting it on
   the board KILLED THE DRAG on 3 of 5 viewports and the failure looked nothing like
   a paint bug: `elementFromPoint` at the card's centre returned <body>. Mechanism,
   measured: `body.nifhr-screen.candidate-move-step::before` (:8860) is a fixed
   z-index 9000 dim that DELIBERATELY captures taps (#83), and the move step escapes
   it by raising the columns to z-index 9042/9044 at the ROOT stacking context. An
   `opacity` below 1 on the board creates a stacking context, which TRAPS those
   z-indexes inside a board whose own z-index is `auto`, so the whole board fell
   under the tap-eating dim. Dimming the columns is safe because each one keeps its
   own root-level z-index; a stacking context on an element that is itself above the
   dim changes nothing.

   ⚠️ NO `pointer-events: none`. #162's capture-phase gate is what blocks the drag,
   and it must not `preventDefault()`; killing pointer events here as well would
   also kill the board's own scrolling on a phone.

   ⚠️ It must beat the move-step spotlight, which sets `opacity: 1` on the curated
   source and target columns. This selector is (0,8,1) against that rule's (0,7,1)
   and is last in the file.

   0.44, not the house 0.39 MD3-inactive used for controls: the coach copy points
   AT these cards ("drag your picks into Maybe"), so they have to stay legible.
   The transition is what makes releasing the gate read as the board waking up. */
body.nifhr-screen.candidate-move-step:not(.move-coach-dismissed):not(.candidate-move-complete) [data-screen-id="nIfHr"] .kanban.import-board .kanban-col {
  opacity: 0.44;
}

/* ⚠️ THE RELEASE IS AN ANIMATION, NOT A `transition`. A `transition` shorthand on
   `.kanban-col` silently CLOBBERS the two transition lists the move step already
   owns: `.is-curated-target` animates `height`/`max-height`/`box-shadow`/
   `background`/`border-color` over 560ms (:8976) and `.is-curated-source` animates
   `box-shadow`/`background`/`border-color` over 560ms (:14697). Measured: the
   generic rule beat the target's on source order and left it with
   `transition-property: opacity` alone, so the drop zone's grow would have gone
   instant. An animation cannot collide with a transition, and it fires exactly
   once, on the frame the gate is released. */
@keyframes nifhrColWake {
  from { opacity: 0.44; }
  to { opacity: 1; }
}

body.nifhr-screen.candidate-move-step.move-coach-dismissed:not(.candidate-move-complete) [data-screen-id="nIfHr"] .kanban.import-board .kanban-col.is-curated-source,
body.nifhr-screen.candidate-move-step.move-coach-dismissed:not(.candidate-move-complete) [data-screen-id="nIfHr"] .kanban.import-board .kanban-col.is-curated-target {
  animation: nifhrColWake 320ms var(--ease-out, cubic-bezier(.16, 1, .3, 1));
}

@media (prefers-reduced-motion: reduce) {
  body.nifhr-screen.candidate-move-step.move-coach-dismissed:not(.candidate-move-complete) [data-screen-id="nIfHr"] .kanban.import-board .kanban-col.is-curated-source,
  body.nifhr-screen.candidate-move-step.move-coach-dismissed:not(.candidate-move-complete) [data-screen-id="nIfHr"] .kanban.import-board .kanban-col.is-curated-target {
    animation: none;
  }
}

/* ==========================================================================
   ENTRY #167 (2026-07-22, user call): the drawer's Cover letter tab is locked
   until the Profile panel has been read.
   ==========================================================================
   The user watched the segment being tapped straight through: "at moment they
   can click on the cover letter for example before scrolling down the bottom
   like the coaching card says". The lock itself is the `disabled` attribute set
   in nifhr.html, which is what actually stops the click; this block is only what
   it LOOKS like, and it is the same MD3-inactive treatment the "Message Marta"
   CTA above it already wears (:19430) so the two locks read as one idea.

   ⚠️ THE `:hover` RULE MUST BE CANCELLED TOO. `.nhsq0-tab:hover` lifts the label
   to --text-primary, so without this a locked tab still lights up under the
   pointer on desktop and tablet, which is the one signal that says "this is
   live". A dimmed control that brightens on hover is worse than no dim.
   ========================================================================== */

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .nhsq0-tab[disabled],
body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .nhsq0-tab[data-inactive-cta] {
  opacity: 0.39;
  cursor: default;
  box-shadow: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .nhsq0-tab[disabled]:hover,
body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-profile-drawer .nhsq0-tab[data-inactive-cta]:hover {
  color: var(--text-secondary);
}

/* ==========================================================================
   ENTRY #168 (2026-07-22, user call): the RANK GATE over the compare modal.
   ==========================================================================
   The modal used to arm ranking the instant it opened, so the viewer landed on
   a live drag list with no idea why they were ranking anyone. This gate explains
   it first, and its CTA is what arms the list.

   ONE node, TWO poses. The COPY is identical on both views, which is what was
   asked for; only the geometry is width-split:
     <= 900px  a bottom sheet flush with the viewport bottom
     >= 901px  a centred coach card in the house dark style

   ⚠️ z-index 9620. The overlay it lives in is 9400 and the modal's own rank hint
   is 9600, so anything below that would paint UNDER the thing it is gating while
   still reporting a perfectly correct getBoundingClientRect (entry #156). The
   probe hit-tests the drag handle rather than trusting the rect.
   ⚠️ `position: absolute` is load-bearing TWICE: it takes the gate out of the
   overlay's `place-items: center` grid (an in-flow second child would make the
   grid two cells and shove the modal off centre), and it resolves against the
   overlay's fixed box rather than the modal's `overflow: hidden` one.
   ========================================================================== */

/* ⚠️ ENTRY #386 ADDED `.rank-why` TO EVERY ONE OF THESE SELECTOR LISTS RATHER THAN PUTTING THE
   `.rank-gate` CLASS ON ITS MARKUP. The two poses are identical and the declarations are genuinely
   shared, but `.rank-gate` is an IDENTITY other code selects on, and joining a family for its look
   is how a count guard reddens on a correct page. The INNER card is a different case and keeps
   `.rank-gate-card`, which is the shared LOOK class `.rank-done-card` already reuses the same way,
   so every `.rank-gate-card` rule below reaches the new card with no edit at all. */
body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate,
body.nifhr-screen [data-screen-id="nIfHr"] .rank-why {
  position: absolute;
  inset: 0;
  z-index: 9620;
  display: grid;
  place-items: center;
}

body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate[hidden],
body.nifhr-screen [data-screen-id="nIfHr"] .rank-why[hidden] { display: none; }

/* A REAL element, never a `box-shadow: 0 0 0 9999px` dim: that never hit-tests and
   can never be blurred, because backdrop-filter samples the element's own box. */
body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-scrim {
  position: absolute;
  inset: 0;
  background: var(--scrim-dim-coach, rgba(0, 0, 0, 0.55));
  backdrop-filter: blur(var(--blur-scrim, 2px));
  -webkit-backdrop-filter: blur(var(--blur-scrim, 2px));
  animation: rankGateScrimIn 240ms var(--ease-standard) both;
}

body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-card {
  position: relative;
  z-index: 1;
  width: min(420px, calc(100% - 48px));
  box-sizing: border-box;
  padding: 22px 22px 20px;
  border-radius: 18px;
  background: #0f1726;
  color: #f8fafc;
  box-shadow: 0 26px 64px rgba(8, 12, 22, 0.52);
  animation: rankGateCardIn var(--motion-drawer) cubic-bezier(.22, 1, .36, 1) both;
}

body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-title {
  margin: 0 0 10px;
  font-size: 22px;
  line-height: 1.2;
  font-weight: var(--fw-extrabold);
  letter-spacing: -0.01em;
  color: #f8fafc;
  /* ⚠️ NO `text-wrap: balance` HERE. It evens the ragged edge by wrapping EARLY, so the title
     stopped short of the sheet it sits in while the subtext right under it ran the full width,
     which reads as a broken heading. MEASURED at nifhr-mob-63: lines of 203/200 inside a 280px
     content box at 320, and 226/223 inside 350px at 390, so 77px and 124px of the container were
     simply unused. The project note has said this since the number-cell work: balance makes
     headings wrap early. Let it fill. */
}

body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-sub {
  margin: 0 0 18px;
  font-size: var(--fs-md);
  line-height: var(--lh-normal);
  color: #cbd5f5;
}

/* `.coach-cta` is the shared house button (:5160), deliberately reused so this reads as the same
   voice as every other coach in the walkthrough.
   ⚠️ ITS 41px min-height IS TOO SHORT FOR A FULL-WIDTH SHEET. `.coach-cta` is sized for a compact
   coach card, and stretched across a bottom sheet it reads as a squashed bar. The numbers here are
   NOT invented: they are `.nhsq0-msg-cta` (:17105), the house full-width dark-sheet CTA, which is
   50px tall at 15px. Measured against the neighbours on this screen: Submit 44px, "Message Marta"
   46px, this was 41px, the shortest primary on the page. The card grows by the difference, which
   is what the user authorised ("if you need to grow the height of the bottomsheet do that").
   ⚠️ Scoped to `.rank-gate-cta`, NEVER to `.coach-cta`: that class reaches every coach in the
   walkthrough and rj0wj's company coach. */
body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-cta {
  width: 100%;
  min-height: 50px;
  height: 50px;
  font-size: var(--fs-lg);
}

@keyframes rankGateScrimIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes rankGateCardIn {
  from { opacity: 0; transform: translateY(14px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}
@keyframes rankGateSheetIn {
  from { opacity: 0; transform: translateY(100%); }
  to   { opacity: 1; transform: none; }
}

/* ── <= 900px: a bottom sheet ─────────────────────────────────────────────── */
@media (max-width: 900px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate,
  body.nifhr-screen [data-screen-id="nIfHr"] .rank-why {   /* #386, see the note at :21340 */
    place-items: end stretch;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-card {
    /* Flush to the viewport edges. ⚠️ The border radius is TOP-ONLY: a sheet stretched
       to full width with an all-round radius leaves a lit seam against the viewport
       edge, the trap entry #116 fixed on the other full-width sheets. */
    width: 100%;
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    padding: 22px 20px calc(20px + env(safe-area-inset-bottom, 0px));
    animation: rankGateSheetIn var(--motion-drawer) cubic-bezier(.22, 1, .36, 1) both;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-title { font-size: 20px; }
}

@media (max-width: 380px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-title { font-size: var(--fs-2xl); }
  body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-sub { font-size: 13px; }
}

@media (prefers-reduced-motion: reduce) {
  body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-scrim,
  body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-card {
    animation: none;
  }
}

/* ==========================================================================
   ENTRY #169 (2026-07-22, user call): the POST-SUBMIT step.
   ==========================================================================
   "Vertically centre the redesign modal like this" plus "ALL VIEWS: add
   bottomsheet or coaching card for desktop".

   The success panel used to be `position: absolute; inset: 0` over a modal that
   kept its full 748px height, so "Ranking submitted." floated in the middle of a
   tall empty white box. It now holds only the tick and the headline, the modal
   collapses around it, and the coaching sheet takes the copy, the live countdown
   and the advance CTA.

   ⚠️ THE MODAL MUST BE TOLD TO COLLAPSE. Its height comes from a fixed
   `grid-template-rows` (:9424) and a `max-height`, NOT from its content, so
   emptying the success panel changes nothing on its own. The rows are collapsed
   to `auto` and every other row is hidden.
   ⚠️ `.compare-success` GOES BACK INTO FLOW. An absolutely positioned child
   contributes no height, so leaving it absolute would collapse the modal to zero.
   ⚠️ THE CARD CENTRES IN THE ROOM ABOVE THE SHEET, not in the viewport. The
   overlay is a `place-items: center` grid, so a bottom padding of the sheet's
   own MEASURED height (`--rank-done-h`, published by measureRankDone()) is what
   moves the centre up. A constant would be wrong: the copy wraps to a different
   number of lines at 320 than at 1920.
   ========================================================================== */

body.nifhr-screen.comparison-submitted [data-screen-id="nIfHr"] .nifhr-compare-modal {
  grid-template-rows: auto;
  height: auto;
  max-height: calc(100vh - 64px);
}

body.nifhr-screen.comparison-submitted [data-screen-id="nIfHr"] .nifhr-compare-modal > *:not(.compare-success) {
  display: none;
}

body.nifhr-screen.comparison-submitted [data-screen-id="nIfHr"] .compare-success {
  position: static;
  inset: auto;
  padding: 34px 28px;
}

/* Make room for the sheet so the confirmation centres in what is left.
   ⚠️ THE OVERLAY'S BASE PADDING IS NOT ONE NUMBER. It is 32px at :9405 but 14px inside
   `(max-width: 700px), (max-width: 900px) and (max-height: 500px)` (:14019) -- the SAME query
   isMobileRankLayout() uses, so 834x1112 is on the 32px side even though it takes the bottom
   sheet. Hardcoding either number put the confirmation 18px off centre at one tier or the other
   (measured gapTop 95 vs 113 at 320, then 345 vs 327 at 834). The padding is published as a token
   so the reservation can never drift from it again. */
body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-compare-overlay { --cmp-pad: 32px; }

@media (max-width: 700px), (max-width: 900px) and (max-height: 500px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-compare-overlay { --cmp-pad: 14px; }
}

@media (max-width: 900px) {
  body.nifhr-screen.rank-done-open [data-screen-id="nIfHr"] .nifhr-compare-overlay {
    padding-bottom: calc(var(--cmp-pad) + var(--rank-done-h, 0px));
  }
}

/* ── >= 901px: the coach card STACKS UNDER the confirmation ───────────────────────
   ⚠️ An absolute layer with `place-items: center` puts the card exactly where the modal already
   is: measured modalBottom 386 against cardTop 299 at 834, so the card sat ON the confirmation it
   describes. ⚠️ AND `.rank-done` PRECEDES THE MODAL IN DOM ORDER, so simply making it static
   stacked it ABOVE the modal (measured cardTop 308 against modalBottom 537). It needs `order` as
   well as flow. The overlay becomes a centred flex COLUMN for this state only, which centres the
   PAIR and gives the same reading order the phone has: confirmation, then coaching. */
@media (min-width: 901px) {
  body.nifhr-screen.rank-done-open [data-screen-id="nIfHr"] .nifhr-compare-overlay {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 18px;
  }

  body.nifhr-screen.rank-done-open [data-screen-id="nIfHr"] .rank-done {
    position: static;
    display: block;
    order: 2;
    width: auto;
  }

  body.nifhr-screen.rank-done-open [data-screen-id="nIfHr"] .nifhr-compare-modal { order: 1; }

  body.nifhr-screen [data-screen-id="nIfHr"] .rank-done > .rank-gate-scrim { display: none; }
}

/* ── the post-submit sheet / card ─────────────────────────────────────────── */

body.nifhr-screen [data-screen-id="nIfHr"] .rank-done {
  position: absolute;
  inset: 0;
  z-index: 9620;
  display: grid;
  place-items: center;
}

body.nifhr-screen [data-screen-id="nIfHr"] .rank-done[hidden] { display: none; }

/* The scrim must NOT re-dim the confirmation the viewer has just been shown: the
   modal is the point of this step. Only the sheet itself is opaque. */
body.nifhr-screen [data-screen-id="nIfHr"] .rank-done > .rank-gate-scrim {
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* The odometer, restyled for a dark card: the header's pill is dark-on-light, this
   one sits ON the dark surface, so it takes a hairline instead of a fill. */
/* ⚠️ THE TIMER MUST NOT STRETCH. `.cdo-timer` is `display: grid`, so inside the card's block
   container it filled the whole width and read as an elongated bar rather than the pill the
   viewer just saw in the modal header. `width: max-content` sizes it to its own tracks, exactly
   like the header instance, and `margin: 0 auto` centres it. `align-self` was a no-op here: the
   card is a block container, not a flex one. */
/* ⚠️ ENTRY #386 MOVED THE ODOMETER NODE ITSELF to `.rank-why` (nifhr.html:455), because that is now
   the card whose copy says "This is the job closing timer you set"; the post-submit card is about
   the selection panel and has no timer to point at. These rules are SHARED with a second selector
   rather than the class being reused, for the reason recorded at :21340. `.rank-done-odo` is kept
   in every list rather than renamed: collaborator-invite.html and any future carrier can still take
   this treatment, and a rename would be a wider edit than the ask. */
body.nifhr-screen [data-screen-id="nIfHr"] .rank-done-odo,
body.nifhr-screen [data-screen-id="nIfHr"] .rank-why-odo {
  margin: 0 0 var(--space-md);
  display: flex;
  justify-content: center;
}

body.nifhr-screen [data-screen-id="nIfHr"] .rank-done-odo .cdo-timer,
body.nifhr-screen [data-screen-id="nIfHr"] .rank-why-odo .cdo-timer {
  width: max-content;
  margin: 0 auto;
  background: rgba(255, 255, 255, 0.06);
  /* ⚠️ AN INSET RING, NOT A `border`. The header pill has no border, so a 1px one here made this
     copy of the same component 179x44 against the header's 177x42: measured 2px bigger in both
     axes at all five viewports, which is exactly the "same size as previous screen" the user
     asked for being missed. An inset box-shadow paints identically and occupies no space. */
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22);
  color: #f8fafc;
}

body.nifhr-screen [data-screen-id="nIfHr"] .rank-done-odo .cdo-label,
body.nifhr-screen [data-screen-id="nIfHr"] .rank-why-odo .cdo-label { color: #94a3b8; }

@media (max-width: 900px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .rank-done { place-items: end stretch; }

  body.nifhr-screen [data-screen-id="nIfHr"] .rank-done-card {
    width: 100%;
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    padding: 22px 20px calc(20px + env(safe-area-inset-bottom, 0px));
    animation: rankGateSheetIn var(--motion-drawer) cubic-bezier(.22, 1, .36, 1) both;
  }

  /* The grabber bar, so it reads as the same family of bottom sheet as the rest. */
  body.nifhr-screen [data-screen-id="nIfHr"] .rank-done-card::before {
    content: "";
    display: block;
    width: 36px;
    height: 4px;
    margin: -8px auto 14px;
    border-radius: var(--radius-pill);
    background: rgba(255, 255, 255, 0.26);
  }
}

/* ENTRY #169b: the animated arrow on the post-submit CTA.
   ⚠️ INLINE SVG, never a glyph and never an emoji: the standing rule is inline SVG or nothing.
   The button becomes a flex row so the label and the arrow share a baseline; `.coach-cta` is
   already `inline-flex`, so only the gap is new. The nudge is a `transform` on the SVG alone, so
   it can never reflow the label or change the button's measured 50px height. */
/* ⚠️⚠️ ENTRY #392: 9px -> the declared token. SUPERSEDED, quoted: `gap: 9px;`.
   The comment above is now a CLAIM ABOUT A DELETED THING: #370 removed every animated arrow, so
   the child this gap was authored for no longer exists. Its only remaining consumer is the #386
   countdown disc on `[data-rank-why-cta]` (and the same family's `[data-rank-done-cta]`), which is
   exactly the spacing she asked to standardise. 9px was a raw number with no token behind it and
   left this family reading 9px while every other disc host reads 8px; `--space-xs` is the declared
   8px step and matches the project's one icon-beside-label precedent,
   `.ns-save,.ns-pay-cta{…gap:var(--space-xs)}`.
   ⚠️ NO LITERAL FALLBACK IS NEEDED ON THIS ONE LINE, unlike the JS sites: this rule lives in
   `hirello-app.css`, so `:root`'s scale is by definition present wherever it applies. */
body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-cta { gap: var(--space-xs); }

/* ⚠️⚠️ ENTRY #370 (user, 2026-08-20): *"remove any animiated arrows from the cta buttons"*.
   EVERY `.cta-arrow` SVG IS DELETED FROM THE MARKUP, so the rules that styled and nudged them
   are deleted here rather than left pointing at nothing. This is the SECOND time she has asked
   for one of these arrows to go: entry #224 removed the x1riqt welcome sheet's on desktop only,
   and its now-dead `display: none` rule is deleted further down this file in the same edit.
   SUPERSEDED, entry #169b and #172, quoted so the reasoning stays readable rather than merely
   gone: `.cta-arrow { flex: 0 0 auto; animation: ctaArrowNudge 1400ms var(--ease-standard)
   infinite; }`, a `ctaArrowNudge` keyframe translating the arrow 4px right at 45%, and a
   reduced-motion override setting that animation to none. #172's claim was that the class was
   "an opt-in class carried only by arrow SVGs; nothing else in the stylesheet targets it",
   which is exactly what made deleting the class safe: 0 elements carry it now.
   ⚠️ The keyframe name is deleted too. A live `@keyframes` with no consumer is how a glyph she
   asked to remove comes back on the next CTA somebody builds. */

/* ==========================================================================
   ENTRY #174 (2026-07-22, user call, annotated screenshot). ONE TITLE TREATMENT
   ACROSS EVERY DARK BOTTOM SHEET AND COACHING MODAL, ON EVERY VIEW.

   The reference is x1riqt's welcome sheet headline: 34px/38px, weight 900,
   -0.04em. Before this, the demo carried FOUR different title languages
   (measured): 34/38 w900 here, 28/32 w900 on the tour and workspace sheets,
   20-28 w900 on the nifhr coach, and 18-22 w800 on the rank gate.

   ⚠️ THIS BLOCK MUST STAY AT THE TAIL. Several of the rules it corrects are
   themselves inside tier blocks at equal or higher specificity
   (`body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-coach h2` at :13380 is
   (0,3,1) inside a media query), so each selector below is written at a
   specificity that matches or beats its own predecessor, and source order at
   the very end is what settles the ties.

   ⚠️ CAPITALISATION IS NOT TOUCHED, by user instruction: the cinematics' sheets
   stay `text-transform: uppercase`, everything else stays sentence case.

   ⚠️ PER-FAMILY SIZE CEILINGS ARE MEASURED, NEVER GUESSED. The rule the user
   set is "no title may gain a rendered row". Every override below carries the
   measurement that forced it. A family with no override runs the full 34px.
   ========================================================================== */

.modal-header h1,
.tour-modal.parsing-tour .tour-step h2,
.company-basics-coach h2,
body.nifhr-screen [data-screen-id="nIfHr"] .nifhr-coach h2,
body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-title,
.mm-intro-title,
.wmode-title {
  font-size: var(--sheet-title-size);
  line-height: var(--sheet-title-lh);
  font-weight: var(--sheet-title-weight);
  letter-spacing: var(--sheet-title-ls);
}

/* MEASURED CEILING, the only family that broke the "keep it to three rows" rule.
   "All your candidates. All your communications. In one place." is 59 characters in a 273px box at
   320px wide, where the flat 34px renders FIVE rows (measured: 5 at 320, 4 at 390). 22px was the
   largest that still held three in SENTENCE CASE.

   ⚠️ ENTRY #201 RE-DERIVED THIS RAMP, because the title is now UPPERCASE and uppercase is wider.
   At the old clamp the uppercase title renders FOUR rows at every width from 320 to 495 (measured:
   4 at 320/360/390/412/430/495, 3 only from 560 up). The largest uppercase size still holding three
   rows was measured per width: 20px at 320, 23 at 360, 25 at 390, 27 at 412, 28 at 430, 33 at 495,
   34 at 560 and above. The binding constraint is 320 (20/320 = 6.25vw), and 6.25vw clears every
   other width with room to spare, so the ramp is `clamp(20px, 6.25vw, 34px)`. The 34px cap is
   reached at 544px, and the card stops growing at its own `max-width: 560px`.
   ⚠️ Re-measure this ramp if the copy changes. It is derived from THIS string. */
.mm-intro-title { --sheet-title-size: clamp(18px, 5.75vw, 34px); }

/* ==========================================================================
   ENTRY #201 (user call). The messenger intro bottom sheet: the title and its body copy are
   LEFT-ALIGNED, and the title is UPPERCASE.

   ⚠️ THIS NARROWS #174's CAPITALISATION RULE, which reads "capitalisation is not touched, by user
   instruction: the cinematics' sheets stay uppercase, everything else stays sentence case." That
   still holds for every other family; this one family is now an explicit exception, asked for on an
   annotated phone screenshot. Do not "restore" it to sentence case, and do not generalise the caps
   to the other sheets either.

   ⚠️ `text-align` is set ON THE TITLE AND BODY, never by flipping `.mm-intro-card`'s
   `text-align: center` (hirello-app.css:16505). The card's centring is what keeps the CTA label
   centred inside its full-width pill; flipping the card would drag the button label left with it.
   text-align INHERITS, so a direct rule on these two elements beats the card's value without any
   specificity contest.

   ⚠️ BOTH `.mm-intro` SHEETS SHARE THIS CLASS: the annotated "All your candidates..." sheet
   (nifhr.html:1302) and the "Your universal inbox" chat sheet (:1312). They are one component and
   both take the treatment. The DESKTOP twin `.messenger-desk-coach-title` (:1378) carries the same
   copy but is a different class and is deliberately NOT touched: the annotation was of the phone.
   ========================================================================== */
.mm-intro-title {
  text-align: left;
  text-transform: uppercase;
}
.mm-intro-body {
  text-align: left;
}

/* ==========================================================================
   ENTRY #202 (user call, ALL VIEWS). The messenger welcome copy: each SENTENCE of the title gets
   its OWN ROW, the body takes the user's supplied wording, and the CTA reads "Show me".

   The title is split into three `<b class="mm-intro-line">` elements in the markup (nifhr.html:1302
   for the phone sheet, :1378 for the desktop coach) and each is made a block here. A forced break
   is the only way to guarantee the split: natural wrapping put "ALL YOUR CANDIDATES. ALL" on row one.

   ⚠️ `<b>`, NOT `<span>` or `<div>`. `<div>` is flow content and the desktop title's sibling in the
   phone sheet sits inside a `<button>` elsewhere in this component family; `<b>` is phrasing content
   and is valid in every one of those positions. It also dodges #99's legacy `span`/`p` ambush.

   ⚠️ THE RAMP HAD TO DROP AGAIN, and this is the third measured derivation of it. #174 set
   clamp(22px, 6.875vw, 34px) for three NATURALLY WRAPPED rows in sentence case. #201 dropped it to
   clamp(20px, 6.25vw, 34px) because uppercase is wider. #202 drops it to clamp(18px, 5.75vw, 34px)
   because a forced break is stricter still: the longest single sentence, "ALL YOUR COMMUNICATIONS.",
   must now fit one row on its own. Measured largest size at which ALL THREE sentences each hold one
   row: 18.5px at 320 (content box 268px), 21 at 360, 23.5 at 390, 25 at 412, 26 at 430, 30.5 at 495,
   34 at 560 and above. 320 binds at 18.5/320 = 5.78vw, so 5.75vw clears every width.
   ⚠️ Re-measure if the copy changes. All three numbers are derived from THESE three sentences.
   ========================================================================== */
.mm-intro-line {
  display: block;
}
/* ⚠️ `text-wrap: balance` must die on the split desktop title. It exists to even a ragged edge by
   wrapping EARLY (see #168b, where it stopped a heading a quarter short), which is the exact
   opposite of "keep each sentence on one row" and would break a line that otherwise fits. */
/* ==========================================================================
   ENTRY #203 (user call). The messenger inbox: a dark "Tap to open" coach hint glued under Marta's
   row, tail pointing UP at it. Same treatment as #178's hint on her kanban card and #200's tooltip
   in the compare modal, so the three read as one component.

   ⚠️ IT IS A CHILD OF THE ROW (nifhr.html renderInbox), which is the whole trick, exactly as #178.
   `.mm-thread` already carries `position: relative` (hirello-app.css:16231) and an infinite
   `mmRowPulse`, so a child inside that box rides the pulse in lockstep and its tail stays glued to
   the row's bottom edge with NO JS keeping them aligned. It also means the containing block is the
   ROW, so `top: 100%` is the row's own padding-box bottom, which is the trap #200 had to fix by
   hand on a `position: static` handle.

   ⚠️ `pointer-events: auto`, NOT none, and for the same reason #178 needed it: the bubble overlaps
   the row beneath. Being a child of Marta's `<button>`, an intercepted tap bubbles to her open
   handler, so tapping the hint opens Marta exactly like tapping the row. With `none` the tap would
   fall through to Ahmed's row, which is `.mm-locked` and does nothing, so the viewer would tap the
   thing that says "Tap to open" and watch nothing happen.

   ⚠️ GATED ON `body.mm-intro-seen`. Without it the hint sits blurred behind the welcome sheet,
   which owns the screen until its CTA is used. The class is added when that CTA fires and cleared
   on the same reset that clears `introSeen`, never separately.
   It also disappears once the row stops being coached: `is-coached` is removed in openChat(), so
   the hint needs no teardown of its own.
   ========================================================================== */
body.mm-intro-seen .mm-thread.is-coached .mm-tap-hint {
  display: block;
  position: absolute;
  /* `100%` is the ROW's padding-box bottom, and `.mm-thread` has padding but NO border, so the
     padding box and the border box coincide and 100% IS the visible bottom edge. The 8px offset is
     exactly the tail's height, so the tail's tip lands ON that edge, the same geometry as #178. */
  top: calc(100% + 8px);
  left: 12px;
  z-index: 6;
  margin: 0;
  padding: 9px 14px;
  border-radius: var(--radius-md);
  background: var(--text-primary);
  color: var(--text-inverse);
  box-shadow: var(--shadow-popover);
  font-size: var(--fs-md);
  line-height: 20px;
  font-weight: var(--fw-extrabold);
  font-style: normal;   /* <b> inherits nothing italic, but pin it: this is a coach label, not emphasis */
  letter-spacing: -0.01em;
  white-space: nowrap;
  pointer-events: auto;
}
/* The tail points UP, 8px tall, its tip on the row's bottom edge. Same geometry as #178 and #200. */
body.mm-intro-seen .mm-thread.is-coached .mm-tap-hint::before {
  content: "";
  position: absolute;
  top: -8px;
  left: 22px;
  width: 16px;
  height: 8px;
  background: var(--text-primary);
  clip-path: polygon(0 100%, 50% 0, 100% 100%);
  pointer-events: none;
}
/* Base state: never rendered unless the gate above is satisfied. A bare `.mm-tap-hint` with no
   `display` would inherit the row's grid and become a fourth column. */
.mm-tap-hint { display: none; }

.messenger-desk-coach-title {
  /* ⚠️ `wrap`, NOT `normal`. `normal` is NOT a valid value of the `text-wrap` shorthand
     (wrap | nowrap | balance | pretty | stable), so the declaration is DROPPED and `balance`
     survives from :18176. Measured: the first attempt computed `balance` and looked like a
     specificity or source-order problem when it was simply an invalid keyword. */
  text-wrap: wrap;
}


/* ==========================================================================
   ENTRY #175. MOBILE AND TABLET ONLY (<=900px): once the base coach's bottom
   sheet is dismissed, Marta's card grows slightly and FLOATS, so the tap the
   scene is waiting for is unmissable.

   "On mobile and tablet view only when the bottomsheet has been dismissed then
    increase the size slightly and float animate Martas card showing users to
    tap it."

   ⚠️ THIS EXTENDS #131, IT DOES NOT REPLACE IT. The purple ripple
   (`nifhrMartaTapCue`) still runs; this adds a second animation on the same
   element and a static scale. Desktop is untouched: #159's float is on the
   `:not(.base-coach-dismissed)` state and above 901px the CTA opens the drawer
   outright, so this state is a phone/tablet state.

   ⚠️ THE FLOAT IS THE HOUSE `nifhrBaseFloat` (#159), REUSED BY NAME, not a
   second amplitude table. Measured, the gap between cards in the stream is 7px
   at every width, and the float travels UPWARD only (0 to -7px), so the bottom
   edge never closes on Ahmed's card below.

   ⚠️ THE SIZE IS THE INDIVIDUAL `scale` PROPERTY, NOT A `transform`. The float
   already owns `transform`; `scale` composes with it instead of fighting it,
   which is what lets the animation stay a plain reuse. It is also why reduced
   motion still gets the size bump with no motion at all.

   ⚠️ THE STREAM HAD TO BE UNCLIPPED, AND THAT IS THE WHOLE REASON THIS IS NOT A
   ONE-LINER. Marta is FLUSH with `.candidate-stream` on all four edges
   (measured at 390: card top 283 left 43 width 304, stream top 283 left 43
   width 304) and that stream is `overflow: hidden`, so both the grow and the
   float were being cut away invisibly. It is safe to release ONLY because the
   stream does not overflow in this state: `scrollHeight === clientHeight` at
   320, 390, 432 and 834 (359 = 6 cards x 54 + 5 gaps x 7). The release is
   scoped to the same three-class state as the cue, so every other nifhr step
   keeps the original clip, and #161's phone guard (which reads the load state,
   before dismissal) still measures `hidden`.

   ⚠️ THE EXTRUSION FITS INSIDE THE COLUMN'S OWN PADDING, MEASURED. The column
   pads 14px; the worst case is the float's -7px peak plus half the scale growth
   (54px x 0.035 / 2 = 0.95px), so 7.95px of 14. Horizontally the card grows
   304 x 0.035 = 10.6px, 5.3px a side, again inside 14.
   ========================================================================== */
@media (max-width: 900px) {
  body.nifhr-screen.base-coach-dismissed:not(.candidate-move-step):not(.profile-drawer-open)
    [data-screen-id="nIfHr"] .kanban.import-board .candidate-stream {
    overflow: visible;
  }
  body.nifhr-screen.base-coach-dismissed:not(.candidate-move-step):not(.profile-drawer-open)
    [data-screen-id="nIfHr"] .candidate-card[data-open-profile] {
    scale: 1.035;
    animation:
      nifhrMartaTapCue 1800ms cubic-bezier(.4, 0, .2, 1) infinite,
      nifhrBaseFloat 2200ms linear infinite both;
  }
}

/* Reduced motion keeps the size bump and the static ring, and drops both clocks.
   ⚠️ THIS MUST FOLLOW THE RULE ABOVE: #131's own reduced-motion block sits far
   earlier in the file at the same specificity, so without this the block above
   would silently re-arm the animation for viewers who asked for none. */
@media (max-width: 900px) and (prefers-reduced-motion: reduce) {
  body.nifhr-screen.base-coach-dismissed:not(.candidate-move-step):not(.profile-drawer-open)
    [data-screen-id="nIfHr"] .candidate-card[data-open-profile] {
    animation: none;
    box-shadow:
      0 0 0 3px rgba(124, 58, 237, 0.45),
      0 18px 42px rgba(17, 24, 39, 0.20);
  }
}


/* ==========================================================================
   ENTRY #178 (user call). MOBILE AND TABLET ONLY (<=900px): a "Tap to open" coach card sits glued
   directly below Marta's card, in the base-coach-dismissed float state (the same #175 state where
   she pulses and floats). Single line, no subtext, tail on top touching her bottom border.

   ⚠️ IT IS A CHILD OF MARTA'S CARD, WHICH IS THE WHOLE TRICK. Marta floats (#175 `nifhrBaseFloat`)
   and scales (1.035) via a transform on her `.candidate-card`; a child inside that box inherits the
   transform, so the hint moves in perfect lockstep with her ("sync") and its tail stays touching her
   border, with NO JS timing to keep them aligned. This is why it beats a separately-positioned card
   synced by copying startTimes (the #159 desktop approach), which would need re-measuring on every
   scroll and resize.

   ⚠️ `position: absolute`, so it is OUT OF FLOW and does not change Marta's card height. Marta's card
   is `overflow: visible` and `position: relative` (verified), so the hint shows below her and
   anchors to her box.

   ⚠️ `pointer-events: auto`, NOT none. The hint overlaps Ahmed's card beneath it, so `none` would
   leak a tap through to Ahmed and navigate away. Being a child of Marta's `<a data-open-profile>`,
   an intercepted tap bubbles to her open handler, so tapping the hint opens the drawer exactly like
   tapping Marta. Every other coach card in this project is inert, but none of them overlaps a live
   sibling target; this one does.

   ⚠️ DESKTOP IS UNTOUCHED. It is `display: none` at base and only shown inside the <=900px block, so
   above 901px #159's floating coach card is the only treatment.
   ========================================================================== */
body.nifhr-screen [data-screen-id="nIfHr"] .marta-tap-hint {
  display: none;
}
@media (max-width: 900px) {
  body.nifhr-screen.base-coach-dismissed:not(.candidate-move-step):not(.profile-drawer-open)
    [data-screen-id="nIfHr"] .candidate-card[data-open-profile] .marta-tap-hint {
    display: block;
    position: absolute;
    top: calc(100% + 8px);
    left: 10px;
    z-index: 5;
    margin: 0;
    padding: 9px 14px;
    border-radius: var(--radius-md);
    background: var(--text-primary);
    color: var(--text-inverse);
    box-shadow: var(--shadow-popover);
    font-size: var(--fs-md);
    line-height: 20px;
    font-weight: var(--fw-extrabold);
    letter-spacing: -0.01em;
    white-space: nowrap;
    pointer-events: auto;
    animation: nifhr-actions-ready 260ms var(--ease-out);
  }
  /* The tail points UP and its tip sits on Marta's bottom border. 8px tall against the 8px gap. */
  body.nifhr-screen.base-coach-dismissed:not(.candidate-move-step):not(.profile-drawer-open)
    [data-screen-id="nIfHr"] .candidate-card[data-open-profile] .marta-tap-hint::before {
    content: "";
    position: absolute;
    top: -8px;
    left: 22px;
    width: 16px;
    height: 8px;
    background: var(--text-primary);
    clip-path: polygon(0 100%, 50% 0, 100% 100%);
    pointer-events: none;
  }
}


/* ==========================================================================
   ENTRY #179 (user call, DESKTOP). The screening drawer's profile body mirrors the main candidate
   profile drawer's content and formatting (`.nhsq0-*`), held at the md3 39% opacity the retired
   `.screening-profile-section`s used. ONLY the body is affected; the header card, screening panel
   and references panel are untouched. The `.nhsq0-*` element styles are screen-scoped, so they
   already apply here; this wrapper only supplies the dim. `opacity: 0.39` is the md3 disabled level,
   matching what the replaced sections had (their text was rgba(...,0.38)).
   ========================================================================== */
body.nifhr-screen [data-screen-id="nIfHr"] .screening-profile-mirror {
  opacity: 0.39;
}


/* ==========================================================================
   ENTRY #180 (user call, DESKTOP >=901px). The screening sequence's 'live' step
   ("Answers on the profile") was a MINT-GREEN panel (#D1FAE5, green border, green text) floating
   beside the drawer. It becomes the house dark coaching card - the exact `.nifhr-coach` contract:
   navy #0f1726 under the purple radial, #f8fafc copy, the 14px radius, the slate hairline and the
   twin shadow - with a RIGHT-pointing tail aimed at the drawer's screening questionnaire panel.

   ⚠️ THE TAIL FORCED A POSITIONING FIX THAT IS NOT COSMETIC. The card's `margin-right` was
   `calc(33vw + 24px)`, but the drawer is `width: 33vw` with `min-width: 460px`. Below ~1394px the
   min-width wins and the drawer is WIDER than 33vw, so the card slid UNDERNEATH it (measured card
   right 548 vs drawer left 441 at 901, and 630 vs 564 at 1024 - it was only hidden because the
   drawer's z-index 9900 beats the overlay's 9800). A tail pointing at a drawer that covers the card
   is meaningless, so the margin now mirrors the drawer's own `max(33vw, 460px)`. The `- 8px` is
   exact, not a fudge: the overlay already contributes its own 32px right padding, so subtracting 8
   leaves a 24px gap, the same GAP the move-step coach uses.

   ⚠️ THE TAIL'S `top` IS A MEASURED CONSTANT, NOT A GUESS. The card's top is invariant
   (`align-self: start` inside the overlay's 32px padding + `margin-top: 104px` = 136px) and the
   drawer panel's vertical centre is 244px at ALL six desktop widths (901/1024/1280/1440/1680/1920),
   because everything above it in the drawer is fixed-height chrome. 136 + 99 + 18/2 = 244, so the
   triangle's tip lands dead on the panel's centre with no JS. The guard asserts the tip against the
   panel's LIVE rect at every width, so if that chrome ever changes height this reddens.

   ⚠️ `position: relative` is load-bearing - the card had no positioning context, so an absolutely
   positioned `::before` would have escaped to the overlay.
   ========================================================================== */
@media (min-width: 901px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-live-modal {
    position: relative;
    margin-right: calc(max(33vw, 460px) - 44px);
    padding: 26px;
    border: 1px solid rgba(148, 163, 184, 0.14);
    border-radius: 14px;
    background: #0f1726;
    color: #f8fafc;
    box-shadow:
      0 28px 72px rgba(15, 23, 42, 0.42),
      0 0 0 1px rgba(124, 58, 237, 0.16);
    overflow: visible;
  }

  /* The house headline treatment (entry #174's `--sheet-title-*`), which every other dark coaching
     card on this screen already carries. The shared `.screening-qr-copy h2, .screening-live-modal
     h2, .screening-out-copy h2` rule paints `var(--text-primary)` at 25px, which is the LIGHT-panel
     language and unreadable on navy. */
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-live-modal h2 {
    color: #f8fafc;
    font-size: var(--sheet-title-size);
    line-height: var(--sheet-title-lh);
    font-weight: var(--sheet-title-weight);
    letter-spacing: var(--sheet-title-ls);
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-live-modal p {
    color: #cbd5e1;
    font-size: 13px;
    line-height: 20px;
    font-weight: var(--fw-semibold);
  }

  /* The "LIVE ANSWERS" eyebrow keeps its pulsing red dot but cannot keep `var(--success-text)`
     (#059669), which is 2.3:1 against navy. #6EE7B7 is the same green family at 9:1. */
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-live-modal .screening-live-pill {
    color: #6EE7B7;
  }

  /* ENTRY #181 SUPERSEDES the `::before` tail that lived here. It could only ever reach the
     drawer's left EDGE, and the user requires the point to touch the questionnaire panel's own left
     border, 25px further in. See `.screening-coach-tail` at the tail of this file. */
}


/* ==========================================================================
   ENTRY #180 part B (user call, DESKTOP >=901px). When the screened-out phone cinematic finishes,
   the whole white modal AND the phone fade to transparent, and a second house dark coaching card
   ("Screened out, but not rejected") floats in with NO CTA. The viewer replays by hand from the
   phone's own "Replay demo" overlay, which keeps doing exactly what it already did.

   The state hook already existed: `markScreenedDemoLoopComplete()` (nifhr.html) adds
   `screening-phone-loop-complete` at t=19450ms and `resetScreenedDemo()` removes it, so a replay
   un-fades the modal and retracts the card for free, with no new JS timing.

   ⚠️ THE FADE CANNOT BE AN `opacity` ON THE PHONE. "Replay demo" (`.screened-demo-replay`) is a
   DESCENDANT of `.screened-demo-phone`, and a parent's opacity is not overridable from a child, so
   fading the phone would take the one control the viewer still needs with it. The fade is therefore
   applied to the SURFACES (background-color, box-shadow, border-color) plus an opacity on the
   screen's children EXCEPT the replay overlay, which additionally drops its own scrim and blur so
   the white pill is left floating alone.

   ⚠️ THE CARD IS `position: absolute`, NOT A GRID ITEM. `.nifhr-screening-overlay` is a
   `display: grid; place-items: center` container whose panels are its items; every panel but one is
   `display: none`, so it is always a one-item grid. A second in-flow child would make it a two-ROW
   grid and halve/shift the modal. Absolute takes it out of flow entirely, and the overlay is
   `position: fixed; inset: 0` with no border, so `right`/`top` resolve against the viewport.

   ⚠️ THE TWO ANIMATIONS DO NOT COLLIDE because they touch disjoint properties: the entrance owns
   `opacity` only and the float owns `transform` only. Give either one both properties and the later
   entry in the list silently wins the whole property.
   ========================================================================== */
body.nifhr-screen [data-screen-id="nIfHr"] .screening-flag-coach {
  display: none;
}

@media (min-width: 901px) {
  /* Declared unconditionally so the loop-complete state has something to transition FROM. */
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-out-modal,
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-phone-shell,
  body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-phone,
  body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-screen {
    transition:
      background-color 520ms ease,
      border-color 520ms ease,
      box-shadow 520ms ease;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-out-copy,
  body.nifhr-screen [data-screen-id="nIfHr"] .screened-demo-screen > * {
    transition: opacity 520ms ease;
  }

  body.nifhr-screen.screening-step-screened.screening-phone-loop-complete
    [data-screen-id="nIfHr"] .screening-out-modal {
    background-color: transparent;
    border-color: transparent;
    box-shadow: none;
  }

  body.nifhr-screen.screening-step-screened.screening-phone-loop-complete
    [data-screen-id="nIfHr"] .screening-out-copy {
    opacity: 0;
  }

  body.nifhr-screen.screening-step-screened.screening-phone-loop-complete
    [data-screen-id="nIfHr"] .screening-phone-shell {
    background-color: transparent;
    box-shadow: none;
  }

  body.nifhr-screen.screening-step-screened.screening-phone-loop-complete
    [data-screen-id="nIfHr"] .screened-demo-phone,
  body.nifhr-screen.screening-step-screened.screening-phone-loop-complete
    [data-screen-id="nIfHr"] .screened-demo-screen {
    background-color: transparent;
  }

  /* Everything inside the screen goes, EXCEPT the replay overlay. */
  body.nifhr-screen.screening-step-screened.screening-phone-loop-complete
    [data-screen-id="nIfHr"] .screened-demo-screen > *:not(.screened-demo-replay) {
    opacity: 0;
  }

  /* The replay overlay sheds its dark scrim and blur, leaving only the white pill. */
  body.nifhr-screen.screening-step-screened.screening-phone-loop-complete
    [data-screen-id="nIfHr"] .screened-demo-replay {
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  /* The card itself: the same house dark contract and the same slot as the 'live' card above, so
     the two coaching beats read as one language and the tail lands on the same drawer panel (now
     red). No CTA, by explicit instruction. */
  body.nifhr-screen.screening-step-screened.screening-phone-loop-complete
    [data-screen-id="nIfHr"] .screening-flag-coach {
    display: block;
    position: absolute;
    top: 136px;
    right: calc(max(33vw, 460px) - 12px);
    z-index: 4;
    width: 360px;
    padding: 26px;
    border: 1px solid rgba(148, 163, 184, 0.14);
    border-radius: 14px;
    background: #0f1726;
    color: #f8fafc;
    box-shadow:
      0 28px 72px rgba(15, 23, 42, 0.42),
      0 0 0 1px rgba(124, 58, 237, 0.16);
    animation:
      screeningFlagCoachIn 420ms ease 520ms both,
      nifhrBaseFloat 2200ms linear 520ms infinite both;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-flag-coach h2 {
    margin: 0;
    color: #f8fafc;
    font-size: var(--sheet-title-size);
    line-height: var(--sheet-title-lh);
    font-weight: var(--sheet-title-weight);
    letter-spacing: var(--sheet-title-ls);
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-flag-coach p {
    margin: 14px 0 0;
    color: #cbd5e1;
    font-size: 13px;
    line-height: 20px;
    font-weight: var(--fw-semibold);
  }

  /* ENTRY #181 SUPERSEDES this card's `::before` tail too, for the same reason. */
}

@keyframes screeningFlagCoachIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

@media (min-width: 901px) and (prefers-reduced-motion: reduce) {
  body.nifhr-screen.screening-step-screened.screening-phone-loop-complete
    [data-screen-id="nIfHr"] .screening-flag-coach {
    animation: none;
  }
}


/* ==========================================================================
   ENTRY #181 (user call, DESKTOP >=901px). Two things.

   (1) `.screening-qr-modal` ("Ask what AI can't", Step 6 of 10) becomes the house dark coaching
       card, in the SAME slot as #180's 'live' card, so the three screening beats read as one voice.

   (2) ONE SHARED TAIL for all three cards, replacing the per-card `::before`s #180 shipped.
       ⚠️ THIS IS THE WHOLE POINT OF THE ENTRY AND IT IS A STACKING PROBLEM, NOT A SIZING ONE.
       The user requires the point to touch the LEFT BORDER OF THE GREEN QUESTIONNAIRE PANEL, not
       the drawer's left edge. That border sits 25px inside the drawer (measured constant at
       901/1024/1280/1440/1680/1920). The cards live in `.nifhr-screening-overlay`, which is
       `position: fixed; z-index: 9800` and therefore a STACKING CONTEXT, while the drawer is
       z-index 9900. Nothing inside the overlay can paint over the drawer: a `::before` stretched to
       49px was measured being clipped dead on the drawer's left edge. The tail is therefore a
       SIBLING of the overlay and the drawer, at z-index 9950.

   ⚠️ THE THREE CARDS SHARE ONE RIGHT EDGE BY ARITHMETIC, WHICH IS WHY ONE TAIL CAN SERVE ALL THREE.
   The qr and live cards are overlay grid items using `justify-self: end` plus
   `margin-right: calc(max(33vw, 460px) - 8px)`, and the overlay contributes 32px of right padding,
   so their right edge is `100vw - max(33vw, 460px) - 24px`. The flag coach is absolutely positioned
   with `right: calc(max(33vw, 460px) + 24px)`, which is the same value. The tail's own left edge
   resolves to that identical expression, so base and card abut EXACTLY (measured 940.81 vs 940.81
   at 1440, no sub-pixel seam).

   ⚠️ `max(33vw, 460px)` IS NOT A FLOURISH: the drawer is `width: 33vw` with `min-width: 460px`, so
   below ~1394px the min-width wins. Anything that reserves a bare 33vw slides under the drawer.

   ⚠️ THE FLOAT MUST BE MIRRORED ONTO THE TAIL. The flag-coach beat floats on `nifhrBaseFloat`; a
   static tail would visibly separate from it by the float's full 7px amplitude. Both rules are
   applied by the same class landing in the same frame, and the guard samples both transforms at one
   instant and requires them equal, so a phase drift cannot pass.

   ⚠️ EVERY DARK OVERRIDE BELOW IS SCOPED TO `.screening-qr-modal` ON PURPOSE. Two of this card's
   selectors are shared with cards that stay WHITE: `.screening-eyebrow` is also the "Screened out"
   danger eyebrow on `.screening-out-modal`, and `.screening-qr-code` (plus its `span`s) is also the
   references card's `.screening-try-qr-code`. Editing either shared rule in place would repaint a
   card nobody asked to touch.

   ⚠️ THE QR PLATE IS DELIBERATELY LEFT ALONE. `.screening-qr-code` already carries its own
   `background: var(--surface)` box, so the black-on-white code keeps its white plate on the navy
   card with no new rule. Do not "fix" it by painting the plate dark: the squares are `#111827`.
   ========================================================================== */
@media (min-width: 901px) {
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-modal {
    width: min(420px, calc(100vw - max(33vw, 460px) - 20px));
    min-height: 0;
    justify-self: end;
    align-self: start;
    margin-top: 104px;
    margin-right: calc(max(33vw, 460px) - 44px);
    gap: 18px;
    padding: 26px;
    border: 1px solid rgba(148, 163, 184, 0.14);
    border-radius: 14px;
    background: #0f1726;
    color: #f8fafc;
    box-shadow:
      0 28px 72px rgba(15, 23, 42, 0.42),
      0 0 0 1px rgba(124, 58, 237, 0.16);
    /* The base rule's `translateX(-9vw)` existed only to shove a CENTRED modal clear of the drawer.
       The margin above now does that exactly, so the nudge has to go or the card lands 9vw left of
       its own slot and the shared tail no longer meets it. */
    transform: none;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-modal .screening-qr-copy {
    max-width: none;
  }

  /* "STEP 6 OF 10". `--brand-dark` (`#5B21B6` since #374, `#4C1D95` before it) is unreadable on
     navy at either value: 1.64:1 then, 2.00:1 now, against a 4.5:1 bar. violet-300 keeps it in the
     brand family. Scoped, so the "Screened out" eyebrow on the white card is untouched. */
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-modal .screening-eyebrow {
    color: #C4B5FD;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-modal .screening-qr-copy h2 {
    color: #f8fafc;
    font-size: var(--sheet-title-size);
    line-height: var(--sheet-title-lh);
    font-weight: var(--sheet-title-weight);
    letter-spacing: var(--sheet-title-ls);
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-modal .screening-qr-copy p {
    color: #cbd5e1;
    font-size: 13px;
    line-height: 20px;
    font-weight: var(--fw-semibold);
  }

  /* The tick list. The mint pill keeps its own background so it still reads as a tick; only the
     label colour has to move off `--text-strong` (#374151), which is invisible on navy. */
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-modal .screening-qr-copy li {
    color: #cbd5e1;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-modal .screening-qr-box strong {
    color: #f8fafc;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-qr-modal .screening-qr-box small {
    color: #94a3b8;
  }

  /* ---- the shared tail ----
     ENTRY #182 adds `screening-step-selfscreen` to this list. It needs no new geometry at all: the
     references panel that replaces the questionnaire panel in that step sits at the SAME 25px inset
     from the drawer's left edge (measured 25.00 at 901/1024/1280/1440/1680/1920) and spans y 180 to
     585, so the existing tip at y 244 is already inside it. */
  body.nifhr-screen.screening-step-qr [data-screen-id="nIfHr"] .screening-coach-tail,
  body.nifhr-screen.screening-step-live [data-screen-id="nIfHr"] .screening-coach-tail,
  body.nifhr-screen.screening-step-selfscreen [data-screen-id="nIfHr"] .screening-coach-tail,
  body.nifhr-screen.screening-step-screened.screening-phone-loop-complete
    [data-screen-id="nIfHr"] .screening-coach-tail {
    display: block;
    position: fixed;
    z-index: 9950;
    top: 235px;
    right: calc(max(33vw, 460px) - 25px);
    width: 15px;
    height: 18px;
    background: #0f1726;
    clip-path: polygon(0 0, 100% 50%, 0 100%);
    pointer-events: none;
  }

  body.nifhr-screen.screening-step-screened.screening-phone-loop-complete
    [data-screen-id="nIfHr"] .screening-coach-tail {
    animation:
      screeningFlagCoachIn 420ms ease 520ms both,
      nifhrBaseFloat 2200ms linear 520ms infinite both;
  }
}

/* Hidden at every other width and in every other state. A base-tier `display: none` keeps mobile
   and tablet byte-identical without needing a counter-rule. */
body.nifhr-screen [data-screen-id="nIfHr"] .screening-coach-tail {
  display: none;
}

@media (min-width: 901px) and (prefers-reduced-motion: reduce) {
  body.nifhr-screen.screening-step-screened.screening-phone-loop-complete
    [data-screen-id="nIfHr"] .screening-coach-tail {
    animation: none;
  }
}


/* ==========================================================================
   ENTRY #181b (user correction). "Use our standard tail and move the coaching card closer", and
   "why is there a gradient on this card, we don't use this anywhere else".

   The gradient is gone: all three screening coach cards are flat `#0f1726` (the purple radial that
   `.nifhr-coach` carries is NOT reproduced here). The tail is back to the house 15x18.

   ⚠️ THOSE TWO ASKS TOGETHER FORCED A STACKING CHANGE, AND WITHOUT IT THEY ARE GEOMETRICALLY
   IMPOSSIBLE. The point must touch the questionnaire panel's left border, which is 25px INSIDE the
   drawer. A 15px tail therefore needs the card's right edge 13px from that border, i.e. 12px INSIDE
   the drawer. But the cards live in `.nifhr-screening-overlay` (z-index 9800, a stacking context)
   and the drawer is 9900, so the card's last 12px would paint UNDER the drawer and the visible card
   edge would stay pinned at the drawer's left edge, leaving a 10px white gutter between the card
   and its own tail. Lengthening the tail to bridge that gutter is exactly what the user rejected.

   So in these three desktop states only, the overlay is raised ABOVE the drawer. Its scrim cannot
   come with it or the drawer would be dimmed, so the scrim moves to `::before` and is CLIPPED to
   stop exactly at the drawer's left edge. `clip-path` on a pseudo-element clips only that pseudo,
   never the overlay's children, so the cards keep painting across the full width. The rendered
   result is byte-identical to the old scrim: same colour, same blur, same undimmed drawer.

   ⚠️ SCOPED TO THE THREE COACH STATES AND TO >=901px ON PURPOSE. Five other screening steps share
   this overlay (try, feature, schedule, schedule-demo, selfscreen) and the whole mobile tier depends
   on its padding and `place-items`; none of them is touched.

   ⚠️ `z-index: -1` ON THE SCRIM PSEUDO IS REQUIRED. Without it the scrim paints over the panels
   inside the overlay's own stacking context. It puts the pseudo behind its host's children while
   staying inside the raised context, which is why the clip is what keeps the drawer clear.
   ========================================================================== */
@media (min-width: 901px) {
  body.nifhr-screen.screening-step-qr [data-screen-id="nIfHr"] .nifhr-screening-overlay,
  body.nifhr-screen.screening-step-live [data-screen-id="nIfHr"] .nifhr-screening-overlay,
  body.nifhr-screen.screening-step-selfscreen [data-screen-id="nIfHr"] .nifhr-screening-overlay,
  body.nifhr-screen.screening-step-screened.screening-phone-loop-complete
    [data-screen-id="nIfHr"] .nifhr-screening-overlay {
    z-index: 9950;
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    /* ⚠️ REQUIRED, AND A GUARD CAUGHT ITS ABSENCE. The overlay's own box is `inset: 0`, so once it
       is raised above the drawer it covers the ENTIRE drawer and swallows every click on it,
       including "Continue demo", the only way out of the screened-out step. The overlay goes
       click-through and the two things that must still block or receive clicks opt back in below:
       the clipped scrim (which stops at the drawer edge, so it blocks the dimmed area only) and the
       overlay's own children.
       ⚠️ `!important` IS REQUIRED, not defensive: `body.screening-result-visible ...
       .nifhr-screening-overlay` (the belt-and-braces force-show for the screened-out step) sets
       `pointer-events: auto !important`, so a plain declaration here loses in exactly one of the
       three states, and it is the state whose drawer footer carries the only exit. */
    pointer-events: none !important;
  }

  body.nifhr-screen.screening-step-qr [data-screen-id="nIfHr"] .nifhr-screening-overlay > *,
  body.nifhr-screen.screening-step-live [data-screen-id="nIfHr"] .nifhr-screening-overlay > *,
  body.nifhr-screen.screening-step-selfscreen [data-screen-id="nIfHr"] .nifhr-screening-overlay > *,
  body.nifhr-screen.screening-step-screened.screening-phone-loop-complete
    [data-screen-id="nIfHr"] .nifhr-screening-overlay > * {
    pointer-events: auto;
  }

  body.nifhr-screen.screening-step-qr [data-screen-id="nIfHr"] .nifhr-screening-overlay::before,
  body.nifhr-screen.screening-step-live [data-screen-id="nIfHr"] .nifhr-screening-overlay::before,
  body.nifhr-screen.screening-step-selfscreen
    [data-screen-id="nIfHr"] .nifhr-screening-overlay::before,
  body.nifhr-screen.screening-step-screened.screening-phone-loop-complete
    [data-screen-id="nIfHr"] .nifhr-screening-overlay::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    background: rgba(17, 24, 39, 0.64);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    clip-path: inset(0 max(33vw, 460px) 0 0);
    /* The scrim, not the overlay, is now what blocks clicks to the page behind. Because it is
       clipped at the drawer edge it can never block the drawer. */
    pointer-events: auto;
  }

  /* The tail now sits above a card that is itself above the drawer, so it only has to beat the
     card. Keeping it at 9950+ costs nothing and keeps one number to reason about. */
  body.nifhr-screen.screening-step-qr [data-screen-id="nIfHr"] .screening-coach-tail,
  body.nifhr-screen.screening-step-live [data-screen-id="nIfHr"] .screening-coach-tail,
  body.nifhr-screen.screening-step-selfscreen [data-screen-id="nIfHr"] .screening-coach-tail,
  body.nifhr-screen.screening-step-screened.screening-phone-loop-complete
    [data-screen-id="nIfHr"] .screening-coach-tail {
    z-index: 9960;
  }
}


/* ==========================================================================
   ENTRY #182 (user call, DESKTOP >=901px). The REFERENCES sequence's two coaching beats become
   the same house dark coaching card the SCREENING sequence got in #180/#181/#181b, so the whole
   guided run reads in one voice. Two annotations, two different asks:

   (A) `.screening-try-modal` ("Send every reference in one click", step `try`) -> "same as the
       screening change modal to our usual dark coaching card". PAINT ONLY. It keeps its width, its
       two-column grid and its dead-centre position.
       ⚠️ IT MUST STAY CENTRED, AND THAT IS NOT AN OVERSIGHT. The drawer is OFF-CANVAS during this
       step (`screening-step-try` is absent from the drawer's visible-state list at :9987, measured
       aria-hidden="true", opacity 0, left edge past the viewport at all six widths), so there is no
       panel for a tail to point at and no gutter to reserve. Giving this card the drawer-facing
       margin would shove it off centre toward a drawer nobody can see. `#70`'s centring test stays
       valid on purpose.

   (B) `.screening-follow-modal` ("David's answers are landing now", step `selfscreen`) -> "the same
       positioning as the screening one we recently did with the tail etc". Here the drawer IS open,
       so this card takes the FULL #181b contract: the dark paint, the `.screening-live-modal` slot,
       the shared `.screening-coach-tail`, and the raised-overlay/clipped-scrim stacking that lets
       the card's last 12px cross the drawer's edge. Those four state lists are extended in place
       above rather than duplicated here.

   ⚠️ THE TAIL NEEDED NO NEW GEOMETRY, WHICH IS WHY THERE IS NO SECOND TAIL RULE. In `selfscreen`
   the drawer swaps `.screening-question-panel` for `.screening-next-panel` (:12242), but that panel
   sits at the SAME 25px inset from the drawer's left edge - measured 25.00 at 901/1024/1280/1440/
   1680/1920 - and spans y 180 to 585, so the existing tip at y 244 is already inside it. One tail,
   one rule, zero new constants.

   ⚠️ THE `margin-right` FIX IS A REAL PRE-EXISTING BUG, THE SAME ONE #180 FOUND ON THE 'live' CARD.
   `.screening-follow-modal` (:12195) still carried the un-corrected `calc(33vw + 24px)` against a
   drawer that is `width: 33vw; min-width: 460px`. Measured BEFORE this entry: the card's right edge
   was 106px INSIDE the drawer at 901 and 66px inside at 1024, hidden only by the drawer's z-index.
   Mirroring `max(33vw, 460px)` puts its right edge at exactly `drawer.left + 12` at every width,
   the same number the 'live' card lands on.

   ⚠️ EVERY OVERRIDE BELOW IS SCOPED TO ITS OWN CARD, because two of the try card's classes are
   SHARED with cards that must stay light. `.screening-qr-code` (plus its `span`s) is also the
   screening card's QR plate, and `.screening-action` is also the "Got it" button on the qr and live
   cards. Neither bare class is touched.

   ⚠️ THE QR PLATE STAYS WHITE, DELIBERATELY. `.screening-try-qr-code` keeps its own
   `background: var(--surface)` because the code squares are `#111827` and would vanish on navy.
   #181's own guard already asserts this plate is `rgb(255, 255, 255)` as its cross-check that the
   references card was not collaterally repainted, so painting it dark would redden a screening test.

   ⚠️ THE CTA STAYS BRAND PURPLE, matching what #181 chose for the qr card's "Got it": the pill is
   `.screening-action`, shared, and a purple pill on navy is the same treatment every other dark
   coaching card in this demo already ships.
   ========================================================================== */
@media (min-width: 901px) {
  /* ---- (A) the references intro card: paint only, still centred ---- */
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-modal {
    padding: 26px;
    border: 1px solid rgba(148, 163, 184, 0.14);
    border-radius: 14px;
    background: #0f1726;
    color: #f8fafc;
    box-shadow:
      0 28px 72px rgba(15, 23, 42, 0.42),
      0 0 0 1px rgba(124, 58, 237, 0.16);
  }

  /* The house headline treatment (#174's `--sheet-title-*`). This family carries no measured
     ceiling: the card is 724px wide, so the title holds one row at every desktop width. */
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-title {
    color: #f8fafc;
    font-size: var(--sheet-title-size);
    line-height: var(--sheet-title-lh);
    font-weight: var(--sheet-title-weight);
    letter-spacing: var(--sheet-title-ls);
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-copy p {
    color: #cbd5e1;
    font-size: 13px;
    line-height: 20px;
    font-weight: var(--fw-semibold);
  }

  /* The QR sub-panel was a LIGHT inset (`var(--bg-subtle)` on a `var(--border)` hairline), which
     reads as a hole punched in the navy card. It becomes a translucent slate inset in the same
     family as the card's own hairline. */
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-qr-card {
    border-color: rgba(148, 163, 184, 0.16);
    background: rgba(148, 163, 184, 0.08);
  }

  /* "DAVID CHEN". `--brand-dark` (`#5B21B6` since #374, `#4C1D95` before it) is unreadable on navy
     at either value, 2.00:1 against a 4.5:1 bar; violet-300 keeps it in the brand
     family, the same substitution #181 made for the qr card's "STEP 6 OF 10" eyebrow. */
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-qr-head span {
    color: #C4B5FD;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-qr-card > strong {
    color: #f8fafc;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-try-qr-card > small {
    color: #94a3b8;
  }

  /* ---- (B) the live-references card: the full #181b contract ---- */
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-follow-modal {
    width: min(420px, calc(100vw - max(33vw, 460px) - 20px));
    margin-right: calc(max(33vw, 460px) - 44px);
    padding: 26px;
    border: 1px solid rgba(148, 163, 184, 0.14);
    border-radius: 14px;
    background: #0f1726;
    color: #f8fafc;
    box-shadow:
      0 28px 72px rgba(15, 23, 42, 0.42),
      0 0 0 1px rgba(124, 58, 237, 0.16);
    overflow: visible;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-follow-modal h2 {
    color: #f8fafc;
    font-size: var(--sheet-title-size);
    line-height: var(--sheet-title-lh);
    font-weight: var(--sheet-title-weight);
    letter-spacing: var(--sheet-title-ls);
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .screening-follow-modal p {
    color: #cbd5e1;
    font-size: 13px;
    line-height: 20px;
    font-weight: var(--fw-semibold);
  }

  /* "LIVE PROFILE UPDATE" keeps its pulsing red dot but cannot keep `var(--success-text)` (#059669),
     which is 2.3:1 against navy. #6EE7B7 is the same green family at 9:1 - the identical
     substitution #180 made on the 'live' card, whose scoped rule never reached this one. */
  body.nifhr-screen [data-screen-id="nIfHr"] .screening-follow-modal .screening-live-pill {
    color: #6EE7B7;
  }
}

/* =====================================================================================
   ENTRY #195 (2026-07-27, user call, MOBILE AND TABLET ONLY)
   (A) "Make your edits" is vertically centred once the drawer head has collapsed.

   MEASURED root cause, not guessed: `.company-celebration` computes `align-items: flex-start`
   at <=900px (:16694, the (B) rule, which top-aligns the badge with the FIRST ROW of a
   multi-row title and says it "applies in BOTH states since #121"). In the COLLAPSED state
   the title is a single 20.4px line next to a 44px badge, so top-aligning puts the text
   12.0px ABOVE the badge's centre. Measured 2026-07-27 at 390 and 834: text centre 131.1 vs
   badge centre 143.0. DESKTOP was already correct at -0.2px, which is why the user scoped
   this to mobile and tablet: there is nothing to fix above 900px.

   Scoped to `.is-collapsed-head` on purpose. The EXPANDED state still has the multi-row
   title the (B) rule exists for, so flipping the row wholesale would reintroduce exactly the
   misalignment (B) was written to fix. Higher specificity (0,3,0) than (B)'s (0,1,0), and in
   the tail block per the house cascade law.
   ===================================================================================== */
@media (max-width: 900px) {
  .company-drawer-body.is-collapsed-head .company-celebration { align-items: center; }
}

/* =====================================================================================
   ENTRY #195 (B) (2026-07-27, user call, MOBILE AND TABLET ONLY)
   Both toasts SLIDE IN FROM THE RIGHT and COVER the "Make your edits" panel.

   Was: both are `position: fixed; right: 28px; bottom: 28px` and rise on a translateY(10px),
   i.e. they sat at the bottom of the screen, far from the field the viewer just tapped.
   Now, at <= 900px only: parked over `.company-celebration` using the box stamped live by
   stampToastCover() in rj0wj.html, and entering on translateX.

   ⚠️ BOTH toasts get this, not just the one in the screenshot. `.demo-detail-toast` fires from
   the "missing" fields and `.company-source-toast` from the "known" ones, and they are NOT
   mutually exclusive: measured, clicking a known field then a missing one within 6.2s leaves
   both `is-visible` with overlapping boxes (they share `right:28px; bottom:28px`). Giving only
   one of them the new position would have turned an overlap into a split-brain layout.

   ⚠️ The exit transform is expressed in the toast's OWN width plus its left inset, so it lands
   just past the right edge at any viewport, rather than a fixed px guess that would leave a
   sliver showing on a wide tablet or overshoot on a 320px phone.

   ⚠️ `position: fixed` resolves against the VIEWPORT here, verified: both toasts are direct
   children of <body>, and neither <body> nor <html> carries a transform/filter/perspective/
   contain/will-change. If any ever does, this becomes the #109/#112 containing-block trap and
   the stamped rect would be measured in the wrong reference frame.
   ===================================================================================== */
@media (max-width: 900px) {
  .demo-detail-toast,
  .company-source-toast {
    top: var(--toast-cover-top, 110px);
    left: var(--toast-cover-left, 16px);
    right: auto;
    bottom: auto;
    width: var(--toast-cover-w, calc(100vw - 32px));
    transform: translateX(calc(100% + var(--toast-cover-left, 16px) + 24px));
    transition: opacity 200ms ease, transform 340ms cubic-bezier(.22, 1, .36, 1);
  }
  .demo-detail-toast.is-visible,
  .company-source-toast.is-visible {
    transform: translateX(0);
  }
  @media (prefers-reduced-motion: reduce) {
    .demo-detail-toast,
    .company-source-toast { transition: opacity 200ms ease; transform: translateX(0); }
  }
}

/* ==========================================================================================
   ENTRY #206 - the MOBILE/TABLET messenger feature walk becomes four DARK TOOLTIPS that
   AIM at the control they name, and advance only when the viewer has actually done the thing.
   MOBILE AND TABLET ONLY (<=900px). Desktop keeps `.messenger-feature-coach` untouched: it
   reads the SAME shared copy array (window.HIRELLO_MESSENGER_STEPS), which is why the mobile
   step table is a SEPARATE local table in the mobile IIFE. Reordering the shared array would
   have moved desktop too.

   The look is lifted verbatim from #203's `.mm-tap-hint` (:20338): --text-primary fill,
   --text-inverse ink, --shadow-popover, --radius-md, 9px/14px padding, 800 14px/20px,
   -0.01em. The 16x8 `clip-path` triangle and the 8px gap are also #203's, and the pairing is
   what makes the tail's TIP TOUCH the target's border (#178's contract) instead of floating
   clear of it.

   ⚠️ COUNT THE CLASSES. The old pill's `bottom` is set TWICE: once at (0,3,1) on :16568 and
   again at (0,4,1) on :16581 (`.mm-chat-open`). A single (0,3,1) `bottom: auto` here loses to
   :16581 and the tooltip silently parks 80px off the composer, so the `.mm-chat-open` twin
   below is load-bearing, not belt-and-braces.

   ⚠️ THE TAIL DIRECTION IS ON `body`, NOT THE COACH. `placeCoach()` writes
   `body.dataset.mmCoachTail`, matching the `[data-move-coach-tail]` precedent at :19246. The
   x/y/tail-x customs go on the COACH itself so nothing else on the page can read them.
   ========================================================================================== */
@media (max-width: 900px) {
  body.nifhr-screen.screening-step-messenger .mm-coach {
    display: block;
    left: var(--mm-coach-x, 12px);
    top: var(--mm-coach-y, 12px);
    right: auto;
    bottom: auto;
    max-width: min(272px, calc(100vw - 24px));
    padding: 9px 14px;
    border-radius: var(--radius-md);
    background: var(--text-primary);
    color: var(--text-inverse);
    box-shadow: var(--shadow-popover);
    font-size: var(--fs-md);
    line-height: 20px;
    font-weight: var(--fw-extrabold);
    font-style: normal;
    letter-spacing: -0.01em;
    pointer-events: none;
  }
  /* (0,4,1), to beat :16581's `.mm-chat-open` bottom. */
  body.nifhr-screen.screening-step-messenger.mm-chat-open .mm-coach { bottom: auto; top: var(--mm-coach-y, 12px); }

  /* The reference pill is text only: the pulsing dot and the Next button both go. The walk is
     action-gated now, so a Next button would be an unwired affordance sitting on the exit. */
  body.nifhr-screen.screening-step-messenger .mm-coach-dot,
  body.nifhr-screen.screening-step-messenger .mm-coach-next { display: none; }

  /* Steps 1-3 must let the tap through to the composer / the + / the sheet item underneath.
     Only the terminal `email` beat is tappable, because it is the one with nothing to operate. */
  body.nifhr-screen.screening-step-messenger .mm-coach.is-shown[data-mm-step="email"] {
    pointer-events: auto;
    cursor: pointer;
  }

  body.nifhr-screen.screening-step-messenger .mm-coach::before {
    content: "";
    display: block;
    position: absolute;
    left: var(--mm-coach-tail-x, 16px);
    width: 16px;
    height: 8px;
    background: var(--text-primary);
    pointer-events: none;
  }
  body.nifhr-screen.screening-step-messenger[data-mm-coach-tail="up"] .mm-coach::before {
    top: -8px;
    clip-path: polygon(0 100%, 50% 0, 100% 100%);
  }
  body.nifhr-screen.screening-step-messenger[data-mm-coach-tail="down"] .mm-coach::before {
    bottom: -8px;
    clip-path: polygon(0 0, 50% 100%, 100% 0);
  }
}

/* ==========================================================================================
   ENTRY #207c - the "Next steps" panel, PORTED from collaborator-invite.html (entry #141,
   Pencil node rKFoM) into the shared system so the MESSENGER segment has somewhere to go.
   A right drawer at >=901px, a full-viewport screen below. Markup is byte-for-byte the #141
   panel; only the following changed, and each is deliberate:

   - the breakpoint 1024 -> 900, to match nifhr's OWN desktop/mobile split;
   - `--drawer-scrim`  -> `var(--scrim-dim)`, the ONE scrim token (entry #111);
   - `--ink`           -> `var(--text-primary)` (identical #111827);
   - `--phone-safe-bottom` -> `env(safe-area-inset-bottom, 0px)`, the real inset;
   - `font-family: var(--font-ui)` DROPPED: hirello-app.css already sets Inter globally, and
     that token does not exist here, so it would have resolved to nothing.
   - ENTRY #218, THE SIXTH DELTA, real since #207c and undocumented until now: this copy carries
     an attribute-hidden display rule covering BOTH the panel and the toast (the first rule below
     this block), which the collaborator-invite copy does not have. ⚠️ That rule is described here
     rather than quoted ON PURPOSE: a comment holding the literal text makes every later grep and
     every `ctl.py` control ambiguous, which is exactly how the control for this delta was refused
     the first time. It is an ADAPTATION, not drift, and the two copies
     must NOT be "synced" by deleting it. The two files drive visibility by different means:
     nifhr's markup carries `hidden` at rest and its controller toggles `panel.hidden` /
     `toast.hidden`, so this rule is what stops the panel sitting over every other segment
     nifhr serves from the same document. `.ns-panel`'s own `display: grid` below is an AUTHOR
     rule and outranks the UA's `[hidden] { display: none }`, so without this line the attribute
     would not hide it. collaborator-invite never sets `hidden` on those two nodes at all: it
     uses `is-open` / `is-show` classes plus `aria-hidden`, so the rule would be inert there.
     (`.ns-scrim[hidden]` exists in BOTH, because both files do toggle `hidden` on the scrim.)

   The remaining four tokens genuinely do not exist in this stylesheet. They are declared on
   the panel's own elements rather than on `:root`, because a `:root` change forces EVERY
   screen's cache tag to move and none of these are wanted anywhere else.

   ⚠️ `#218D` in tests/collaborator.spec.js is the SYNC GUARD over these two copies. It compares
   them rule by rule through the six-delta substitution table above, so a genuine drift reddens
   while every intended difference stays green. Add a seventh delta THERE as well as here, or the
   guard will call your deliberate change a regression.
   ========================================================================================== */
/* ⚠️⚠️ ENTRY #371: FOUR OF THESE FIVE MOVED TO `:root` AND THE FIFTH BECAME AN ALIAS.
   They were declared on this selector rather than at `:root` because, in #207c's words, a `:root`
   change forces EVERY screen's cache tag to move. That cost is paid by this entry anyway, and
   keeping them local had a measured price: `.company-profile-drawer` re-types the IDENTICAL shadow
   literal because `--drawer-shadow` was not reachable from rj0wj, and `collaborator-invite.html`
   re-declares the identical `--warning` / `--warning-tint` / `--indeed-blue` at its own `:root` for
   its phone scene. That is two copies of one colour in two files with nothing holding them equal.
   SUPERSEDED, quoted: `--drawer-shadow: -12px 0 32px rgba(0, 0, 0, 0.20); --indeed-blue: #2557A7;
   --accent-teal: #14B8A6; --warning: #F59E0B; --warning-tint: #FFEDD5;`
   ⚠️ `--accent-teal` WAS ALREADY WRONG BEFORE THIS ENTRY, and its own `:root` neighbour says so:
   entry #340 added `--cue-teal` with a comment stating it existed so a generic consumer would have
   "a legal token to point at" instead of writing the hex, and this line went on writing the hex.
   It is kept as a NAME rather than deleted, because the panel's rules read `--accent-teal` and the
   name says what the colour is FOR here. */
.ns-panel, .ns-scrim, .ns-toast {
  --accent-teal: var(--cue-teal);
}
/* Base: hidden until nifhr opens it. The panel is only ever built on nifhr, but the rules live
   in the shared stylesheet, so nothing else can be caught by a stray `.ns-*` class. */
.ns-panel[hidden], .ns-toast[hidden] { display: none; }

.ns-scrim{position:fixed;inset:0;z-index:2147483032;background:var(--scrim-dim);opacity:0;transition:opacity var(--motion-ui) ease;touch-action:none;overscroll-behavior:none;}
.ns-scrim[hidden]{display:none;}
.ns-scrim.is-open{opacity:1;}
.ns-panel{position:fixed;inset:0 0 0 auto;z-index:2147483033;width:var(--drawer-w);min-width:var(--drawer-min-w);max-width:calc(100vw - var(--space-xl));height:100dvh;display:grid;grid-template-rows:auto minmax(0,1fr) auto;background:var(--surface);border-left:var(--border-w) solid var(--border);box-shadow:var(--shadow-drawer);transform:translateX(100%);transition:transform var(--motion-drawer) var(--ease-out);color:var(--text-primary);}
.ns-panel.is-open{transform:translateX(0);}
.ns-panel *{box-sizing:border-box;}

.ns-head{display:flex;align-items:center;justify-content:space-between;gap:var(--space-md);padding:calc(var(--space-md) + var(--space-2xs)) var(--space-lg);background:var(--surface);border-bottom:var(--border-w) solid var(--border);}
.ns-head h2{margin:0;font-size:var(--fs-3xl);font-weight:var(--fw-bold);line-height:var(--lh-tight);color:var(--text-primary);}
.ns-head p{margin:var(--space-3xs) 0 0;font-size:var(--fs-sm);line-height:var(--lh-snug);color:var(--text-secondary);}
.ns-close{flex-shrink:0;width:calc(var(--space-lg) + var(--space-sm));height:calc(var(--space-lg) + var(--space-sm));display:flex;align-items:center;justify-content:center;border:0;cursor:pointer;border-radius:var(--radius-pill);background:var(--bg-subtle);color:var(--text-secondary);}
.ns-close svg{width:var(--fs-2xl);height:var(--fs-2xl);}

.ns-body{padding:var(--space-lg);display:flex;flex-direction:column;gap:var(--space-md);overflow:auto;}
.ns-card{border-radius:var(--radius-lg);padding:var(--space-md);display:flex;flex-direction:column;gap:calc(var(--space-xs) + var(--space-3xs));}
.ns-card--src{background:var(--success-bg);border:var(--border-w) solid var(--success);gap:var(--space-sm);}
.ns-card--white{background:var(--surface);border:var(--border-w) solid var(--border);}

.ns-src-top{display:flex;align-items:center;justify-content:space-between;gap:var(--space-sm);}
.ns-src-l{display:flex;align-items:center;gap:var(--space-sm);}
/* ⚠️⚠️ ENTRY #373: THE SOURCE MARK IS DELETED ENTIRELY, on her call: *"remove the logo of the scrape
   origin on the drawer as not needed you didnt need to add that element so remove it"*. The markup
   went from both carriers, so these five rules had nothing left to select and they go with it: a
   live rule with no element is how a deleted thing comes back on the next edit.
   SUPERSEDED, entry #372, quoted so the reasoning stays readable: `.ns-src-logo` was a 36px tile
   (`flex-shrink:0`, square, `--radius-sm`, a `--border-w` hairline, centred) holding
   `.ns-src-logo-img` (a 28px block image) and `.ns-src-logo-word` (a hidden, heavy, lowercase
   wordmark on `--ls-tight`), with two `:not([data-source-variant="seek"])` rules swapping which of
   the two showed. #372 built that to replace an even worse thing it inherited: a hardcoded `in` on
   `var(--indeed-blue)`, on a demo whose configured source is `seek.com.au`.
   ⚠️ `--ls-tight` had exactly one consumer, the wordmark above, and is deleted in the same edit.
   `--indeed-blue` has had none since #372 and is now deleted too.
   ⚠️ opening.html's own `data-employer-scraped-logo` brandmark is a DIFFERENT element on a page that
   never loads this stylesheet. It is untouched, and the SVG asset it uses stays. */
.ns-src-lab{display:flex;flex-direction:column;gap:var(--space-3xs);}
.ns-src-lab b{font-size:var(--fs-md);font-weight:var(--fw-bold);color:var(--success-text);}
.ns-src-lab span{font-size:var(--fs-sm);color:var(--success-text);}
/* ⚠️⚠️ ENTRY #373: `cursor: default` BECOMES `pointer`, AND THAT IS THE WHOLE POINT OF THE CHANGE.
   The comment above is SUPERSEDED. It read: *"View original carries no handler and never has. A
   pointer cursor on a control that does nothing is a false affordance, so it presents as static
   text. Wiring it is the change that would earn the cursor back."* She asked for it wired, so it
   is now a real `<a>` with a config-bound `href`, and it has earned the cursor back exactly as that
   comment said it would. `#221A` and `#221B` moved it from their dead list to their wired list. */
.ns-link{display:inline-flex;align-items:center;gap:var(--space-2xs);font-size:var(--fs-sm);font-weight:var(--fw-semibold);color:var(--brand);cursor:pointer;background:none;border:0;padding:0;}
.ns-link svg{width:var(--fs-sm);height:var(--fs-sm);}
.ns-divline{height:var(--border-w);background:var(--success);}
.ns-stat{display:flex;align-items:flex-start;gap:var(--space-xs);}
.ns-stat svg{flex-shrink:0;width:var(--fs-xl);height:var(--fs-xl);color:var(--success-text);margin-top:calc(var(--space-3xs) / 2);}
.ns-stat span{font-size:var(--fs-sm);line-height:var(--lh-normal);color:var(--success-text);}

.ns-nhd{display:flex;align-items:center;justify-content:space-between;gap:var(--space-xs);}
.ns-nhl{display:flex;align-items:center;gap:var(--space-xs);}
.ns-nbadge{flex-shrink:0;width:calc(var(--space-md) + var(--space-sm));height:calc(var(--space-md) + var(--space-sm));display:flex;align-items:center;justify-content:center;border-radius:var(--radius-sm);}
.ns-nbadge svg{width:var(--fs-md);height:var(--fs-md);}
.ns-nbadge--info{background:var(--info-bg);color:var(--info-text);}
.ns-nbadge--warn{background:var(--warning-bg);color:var(--warning-text);}
.ns-ntitle{font-size:var(--fs-md);font-weight:var(--fw-semibold);color:var(--text-primary);}
.ns-ndesc{font-size:var(--fs-sm);line-height:var(--lh-normal);color:var(--text-secondary);}
.ns-dot{display:inline-flex;align-items:center;gap:var(--space-2xs);}
.ns-dot i{width:calc(var(--space-2xs) + var(--space-3xs));height:calc(var(--space-2xs) + var(--space-3xs));border-radius:var(--radius-pill);background:var(--warning);}
.ns-dot span{font-size:var(--fs-2xs);font-weight:var(--fw-semibold);color:var(--warning-text);}
.ns-nfoot{display:flex;align-items:center;justify-content:space-between;}
.ns-avs{display:flex;}
.ns-av{width:var(--space-lg);height:var(--space-lg);display:flex;align-items:center;justify-content:center;border-radius:var(--radius-pill);border:var(--border-w-strong) solid var(--surface);font-size:var(--fs-2xs);font-weight:var(--fw-bold);margin-right:calc((var(--space-2xs) + var(--space-3xs)) * -1);}
.ns-av--k{background:var(--warning-tint);color:var(--warning-text);}
.ns-av--t{background:var(--info-bg);color:var(--info-text);}
.ns-av--more{background:var(--bg-subtle);color:var(--text-secondary);font-size:var(--fs-sm);}
/* Same as .ns-link above: Invite / Open editor / Open profile carry no handler, so they must not
   present as clickable. */
.ns-cta-link{display:inline-flex;align-items:center;gap:var(--space-2xs);font-size:var(--fs-md);font-weight:var(--fw-semibold);color:var(--brand);cursor:default;background:none;border:0;padding:0;}
.ns-cta-link svg{width:var(--fs-md);height:var(--fs-md);}
/* ⚠️ ENTRY #384: "Invite" is a real BUTTON now, not a text link, held permanently inactive. Her
   words: *"Set as a standard colour token button that is md3 39% opacity disabled"*. Three things
   are deliberate here and each has a reason that has bitten this project before:
   ⚠️ (1) THE TOKENS ARE `.ns-save`'s, NOT `.btn-primary`'s. This panel already carries its own
   button (`--brand` on `--surface`, `--radius-pill`, `--control-h`); reaching for the global
   primary would have introduced a second button system inside one drawer.
   ⚠️ (2) THE INACTIVE LOOK IS THE HOUSE ONE, COPIED NOT INVENTED: `opacity:.39` plus
   `cursor:default` plus `box-shadow:none`, the same three declarations `.job-card-intro-cta`,
   `.nhsq0-primary` and `.nhsq0-tab` already use, each of which also carries the hover-cancel below.
   The first draft of this markup reached for an `is-md3-inactive` class that DOES NOT EXIST
   anywhere in public/, which would have shipped a button at full strength.
   ⚠️ (3) THE HOVER-CANCEL IS NOT DECORATION. Without it a pointer lights a dead control up, which
   is the "unwired affordance" defect: a lit control that cannot act reads as "nothing happened".
   The `[disabled]` attribute is what actually blocks the click; `aria-disabled` does not.
   ⚠️ `.ns-cta-link` above is UNTOUCHED and still live: collaborator-invite.html carries three of
   them. Deleting its rules here would break that page, which is why #384 removes no CSS at all. */
.ns-cta-btn{display:inline-flex;align-items:center;gap:var(--space-2xs);min-height:var(--control-h);padding:0 var(--space-md);border:0;border-radius:var(--radius-pill);background:var(--brand);color:var(--surface);font-size:var(--fs-md);font-weight:var(--fw-semibold);line-height:var(--lh-tight);cursor:pointer;}
.ns-cta-btn svg{flex-shrink:0;width:var(--fs-md);height:var(--fs-md);}
.ns-cta-btn[disabled]{opacity:.39;cursor:default;box-shadow:none;}
.ns-cta-btn[disabled]:hover{background:var(--brand);}
/* The avatar cluster was the other half of this row; with one child left, `space-between` resolves
   to flex-start and the button would jump to the LEFT edge, where nothing was before. Pinning it
   to the end keeps it exactly where the old "Invite" link sat. */
.ns-nfoot--solo{justify-content:flex-end;}

.ns-next{background:var(--surface);border:var(--border-w) solid var(--border);border-radius:var(--radius-lg);padding:calc(var(--space-md) + var(--space-2xs));display:flex;flex-direction:column;gap:var(--space-md);}
.ns-next-hd h3{margin:0;font-size:var(--fs-2xl);font-weight:var(--fw-bold);color:var(--text-primary);}
.ns-next-hd p{margin:var(--space-2xs) 0 0;font-size:var(--fs-sm);line-height:var(--lh-normal);color:var(--text-secondary);}
.ns-opts{display:flex;flex-direction:column;gap:calc(var(--space-xs) + var(--space-3xs));}
.ns-opt{display:flex;align-items:flex-start;gap:var(--space-sm);border-radius:var(--radius-md);padding:var(--space-md);cursor:pointer;background:var(--surface);border:var(--border-w) solid var(--border);}
.ns-opt.is-selected{background:var(--brand-tint);border:var(--border-w-strong) solid var(--brand);}
.ns-radio{flex-shrink:0;width:calc(var(--space-md) + var(--space-2xs));height:calc(var(--space-md) + var(--space-2xs));display:flex;align-items:center;justify-content:center;border-radius:var(--radius-pill);background:var(--surface);border:var(--border-w-strong) solid var(--border-strong);}
.ns-opt.is-selected .ns-radio{border-color:var(--brand);}
.ns-radio i{width:calc(var(--space-xs) + var(--space-3xs));height:calc(var(--space-xs) + var(--space-3xs));border-radius:var(--radius-pill);background:var(--brand);opacity:0;}
.ns-opt.is-selected .ns-radio i{opacity:1;}
.ns-opt-body{display:flex;flex-direction:column;gap:calc(var(--space-2xs) + var(--space-3xs));}
.ns-opt-head{display:flex;align-items:center;justify-content:space-between;gap:var(--space-xs);}
.ns-opt-head b{font-size:var(--fs-md);font-weight:var(--fw-bold);color:var(--text-primary);}
.ns-free{flex-shrink:0;border-radius:var(--radius-pill);background:var(--success);color:var(--surface);font-size:var(--fs-2xs);font-weight:var(--fw-bold);letter-spacing:var(--ls-caps);padding:var(--space-2xs) var(--space-xs);}
.ns-opt-desc{font-size:var(--fs-sm);line-height:var(--lh-normal);color:var(--text-strong);}
.ns-opt-desc--muted{color:var(--text-secondary);}
.ns-opt-foot{display:flex;align-items:center;gap:var(--space-sm);flex-wrap:wrap;}
.ns-chip{display:inline-flex;align-items:center;gap:var(--space-2xs);font-size:var(--fs-xs);font-weight:var(--fw-medium);}
.ns-chip svg{width:var(--fs-xs);height:var(--fs-xs);}
.ns-chip--rec{color:var(--success-text);font-weight:var(--fw-bold);letter-spacing:var(--ls-caps);}
.ns-chip--wait span{color:var(--text-secondary);}
.ns-chip--wait svg{color:var(--text-tertiary);}
.ns-chip--saved{color:var(--text-secondary);}
.ns-chip--saved svg{color:var(--accent-teal);}
.ns-price{display:flex;align-items:baseline;gap:calc(var(--space-2xs) + var(--space-3xs));}
.ns-price b{font-size:var(--fs-3xl);font-weight:var(--fw-bold);color:var(--text-primary);}
.ns-price s{font-size:var(--fs-md);font-weight:var(--fw-medium);color:var(--text-tertiary);text-decoration:line-through;}
.ns-price span{font-size:var(--fs-sm);color:var(--text-secondary);}

.ns-foot{background:var(--surface);border-top:var(--border-w) solid var(--border);padding:var(--space-md) var(--space-lg);display:flex;flex-direction:column;gap:calc(var(--space-xs) + var(--space-3xs));}
/* ⚠️ ENTRY #385: `.ns-pay-cta` SHARES THIS DECLARATION RATHER THAN REUSING THE CLASS `.ns-save`,
   and that distinction was found by a guard, not by reasoning. The payment sheet's two
   buttons were first written as `class="ns-save"`, which silently made `#nsPanel .ns-save`
   match THREE elements instead of one and reddened `#221A`'s wired-control count 4 -> 6.
   `.ns-save` is the panel's PRIMARY-CTA identity and other guards count it; a second
   consumer of the look must not join that identity. Sharing the selector keeps one source
   for the look with two names for the roles. */
.ns-save,.ns-pay-cta{display:flex;align-items:center;justify-content:center;gap:var(--space-xs);width:100%;min-height:var(--control-h);border:0;cursor:pointer;border-radius:var(--radius-pill);background:var(--brand);color:var(--surface);font-size:var(--fs-lg);font-weight:var(--fw-bold);box-shadow:var(--shadow-float);}
.ns-save svg,.ns-pay-cta svg{width:var(--fs-xl);height:var(--fs-xl);flex-shrink:0;}
.ns-help{display:flex;align-items:center;justify-content:center;gap:calc(var(--space-2xs) + var(--space-3xs));color:var(--text-tertiary);}
.ns-help svg{width:var(--fs-xs);height:var(--fs-xs);}
.ns-help span{font-size:var(--fs-sm);color:var(--text-tertiary);}

/* ══ ENTRY #385: the payment sheet ═════════════════════════════════════════════════════════════
   ⚠️ THE WIDTH IS NOT DECLARED ANYWHERE, ON PURPOSE. `.ns-pay` is an ABSOLUTELY positioned child of
   `.ns-panel`, which is `position: fixed` and carries a `transform`, so it is the containing block.
   `left:0;right:0;bottom:0` therefore spans exactly the drawer at >=901px and exactly the full
   viewport at <=900px, where `.ns-panel` itself becomes full-bleed. Her ask was "for width of this
   drawer and for the mobile and tablet views"; one rule satisfies both and there is no second
   number that can drift away from `--drawer-w`.
   ⚠️ `.ns-panel` IS A 3-ROW GRID and this is out of flow, so it adds no row and cannot push the
   footer. It sits ABOVE the footer's stacking order by DOM order plus its own z-index.
   ⚠️ THE SCRIM IS A SIBLING INSIDE THE PANEL, not `position: fixed` over the document: the drawer
   behind it stays visible on desktop, which is what makes this read as a sheet rising inside the
   drawer rather than a second modal over the whole app.
   ⚠️ `visibility` RIDES THE TRANSITION rather than `display`, because a `display` swap kills the
   slide entirely; `hidden` is still toggled by the controller so the sheet is out of the a11y tree
   and out of hit-testing when it is down. An `opacity:0` overlay still eats taps on this project. */
.ns-pay-scrim{position:absolute;inset:0;z-index:40;background:rgba(2,6,23,.42);opacity:0;pointer-events:none;transition:opacity var(--motion-drawer) var(--ease-out);}
.ns-pay-scrim.is-open{opacity:1;pointer-events:auto;}
.ns-pay{position:absolute;left:0;right:0;bottom:0;z-index:41;display:flex;flex-direction:column;gap:var(--space-md);max-height:88%;overflow:auto;padding:var(--space-sm) var(--space-lg) calc(var(--space-lg) + env(safe-area-inset-bottom,0px));background:var(--surface);border-top:var(--border-w) solid var(--border);border-radius:var(--radius-sheet) var(--radius-sheet) 0 0;box-shadow:var(--shadow-drawer);transform:translateY(100%);transition:transform var(--motion-drawer) var(--ease-out);}
.ns-pay.is-open{transform:translateY(0);}
.ns-pay-grab{flex-shrink:0;align-self:center;width:calc(var(--space-lg) + var(--space-md));height:var(--space-2xs);border-radius:var(--radius-pill);background:var(--border);}
.ns-pay-head{display:flex;align-items:center;justify-content:space-between;gap:var(--space-md);}
.ns-pay-title{margin:0;font-size:var(--fs-2xl);font-weight:var(--fw-bold);line-height:var(--lh-tight);color:var(--text-primary);}
.ns-pay-close{flex-shrink:0;width:calc(var(--space-lg) + var(--space-sm));height:calc(var(--space-lg) + var(--space-sm));display:flex;align-items:center;justify-content:center;border:0;border-radius:var(--radius-pill);background:var(--bg-subtle);color:var(--text-secondary);cursor:pointer;}
.ns-pay-close svg{width:var(--fs-xl);height:var(--fs-xl);}
.ns-pay-step{display:flex;flex-direction:column;gap:var(--space-sm);}
.ns-pay-step[hidden]{display:none;}
.ns-pay-total{display:flex;align-items:center;justify-content:space-between;gap:var(--space-sm);padding:var(--space-sm) var(--space-md);border:var(--border-w) solid var(--border);border-radius:var(--radius-md);background:var(--bg-subtle);}
.ns-pay-total span{font-size:var(--fs-sm);color:var(--text-secondary);}
.ns-pay-total b{font-size:var(--fs-2xl);font-weight:var(--fw-bold);color:var(--text-primary);}
/* ⚠️ THE WALLET BUTTONS ARE BLACK AND WHITE RESPECTIVELY BECAUSE THAT IS THE VENDORS' OWN
   PRESENTATION, not because this app has a black button. They deliberately do not use `--brand`:
   a purple Apple Pay button is not a thing a viewer has ever seen, and the whole point of a wallet
   mark is that it is recognised before it is read. */
.ns-pay-wallet{display:flex;align-items:center;justify-content:center;gap:var(--space-xs);width:100%;min-height:var(--control-h);border:0;border-radius:var(--radius-pill);font-size:var(--fs-xl);font-weight:var(--fw-bold);cursor:pointer;}
/* ⚠️ TOKENS, NOT `#000`/`#FFF`, AND THIS IS THE PROJECT'S OWN PRECEDENT RATHER THAN A
   COMPROMISE. `#371C` forbids a hex literal in any `.ns-*` rule and reddened on the first
   version of this line. The tempting fix, a `--apple-black` token, is exactly what #372/#373
   rejected and then deleted when `--indeed-blue` existed for the same reason: this app's
   palette must not learn another company's brand. Apple specifies pure black for its own
   button; a demo mock takes the app's near-black instead, and the MARK is what carries the
   recognition. The four Google hexes stay on the SVG `fill` attributes in the markup, where
   they belong to the artwork rather than to the stylesheet. */
.ns-pay-wallet--apple{background:var(--text-primary);color:var(--surface);}
.ns-pay-wallet--apple svg{width:var(--fs-2xl);height:var(--fs-2xl);}
.ns-pay-wallet--google{background:var(--surface);color:var(--text-primary);border:var(--border-w) solid var(--border);}
.ns-pay-wallet--google svg{width:var(--fs-xl);height:var(--fs-xl);}
.ns-pay-or{display:flex;align-items:center;gap:var(--space-sm);color:var(--text-tertiary);}
.ns-pay-or::before,.ns-pay-or::after{content:"";flex:1;height:var(--border-w);background:var(--border);}
.ns-pay-or span{font-size:var(--fs-sm);}
.ns-pay-alt{width:100%;min-height:var(--control-h);border:var(--border-w) solid var(--border);border-radius:var(--radius-pill);background:var(--surface);color:var(--text-primary);font-size:var(--fs-md);font-weight:var(--fw-semibold);cursor:pointer;}
.ns-pay-alt:hover{background:var(--bg-subtle);}
.ns-pay-field{display:flex;flex-direction:column;gap:var(--space-2xs);flex:1;min-width:0;}
.ns-pay-lab{font-size:var(--fs-sm);font-weight:var(--fw-semibold);color:var(--text-secondary);}
/* ⚠️ `--fs-xl` IS 16px AND THAT IS LOAD BEARING, not a taste call: iOS Safari zooms ANY input whose
   font size is under 16px the moment it takes focus, and this sheet exists for the mobile and
   tablet views. `--fs-lg` is 15px and would zoom. `min-width:0` on the field keeps a two-up row
   from overflowing, which a flex child does by default. */
.ns-pay-input{width:100%;min-width:0;min-height:var(--control-h);padding:0 var(--space-md);border:var(--border-w) solid var(--border);border-radius:var(--radius-md);background:var(--surface);color:var(--text-primary);font-family:inherit;font-size:var(--fs-xl);}
.ns-pay-input::placeholder{color:var(--text-tertiary);}
/* ⚠️ THE FOCUS RING IS DERIVED, NOT TYPED. It was `3px` and `#371C` reddened on it within one
   run: that guard forbids a px literal anywhere in a `.ns-*` rule, and it was right to. There
   is no `--focus-ring` token on this project, so the ring is built from the border token that
   already exists rather than from a fourth number nobody declared. */
.ns-pay-input:focus{outline:none;border-color:var(--brand);box-shadow:0 0 0 var(--border-w-strong) var(--brand-tint);}
.ns-pay-input[data-invalid="true"]{border-color:var(--danger);}
.ns-pay-row{display:flex;gap:var(--space-sm);}
.ns-pay-hint{margin:0;font-size:var(--fs-sm);line-height:var(--lh-normal);color:var(--text-tertiary);}
.ns-pay-hint[data-invalid="true"]{color:var(--danger-text);font-weight:var(--fw-semibold);}
.ns-pay-step--done{align-items:center;text-align:center;gap:var(--space-sm);padding-top:var(--space-sm);}
.ns-pay-tick{display:flex;align-items:center;justify-content:center;width:calc(var(--space-xl) + var(--space-sm));height:calc(var(--space-xl) + var(--space-sm));border-radius:var(--radius-pill);background:var(--success-bg);color:var(--success-text);}
.ns-pay-tick svg{width:var(--fs-2xl);height:var(--fs-2xl);}
.ns-pay-done-title{font-size:var(--fs-2xl);font-weight:var(--fw-bold);color:var(--text-primary);}
.ns-pay-done-copy{margin:0;font-size:var(--fs-sm);line-height:var(--lh-normal);color:var(--text-secondary);}

/* Mobile and tablet: full-viewport screen sliding up. Retuned 1024 -> 900 so the split matches
   nifhr's OWN system: 900 is where nifhr swaps the desktop messenger for the mobile inbox, and a
   panel that went full-screen at 1000px while the page behind it was still the desktop two-pane
   would read as a bug. */
@media (max-width:900px){
  .ns-panel{inset:0;width:100%;min-width:0;max-width:none;height:100dvh;border-left:0;box-shadow:none;transform:translateY(100%);}
  .ns-panel.is-open{transform:translateY(0);}
  .ns-body{padding-bottom:calc(var(--space-lg) + env(safe-area-inset-bottom,0px));}
  /* ⚠️⚠️ ENTRY #371, THE SECOND HALF OF HER ASK: *"and then the elements on this resized drawer
     for all views as well"*. MEASURED before and after: at 834x1112 this panel is full-bleed, so
     every card ran the full 810px and a line of body copy was ~786px long, which is roughly twice
     a readable measure and looked nothing like the same component that reads correctly at 460.
     The COLUMN is capped at the drawer's own floor, so a tablet sees the drawer's proportions
     rather than a stretched copy of them, and the cap is the same token the width consumes rather
     than a second number that could drift away from it.
     ⚠️⚠️ ALL THREE REGIONS TAKE IT, AND CAPPING ONLY THE BODY WAS A DEFECT I RENDERED AND SAW.
     The first version capped `.ns-body > *` and `.ns-foot > *` and left `.ns-head` alone, so at 834
     the title sat hard against the left edge and its close button against the right while every
     card floated in a centred 460 column: two different layouts in one panel. A width number
     cannot show you that. Padding the three regions to the SAME column is also simpler than
     capping their children, because `.ns-head` is a `space-between` flex row whose two ends must
     move together rather than each shrink.
     ⚠️ IT IS INERT ON A PHONE BY CONSTRUCTION: `max()` floors it at the panel's own `--space-lg`,
     and at 390 the viewport is already narrower than `--drawer-min-w`, so the calc loses and
     nothing about the phone tier changes. */
  .ns-head, .ns-body, .ns-foot { padding-inline: max(var(--space-lg), calc((100% - var(--drawer-min-w)) / 2)); }
  /* ⚠️ ENTRY #385: THE PAYMENT SHEET TAKES THE SAME COLUMN, from the same expression, for the same
     reason #371 gave above. Measured at 834x1112 before this line: the sheet is a child of the
     full-bleed panel, so its wallet buttons and card fields ran the whole 834 while every card
     behind them sat in a centred 460 column. Two layouts in one panel is exactly the defect #371
     found and fixed, and a new sheet added after it would have quietly reintroduced it.
     It is INERT ON A PHONE by the same construction: at 390 the viewport is already narrower than
     `--drawer-min-w`, the calc loses to the `max()` floor, and nothing changes. */
  .ns-pay { padding-inline: max(var(--space-lg), calc((100% - var(--drawer-min-w)) / 2)); }
}

.ns-toast{position:fixed;left:50%;bottom:calc(env(safe-area-inset-bottom, 0px) + calc(var(--space-lg) + var(--space-3xs)));transform:translate(-50%,var(--space-sm));z-index:2147483034;background:var(--text-primary);color:var(--surface);border-radius:var(--radius-pill);padding:var(--space-sm) calc(var(--space-md) + var(--space-2xs));font-size:var(--fs-md);font-weight:var(--fw-semibold);box-shadow:var(--shadow-popover);opacity:0;pointer-events:none;transition:opacity var(--motion-ui) ease,transform var(--motion-ui) ease;}
.ns-toast.is-show{opacity:1;transform:translate(-50%,0);}
@media (prefers-reduced-motion: reduce){.ns-panel,.ns-scrim,.ns-toast{transition:none;}}

/* ==========================================================================================
   ENTRY #207d - DESKTOP (>=901px): the messenger attachment menu is locked to the option the
   current coached beat asks for, mirroring the mobile `.mm-sheet-item[disabled]` rule (:16514).
   Desktop had NO disabled styling for these options at all, so this rule is new rather than
   reused: the mobile one is scoped to `.mm-sheet-item` and never reached the desktop menu.
   Same numbers as mobile so the two views read as one system.
   ========================================================================================== */
.messenger-attachment-menu button[disabled] {
  opacity: .4;
  pointer-events: none;
}

/* ==========================================================================================
   ENTRY #209 (user call, annotated screenshot). DESKTOP ONLY (>= 901px).

   THE ASK: three coaching cards must take "the same style and position" as ONE reference card,
   the intro bottom sheet on collaborator-invite.html (`.qq-sheet`, that file's inline style block
   4, rules at :2604-2611 + :2655). The three:
     1. rj0wj  `.company-basics-coach`  "NO WE DIDN'T HACK YOU!"
     2. nifhr  `.rank-gate-card`        "So now you've whittled the 500 down to just 3!"
     3. nifhr  `.rank-done-card`        "OK and what was this?"

   THE REFERENCE, measured live at BOTH 1440x900 and 1920x1080 (identical at both; it carries no
   desktop breakpoint at all, which is why it reads the same at every width):
     scrim   position:fixed; inset:0; display:flex; align-items:flex-end; justify-content:center
     sheet   width min(560px,100vw); max-height 88dvh; FLUSH to the viewport bottom; centre-delta 0
             background #111827; radius 24px 24px 0 0; no border; padding 34px 26px 30px
             box-shadow 0 -18px 60px rgba(17,24,39,.5), 0 0 0 1px rgba(124,58,237,.16)
             a 36x4 drag-handle ::before pill at top:9px
     title   34px / 900 / lh 1.12 / ls -0.04em, UPPERCASE VIA CSS (source text is sentence case),
             #F9FAFB, left aligned, margin 6px 0 14px
     body    16px / 500 / lh 24px, #C7CBD4, left aligned, margin 0 0 22px
     CTA     full inner width, 50px tall, #7C3AED, radius 999px, 16px / 800

   ⚠️ WHY THIS IS A NEW BLOCK AT THE END AND NOT AN EDIT TO THE BASE RULES. All three cards get
   their current look from BASE (no-media) rules, and each is overridden at <= 900px for its own
   phone treatment. Editing the base rules would change the phone too, which the user explicitly
   scoped out. A `min-width: 901px` block appended at the END of this file ties every base rule on
   specificity and wins on source order, which is this project's cascade convention.

   ⚠️ THE TITLE FONT IS ALREADY RIGHT AND MUST NOT BE RE-DECLARED. `.rank-gate-title` and
   `.company-basics-coach h2` are both already in the entry #174 token group at :20231, so they
   ALREADY compute 34px / 900 / 1.12 / -0.04em. (Measured: the `font-size: 22px` at :19946 and the
   `28px` at :2431 never paint; the token rule is equal specificity and later. The in-file comments
   there describe styling that does not exist.) Only case, colour and margin are missing.
   ========================================================================================== */

/* -- 1. rj0wj: the workspace-tour explainer ------------------------------------------------ */
@media (min-width: 901px) {
  /* From a viewport-centred 460px modal to the reference's flush-bottom 560px sheet.
     ⚠️ `right: auto` is load-bearing: the <= 900px block sets `left:0; right:0` and, while that
     block cannot match here, the base rule's `left: 50%` combined with a stray `right` would
     stretch the box instead of centring it. Declared explicitly so the box is fully specified. */
  .company-basics-coach {
    top: auto;
    bottom: 0;
    left: 50%;
    right: auto;
    transform: translateX(-50%);
    width: min(560px, 100vw);
    max-height: 88dvh;
    padding: 34px 26px 30px;
    border: 0;
    border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
    background: #111827;
    box-shadow:
      0 -18px 60px 0 rgba(17, 24, 39, 0.5),
      0 0 0 1px rgba(124, 58, 237, 0.16);
    text-align: start;
    animation: companyCoachDeskSheetIn 340ms var(--ease-out, cubic-bezier(.16, 1, .3, 1));
  }

  /* The reference's drag-handle pill. The <= 900px sheet already draws one (:16825); this is the
     desktop copy, identical numbers, because that rule is inside a max-width block. */
  .company-basics-coach::before {
    content: "";
    position: absolute;
    top: 9px;
    left: 50%;
    transform: translateX(-50%);
    width: 36px;
    height: 4px;
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--text-inverse) 26%, transparent);
  }

  /* ⚠️⚠️ THESE KEYFRAMES EXIST BECAUSE THE PHONE'S DO NOT WORK HERE. `companyCoachSheetIn`
     (:16840) animates `transform: translateY(100%) -> translateY(0)`, which on the phone is
     correct because that sheet is full-bleed with NO x-transform. This card is CENTRED with
     `translateX(-50%)`, so borrowing those keyframes would drop the x-component for the whole
     340ms and slam the card half its own width to the right mid-animation. The `is-closing` case
     is worse: it is `forwards`, so the card would come to REST mis-centred. Both keyframes
     therefore carry the x-component themselves. */
  .company-basics-coach.is-closing {
    pointer-events: none;
    animation: companyCoachDeskSheetOut 300ms var(--ease-in, cubic-bezier(.4, 0, 1, 1)) forwards;
  }

  .company-basics-coach h2 {
    margin: 6px 0 14px;
    text-transform: uppercase;
    color: #F9FAFB;
    text-align: start;
  }

  .company-basics-coach p {
    margin: 0 0 22px;
    font-size: var(--fs-xl);
    font-weight: var(--fw-medium);
    line-height: 24px;
    color: #C7CBD4;
    text-align: start;
  }
  /* The card has two stacked paragraphs; only the last one owns the gap above the CTA. */
  .company-basics-coach p + p { margin-top: 0; }

  .company-coach-cta {
    width: 100%;
    min-height: 50px;
    height: 50px;
    margin-top: 0;
    border-radius: var(--radius-pill);
    background: var(--brand);
    font-size: var(--fs-xl);
    font-weight: var(--fw-extrabold);
  }
}

@keyframes companyCoachDeskSheetIn {
  from { transform: translate(-50%, 100%); }
  to   { transform: translate(-50%, 0); }
}
@keyframes companyCoachDeskSheetOut {
  from { transform: translate(-50%, 0); }
  to   { transform: translate(-50%, 100%); }
}

/* -- 2 + 3. nifhr: the rank gate and the post-submit sheet --------------------------------- */
@media (min-width: 901px) {
  /* MEASURED, and it is why no padding compensation appears below: `.rank-gate` / `.rank-done`
     are `position: absolute; inset: 0` inside the compare overlay, and their rendered box is the
     FULL viewport (0,0,1440,900 at 1440x900) with `bottom` exactly at the viewport bottom, NOT
     the overlay's 32px-inset padding box. So `place-items: end center` alone lands the card
     flush and centred, matching the reference's centre-delta of 0.
     ⚠️ Also measured: the overlay's `backdrop-filter: blur(2px)` does NOT establish a containing
     block for fixed descendants here (a fixed probe resolved to the full viewport). That is worth
     recording because a blurred/filtered ancestor usually DOES, and it is the trap that bit
     entries #109 and #112. */
  body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate,
  body.nifhr-screen [data-screen-id="nIfHr"] .rank-why,   /* #386, see the note at :21340 */
  body.nifhr-screen [data-screen-id="nIfHr"] .rank-done {
    place-items: end center;
  }

  /* ⚠️ THIS REPLACES ENTRY #169's DESKTOP FLEX-COLUMN (:20093). That block centred the
     confirmation modal and this card as a PAIR, which cannot survive the card being pinned to the
     viewport bottom. Restored to the same shape the phone uses: the overlay reserves the sheet's
     own MEASURED height as bottom padding (`--rank-done-h`, stamped by measureRankDone() at
     nifhr.html:2267 and re-stamped on resize, unconditionally, so it is available here), and the
     modal centres in what is left. Without this the modal and the sheet OVERLAP: modal centred at
     190..710 against a sheet at 570..900, measured before the fix. */
  body.nifhr-screen.rank-done-open [data-screen-id="nIfHr"] .nifhr-compare-overlay {
    display: grid;
    flex-direction: row;
    place-items: center;
    gap: 0;
    padding-bottom: calc(var(--cmp-pad) + var(--rank-done-h, 0px));
  }

  body.nifhr-screen.rank-done-open [data-screen-id="nIfHr"] .rank-done {
    position: absolute;
    display: grid;
    order: 0;
    width: auto;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-card,
  body.nifhr-screen [data-screen-id="nIfHr"] .rank-done-card {
    width: min(560px, 100vw);
    max-height: 88dvh;
    overflow-y: auto;
    padding: 34px 26px 30px;
    border: 0;
    border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
    background: #111827;
    box-shadow:
      0 -18px 60px 0 rgba(17, 24, 39, 0.5),
      0 0 0 1px rgba(124, 58, 237, 0.16);
    text-align: start;
  }

  /* The reference's drag-handle pill. `.rank-gate-card` is already `position: relative`. */
  body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-card::before {
    content: "";
    position: absolute;
    top: 9px;
    left: 50%;
    transform: translateX(-50%);
    width: 36px;
    height: 4px;
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--text-inverse) 26%, transparent);
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-title {
    margin: 6px 0 14px;
    text-transform: uppercase;
    color: #F9FAFB;
    text-align: start;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-sub {
    margin: 0 0 22px;
    font-size: var(--fs-xl);
    font-weight: var(--fw-medium);
    line-height: 24px;
    color: #C7CBD4;
    text-align: start;
  }

  body.nifhr-screen [data-screen-id="nIfHr"] .rank-gate-cta {
    font-size: var(--fs-xl);
    font-weight: var(--fw-extrabold);
  }
}

/* ==========================================================================================
   ENTRY #211 (user call, annotated screenshot). DESKTOP ONLY (>= 901px).

   "KEEP THIS SCREEN AND ANIMATE THE NEW ANIMATION SEGMENT AS A RESULT OF THIS SCREEN ...
    use the same background as this screen 1 with the animations using the assets on screen 1
    there so its a seamless demo from the show me CTA button instead of this self contained
    animation. leave the mobile and tablet views though this affects just the desktop views"

   The JobScore explainer (entry #210) stops being a page you navigate to on desktop and becomes
   an overlay that plays over x1riqt's REAL board. jobscore-inplace.js builds every element below
   and owns all placement; this block is only the paint.

   ⚠️ WHY THIS IS A TAIL BLOCK. Appended at the END of this file it ties every base rule on
   specificity and wins on source order, which is this project's cascade convention. It is scoped
   `@media (min-width: 901px)` + `body.x1riqt-screen`, so it cannot reach any other screen and it
   cannot reach a phone or a tablet, which the user explicitly scoped out. No `:root` token moves,
   so only x1riqt's own cache tag needed the bump (x1riqt-mob-32 -> 33).

   ⚠️ THE SHEETS ARE THE ENTRY #209 DESKTOP LANGUAGE, deliberately, not the standalone page's
   own `.sheet`. That page has no shared stylesheet and hand-copies its tokens; here the house
   already has one flush-bottom centred 560px dark sheet (`.company-basics-coach` / `.rank-gate-card`
   at the #209 block above) and this is the same thing, so it reuses the #174 `--sheet-title-*`
   tokens rather than re-typing 34px/900/1.12/-0.04em.

   ⚠️ THE LAYER MUST SIT UNDER 2147483000. `hirello-cinematic-timer.js` paints its full-bleed
   drain bar at exactly that z-index, and the sheets here are genuinely flush to the bottom edge,
   so the bar has to ride OVER them or a 560px-wide bite is taken out of the middle of it. Do not
   "fix" that by lifting the sheet off the bottom: flush is the reference geometry.

   ⚠️ GSAP OWNS EVERY TRANSFORM ON THESE ELEMENTS. Nothing below declares `transform`: a CSS
   `translate(-50%,-50%)` is destroyed the moment a tween writes `scale`, so the centring is done
   with `xPercent`/`yPercent` in the module. `left: 50%` here, and nothing else.
   ========================================================================================== */
@media (min-width: 901px) {

  /* Nothing on the app screen may be clicked while the segment runs. The module also sets
     `inert` on `.app-shell`, which covers the keyboard; this is the pointer half, and it is
     written as `pointer-events: none` rather than an overlay on purpose, so a click still
     reaches the document and `hirello-cinematic-hold.js`'s click-to-pause keeps working. */
  body.x1riqt-screen.jobscore-inplace-active .app-shell {
    pointer-events: none;
  }

  /* ⚠️ THE CLIP IS THE WHOLE JOB (the entry #175 lesson, on this screen). Marta's card is flush
     with `.candidate-stream` on all four edges and that element is `overflow: hidden`, so her
     `scale: 1.045` focus beat would be cut away invisibly: a rect cannot detect a clip. Only the
     STREAM is released. The COLUMN keeps its own `overflow: hidden`, which is what still clips
     the rest-of-the-pile filler rows and lets the `.has-filler-pile::after` bottom mask work, and
     its 14px padding is more than the 7.9px per side that 1.045 adds. */
  body.x1riqt-screen.jobscore-inplace-active [data-screen-id="x1RIQT"] .candidate-stream {
    overflow: visible;
  }

  .jsx-layer {
    position: fixed;
    inset: 0;
    z-index: 2147482000;
    pointer-events: none;
    font-family: Inter, system-ui, sans-serif;
  }

  /* The standalone fades its fake board to .18 behind the payoff. The real board cannot be
     faded: an opacity below 1 on it creates a stacking context and traps the columns' own
     z-indexes, which is the entry #166d trap that killed the drag on three viewports. A scrim
     does the same job with no side effects. */
  .jsx-scrim {
    position: absolute;
    inset: 0;
    z-index: 1;
    background: rgba(2, 6, 23, 0.82);
  }

  .jsx-pin {
    position: absolute;
    left: 0;
    top: 0;
    z-index: 20;
  }

  /* The enlarged copy of Marta's badge, in the real `.match-score` tokens so the two agree. */
  .jsx-big {
    position: relative;
    z-index: 30;
    width: 124px;
    height: 124px;
    border-radius: var(--radius-pill);
    display: grid;
    place-items: center;
    background: var(--success-bg);
    color: var(--success-text);
    font-size: 46px;
    font-weight: var(--fw-black);
    line-height: 1;
    box-shadow: 0 30px 90px rgba(5, 150, 105, 0.34);
  }

  .jsx-chip-pin { z-index: 29; }

  .jsx-chip {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    padding: 10px 15px;
    border-radius: var(--radius-pill);
    background: var(--surface);
    box-shadow: 0 16px 40px rgba(2, 6, 23, 0.3);
    font-size: 13px;
    font-weight: var(--fw-bold);
    line-height: 1;
    color: var(--text-primary);
    white-space: nowrap;
  }
  .jsx-chip b {
    font-size: var(--fs-xs);
    font-weight: var(--fw-extrabold);
    color: var(--success-text);
  }
  /* ENTRY #226: the MEDIUM rating takes the Demo-sections FAB gold. That colour is a RAW HEX in
     hirello-shell.js:169 (`launcher.style.background`), NOT a token: no --fab custom property
     exists anywhere in this project, and the FAB's own halo keyframes carry the same
     rgba(245,166,35). It is mirrored as a literal here and in jobscore-explained.html, which never
     loads this stylesheet. Those three sites move together. */
  .jsx-chip b.med {
    color: #F5A623;
  }

  /* ENTRY #214: the purple rounded-square glyph raised at the AI beat is deleted at the user's
     call, so its two rules went with it. Its `layoutPins()` placement went too, which is what
     hands the whole empty span to the panel below. */

  /* ENTRY #213 (user call, annotated screenshot): "Grow size and move to this position no matter
     what desktop screen size". The size is here; the position is `layoutPins()`, anchored to the
     candidate list's live rect so it holds at every desktop width.
     ⚠️ `white-space: nowrap` is load-bearing at 901px. The pin shrink-wraps its content, so
     without it the grown mark wraps to two lines inside a narrow column and the rotated box the
     clamp computed from `offsetWidth` no longer describes what is painted. */
  .jsx-norank {
    position: relative;
    z-index: 31;
    padding: 18px 34px;
    border-radius: 18px;
    border: 4px solid #F87171;
    color: #FCA5A5;
    background: rgba(15, 23, 42, 0.72);
    font-size: 44px;
    font-weight: var(--fw-black);
    line-height: 1;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    white-space: nowrap;
    box-shadow: 0 22px 60px rgba(2, 6, 23, 0.5);
  }
  /* The strike. `transform-origin` is safe to declare because GSAP only ever writes `scaleX`
     here and never a translate, so there is no centring for it to destroy. */
  .jsx-norank-line {
    position: absolute;
    left: 6%;
    right: 6%;
    top: 50%;
    height: 6px;
    border-radius: 3px;
    background: #F87171;
    transform-origin: 0 50%;
  }

  /* ENTRY #214 (user call, annotated screenshot): "Make larger and move to here", the line drawn
     down the middle of the empty span right of the candidate list. 1.5x on the circles, and the
     gap grows by the same factor so the group keeps its proportion. The position is
     `layoutPins()`; `PANEL_STEP` is measured from `offsetLeft`, so it follows both numbers on its
     own and neither may be hardcoded into a tween. */
  /* ── ENTRY #230: the selection panel's own container ────────────────────────────────────
     Three bare photographs said nothing about who these people are. This is the standalone
     engine's `#panelCard` mirrored onto the in-place engine, in the house `.jsx-chip` language
     (same `var(--surface)`, same `0 16px 40px rgba(2,6,23,.3)`), and it CLOSES IN FROM THE RIGHT
     by one member pitch as each member votes and pops out.
     ⚠️ THE CARD TOOK OVER `.jsx-panel`'s opacity and stacking. The row below is now a plain flex
     child, which is the whole safety argument: `panelSlot0Pt()` measures that row's live box and
     is the launch point for every later vote, so the row must not move when the card narrows.
     ⚠️ `overflow: hidden` is what makes the shrink legal: a popped member keeps its flex slot
     (it is `scale: 0`, never removed), so the width the card gives up is always an EMPTY slot.
     ⚠️ `align-items: flex-start` is load-bearing. Centred children would slide as the card
     narrows, which is exactly the movement this design exists to avoid.
     ⚠️ This engine only runs at 901px and up, so it is always the stacked treatment. The phone
     and tablet tiers are the standalone's, and they get the one-row pill instead: there is no
     vertical room for a title row in the band above the app card. */
  .jsx-panel-card {
    position: relative;
    z-index: 29;
    opacity: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 14px;
    padding: var(--space-lg);
    border-radius: 36px;
    background: var(--surface);
    box-shadow: 0 16px 40px rgba(2, 6, 23, 0.3);
    overflow: hidden;
  }
  /* Intrinsically sized, NEVER `width: 100%`: the width table floors the card on this element's
     own width, and a full-width head would make that measurement circular. */
  .jsx-panel-head {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    flex: none;
    white-space: nowrap;
  }
  .jsx-panel-head b {
    font-size: 13px;
    font-weight: var(--fw-extrabold);
    line-height: 1;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-secondary);
  }
  #jsxPanelCount {
    display: grid;
    place-items: center;
    min-width: 24px;
    height: 24px;
    padding: 0 7px;
    border-radius: var(--radius-pill);
    background: var(--brand-tint);
    color: var(--brand);
    font-size: 13px;
    font-weight: var(--fw-extrabold);
    line-height: 1;
  }
  .jsx-panel {
    position: relative;
    display: flex;
    gap: 18px;
    flex: none;
  }
  /* ENTRY #213: these are PHOTOGRAPHS now, not lettered circles. The three files are built from
     the user's supplied portraits by `harness/build-panel-avatars.py`, which bakes the round alpha
     so the source watermark cannot survive a `border-radius` being overridden later. `url()` in
     this file resolves against the stylesheet, so `assets/...` is `public/assets/...`, the same
     path shape `.gesture-coach` already uses. */
  .jsx-panel i {
    width: 94px;
    height: 94px;
    border-radius: var(--radius-pill);
    display: grid;
    place-items: center;
    color: var(--text-inverse);
    font-size: 20px;
    font-weight: var(--fw-extrabold);
    font-style: normal;
    line-height: 1;
    background-color: var(--bg-subtle);
    background-size: cover;
    background-position: center top;
    background-repeat: no-repeat;
    box-shadow: 0 18px 44px rgba(2, 6, 23, 0.42);
  }
  #jsxPanel1 { background-image: url("assets/panel-1.png"); }
  #jsxPanel2 { background-image: url("assets/panel-2.png"); }
  #jsxPanel3 { background-image: url("assets/panel-3.png"); }

  /* ── the scan (entry #213) ──────────────────────────────────────────────────────────────────
     ⚠️ THE WASH IS A SOLID BRAND FILL WHOSE OPACITY IS TWEENED TO 0.39, never a colour that is
     already 39% transparent at `opacity: 1`. The two are not the same paint over a white card,
     and only the first can be faded in and out by the one property a seek renders reliably. */
  .jsx-wash-pin { z-index: 27; }
  .jsx-wash {
    position: relative;
    border-radius: var(--radius-md);
    background: var(--brand);
    box-shadow: 0 0 0 2px var(--brand);
  }
  .jsx-scan {
    position: relative;
    z-index: 28;
    width: 74px;
    height: 74px;
  }
  /* ENTRY #214: a magnifying glass, so the rule that filled the old centre dot went with the dot
     rather than being left behind for a selector nothing matches. The stroke is heavier than the
     crosshair's 2.4 because a lens and a handle are two strokes where the crosshair had six. */
  .jsx-scan svg {
    width: 100%;
    height: 100%;
    stroke: var(--brand);
    stroke-width: 2.9;
    filter: drop-shadow(0 2px 6px rgba(2, 6, 23, 0.45));
  }

  /* ── the vote badge (entry #213) ────────────────────────────────────────────────────────── */
  .jsx-vote-pin { z-index: 33; }
  .jsx-vote {
    position: relative;
    display: grid;
    place-items: center;
    min-width: 42px;
    height: 32px;
    padding: 0 11px;
    border-radius: var(--radius-pill);
    background: var(--success-bg);
    color: var(--success-text);
    font-size: var(--fs-xl);
    font-weight: var(--fw-black);
    line-height: 1;
    box-shadow: 0 12px 30px rgba(5, 150, 105, 0.42);
  }

  /* ⚠️ ENTRY #213 DELETED `.jsx-reorder` from here ("Remove this from all views not needed"), the
     dark caption that used to narrate the Clare/Priya swap. Do not reinstate it: deleting it is
     what frees the AUTO RANK stamp to sit on the candidate list, since the old stamp placement
     existed only to keep those two elements off each other. Gone from the standalone too
     (`#reorderCap` in jobscore-explained.html).
     ⚠️ ITS COPY IS DELIBERATELY NOT REPEATED IN THIS COMMENT. A parity guard checks both files
     for that sentence, so quoting it here would keep the guard green against a comment while the
     element itself was gone: a check that can match its own explanation cannot fail. */

  /* ── the narration sheets: the entry #209 reference geometry, flush bottom ──────────────── */
  .jsx-sheet {
    position: absolute;
    left: 50%;
    bottom: 0;
    z-index: 40;
    width: min(560px, 100vw);
    max-height: 88dvh;
    padding: 34px 26px 30px;
    border: 0;
    border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
    background: #111827;
    box-shadow:
      0 -18px 60px 0 rgba(17, 24, 39, 0.5),
      0 0 0 1px rgba(124, 58, 237, 0.16);
    text-align: start;
  }
  /* The reference's drag-handle pill. */
  .jsx-sheet::before {
    content: "";
    position: absolute;
    top: 9px;
    left: 50%;
    transform: translateX(-50%);
    width: 36px;
    height: 4px;
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--text-inverse) 26%, transparent);
  }
  /* ENTRY #214: the small purple label every sheet carried above its headline is gone at the
     user's call, on all six cards, so its rule went with it rather than being left behind for a
     selector nothing matches. The headline's own top margin below is now what sets the gap under
     the drag-handle pill. */
  /* The house headline treatment (entry #174's tokens), never a re-typed size. */
  .jsx-sheet h2 {
    margin: 6px 0 14px;
    font-size: var(--sheet-title-size);
    line-height: var(--sheet-title-lh);
    font-weight: var(--sheet-title-weight);
    letter-spacing: var(--sheet-title-ls);
    text-transform: uppercase;
    color: #F9FAFB;
    text-align: start;
  }
  /* ENTRY #213 (user call): "Make weight super heavy to emphasise just the word NOT".
     ⚠️ THERE IS NO HEAVIER WEIGHT TO REACH FOR. `--sheet-title-weight` is already 900, the top of
     the scale, so `font-weight` on this `<b>` cannot do anything at all: the emphasis has to come
     from added INK, which is what `-webkit-text-stroke` gives at any weight. The stroke colour is
     `currentColor`, so paint order is irrelevant (stroke and fill are the same colour and a
     `paint-order` declaration would be a no-op dressed as a safeguard).
     ⚠️ The alternative, dropping the rest of the headline to 800 so 900 reads as heavy, was
     rejected: it would break the entry #174 `--sheet-title-*` treatment that this sheet's four
     siblings share, to fix one word. */
  .jsx-sheet h2 b {
    font-weight: inherit;
    -webkit-text-stroke: 0.9px currentColor;
  }
  .jsx-sheet p {
    margin: 0;
    font-size: var(--fs-xl);
    font-weight: var(--fw-medium);
    line-height: 24px;
    color: #C7CBD4;
    text-align: start;
  }

  /* The payoff is the sixth sheet and carries the segment's only exit, so it is the one thing in
     the layer that takes pointer events back. */
  .jsx-payoff {
    z-index: 45;
    background: linear-gradient(160deg, #1E1B4B 0%, #111827 62%);
    box-shadow:
      0 -18px 60px 0 rgba(2, 6, 23, 0.6),
      0 0 0 1px rgba(124, 58, 237, 0.28);
  }
  .jsx-payoff p { margin: 0 0 22px; }

  .jsx-cta {
    pointer-events: auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-xs);
    width: 100%;
    min-height: 50px;
    height: 50px;
    border: 0;
    border-radius: var(--radius-pill);
    background: var(--brand);
    color: var(--text-inverse);
    font-family: inherit;
    font-size: var(--fs-xl);
    font-weight: var(--fw-extrabold);
    cursor: pointer;
  }
  /* ENTRY #370: DELETED with the arrow. `jobscore-inplace.js` no longer builds one, so this rule
     had nothing to animate and `ctaArrowNudge` no longer exists.
     SUPERSEDED, entry #172: `.jsx-cta .cta-arrow { animation: ctaArrowNudge 2.4s ease-in-out
     infinite; }`, described as "Entry #172's nudge, on the arrow this CTA already carries". */

  /* ── ENTRY #231: "Watch again" joins the desktop END sheet ──────────────────────────────
     Every other cinematic's END sheet offers a replay beside its forward CTA; this engine was
     the only one that did not, because it was built as a one-way link in the linear chain.
     The shape is the standalone's `.qqe-actions` ported: ghost left, primary right, 10px gap.
     ⚠️ `.jsx-cta` is `width: 100%` on its own, which is right when it is the only child and
     wrong the moment it has a sibling, so the row overrides it rather than the base rule
     changing: the CTA is still full width anywhere it stands alone. */
  .jsx-actions {
    display: flex;
    gap: 10px;
  }
  .jsx-actions .jsx-cta {
    width: auto;
    flex: 1 1 auto;
  }
  .jsx-actions .jsx-cta-ghost {
    flex: 0 1 auto;
    padding: 0 20px;
    border: 1.5px solid rgba(249, 250, 251, 0.28);
    background: transparent;
    color: #F9FAFB;
    white-space: nowrap;
  }

  .jsx-cursor {
    position: absolute;
    left: 0;
    top: 0;
    z-index: 50;
    width: 26px;
    height: 26px;
  }
  .jsx-cursor svg {
    width: 100%;
    height: 100%;
    filter: drop-shadow(0 4px 10px rgba(2, 6, 23, 0.5));
  }
}

/* ==========================================================================================
   ENTRY #218: the compare/rank modal's JobScore joins the DERIVED tone system.

   #215 recorded this modal as deliberately excluded, because `<small>92% JobScore</small>` is one
   node holding the number AND its label and the tone walker tests an element's OWN text against
   `/^\d{1,3}%?$/`. #218 isolates the number in a child span, the same shape as `.nhsq0-score
   strong`, so the span can be enrolled while the `<small>` still reads "92% JobScore".

   ⚠️ THIS IS PLAIN TEXT COLOUR, NOT A BADGE. The other score selectors (`.match-score`,
   `.workspace-score`) are pills with their own background; giving this one `--warning-bg` would
   paint a chip inside a caption line that has never had one and would read as a defect.
   ⚠️ NOTHING RENDERS DIFFERENTLY TODAY: the three scores are 92/89/86 against a threshold of 80,
   so all three derive green and inherit the caption's own colour. The rule exists so a future
   sub-80 score is toned. Do not "verify" it by looking for amber on screen.
   ⚠️ Appended at the END of the file on purpose: a rule added inside a tier block loses to the
   equal-specificity rules already in the tail.
   ========================================================================================== */
.compare-score.warn,
.compare-mob-card .compare-score.warn,
.compare-mob-drawer .compare-score.warn {
  color: var(--warning-text);
  font-weight: var(--fw-bold);
}

/* ==========================================================================================
ENTRY #222 - x1riqt's welcome title is now a QUOTE (it carries curly quote marks), and the user's
rule for it is that it must never reach a third rendered row. It already did below 430px, BEFORE
the quotes were added: measured at the flat 34px, 320 renders FOUR rows and 360 / 375 / 390 / 412
render THREE. 430 and up render two, so this block stops at 429 and every wider view is untouched.

⚠️ THE SIZES ARE MEASURED, NEVER GUESSED. Largest px that still holds two rows, by bisection:
320 -> 24, 360 -> 28, 375 -> 29, 390 -> 31, 412 -> 33. That is a near-constant 7.5 to 8.0 percent
of the viewport, so one vw expression covers the whole range. 7.3vw is used rather than 7.5,
because 320 needs exactly 7.50vw and sitting on the boundary is one font-metric rounding away
from a third row.

⚠️ AN ID SELECTOR IS USED ON PURPOSE. #174's shared `--sheet-title-*` rule sets this title's size
through `.modal-header h1` and sits later in the file, so a matching class-level selector here
would tie and lose. (1,0,0) wins wherever it is written.

⚠️ line-height is a RATIO here, not the flat 38px, or the rows keep desktop's leading at 24px type.
========================================================================================== */
@media (max-width: 429px) {
  #welcome-title {
    font-size: min(34px, 7.3vw);
    line-height: 1.12;
  }
}

/* ==========================================================================================
ENTRY #223 - DESKTOP ONLY (>=901px): x1riqt's page subtitle under the job title is hidden.

⚠️ `visibility`, NOT `display: none`, and that is the whole point of the ask. The user's words were
"hide this text I don't want it showing at all but I don't want to move anything around right now".
`display: none` would collapse the line out of the flow and lift the drop zone, the board and the
welcome sheet by its height plus its 6px top margin. `visibility: hidden` keeps the box, so nothing
below it moves by a single pixel.

⚠️ Scoped to `body.x1riqt-screen` because `.screen-sub` is a SHARED class: rj0wj carries one too
(and so does the unlinked zz-baseline dev artefact), and neither was in scope.

⚠️ Phone and tablet are deliberately untouched: the ask was DESKTOP VIEWS ONLY.
========================================================================================== */
@media (min-width: 901px) {
  body.x1riqt-screen .screen-sub { visibility: hidden; }
}

/* ==========================================================================================
ENTRY #224 - DESKTOP ONLY (>=901px): the forward-arrow icon is removed from x1riqt's welcome-sheet
CTA ("Show using demo candidates"). The user asked for the icon gone, not merely stilled, so this
is display, not visibility: the button centres its label and the flex gap collapses with it.

⚠️ SCOPED TO THIS ONE CTA ON THIS ONE SCREEN STATE. x1riqt carries a SECOND `.cta-arrow` on the
parsing tour's "Show me" button (x1riqt.html:215), and #172 put the same arrow on 16 CTAs across
the demo. Only the welcome sheet's was in scope, so the selector names its container.

⚠️ Phone and tablet keep the arrow AND its nudge: the ask was DESKTOP VIEWS ONLY, and #172's own
guard sweep measures this exact arrow at 390x844.
========================================================================================== */
/* ⚠️ ENTRY #370 SUPERSEDES ALL OF THE ABOVE AND THE RULE IS DELETED. She has now asked for the
   arrows to go on EVERY view, not only desktop, so the SVG itself is gone from x1riqt.html:214
   and this desktop-only `display: none` had nothing left to hide. Superseded rule, quoted:
   `@media (min-width: 901px) { body.x1riqt-screen .welcome-modal [data-start-demo] .cta-arrow
   { display: none; } }`. The #224 note above is kept because it records that the phone and
   tablet tiers deliberately KEPT the arrow at the time, which #370 is what reverses. */

/* ==========================================================================================
ENTRY #233 - ALL VIEWS: the POST-DROP coaching tooltip on nifhr's compare/rank modal. When a
viewer finishes a rank drag they now get the house dark tooltip, tail pointing DOWN, sitting
ABOVE the thing it names. Desktop gets one beat (Submit my ranking). The stacked phone layout
gets two: Marta's `.compare-mob-toggle` chevron first, then Submit once the detail drawer has
been opened and dismissed.

⚠️ ANATOMY IS #206's `.mm-coach`, NOT a new language: `var(--text-primary)` fill,
`var(--text-inverse)` text, `var(--radius-md)`, 9px/14px padding, 14px/20px weight 800,
`var(--shadow-popover)`, and a 16x8 `clip-path` triangle. Never a border trick, never a rotated
square. The DOWN clip-path is `polygon(0 0, 50% 100%, 100% 0)`; the UP mirror is only ever used
by the fit-flip guard in `placeRankTip()`.

⚠️ `position: fixed` AT BASE TIER, never inside a media query. `.nifhr-compare-overlay` is
`display: grid; place-items: center`, so an IN-FLOW second child makes the grid two cells and
shoves the compare modal off centre. The existing #168 comment in nifhr.html says the same about
the rank gate; this element is out of flow for exactly that reason.

⚠️ `z-index: 9500` is chosen against real neighbours in the overlay's stacking context: above the
modal (z auto) and below both the #168 rank gate (9620) and the mobile detail drawer (9700), which
must cover it. The drag ghost is 10020 and also stays clear.

⚠️ `pointer-events: none` on the card AND its tail. It floats over the list and the footer and
must never eat the tap on the chevron or the button it is aiming at.

⚠️ `display: none !important` for `[hidden]`: the base rule below is an AUTHOR `display: block`
and outranks the UA `[hidden]` rule, so `hidden` alone cannot hide it. Same fix as
`.compare-mob-drawer[hidden]` at :14052.

⚠️ NOT named `.compare-rank-coach`. That class still carries ~8 live rules from the retired
desktop rank coach (paint at :9895, hide rules at :11644/:12873, tier rules at :13156/:13573) even
though its markup is long gone, and reusing the name would inherit every one of them.
========================================================================================== */
body.nifhr-screen [data-screen-id="nIfHr"] .rank-tip {
  display: block;
  position: fixed;
  left: var(--rank-tip-x, 12px);
  top: var(--rank-tip-y, 12px);
  z-index: 9500;
  box-sizing: border-box;
  max-width: min(272px, calc(100vw - 24px));
  padding: 9px 14px;
  border-radius: var(--radius-md);
  background: var(--text-primary);
  color: var(--text-inverse);
  box-shadow: var(--shadow-popover);
  font-size: var(--fs-md);
  line-height: 20px;
  font-weight: var(--fw-extrabold);
  letter-spacing: -0.01em;
  pointer-events: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .rank-tip[hidden] { display: none !important; }

body.nifhr-screen [data-screen-id="nIfHr"] .rank-tip::before {
  content: "";
  display: block;
  position: absolute;
  left: var(--rank-tip-tail-x, 16px);
  width: 16px;
  height: 8px;
  background: var(--text-primary);
  pointer-events: none;
}

body.nifhr-screen [data-screen-id="nIfHr"] .rank-tip[data-rank-tip-tail="down"]::before {
  bottom: -8px;
  clip-path: polygon(0 0, 50% 100%, 100% 0);
}

body.nifhr-screen [data-screen-id="nIfHr"] .rank-tip[data-rank-tip-tail="up"]::before {
  top: -8px;
  clip-path: polygon(0 100%, 50% 0, 100% 100%);
}

/* ==========================================================================================
ENTRY #236 - DESKTOP ONLY (>=901px): the collapsed SOURCE column hugs its own content.

When the curated move completes, entry #88 gave the source column the TARGET column's completed
height so it would stop standing at full board height with dead space under it. But the two
columns never hold the same content: the target's cards keep their 2px selected border while
:9402 flattens the source's to 1px, so one shared number fits one column exactly and leaves the
other short. MEASURED at 1728x940 after a real mouse drag to completion: 28px of empty column
below the source's last card against the target's 14px, i.e. exactly one card's worth of dead
space, on a panel the viewer reads as "the card doesn't line up with the candidates in it".

⚠️ THE VALUE IS RE-MEASURED AFTER `candidate-move-complete` LANDS, not before, because that class
is what flattens the source cards' borders. `setReviewingCompletionMetrics()` already ran three
times (before the class, next frame, and at 680ms once the FLIP settles), so the correction is
free; measured before the class the source reads about 6px too tall over three cards.

⚠️ IT IS A px NUMBER, NEVER `height: auto`. The collapse is a 520ms `height` transition (:9400) and
`auto` is not an animatable value, so switching to content height would make the column SNAP shut
instead of easing.

⚠️ FALLBACK CHAIN IS DELIBERATE: `var(--nifhr-source-complete-h, var(--nifhr-review-complete-h,
342px))`. If the JS has not written the new variable yet (first frame, or a state reached some
other way) the column falls back to exactly #88's behaviour rather than to a literal.

⚠️ SUPERSEDES #88's "shrink to MATCH the target column" for >=901px only. The variable is written
at every width but only this rule consumes it, so mobile and tablet stay byte-identical.
========================================================================================== */
@media (min-width: 901px) {
  body.nifhr-screen.candidate-move-step.candidate-move-complete [data-screen-id="nIfHr"] .kanban.import-board .new-col {
    height: var(--nifhr-source-complete-h, var(--nifhr-review-complete-h, 342px));
    max-height: var(--nifhr-source-complete-h, var(--nifhr-review-complete-h, 342px));
  }
}

/* ==========================================================================================
   ENTRY #249 (2026-07-30, user call, then CORRECTED on an annotated screenshot the same day).
   DESKTOP ONLY (>=901px).

   ⚠️⚠️ THE FIRST BUILD SHIPPED THE WRONG UI PATTERN AND THE USER WAS RIGHT TO REJECT IT. It used
   centred dark CARDS with a "STEP 6 OF 10" eyebrow, a sentence-case title, and a caption card that
   sat beside the phone THROUGHOUT playback carrying its own progress line and controls. Four
   corrections, all of them the same underlying mistake, inventing a pattern instead of using the
   house one:
     1. NO "step x of y" eyebrow. Deleted, not reworded.
     2. The title is ALLCAPS via `text-transform`, like `.qq-title` and `.rank-gate-title`. The
        source text stays sentence case, which is the house convention (#246 documents that the same
        rendered result is AUTHORED in one place and APPLIED in another, so guards assert the
        computed `textTransform` too).
     3. Every sheet here is a FLUSH-BOTTOM SHEET, not a centred card.
     4. Nothing is drawn beside the phone during playback: the caption card, its progress line and
        its controls are all GONE. Watching means watching.

   THE SHEET LANGUAGE IS ENTRY #209's, NOT A NEW ONE. #209 ported the cinematics' own `.qq-sheet`
   into this stylesheet for `.company-basics-coach`, `.rank-gate-card` and `.rank-done-card`, with
   the reference measured live at 1440x900 and 1920x1080. Every value below is that same set, and
   the entrance keyframes are #209's OWN `companyCoachDeskSheetIn` rather than a second copy.
   ⚠️ Those keyframes carry `translate(-50%, ...)`, and that is load-bearing rather than tidiness:
   the sheet is centred with `translateX(-50%)`, so borrowing the phone's plain `translateY`
   keyframes drops the x-component for the whole animation and slams the sheet half its own width
   to the right.

   ⚠️⚠️ OPENING THE OVERLAY TAKES TWO SEPARATE LISTS AND ONLY ONE OF THEM IS IN THE JS. Adding a
   step to `overlayOpen` in nifhr.html clears `hidden` and the aria state, but the overlay's own
   `opacity` and `pointer-events` are gated by a SECOND list, the chain of `body.screening-step-*`
   selectors in the block that starts
   `body.nifhr-screen.screening-step-qr ... .nifhr-screening-overlay`. A new step that joins only the
   JS list renders a sheet that IS in the DOM, IS displayed and reports a real rect and a non-null
   `offsetParent`, while sitting at `opacity: 0` and `pointer-events: none`: present to every
   structural check, invisible and dead to the viewer. Measured while building this entry:
   `elementFromPoint` on the "Just watch" button centre returned `body`, and a real click timed out
   against the kanban column behind it. Hence the first rule below, and hence guards that assert the
   rendered opacity and drive a REAL click.

   ⚠️ THE WATCH LAYER OWNS NO PROGRESS BAR AND NO PAUSE UI. Both come from the shared cinematic
   modules `hirello-cinematic-timer.js` (the full-bleed drain bar in `--cand-priya`) and
   `hirello-cinematic-hold.js` (click to pause, with the viewport-wide DEMO PAUSED stripe), which
   nifhr-watch-mode.js injects on first use. Do not re-style either here: they carry their own CSS
   precisely so every cinematic looks identical, and #117's measured 4px bottom offset and #197b's
   stripe are not ours to re-derive.
   ========================================================================================== */

body.nifhr-screen.screening-step-mode-screening [data-screen-id="nIfHr"] .nifhr-screening-overlay,
body.nifhr-screen.screening-step-mode-references [data-screen-id="nIfHr"] .nifhr-screening-overlay,
body.nifhr-screen.screening-step-mode-scheduling [data-screen-id="nIfHr"] .nifhr-screening-overlay,
/* The messenger arm. It is in BOTH lists deliberately: this one and the `.wmode-sheet` display
   list below. The comment above this block is the whole reason, so joining only one of them is
   the exact defect it describes. */
body.nifhr-screen.screening-step-mode-messenger [data-screen-id="nIfHr"] .nifhr-screening-overlay {
  opacity: 1;
  pointer-events: auto;
}

body.nifhr-screen [data-screen-id="nIfHr"] .wmode-sheet {
  display: none;
}

/* The choice sheets. `position: fixed` resolves to the real viewport: nothing between <body> and
   `.nifhr-screening-overlay` carries a transform (checked), which is also why the overlay's own
   `inset: 0` means the viewport. */
body.nifhr-screen.screening-step-mode-screening [data-screen-id="nIfHr"] .wmode-sheet[data-mode-segment="screening"],
body.nifhr-screen.screening-step-mode-references [data-screen-id="nIfHr"] .wmode-sheet[data-mode-segment="references"],
/* ENTRY #270: the scheduling arm, joined to the selector list rather than written as a new rule
   later in the file. Specificity is (0,5,1) counted out: two body classes, `[data-screen-id]`,
   `.wmode-sheet` and `[data-mode-segment]` make five, plus the `body` element. That is IDENTICAL to
   the two arms above it, which is the point: a tie between rules in one list is not a tie at all,
   so the three are genuinely interchangeable and none can start quietly winning over another. */
body.nifhr-screen.screening-step-mode-scheduling [data-screen-id="nIfHr"] .wmode-sheet[data-mode-segment="scheduling"],
/* The messenger arm, same (0,5,1) chain as the three above it for the same reason. */
body.nifhr-screen.screening-step-mode-messenger [data-screen-id="nIfHr"] .wmode-sheet[data-mode-segment="messenger"] {
  display: block;
  animation: companyCoachDeskSheetIn 340ms var(--ease-out, cubic-bezier(.16, 1, .3, 1));
}

/* One box for every sheet this entry paints: the two choice sheets and the watch layer's END
   sheet. #209's measured reference, value for value. */
.wmode-sheet {
  position: fixed;
  left: 50%;
  right: auto;
  bottom: 0;
  transform: translateX(-50%);
  width: min(560px, 100vw);
  max-height: 88dvh;
  overflow-y: auto;
  padding: 34px 26px 30px;
  border: 0;
  border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
  background: #111827;
  color: #F9FAFB;
  box-shadow:
    0 -18px 60px 0 rgba(17, 24, 39, 0.5),
    0 0 0 1px rgba(124, 58, 237, 0.16);
  text-align: start;
}

/* The reference's drag-handle pill. */
.wmode-sheet::before {
  content: "";
  position: absolute;
  top: 9px;
  left: 50%;
  transform: translateX(-50%);
  width: 36px;
  height: 4px;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--text-inverse) 26%, transparent);
}

/* #332's eyebrow, built ONLY when a segment supplies `endEyebrow` (scheduling, so far).
   ⚠️ #333 UPDATE: THIS RULE IS NOW THE ONLY COPY OF THESE VALUES. They were taken from
   `.screening-feature-eyebrow`, which #333 DELETED along with the feature card, so there is no
   longer a second rule to keep in sync and no source to "restore" them from. Do not read the
   sentence below as naming a live dependency; it is the provenance.
   ⚠️ EVERY VALUE HERE WAS COPIED, NOT CHOSEN: this sheet carries that
   card's copy verbatim, so it carries that card's eyebrow treatment verbatim too, and the two
   consecutive screens read as one thought. No margin of its own on purpose: `.wmode-title`'s
   existing 6px top margin is the gap, so no new spacing number was invented. */
.wmode-eyebrow {
  display: block;
  color: #c4b5fd;
  font-size: 9px;
  line-height: 12px;
  font-weight: var(--fw-black);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* Size, weight and tracking already come from #174's `--sheet-title-*` token group, which
   `.wmode-title` is enrolled in. Only case, colour and margin are missing, exactly as #209 found
   for `.rank-gate-title`. */
.wmode-title {
  margin: 6px 0 14px;
  text-transform: uppercase;
  color: #F9FAFB;
  text-align: start;
}

.wmode-sub {
  margin: 0 0 22px;
  font-size: var(--fs-xl);
  font-weight: var(--fw-medium);
  line-height: 24px;
  color: #C7CBD4;
}

/* Ghost left, primary right, 10px gap: the house `.qqe-actions` row. The primary GROWS to fill the
   rest of the row so the forward action reads as dominant. */
.wmode-actions {
  display: flex;
  gap: 10px;
}

.wmode-actions[hidden] {
  display: none;
}

.wmode-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-height: 50px;
  border-radius: var(--radius-pill);
  cursor: pointer;
  /* ⚠️ `inherit` IS DELIBERATE, AND ITS ORIGINAL REASONING IS NOW SUPERSEDED RATHER THAN WRONG.
     Superseded text, kept because it is the record of how the bug was found: "`var(--font-sans)`
     WOULD BE A BUG HERE. That name is used a handful of times in this file and is DEFINED
     NOWHERE, so it resolves to the guaranteed-invalid value and the declaration is dropped."
     ⚠️ ENTRY #374 DECLARED `--font-sans`, so BOTH clauses of that sentence are now false: the name
     exists and nothing is dropped. This comment had correctly diagnosed a live defect at ONE site
     and the other thirteen were never fixed, which is why #374 found them still dead months later.
     `inherit` STAYS here on its own merits: this button wants whatever the body is using, and
     saying so directly is better than naming a token to mean the same thing. */
  font-family: inherit;
  font-size: var(--fs-lg);
  font-weight: var(--fw-extrabold);
  white-space: nowrap;
}

/* ⚠️ ENTRY #278: THE HOUSE PRIMARY, FROM TOKENS. `--brand` and `--text-inverse` are byte-identical
   to the `#7C3AED` / `#fff` literals they replace, so the other three sheets do not move; the shadow
   keeps its own tuned geometry (a bigger lift than `--shadow-brand`, because this one sits on a dark
   sheet) while its colour now comes from `--brand` rather than a hand-typed rgba of the same hue.
   The hover matches `.btn-primary`, which this button had never had. */
.wmode-btn-primary {
  flex: 1 1 auto;
  border: 0;
  padding: 0 18px;
  background: var(--brand);
  color: var(--text-inverse);
  box-shadow: 0 14px 32px color-mix(in srgb, var(--brand) 34%, transparent);
}
.wmode-btn-primary:hover { background: var(--brand-hover); }

/* ⚠️ ENTRY #278: THE SOLE-BUTTON ROW. The messenger sheet lost its "Try it yourself", so its one
   remaining button must fill the row rather than hug its label. Scoped to a class the markup opts
   into, NOT to `.wmode-btn-ghost`, because the other three sheets still run two buttons and the
   ghost one is deliberately `flex: 0 1 auto` there so the primary reads as dominant. */
.wmode-actions-solo .wmode-btn {
  flex: 1 1 auto;
}
.wmode-btn-ghost {
  flex: 0 1 auto;
  padding: 0 20px;
  border: 1.5px solid rgba(249, 250, 251, 0.28);
  background: transparent;
  color: #F9FAFB;
}

/* -- The watch layer ------------------------------------------------------------------------
   Built by nifhr-watch-mode.js as a body-level sibling of `.app-shell`, so `position: fixed` here
   means the real viewport. Its geometry (scale, left, top) is written inline from live
   measurements; only paint and stacking live here.
   ⚠️ Z-INDEX IS BOUNDED ON BOTH SIDES, not just "high enough". Above `.nifhr-screening-drawer`
   (9900) and the raised overlay plus its coach tail (9950 / 9960) so the segment's own chrome
   cannot paint through; BELOW the Demo-sections launcher (2147483030), the standing escape hatch
   (#72/#194), and far below the pause stripe (2147483035) and the drain bar (2147483000), which
   must both cover the whole viewport including this layer. */
.wmode-layer {
  position: fixed;
  inset: 0;
  z-index: 9970;
  pointer-events: none;
}

/* ⚠️⚠️ ENTRY #272e: THE GHOST HAND WAS PAINTED BEHIND EVERY DRAWER AND HAD BEEN SINCE #271. The
   layer is z 9970; the three attachment drawers are z 2147483010. The user asked to *"Add the cursor
   moving and rollover and selecting the option to send"* on the questionnaire drawer, and the hand
   was already travelling there with `opacity: 1` at the right coordinates: it was simply invisible,
   under the panel, for the whole beat.
   ⚠️ RAISING `.wmode-cursor` ITSELF CANNOT WORK, and this file already records why: a z-index cannot
   climb out of an ancestor's stacking context, and `.wmode-layer` is one (`position: fixed` plus a
   z-index). The LAYER has to move, or the cursor has to be portalled out of it; moving the layer is
   the smaller change and keeps the layer's teardown as the single owner of every node in it.
   ⚠️ SCOPED TO THE MESSENGER STEP, because only this segment drives drawers. 2147483020 sits ABOVE
   the drawers (…010) and BELOW the Demo-sections launcher (…030), so the standing escape hatch stays
   on top, which `#271H` asserts.
   ⚠️ Safe against the layer's own contents: the phone stage and the END sheet also live here, and
   neither is ever on screen while a drawer is open (phone 13.15 to 23.45, drawers 9.35 to 11.95,
   28.15 to 30.35 and 34.55 to 36.75, END 43.35). Checked against the beat table rather than assumed. */
body.nifhr-watch-active.screening-step-messenger .wmode-layer {
  z-index: 2147483020;
}

.wmode-layer > * {
  pointer-events: auto;
}

/* ⚠️ ENTRY #267: THE DIM CARRIES THE HIGHLIGHT NOW, AND IT STAYS A REAL ELEMENT.
   `nifhr-watch-mode.js` writes an inline `clip-path: path(evenodd, ...)` here every frame: an
   overscanned outer rectangle plus a rounded-rect subpath traced round whatever the segment is
   pointing at, so the dim has a hole shaped to that thing and the live thing underneath reads
   through it at full brightness.
   ⚠️ A `box-shadow: 0 0 0 9999px` spot (the #265 `#jsxSpot` idiom) was the obvious alternative and
   is WRONG here: trap T069, a box-shadow dim can never be blurred, and it would have silently
   dropped the `backdrop-filter` below. `path()` also follows a border-radius, which `clip-path:
   polygon()` cannot (T072).
   ⚠️ The path is written in THIS element's own box (T076). That box is the viewport, because the
   rule below is `position: fixed; inset: 0` — if that ever changes, the coordinates change with it. */
.wmode-scrim {
  position: fixed;
  inset: 0;
  background: rgba(17, 24, 39, 0.64);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}

/* ⚠️ THE STAGE IS A FIXED 390x844 BOX THAT IS THEN SCALED, AND BOTH HALVES ARE LOAD-BEARING.
   `hirello-cinematic.js` resolves the grafted source's @media queries and its vw/vh units against
   the host's OWN clientWidth/clientHeight at build time, so 390x844 is what makes the cinematic
   render its MOBILE composition. Scaling the host afterwards does not change clientWidth, so the
   composition is frozen and only its size moves. Sizing this box in vh instead would silently hand
   the source a different viewport and it would compose for it: at desktop width both cinematics
   switch to a right-anchored phone with half the stage empty (rendered and rejected while planning
   this entry). */
/* ⚠️ ENTRY #267 STRIPPED THIS BOX'S OWN PANEL, AND THAT DELETION IS THE POINT OF THE ENTRY.
   It used to carry `border-radius: 26px`, `background: #F3F4F6` and `box-shadow: 0 40px 90px`,
   which framed the phone inside a second lit card: the user's words were "have the phone component
   highlighted not a highlighted area with the phone component in it". The stage is now an invisible
   window and the only lit thing is whatever the scrim's hole is cut around.
   ⚠️ `overflow: hidden` STAYS, and so does the fixed 390x844 sizing in JS. Both are load-bearing for
   the reasons in the block above. The background went with no replacement: there is no
   flash-before-mount to hide any more, because a transparent stage over the dim looks like the dim. */
.wmode-stage {
  position: fixed;
  left: 0;
  top: 0;
  display: block;
  transform-origin: top left;
  overflow: hidden;
  background: transparent;
}

/* ⚠️ ENTRY #272c, USER CALL: *"add heavy dropshadow using only our tokens nothing hardcoded values
   to the phone case element"*. Two token shadows stacked: `--shadow-popover` carries the weight and
   `--shadow-float` fills the contact edge, which is how depth is built everywhere else in this file
   rather than by inventing a third rgba.

   ⚠️ `filter: drop-shadow`, NEVER `box-shadow`, and that is the whole reason this works. The phone
   case lives inside the graft's SHADOW ROOT, so no selector in this stylesheet can reach it; and a
   `box-shadow` on the host would draw a rectangle around a rounded phone. `drop-shadow` is computed
   from the rendered ALPHA of the subtree, shadow DOM included, so it traces the case's own rounded
   silhouette from the outside. It only works because `.wmode-stage` is `background: transparent`
   above: give this element a background and the shadow becomes a box again.
   ⚠️ Applied only where `layout()` stamps the class, so the three phone-led segments keep the
   flat mockup they were tuned with. */
/* ⚠️⚠️ ENTRY #272za: ONE DROP-SHADOW, NOT THREE, AND THIS IS THE 60fps FIX RATHER THAN A STYLE
   TWEAK. `filter: drop-shadow()` is computed from the element's ALPHA, so each one re-rasterises the
   whole 390x844 phone including its shadow-DOM subtree, and three of them stack that cost three
   times on every frame the phone slides in. Measured per rAF at real 1x playback: a ~110ms window
   with NO PAINTED FRAME AT ALL at the phone's entrance, which is exactly where the user reported the
   move "not 60fps". A `box-shadow` cannot replace it (the phone is a rounded, notched silhouette,
   not a box), so the fix is to stop STACKING while keeping the strongest single token,
   `--shadow-popover`, which is what carried most of the depth anyway. */
/* ⚠️ ENTRY #276: `--shadow-hero`, NOT `--shadow-popover`. Still exactly ONE `drop-shadow()`, which
   is the whole of #272za's fix above; only the token got heavier. See the token's own note at the
   top of this file for why a second filter was not the answer. */
.wmode-stage.wmode-stage-hero {
  filter: drop-shadow(var(--shadow-hero));
}

/* The END sheet rides the same `.wmode-sheet` box; only its entrance and stacking differ. */
.wmode-layer .wmode-sheet {
  display: block;
  z-index: 1;
  animation: companyCoachDeskSheetIn 420ms var(--ease-out, cubic-bezier(.16, 1, .3, 1));
}

.wmode-layer .wmode-sheet[hidden] {
  display: none;
}

/* ═══ ENTRY #267: the opening act, performed on the REAL app shell ═══════════════════════════════
   The grafted cinematic opens on a MOCKUP of this very app shell, in which a fake cursor clicks a
   fake kebab on a fake "Maybe" column holding fake copies of Marta, Ahmed and Priya. On a phone
   that mockup is the only shell there is. On desktop the real one is on screen the whole time, two
   inches to the left, holding the same three candidates, so the mockup is suppressed inside the
   graft (`suppressGraftChrome`) and the choreography is replayed here instead. That is what earns
   the link the segment is trying to draw: THIS is your employer shell, and THAT lit phone is what
   the jobseeker sees.
   ⚠️ These deliberately copy `screening-mobile.html`'s own `.cursor` / `.menu` / `.menu button`
   rules rather than the messenger popover box. The ask was for the real shell to behave "the same
   as the animation version", so the animation's own chrome IS the house pattern to match here.
   ⚠️ NOTHING HERE IS EVER REALLY CLICKED. The kebab has had no handler and no menu behind it since
   it was added, and `.app-shell` is `inert` for the whole watch anyway. The menu below is a
   cinematic prop that lives in `.wmode-layer`, outside the inert subtree, and dies with it. */

/* ⚠️ (0,4,1), which beats the base `display: none` at (0,3,1) ON SPECIFICITY, not on source order.
   The attribute selector is load-bearing for that count: drop `[data-screen-id="nIfHr"]` and this
   ties with the hide rule, at which point whichever is written later wins and the reveal breaks the
   next time either block moves. `body` can never carry `.shortlist-scheduling-step` at the same
   time: `runSegmentWatch()` calls `setScreeningSequence(null)` before `start()`, which toggles that
   class off.
   ⚠️⚠️ ENTRY #270 REPLACED A HARDCODED `[data-board-column="reviewing"]` HERE WITH THE STAMPED
   `[data-wmode-column]`, AND THE HARDCODE WAS A REAL BUG THE MOMENT A SEGMENT ACTED ON A DIFFERENT
   COLUMN. Scheduling acts on SHORTLIST, whose kebab therefore stayed at the base `display: none`;
   `renderOpeningAct()` gates its placement on `kebab.offsetParent !== null`, so with no live kebab
   it never positioned the menu and the card sat at x=0, y=0 in the top-left corner of the screen
   for the whole segment. Measured: reviewing kebab `display: grid`, rect [980,198,30,30]; shortlist
   kebab `display: none`, offsetParent null, rect all zeros.
   ⚠️ The stamp keeps the count IDENTICAL at (0,4,1): `.nifhr-watch-active`, `[data-screen-id]`,
   `[data-wmode-column]` and `.column-kebab` are four, plus the `body` element. So the arithmetic the
   note above depends on is unchanged, and the reveal now follows whichever column the segment
   actually acts on instead of naming one. */
body.nifhr-watch-active [data-screen-id="nIfHr"] [data-wmode-column] .column-kebab {
  display: grid;
}

body.nifhr-watch-active [data-screen-id="nIfHr"] [data-wmode-column] .column-kebab[data-wmode-kebab="lit"] {
  background: var(--brand-tint);
  border-color: rgba(124, 58, 237, 0.42);
}

body.nifhr-watch-active [data-screen-id="nIfHr"] [data-wmode-column] .column-kebab[data-wmode-kebab="press"] {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--text-inverse);
}

.wmode-cursor {
  position: fixed;
  left: 0;
  top: 0;
  width: 34px;
  height: 34px;
  margin: -17px 0 0 -17px;
  border-radius: var(--radius-pill);
  background: var(--info-text);
  box-shadow:
    0 0 0 10px rgba(29, 78, 216, 0.16),
    0 18px 38px rgba(30, 58, 138, 0.24);
  opacity: 0;
  pointer-events: none;
}

.wmode-cursor::after {
  content: "";
  position: absolute;
  left: 13px;
  top: 13px;
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--surface);
}

.wmode-colmenu {
  position: fixed;
  left: 0;
  top: 0;
  width: 296px;
  display: grid;
  gap: 10px;
  padding: 13px;
  border: 1px solid rgba(124, 58, 237, 0.16);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: 0 24px 54px rgba(17, 24, 39, 0.22);
  opacity: 0;
  transform: translateY(-8px) scale(0.98);
  transform-origin: top right;
  pointer-events: none;
}

.wmode-colmenu button {
  height: 60px;
  border: 0;
  border-radius: 14px;
  background: transparent;
  color: #667085;
  text-align: left;
  padding: 0 var(--space-md);
  font: inherit;
  font-size: var(--fs-xl);
  font-weight: var(--fw-850);
  white-space: nowrap;
}

.wmode-colmenu button[data-wmode-on] {
  background: #1F2937;
  color: var(--text-inverse);
}

/* ═══ ENTRY #300: THE QUESTIONNAIRE BUILDER DRAWER ═══════════════════════════════════════════════
   A right-hand slideout over the dimmed real shell, shown only during the screening segment's
   prologue. User: "The questionnaire building sequence would be a slideout drawer with the rest of
   the screen dimmed for desktop view."

   ⚠️ THE DIM IS NOT HERE. It is `.wmode-scrim`, already built and already painted every frame; the
   prologue simply stops `placeHole()` punching a hole in it. Adding a second scrim would put two
   dims on screen and double the darkness. See `placeHole`'s own prologue branch.

   ⚠️ NO `position: fixed` GAMBLE. `.wmode-layer` is a body-level sibling of `.app-shell` with
   nothing transformed between it and <body>, which is the documented reason `fixed` inside the
   layer means the real viewport. This panel relies on that and on nothing else.

   ⚠️ WIDTH IS THE SAME `max(33vw, 460px)` GUTTER `layout()` PARKS THE PHONE IN, capped so it never
   eats the board at 901px. The phone is not on screen during the prologue (the graft is held at 0
   and its own shell is suppressed), so the two never share the gutter in time. */
.wmode-builder {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(max(33vw, 460px), 560px);
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: var(--space-md, 16px);
  padding: var(--space-xl, 32px) var(--space-lg, 24px);
  background: var(--surface, #fff);
  border-left: 1px solid var(--border, #E5E7EB);
  box-shadow: -22px 0 58px rgba(17, 24, 39, 0.24);
  overflow: hidden;
  pointer-events: none;
  will-change: transform, opacity;
}

.wmode-builder .wmb-head { display: grid; gap: 2px; }
.wmode-builder .wmb-eyebrow {
  font-size: var(--fs-sm); font-weight: var(--fw-extrabold); letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-tertiary, #9CA3AF);
}
.wmode-builder .wmb-name { font-size: 22px; font-weight: var(--fw-850); color: var(--text-primary, #111827); }

.wmode-builder .wmb-plabel {
  display: block; margin-bottom: var(--space-xs, 8px);
  font-size: var(--fs-sm); font-weight: var(--fw-extrabold); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-tertiary, #9CA3AF);
}
.wmode-builder .wmb-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-xs, 8px); }
.wmode-builder .wmb-tile {
  position: relative;
  display: flex; align-items: center; justify-content: center; text-align: center;
  min-height: 52px; padding: 0 var(--space-xs, 8px);
  border: 1px dashed var(--border, #E5E7EB); border-radius: var(--radius-lg, 14px);
  background: var(--bg-app, #F9FAFB);
  font-size: var(--fs-md); font-weight: var(--fw-750); color: var(--text-muted, #4B5563);
  opacity: 0; transform: translateY(8px);
  transition: border-color var(--motion-fast) ease, background var(--motion-fast) ease;
}
/* the one the hand is picking up */
.wmode-builder .wmb-tile[data-wmb-lift] {
  border-color: var(--brand, #7C3AED); border-style: solid; background: var(--brand-tint, #EDE9FE);
}
/* ⚠️ ENTRY #301: THE BADGE CARRIES THE RECORDING CAP, NOT THE WORD "soon". Superseded requirement,
   kept because it explains the shape: "the video tile is a TEASER by requirement: shown, never
   placed". The user reversed that, so the tile is now dragged in as question three and the badge
   states the 60 second limit from the palette reveal onward. Same box, same position, so no beat
   before the third drag changes by a pixel. */
/* ⚠️ NO `text-transform: uppercase` HERE, WHICH THE WORD "soon" CARRIED AND THIS DOES NOT. It
   rendered the cap as "60S", i.e. sixty-S, and only the screenshot caught it: every measurement of
   this element was correct, because `textContent` is "60s" either way and the transform lives in
   the paint. A units abbreviation is lower case. */
.wmode-builder .wmb-cap {
  position: absolute; top: 6px; right: 8px;
  font-size: 9px; font-weight: var(--fw-850); letter-spacing: 0.06em;
  color: var(--brand, #7C3AED);
}

/* ⚠️⚠️ ENTRY #301: `overflow` AND `flex-shrink: 0` ARE BOTH LOAD BEARING, AND THEIR ABSENCE WAS A
   MEASURED DEFECT THAT PREDATES THE VIDEO CARD. With question three removed from the DOM at the
   same beat, the content still exceeded this box by 146px at 901x800 and 46px at 1440x900; with no
   `overflow` rule that spill was PAINTED OVER the branching hint and the Save button below, and
   `.wmode-builder`'s own clip then cut whatever reached the panel edge. `hidden` rather than `auto`
   because the renderer drives `scrollTop` itself: this is a seek-driven film and a scrollbar would
   be chrome the viewer cannot use. `flex-shrink: 0` on the cards stops flexbox "solving" the
   overflow by squashing every question instead. */
.wmode-builder .wmb-canvas {
  flex: 1; min-height: 0;
  display: flex; flex-direction: column; gap: var(--space-xs, 8px);
  padding: var(--space-sm, 12px);
  border: 1px solid var(--border, #E5E7EB); border-radius: var(--radius-lg, 14px);
  background: var(--bg-app, #F9FAFB);
  overflow: hidden;
}
.wmode-builder .wmb-canvas > * { flex-shrink: 0; }
.wmode-builder .wmb-q {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--space-xs, 8px);
  padding: var(--space-sm, 12px);
  border: 1px solid var(--border, #E5E7EB); border-radius: var(--radius-md, 12px);
  background: var(--surface, #fff);
  opacity: 0; transform: translateY(10px) scale(0.98);
}
.wmode-builder .wmb-qnum {
  width: 22px; height: 22px; display: grid; place-items: center;
  border-radius: var(--radius-pill, 999px); background: var(--brand-tint, #EDE9FE);
  font-size: var(--fs-xs); font-weight: var(--fw-850); color: var(--brand, #7C3AED);
}
.wmode-builder .wmb-qtext { font-size: var(--fs-md); font-weight: var(--fw-750); color: var(--text-primary, #111827); }
.wmode-builder .wmb-qtype { font-size: var(--fs-xs); font-weight: var(--fw-bold); color: var(--text-tertiary, #9CA3AF); }
/* the answer choices, typed in one character at a time. `min-height` holds the row's height from
   the first frame so the card does not grow as each option is typed and shove the rows below it
   around for the whole beat. */
/* ⚠️ GOOGLE-FORMS ANSWER ROWS, NOT CHIPS (user call, verbatim: "no fucking chips it has to be like
   google forms for this"). Each answer is a radio ring plus a text line, and an "Add option" row
   sits under them which the ghost hand actually PRESSES to bring the next row into existence.
   ⚠️ The ring is the `.ns-radio` idiom that already ships in this stylesheet (the next-steps plan
   selector): 20px ring, 2px border, inner dot. Re-expressed at this panel's smaller scale rather
   than copied wholesale, because that one is a 16px-padded plan card and this is a form row.
   Superseded: a flex-wrap row of pill chips. */
.wmode-builder .wmb-opts {
  grid-column: 1 / -1;
  display: flex; flex-direction: column; gap: var(--space-2xs, 4px);
}
/* ⚠️ ENTRY #300c: SIZED UP (user call: "i want the answers elements larger as too small at
   moment"). Was 12px on a 28px row with a 14px ring and 4px gaps. */
.wmode-builder .wmb-opt,
.wmode-builder .wmb-addopt {
  display: flex; align-items: center; gap: var(--space-sm, 12px);
  min-height: 38px;
  font-size: var(--fs-lg); font-style: normal; font-weight: var(--fw-semibold); color: var(--text-muted, #4B5563);
}
/* the row's own text sits on a form underline, which is what makes it read as a FIELD */
.wmode-builder .wmb-opt em {
  flex: 1; min-width: 0;
  border-bottom: 1px solid var(--border, #E5E7EB);
  padding-bottom: 2px;
  font-style: normal;
  white-space: pre;   /* a trailing space mid-type must not collapse, or the text jitters */
}
.wmode-builder .wmb-radio {
  flex: none; width: 18px; height: 18px;
  border: 2px solid var(--border-strong, #9CA3AF); border-radius: var(--radius-pill, 999px);
  background: var(--surface, #fff);
}
.wmode-builder .wmb-opts { gap: var(--space-xs, 8px); }
.wmode-builder .wmb-addopt { color: var(--text-tertiary, #9CA3AF); }
/* ⚠️ A PLUS IN A CIRCLE, on the user's call. The dashed ring it replaces read as a broken radio
   button rather than as an action. Inline SVG, never a glyph or an emoji. */
.wmode-builder .wmb-plus {
  flex: none; width: 16px; height: 16px; display: grid; place-items: center;
  border: 2px solid currentColor; border-radius: var(--radius-pill, 999px);
}

/* ⛔ ENTRY #301b DELETED THE VIDEO ANSWER CARD, on the user's call, along with the four rules that
   styled its recording-spec row, those chips, the record dot and its grounds line. The palette tile
   and its cap badge STAY: the QUESTION was removed, not the type.
   ⚠️ The deleted selectors are described here rather than quoted, so a grep proving they are gone
   cannot match this note. */

/* the auto-flag chip: the whole point of the beat, so it wears the danger token, not a grey */
.wmode-builder .wmb-flagchip {
  grid-column: 1 / -1;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px; border-radius: var(--radius-pill, 999px);
  background: color-mix(in srgb, var(--danger, #B91C1C) 12%, transparent);
  color: var(--danger, #B91C1C);
  font-size: var(--fs-xs); font-weight: var(--fw-extrabold);
  opacity: 0; transform: scale(0.9);
}
.wmode-builder .wmb-flagchip svg { width: 13px; height: 13px; }

.wmode-builder .wmb-drop {
  display: grid; place-items: center; min-height: 56px;
  border: 1px dashed var(--border, #E5E7EB); border-radius: var(--radius-md, 12px);
  font-size: var(--fs-sm); font-weight: var(--fw-bold); color: var(--text-tertiary, #9CA3AF);
  transition: background var(--motion-fast) ease, border-color var(--motion-fast) ease, color var(--motion-fast) ease;
}
/* ⚠️ ENTRY #300c: THE DROP ZONE ARMS (user call: "when dragging the question type into the drop zone
   it never arms and goes our darker purple colour token"). It sat inert through the whole drag, so
   nothing on screen said "let go here". The bounce on drop is a transform written per-frame by the
   renderer from a `back.out(3)` scalar, not a CSS transition: this is a seek-driven film, and a
   transition cannot be scrubbed.
   ⚠️⚠️ ENTRY #301b: THE FILL IS THE DARK TOKEN, NOT THE TINT (user call: "use the darker purple token
   not this current one which is the light purple token colour i think"). She read it exactly right:
   the armed FILL was `--brand-tint`, which IS the light purple, and only the border and the label
   were dark. Superseded: `background: var(--brand-tint)` with a `color-mix(--brand 82%, #000)` border
   and label. It is now `--brand-dark`, the token that already exists for this, with `--text-inverse`
   on top so the label survives the dark fill. An armed zone should be the loudest thing in the panel
   for the third of a second it is armed.
   ⚠️ #301b ALSO CHANGED WHEN THIS FIRES, not just what it looks like: the renderer now arms on a real
   overlap of the carried tile and this element, so the state means "the tile is HERE" rather than
   "a drag is in progress somewhere". */
.wmode-builder .wmb-drop[data-wmb-armed] {
  border-style: solid;
  /* ⚠️ ENTRY #374: THE FALLBACKS SAID `#4C1D95` AND THE TOKEN NOW SAYS `#5B21B6`. A fallback that
     has drifted from its own token is a live trap, not dead text: it fires the moment the token is
     ever out of scope, and it would then paint the OLD purple beside the new one with nothing
     going red. The audit found the same shape once more in this file (`var(--motion-drawer, 300ms)`
     against a 320ms token). Kept as fallbacks rather than deleted, because that is how the rest of
     this rule is written; corrected so the two hands of the clock agree. */
  border-color: var(--brand-dark, #5B21B6);
  background: var(--brand-dark, #5B21B6);
  color: var(--text-inverse, #FFFFFF);
}

/* ENTRY #319: the branching-hint row's two rules were deleted here with the element they styled.
   Both selectors were unique to it, so nothing else lost styling. The `.wmb-canvas` rules above and
   the `.wmb-foot`/`.wmb-save` rules below are its siblings and are deliberately untouched. */

.wmode-builder .wmb-foot { display: flex; }
.wmode-builder .wmb-save {
  flex: 1; display: grid; place-items: center; min-height: 46px;
  /* ⚠️ THE HOUSE PILL TOKEN. It was `--radius-lg`, which is the CARD radius, so the primary CTA did
     not match every other primary button in the product. */
  border-radius: var(--radius-pill, 999px);
  background: var(--brand, #7C3AED); color: var(--text-inverse, #fff);
  font-size: var(--fs-lg); font-weight: var(--fw-extrabold);
}
.wmode-builder .wmb-save[data-wmb-press] { background: color-mix(in srgb, var(--brand, #7C3AED) 82%, #000); }

/* ⚠️⚠️ (#400) HELD MD3-INACTIVE UNTIL THE COACHING CARD IS GONE, HER CALL from an annotated
   `?scene=screening` frame: *"Md3 inactive this CTA button until the exact time the coaching card
   disappears. And then at that time, where this CTA button becomes active (not clickable by user
   though!) then add pie countdown timer here too for how long this drawer remains open onscreen
   for already from the going button live to when the scene moves on"*.
   ⚠️ 0.39 IS THE HOUSE MD3 INACTIVE VALUE, NOT A NUMBER I PICKED. It is the same one rj0wj's
   landing CTA, the rank gate's CTA and the drawer's "Message Marta" already use, and this file
   says so out loud in its own comment above `.compare-primary`.
   ⚠️ NO `pointer-events` RULE HERE, AND ITS ABSENCE IS DELIBERATE RATHER THAN AN OVERSIGHT. Her
   "not clickable by user though" is ALREADY TRUE and always was: `.wmode-builder` itself carries
   `pointer-events: none`, so nothing in this panel has ever been reachable by a human. This is a
   cinematic prop, not a live control. Adding a second, redundant declaration here would read to
   the next person as if the panel were interactive and this one button had been carved out of it.
   The guard asserts the panel-level block instead, so the real mechanism is what is pinned.
   ⚠️ AND NO `disabled` ATTRIBUTE: `.wmb-save` is a `<span>`, where `disabled` means nothing. The
   attribute below is a styling hook only, which is exactly what `data-inactive-cta` is elsewhere.
   -> today-update.md #400 */
.wmode-builder .wmb-save[data-wmb-inactive] { opacity: .39; }
/* ⚠️ (#400) ONE ROW WHILE IT COUNTS. The base rule is `display: grid; place-items: center`, which
   puts an appended disc in a SECOND ROW under the label (measured on the first render, not
   predicted). Scoped to the counting state so the resting pill is unchanged, and the gap is
   `--space-xs`, the same token #392 settled on for the space between a CTA label and its disc. */
.wmode-builder .wmb-save[data-wmb-counting] {
  display: flex; align-items: center; justify-content: center; gap: var(--space-xs, 8px);
}

/* ⛔ ENTRY #300c REMOVED `.wmb-preview` AND ITS FOUR CHILD RULES (user call: "remove the black
   modal preview its shit"). It was a dark floating card that flashed the first question back for
   1.4s. Deleted rather than hidden, so nothing keeps paying for it. */

/* the tile in flight. One node, reused by both drags, positioned in LAYER coordinates. */
.wmode-builder .wmb-ghost {
  position: absolute; left: 0; top: 0;
  display: none; align-items: center; justify-content: center;
  min-height: 52px; padding: 0 var(--space-xs, 8px);
  border: 1px solid var(--brand, #7C3AED); border-radius: var(--radius-lg, 14px);
  background: var(--brand-tint, #EDE9FE);
  font-size: var(--fs-md); font-weight: var(--fw-750); color: var(--brand, #7C3AED);
  box-shadow: 0 18px 38px rgba(76, 29, 149, 0.22);
  pointer-events: none;
}

/* ═══ ENTRY #267: the screening payoff, landed on the REAL Priya card ════════════════════════════
   The graft's own morph flies its red panel onto a MOCKUP "Maybe" board (`#boardReveal`) that this
   entry suppresses, so the panel and the flag are rebuilt here and flown onto the real card
   instead. They are ports of `screening-mobile.html`'s `.sr-card` and `.cand-flag` at their BASE
   (unscaled) sizes; the fit to the gutter is a `transform: scale()` written from JS, so every
   proportion stays the cinematic's own rather than being re-tuned by hand.
   ⚠️ REFERENCES GETS NONE OF THIS. `references-mobile.html` has no board act, no `#screenedResult`
   and no `#priyaCard`; its payoff is the reference drawer inside the graft.
   ⚠️ Priya's teal comes from `--cand-priya`, never the source's literal `#14B8A6`: one candidate
   palette, and the source is a standalone page that cannot see these tokens. */
.wmode-sr {
  position: fixed;
  left: 0;
  top: 0;
  width: 640px;
  padding: 28px 30px;
  border: 1.5px solid #f5a0a0;
  border-radius: 22px;
  background: var(--danger-bg);
  box-shadow: 0 30px 80px rgba(153, 27, 27, 0.20);
  transform-origin: 50% 50%;
  opacity: 0;
  pointer-events: none;
}

.wmode-sr-top {
  display: flex;
  align-items: center;
  gap: 14px;
}

.wmode-sr-avatar {
  width: 52px;
  height: 52px;
  border-radius: var(--radius-pill);
  background: var(--cand-priya);
  color: var(--text-inverse);
  font-size: var(--fs-2xl);
  font-weight: var(--fw-extrabold);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.wmode-sr-name {
  font-size: 20px;
  font-weight: var(--fw-extrabold);
  color: #7F1D1D;
  min-width: 0;
}

.wmode-sr-pill {
  margin-left: auto;
  padding: 7px var(--space-md);
  border-radius: var(--radius-pill);
  background: var(--danger-text);
  color: var(--text-inverse);
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  white-space: nowrap;
}

.wmode-sr-reason {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 20px 0 0;
  font-size: var(--fs-2xl);
  font-weight: var(--fw-semibold);
  color: #991B1B;
}

.wmode-sr-x {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--danger-text);
  color: var(--text-inverse);
  font-size: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.wmode-flag {
  position: fixed;
  left: 0;
  top: 0;
  width: 54px;
  height: 54px;
  margin: -27px 0 0 -27px;
  border-radius: 50%;
  background: var(--surface);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow:
    0 10px 26px rgba(185, 28, 28, 0.38),
    0 0 0 1px rgba(185, 28, 28, 0.14);
  opacity: 0;
  pointer-events: none;
}

.wmode-flag svg {
  width: 30px;
  height: 30px;
  display: block;
}

/* ============================================================================
   (Entry #256) THE PAUSE COACH, rj0wj's primer. A FLUSH-BOTTOM SHEET, not a
   floating card: it first shipped as a `.fab-coach` modal and the user's call
   was that this content belongs in the bottom sheet.

   ⚠️⚠️ THE FIRST RULE IS A BUG FIX, NOT HOUSEKEEPING, AND IT IS THE SAME BUG
   ENTRY #124 HIT. hirello-app.css:17723 already documents that
   `.company-basics-coach.is-dismissed` is (0,2,0) and LOSES to the (0,2,1)
   show rule `body.workspace-tour-active .company-basics-coach`, so the class
   the JS sets has never hidden that sheet. #124's `body.fab-coach-active` rule
   was the only thing doing it, and this step removes that class, so phase A's
   "NO WE DIDN'T HACK YOU!" sheet came straight back and sat under the pause
   explainer. Measured before this rule existed: display block, visible, 492 to
   900 at 1440x900 and 466 to 844 at 390x844, on BOTH tiers. Same (0,2,1) shape
   as #124's, and in the tail so source order carries it.
   ============================================================================ */

body.pause-coach-active .company-basics-coach,
body.speed-coach-active .company-basics-coach {
  display: none;
}

/* THE SHEET LANGUAGE IS #209's, NOT A NEW ONE, value for value: the same box
   `.company-basics-coach`, `.rank-gate-card`, `.rank-done-card` and
   `.wmode-sheet` all speak. It is restated here rather than joining those
   selector groups because they are spread across a max-width tier, a min-width
   tier and this tail, and a `.pause-coach` added to all three would be three
   places to keep in step instead of one.
   ⚠️ THE MIRROR IS SELF-CHECKING: guard `#256I` measures this sheet's rendered
   box against the phase A sheet's LIVE box at the same viewport, so a drift in
   either direction reddens rather than rotting quietly. Never replace that
   guard with hard-coded pixel literals. */
.pause-coach,
.speed-coach {
  position: fixed;
  /* Sits with the other coach cards, and DELIBERATELY BELOW the PAUSED stripe's
     2147483035, so the thing being taught paints over its own explainer rather
     than under it. */
  z-index: 2147483031;
  box-sizing: border-box;
  top: auto;
  left: 0;
  right: 0;
  bottom: 0;
  width: auto;
  max-height: calc(100dvh - 64px);
  overflow-y: auto;
  padding: 26px 22px calc(22px + env(safe-area-inset-bottom, 0px));
  border: 0;
  border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
  /* ⚠️ THE INK IS NOT ONE VALUE ACROSS THE TIERS, and assuming it was is the drift `#256I`
     caught on its first run. The phone sheet is the BASE `.company-basics-coach` treatment,
     `#0f1726` under a purple radial glow, because the <= 900px block overrides no background
     at all. Desktop's #209 block replaces it with a flat `#111827`. Measured, not guessed:
     rgb(15,23,38) against rgb(17,24,39). Matched per tier below. */
  background:
    radial-gradient(circle at 18% 14%, rgba(124, 58, 237, 0.28), rgba(124, 58, 237, 0) 34%),
    #0f1726;
  color: #F9FAFB;
  box-shadow:
    0 -18px 60px 0 rgba(17, 24, 39, 0.5),
    0 0 0 1px rgba(124, 58, 237, 0.16);
  text-align: start;
}

/* The reference's drag-handle pill, same numbers as every other sheet's. */
.pause-coach::before,
.speed-coach::before {
  content: "";
  position: absolute;
  top: 9px;
  left: 50%;
  transform: translateX(-50%);
  width: 36px;
  height: 4px;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--text-inverse) 26%, transparent);
}

.pause-coach p,
.speed-coach p {
  margin: 0;
  font-size: var(--fs-xl);
  font-weight: var(--fw-medium);
  line-height: 24px;
  color: #C7CBD4;
  text-align: start;
}

.pause-coach [data-pause-coach-gesture] {
  display: block;
  margin-top: 10px;
  font-weight: var(--fw-bold);
  font-style: normal;
  color: #F9FAFB;
}

/* The thing that visibly stops. Teal is not decorative: it is the same #14B8A6
   as the cinematics' time-left bar, so the viewer meets one visual language for
   "this is playing" here and then meets it again for real. */
.pause-coach-demo {
  margin-top: 18px;
}

.pause-coach-track {
  display: block;
  height: 8px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.16);
  overflow: hidden;
}

.pause-coach-fill {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--cue-teal);
  /* Seeded empty so the first painted frame cannot flash a full bar before
     GSAP's first tick writes the inline transform. */
  transform: scaleX(0);
  transform-origin: left center;
}

/* ⚠️ (Entry #259) HIDDEN, BUT IT STILL OCCUPIES ITS BOX, AND THAT IS THE WHOLE REQUIREMENT.
   The user's words were "hide the go on try it dont delete it just hide it so the spacing remains
   nothing is moved around", which is the house law: `visibility: hidden`, NEVER `display: none`.
   `display: none` would collapse this line's ~15px plus its 12px margin and lift the sheet's whole
   bottom edge, moving the bar and the copy above it. Every view.
   ⚠️ DO NOT "TIDY THIS UP" into `display: none`, and do not delete the element or the JS that
   writes to it: `setPauseHint('Like this.')` still runs on the safeguard path, and it is what keeps
   this box exactly one line tall in both states. Guard `#259B` asserts the element is hidden AND
   still has a real height, so a later `display: none` reddens. */
.pause-coach-hint {
  visibility: hidden;
  margin-top: var(--space-sm);
  font-size: var(--fs-xs);
  font-weight: var(--fw-extrabold);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.68);
}

/* ⚠️ THE TWO TIERS NEED DIFFERENT KEYFRAMES AND THAT IS LOAD-BEARING, not a
   duplicate. `companyCoachSheetIn` animates translateY(100%) to translateY(0),
   which is right for a full-bleed sheet carrying no x-transform. The desktop
   sheet is CENTRED with translateX(-50%), so borrowing those keyframes would
   drop the x-component for the whole 340ms and slam the sheet half its own
   width to the right. #209 wrote `companyCoachDeskSheetIn` for exactly that,
   carrying the x-component itself. */
@media (max-width: 900px) {
  .pause-coach,
  .speed-coach {
    animation: companyCoachSheetIn 340ms var(--ease-out, cubic-bezier(.16, 1, .3, 1));
  }
}

@media (min-width: 901px) {
  .pause-coach,
  .speed-coach {
    left: 50%;
    right: auto;
    transform: translateX(-50%);
    width: min(560px, 100vw);
    max-height: 88dvh;
    padding: 34px 26px 30px;
    /* #209's desktop sheet drops the base purple glow for a flat ink. Restated here so the
       two tiers match their own reference rather than each other. */
    background: #111827;
    animation: companyCoachDeskSheetIn 340ms var(--ease-out, cubic-bezier(.16, 1, .3, 1));
  }
}

@media (prefers-reduced-motion: reduce) {
  .pause-coach,
  .speed-coach {
    animation: none;
  }
}

/* ============================================================================
   (Entry #257) DESKTOP ONLY (>=901px): the job-coach step's "Claim & Review"
   button floats, and the coaching tooltip floats WITH it so the two read as one
   object. Mobile and tablet are deliberately byte-identical: no rule below
   applies under 901px.

   ⚠️⚠️ THE PHASE LOCK IS THE WHOLE POINT, AND IT COMES FROM ONE GROUPED RULE.
   The tooltip's tail points at the button, so the instant the two drift out of
   phase the tail detaches from the thing it is aiming at, which is worse than
   no float at all. Both selectors are gated on the SAME class (`job-tour-live`)
   and take the SAME animation in the SAME declaration, so both animations are
   created in one style recalc and share a `startTime`. Guard `#257C` asserts
   that `startTime` equality from the WAAPI clock, which is the actual
   definition of "in phase".
   ⚠️ DO NOT split this into two rules, and DO NOT give either one its own
   duration, delay or easing. That is exactly how they come apart.
   ⚠️ `.tooltip` is OPACITY-gated (`.coachmark-overlay.tour-ready .tooltip`),
   never `display: none`, which is what makes this safe: a `display: none`
   element runs no animation and would start late and out of phase when shown.
   Only `.coachmark-overlay.is-dismissed` sets `display: none`, and by then the
   step is over.

   ⚠️ NOTHING THE TOUR MEASURES IS DISTURBED BY THIS, checked before shipping:
   `placeTour()` derives the scrim hole, `--tour-*` and `--tip-top` from
   `.tour-target` (the WHOLE job card), which does not float. It reads the
   button only for `--tail-x`, and that is `cr.left + cr.width / 2`, a
   HORIZONTAL centre, so a vertical translate cannot move it. The tail is
   live-aimed on desktop by the rule at :15441, not the static `left: 40px`
   the base rule declares.
   ⚠️ The button stays inside the spotlight hole: the hole tracks the 108px-tall
   card and the button is a ~40px pill centred in it, so 6px of travel cannot
   reach an edge.

   ⚠️ NO `will-change` HERE, ON PURPOSE. Promoting this to its own compositor
   layer is what makes a transform animation invisible to
   `getComputedStyle().transform`, and the guards need to see it move. 6px on
   two small elements does not need the layer.
   ============================================================================ */

/* Declared at BASE tier, applied only inside the desktop block below. A
   `@keyframes` written INSIDE a media query does not exist outside it, and a
   rule referencing a name that is out of scope is a SILENT no-op. */
@keyframes jobCoachFloat {
  0%, 100% { transform: translate3d(0, 0, 0); }
  50% { transform: translate3d(0, -6px, 0); }
}

@media (min-width: 901px) {
  body.job-tour-live .tour-target .row-claim,
  body.job-tour-live .coachmark-overlay .tooltip {
    animation: jobCoachFloat 2400ms ease-in-out infinite;
  }
}

/* House convention for an idle float: the motion is the only thing switched
   off, never the element. Scoped to the same tier so it cannot reach phones. */
@media (min-width: 901px) and (prefers-reduced-motion: reduce) {
  body.job-tour-live .tour-target .row-claim,
  body.job-tour-live .coachmark-overlay .tooltip {
    animation: none;
  }
}


/* ==========================================================================
   ENTRY #260 (2026-07-31, user call, annotated screenshot). x1riqt's post-parse
   card becomes a TAIL-BASED COACHING CARD aimed at Marta's score pill, on EVERY
   view. It REPLACES the old card outright, on the user's call: one card, one CTA.

   What it supersedes, and why each override below exists:
   (a) ENTRY #114's phone/tablet treatment (:17067) made `.tour-step` a full-width
       flush-bottom SHEET with a grab handle. A sheet pinned to the bottom edge
       cannot point at a row near the top of the board, so at `[data-ts-pose]`
       every one of those declarations is reversed here.
   (b) ENTRY #156's `>=901px` pose rule (:18764) parked the card at the bottom of
       the Applied column and gave it `x1riqt-tour-float ... infinite`.

   ⚠️⚠️ THE FLOAT IS THE LOAD-BEARING DELETION. #257's law is that a tail and its
   target must ride ONE animation or the aim drifts, and Marta's card is static:
   any perpetual motion here pulls the tail off the pill by the float's full 6px,
   forever, and it would read as a mis-aimed arrow rather than as motion. The card
   is at rest the moment its entrance ends. `__tourCardProbe().infiniteAnims` is
   the machine-checkable form of that, and `#260E` asserts it is empty.

   ⚠️ SPECIFICITY, COUNTED, NOT ASSUMED. #114's sheet rules are (0,1,0) and its
   grab handle is (0,1,1); #156's pose rule is (0,3,0) and its `animation: none`
   reduced-motion twin is the same. Every rule below is written at (0,4,0) or
   better AND sits at the end of the file, so it wins on specificity first and
   would still win on source order alone. A rule written at (0,3,0) next to #156's
   would have TIED and then depended purely on order, which is the #98 law 7 trap.

   ⚠️ THE GRAB HANDLE AND THE TAIL ARE THE SAME `::before`. #114 uses it for the
   sheet handle (a 36x4 rounded bar, centred, `translateX(-50%)`), so the tail rule
   must reset EVERY property that rule sets (top, left, transform, width, height,
   border-radius and background), or a leftover declaration survives into the tail.
   All seven are reset below; do not trim any of them as redundant.

   ⚠️ THE TAIL IS FLAT NAVY ON PURPOSE, matching #181b's screening coach. The card's
   own background is a radial purple wash over #0f1726, so a tail that tried to
   sample "the card colour" would seam wherever `--ts-tail-x` lands.
   ========================================================================== */
.tour-modal.parsing-tour .tour-step[data-ts-pose="jobscore"] {
  position: fixed;
  left: var(--ts-x, 12px);
  top: var(--ts-y, 12px);
  right: auto;
  bottom: auto;
  width: var(--ts-w, 320px);
  max-width: none;
  gap: var(--space-xs);
  padding: var(--space-md) 18px 18px;
  border: 1px solid rgba(148, 163, 184, 0.14);
  border-radius: 14px;
  /* Kills BOTH #114's `parsingTourSheetIn` slide and #156's infinite float. The
     entrance is re-armed on `.is-visible` below, so it plays at the reveal rather
     than being burned invisibly when the pose is first stamped mid-parse. */
  animation: none;
}

/* MEASURED CEILING, in the #174 spirit. This family's title is now 18 characters,
   not 46, so the shared `--sheet-title-size` (34px) is oversized for a compact card.
   The rule here is "the title holds ONE row at every width", and the largest size
   that still does was measured per width, in the real card box, with a Range (never
   `el.getClientRects()` on the block, which returns one rect whatever the line count):
     width  320  360  390  412  430  768+     card box  296 336 366 388 406 420
     max px  25   29   32   34   36   37
   ⚠️ The counter was CANARIED at 90px first and reported 3 rows at every width, so a
   "1 row" result is a real measurement and not a check that cannot fail.
   The binding width is 320 (22/320 = 6.875vw), and the ramp sits under every ceiling
   with headroom: applied sizes measured 22 / 24.75 / 26.81 / 28 / 28 / 28, one row at
   all nine widths tested. The cap is reached at 408px.
   ⚠️ Re-measure if the copy changes: it is derived from THIS string. */
.tour-modal.parsing-tour .tour-step[data-ts-pose="jobscore"] h2 {
  --sheet-title-size: clamp(22px, 6.875vw, 28px);
  --sheet-title-lh: 1.12;
  max-width: none;
}
.tour-modal.parsing-tour .tour-step[data-ts-pose="jobscore"] p {
  max-width: none;
}

/* #114 stacks the actions row and stretches the CTA edge to edge, which reads as a
   sheet footer. On a compact tail card it has to go back to an inline pill. */
.tour-modal.parsing-tour .tour-step[data-ts-pose="jobscore"] .tour-step-actions {
  flex-direction: row;
  align-items: center;
  margin-top: 2px;
}
.tour-modal.parsing-tour .tour-step[data-ts-pose="jobscore"] .tour-primary {
  width: auto;
  height: 44px;
}

.tour-modal.parsing-tour .tour-step[data-ts-pose="jobscore"]::before {
  content: "";
  position: absolute;
  left: var(--ts-tail-x, 24px);
  top: auto;
  bottom: auto;
  transform: none;
  width: 18px;
  height: 9px;
  border-radius: 0;
  background: #0f1726;
  pointer-events: none;
}
.tour-modal.parsing-tour .tour-step[data-ts-pose="jobscore"][data-ts-tail="up"]::before {
  top: -9px;
  clip-path: polygon(0 100%, 50% 0, 100% 100%);
}
.tour-modal.parsing-tour .tour-step[data-ts-pose="jobscore"][data-ts-tail="down"]::before {
  bottom: -9px;
  clip-path: polygon(0 0, 50% 100%, 100% 0);
}

/* The entrance, armed on the reveal so it is never spent while the layer is still
   opacity:0. One-shot: it ends at `transform: none`, so the tail settles ONTO the
   pill and then stops, which is the whole point of dropping the float. */
.tour-modal.parsing-tour.is-visible .tour-step[data-ts-pose="jobscore"] {
  animation: x1riqt-jobscore-tip-in 260ms var(--ease-out, cubic-bezier(.16, 1, .3, 1)) both;
}
@keyframes x1riqt-jobscore-tip-in {
  from { opacity: 0; transform: translateY(7px) scale(.984); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .tour-modal.parsing-tour.is-visible .tour-step[data-ts-pose="jobscore"] {
    animation: none;
  }
}

/* ==========================================================================
   ENTRY #261 (2026-07-31, user call), PHONE AND TABLET ONLY. Marta's score badge
   reads THROUGH the dim while #260's card asks "WHAT'S THIS SCORE?" about it.
   Every other badge stays dimmed, and desktop is untouched.

   ⚠️ WHY THIS IS A HOLE IN THE DIM AND NOT A Z-INDEX. `.match-score` lives inside
   `.kanban-col.new-col`, which the processing spotlight raises to `z-index: 25`
   (:3749). That is a stacking context, and a z-index CANNOT climb out of an
   ancestor's stacking context, so no number on the badge or on its card can ever
   beat the tour layer's z 26. Raising the COLUMN instead would lift all six rows
   over the scrim, which is the opposite of the ask. Removing the dim over exactly
   one circle is the only move that touches nothing else.

   ⚠️ WHY A MASK AND NOT `clip-path`. #119's law: a `path()` string cannot
   interpolate custom properties, and this hole has to track a LIVE rect on scroll
   and resize, so it must be driven by properties the JS rewrites. A
   `radial-gradient` mask takes custom properties happily. It also has to be
   ROUND, and #119's other half is that `polygon()` can only cut a square hole;
   the badge is a 34px circle (`border-radius: var(--radius-pill)`, :4070).

   ⚠️ DESKTOP IS BYTE-IDENTICAL, TWICE OVER: this block is inside
   `@media (max-width: 900px)`, and above 900px `.parsing-tour::before` is
   `background: transparent` (:4139) so there is no dim there to cut a hole in.
   `#261C` asserts both badges render the SAME colour at 1440.

   ⚠️ THE `[data-jsx-hole="on"]` GATE IS LOAD-BEARING, not decoration. The placer
   stamps it only on a run that actually found Marta and posed the card, so in the
   fallback state (tour revealed before the parse produced her) the scrim is a
   plain, unpunched dim rather than one with a hole over empty board.
   ========================================================================== */
@media (max-width: 900px) {
  .tour-modal.parsing-tour[data-jsx-hole="on"]::before {
    /* transparent in the middle = the dim is REMOVED there. The 88%-to-100% ramp
       is a ~2px feather at this radius, purely to stop the circle aliasing into a
       visible staircase; a hard stop reads as a cut-out sticker. */
    -webkit-mask-image: radial-gradient(circle var(--jsx-hole-r, 0px) at var(--jsx-hole-x, -999px) var(--jsx-hole-y, -999px), transparent 88%, #000 100%);
    mask-image: radial-gradient(circle var(--jsx-hole-r, 0px) at var(--jsx-hole-x, -999px) var(--jsx-hole-y, -999px), transparent 88%, #000 100%);
  }
}

/* ==========================================================================
   ENTRY #262 (2026-07-31, user call), widened to EVERY VIEWPORT by ENTRY #264.
   Marta's JobScore badge ELASTIC-bounces while #260's card asks "WHAT'S THIS
   SCORE?" about it, and the score NUMBER grows with it.
   Retuned on the user's follow-up: "the bounce needs to be more pronunced and the
   size growth more and the score number text size too during bounce and grow and
   elastic bounce on the the mobile and tablet."

   ⚠️ ENTRY #264 WIDENED THIS TO EVERY VIEWPORT ("desktop views only as mobile and
   tablet already done"). #261's hole did NOT move with it, and that divergence is
   correct rather than an oversight, so do not "restore" the pair.

   The old note here said the pulse and the hole were two halves of one gesture and
   that moving one meant moving both, on the reasoning that a badge pulsing where no
   hole was cut would grow straight into the scrim. That holds ONLY where a scrim
   exists. It does not exist on desktop: `.parsing-tour::before` is
   `background: transparent` in the base rule (:4139) and the dim is re-armed ONLY
   inside `@media (max-width: 900px)` (:17074, `var(--scrim-dim)`). Measured live at
   1280, 1440 and 1920 rather than read off that source: computed `backgroundColor`
   is `rgba(0, 0, 0, 0)` and `backgroundImage` is `none` at all three. There is no
   dim above 900px, so there is nothing for the badge to grow into and nothing to cut.
   The hole rule therefore stays phone-and-tablet-only, and `#261C` still asserts the
   desktop scrim carries no mask.

   ⚠️ THE PEAK HAS ABOUT 4.5px OF HEADROOM ON DESKTOP AND THAT IS THE BINDING LIMIT.
   `.candidate-stream` is `overflow: hidden` and Marta is row 0, so her card's top
   edge is flush with the stream's padding box. At the 1.32 peak the badge grows
   5.44px per side and the grown rim still sits 4.56px INSIDE that top edge (and
   5.23px to 5.90px inside the right edge), identical at 1280/1440/1920 because the
   badge is a fixed 34px circle. So it does not clip today, but the peak cannot rise
   past roughly 1.58 before `.candidate-stream` starts shaving the top of the rim.
   Retune the peak and you must re-measure that clearance; the next clip ancestor out
   (`.kanban-col.new-col`) is a further 19px away and is not the constraint.

   ⚠️⚠️ THE TIMING FUNCTION MUST NOT OVERSHOOT, and that is a correctness
   constraint, not taste. #261's hole is sized from `--jsx-pulse-peak`, so if an
   easing like `cubic-bezier(.34,1.56,.64,1)` overshot BETWEEN keyframes the real
   maximum would exceed the declared peak and the grown rim would fall back under
   the dim. All of the elasticity therefore lives in the KEYFRAMES, and the easing
   is a plain non-overshooting ease-in-out. `#262C` measures the ACTUAL maximum
   across a full period and asserts the hole clears THAT, not the declared number,
   so a future retune that reintroduces an overshooting easing is caught.

   ⚠️⚠️ ONE ANIMATION CARRIES BOTH `transform` AND `font-size`. Two animations on
   the same keyframes are two clocks and drift (#153 measured 17.2ms apart), which
   would show as the digits swelling out of time with the circle.

   ⚠️ `--jsx-font-base` IS PUBLISHED BY THE PLACER, NOT TYPED HERE, and that is
   load-bearing: this badge is NOT 14px. The x1riqt processing rule overrides
   `.match-score` to `font-size: 13px`, so a keyframe authored against the 14px
   base would make the number JUMP on the first frame and again on the last.
   The JS reads the resting size once, BEFORE it stamps the pulse on, precisely so
   it can never read a value the animation is already changing.
   ========================================================================== */
:root {
  --jsx-pulse-peak: 1.32;
}

/* ENTRY #264: NO MEDIA QUERY. The stamp is what scopes this, not the width. The JS
   in x1riqt.html only ever stamps `data-jsx-pulse="on"` on the one badge the coach
   card is asking about, and clears it the moment that card goes, so an unwrapped
   rule cannot leak onto another badge or another screen. */
.candidate-final .match-score[data-jsx-pulse="on"] {
  animation: x1riqt-jobscore-badge-pulse 1300ms cubic-bezier(.4, 0, .2, 1) infinite both;
}

/* An ELASTIC settle: a big pop, a deep compress, then decaying oscillation that
   rings out and RESTS. The font tracks the same curve at a slightly shallower
   amplitude, so the digits grow with the circle instead of appearing to float
   inside it.
   ⚠️ RETUNED QUICKER on the user's follow-up ("make the bounce elestic effect
   quicker"). The whole cycle went 1800ms -> 1300ms AND the ring-out was compressed
   into the first 62% instead of the first 93%, so the elastic itself runs in about
   806ms where it used to take 1674, roughly twice as fast. Shortening the DURATION
   alone would have been the wrong knob: it speeds the bounce and the rest pause
   equally, so the badge simply repeats more often and reads busier rather than
   snappier. The flat 62%-100% tail is that pause, about 494ms; without it the badge
   breathes continuously and reads as a spinner, not as "look here". */
@keyframes x1riqt-jobscore-badge-pulse {
  0% { transform: scale(1); font-size: var(--jsx-font-base, 13px); }
  12% { transform: scale(var(--jsx-pulse-peak, 1.32)); font-size: calc(var(--jsx-font-base, 13px) * 1.30); }
  23% { transform: scale(0.90); font-size: calc(var(--jsx-font-base, 13px) * 0.95); }
  32% { transform: scale(1.17); font-size: calc(var(--jsx-font-base, 13px) * 1.15); }
  40% { transform: scale(0.945); font-size: calc(var(--jsx-font-base, 13px) * 0.975); }
  47% { transform: scale(1.075); font-size: calc(var(--jsx-font-base, 13px) * 1.06); }
  53% { transform: scale(0.975); font-size: calc(var(--jsx-font-base, 13px) * 0.99); }
  58% { transform: scale(1.025); font-size: calc(var(--jsx-font-base, 13px) * 1.02); }
  62% { transform: scale(1); font-size: var(--jsx-font-base, 13px); }
  100% { transform: scale(1); font-size: var(--jsx-font-base, 13px); }
}

/* ENTRY #264: the width condition came off this one too, in lockstep with the rule
   it cancels. Leaving it at `max-width: 900px` would have been the worst outcome of
   the pair: a reduced-motion desktop viewer would then be the ONLY person still
   getting an infinite bounce, because the canceller would not have reached them.
   Same selector specificity (0,3,0) as the rule above and written LATER in the
   file, which is what makes it win; keep them adjacent and in this order. */
@media (prefers-reduced-motion: reduce) {
  .candidate-final .match-score[data-jsx-pulse="on"] { animation: none; }
}

/* ==========================================================================
   ENTRY #265 (2026-07-31, user call). At the JobScore in-place segment's END
   frame, Marta's whole CARD reads through the dim and elastic-pulses.
   "At this stage fade the background to this dark current level but keep the
   Marta card and pulse elastic it slightly and make sure you keep the rounded
   corners not my shit cut and paste job lol"

   ⚠️ DESKTOP IN-PLACE ENGINE ONLY, AND THAT IS STRUCTURAL, NOT A SCOPING CALL.
   `jobscore-inplace.js` gates on `(min-width: 901px)` (:80) and below that
   x1riqt.html routes to `jobscore-explained.html?auto=1` instead (:776), a
   different page with its own end sheet. These rules need no media query because
   the elements they style only exist when that module runs. The phone and tablet
   equivalent is a SEPARATE piece of work on the standalone page.

   ⚠️ WHY A SPOTLIGHT ELEMENT AND NOT A MASK. #261 cut its hole with a
   `radial-gradient` mask, which is round by construction. This hole is a 12px
   rounded RECTANGLE and a radial gradient cannot express one. #119's law closes
   the other two doors: `clip-path: path()` cannot interpolate the custom
   properties this needs to track a live rect, and `polygon()` cannot be round.
   So the dim is not a sheet with a hole in it: it is this element's own
   `box-shadow`, spread far enough to cover the viewport, and the element's
   INTERIOR is simply never painted. The real card underneath shows through
   undimmed. Nothing is cloned, which is the "not my shit cut and paste job"
   half of the ask: what you see lit IS the live card, not a copy of it.

   ⚠️ WHY NOT A Z-INDEX, same wall as #261. Marta's card sits inside
   `.kanban-col.new-col`, which is `position: relative; z-index: 25` and
   therefore a stacking context; the dim lives in `.jsx-layer` at z 2147482000.
   No number on the card can climb out of its ancestor's context, and raising the
   COLUMN would lift all nine Applied rows out of the dim instead of one.

   ⚠️ THE RADIUS IS READ FROM THE CARD AT RUNTIME, never typed here. The 12px
   below is only a fallback for the frame before the first layout pass. If the
   card's radius is ever retuned, this follows it for free; a literal would drift
   and the mismatch would show as exactly the square-cornered paste the user
   objected to.
   ========================================================================== */
.jsx-spot {
  position: absolute;
  left: 0;
  top: 0;
  /* The same layer depth the flat scrim used to paint at, so the sheets, pins and
     the payoff CTA all still stack above it exactly as before. */
  z-index: 1;
  opacity: 0;
  border-radius: var(--radius-md);
  /* ⚠️ THE SPREAD IS THE DIM. Colour and alpha are byte-identical to `.jsx-scrim`
     above, because this REPLACES that paint rather than adding to it: running both
     would double-darken the board to 0.97 effective and the end frame would read
     almost black. `jobscore-inplace.js` tweens this in where it used to tween the
     scrim, so the fade timing the user approved is unchanged.
     ⚠️ A box-shadow NEVER participates in hit-testing (the #72 law), so this
     paints the dim but cannot swallow a click. That is safe ONLY because
     `.jsx-layer` is already `pointer-events: none` and the payoff buttons opt
     back in themselves; do not give this element pointer-events. */
  box-shadow: 0 0 0 9999px rgba(2, 6, 23, 0.82);
}

/* ⚠️⚠️ THE CARD PULSE WAS DELETED AT ENTRY #265c ON THE USER'S CALL: "stop the pulse at the end
   i hate it just keep the card highlighted for marta is all". DO NOT REINSTATE IT. What #265 and
   #265b shipped here was an elastic scale bounce on Marta's card at the payoff; the highlight is
   the part that was wanted, the motion was not. Gone with it: `x1riqt-jobscore-card-pulse`, the
   `data-jsx-cardpulse` stamp in both engines, the `prefers-reduced-motion` canceller that existed
   only to switch the bounce off, and `pinPulseOrigin()`, which existed only to stop the bounce
   dragging the card sideways.

   ⚠️ THIS IS NOT THE BADGE PULSE AND THAT ONE STAYS. `x1riqt-jobscore-badge-pulse` (#262, widened
   to desktop by #264) runs on Marta's SCORE BADGE at the earlier "WHAT'S THIS SCORE?" coach card,
   a different beat that the user asked for twice. Only the END-frame CARD bounce was cut.

   ⚠️ THE LESSONS THE DELETED CODE PAID FOR ARE NOT DELETED: `transform: scale()` in keyframes
   REPLACES an inline transform and wipes a GSAP-positioned element's place, and the independent
   `scale` property is applied OUTSIDE `transform` so it scales the translate too. Both are written
   up in the memory file behind today-update.md #265b; they will bite the next animation on a
   GSAP-positioned node. */

/* The spotlight's inset, i.e. the factor the card's own box is grown by to make the lit rect.
   ⚠️⚠️ THE USER REVERSED THIS AT ENTRY #318 (2026-08-11, on her annotated end frame): "The
   highlight is too wide it should just be the candidate panel". So it is now 1, and the lit rect
   IS the card's box: no ring of light proud of it on any side.
   SUPERSEDED REASONING, kept because it explains why the number was ever above 1, not because it
   is still the design: "It is what makes the lit region read as a HIGHLIGHT rather than as a patch
   of undimmed board: the lit rect is the card's own box grown by this factor, so a thin ring of
   light sits proud of the card on every side." At 1.045 that ring was 2.25% of the card's width on
   each edge, which at 1440x900 is what she was pointing at past the score pill.
   ⚠️ It was `--jsx-card-peak` until #265c, when it named the pulse's peak and the spotlight was
   sized from it so the growing card could never swell back under its own dim. With the pulse gone
   the number survives but its MEANING changed, so the name changed with it: a token still called
   "peak" would send the next reader looking for an animation that is not there. The name is kept
   again here rather than deleted, because `placeSpot()` in BOTH engines still multiplies by it and
   a token at 1 stays the single place to retune this if she ever wants the ring back.
   ⚠️ THE TOKEN IS DECLARED IN THREE PLACES and they must move together, because the standalone
   page does not link this stylesheet: here, `jobscore-explained.html`'s own `:root`, and the
   `|| 1.045` fallback inside each engine's `placeSpot()` (jobscore-inplace.js, and the copy inside
   jobscore-explained.html). All four were moved to 1 at #318.
   ⚠️ Headroom is no longer a constraint at 1: the old note measured `.kanban-col.new-col` as the
   binding clipper at maxScale 1.0803, which 1.045 sat inside; 1 cannot clip at all. */
:root {
  --jsx-card-halo: 1;
}

/* ==========================================================================
   ENTRY #266 (2026-07-31, user call), DESKTOP COLUMN LAYOUT. "I want this to be our dark tailed
   coaching card instead of this and anchor the float animation to it as well like this chip is."

   #200 gave the STACKED phone layout this exact treatment and deliberately left desktop on the
   small brand-dark chip, recording why: the bigger bubble lands on Marta's NAME at 1440, and
   "if desktop should match, it needs its own anchor, not this rule copied". This is that anchor.
   The phone rules are untouched; they already ship the card.

   ⚠️ WHY IT IS ANCHORED ABOVE THE HANDLE AND NOT BELOW IT. Measured with the bubble built and read
   rather than estimated (130x38 at every desktop width): below the handle it spans the name row,
   which is #200's documented collision. Above it, the only thing in reach is the
   `.compare-rank-labels` gradient band, whose "Most preferred" INK stops well short.

   ⚠️⚠️ AND WHY ITS LEFT EDGE IS PINNED TO THE HANDLE, WHICH LETS IT OVERHANG THE COLUMN. Two
   anchors were built and MEASURED before this one was kept, and both failed at the narrow end:
   right-aligned to the HANDLE cleared the "Most preferred" ink by 31px at 1280/1440/1920 and
   overlapped it by 3px at 1024; right-aligned to the CELL fixed 1024 (+9px) and still overlapped
   by **32px at 901x900 and 54px at 834x1112**. 834 IS A COLUMN-LAYOUT VIEWPORT despite being a
   tablet width (`isMobileRankLayout` is `max-width: 700px`, or `max-width: 900px` AND
   `max-height: 500px`), so that would have shipped broken on tablets while looking perfect at 1440.
   Pinning the LEFT edge to the handle puts the bubble to the RIGHT of that ink at every tier, and
   the overhang past the column edge lands on empty gradient: the band's only other ink,
   "Least preferred", sits far to the right and is never reached.
   ⚠️ THE LESSON, not the number: this tier only fails at its NARROWEST width, which a 1440-only
   eyeball never sees. Measure the narrow end of a range first.

   ⚠️ THE BOX IS NOT THE INK. `.compare-rank-labels strong` spans the FULL cell width, so measuring
   its rect says "no room" when there is plenty; the clearances above come from a Range around the
   text node. Never size a gap from a full-width label's box.

   ⚠️ BOTH PSEUDOS SIT ON THE HANDLE, the same pair of owners #200 uses on the phone twin, so the
   two twins stay structurally comparable: the bubble is the handle's `::after` (restyled from the
   small chip it replaces) and the tail is its `::before`. They meet exactly: the tail's bottom is
   `100%`, i.e. the handle's top edge, it is 8px tall, and the bubble's bottom is
   `calc(100% + 8px)`, i.e. that same line. Move one and you must move the other.
   ⚠️ `.compare-person::after` is ALREADY TAKEN by the rank-position divider (:9871), so do not
   reach for it if this ever needs a third element.

   ⚠️ THE FLOAT NEEDS NO NEW CODE, which is the second half of the ask. #136 put
   `cmpDragHintFloat` on the ROW rather than on the button, and both pseudos live inside that row,
   so they ride it. Do not add a second animation here: two clocks drift, and a bubble bobbing out
   of step with the handle it points at reads as a bug.

   ⚠️ `.compare-person` is ALREADY `position: relative` with no box offsets, so making it the
   containing block costs nothing and #234's reactivation trap does not apply. Do not "tidy" that
   `position` away: under `prefers-reduced-motion` the float is `animation: none`, so the row has
   NO transform, and a containing block inherited from a transform would silently disappear for
   exactly the users least able to cope with a mispositioned overlay.
   ========================================================================== */

/* The bubble REPLACES the small brand-dark chip in place: same owner, new anchor and new skin.
   The phone twin's `::after` is untouched; #200 already restyled that one into this same card. */
body.nifhr-screen.comparison-rank-active:not(.comparison-rank-touched) [data-screen-id="nIfHr"] .compare-marta .compare-rank-handle::after {
  content: "Drag to reorder";
  position: absolute;
  /* LEFT edge on the handle, so it opens rightward across the empty band. See the note above for
     the two anchors that were measured and rejected. */
  left: 0;
  right: auto;
  top: auto;
  /* Its bottom is the tail's top: the tail is 8px tall and its bottom is the handle's top edge. */
  bottom: calc(100% + 8px);
  width: max-content;
  white-space: nowrap;
  /* #178's tokens, the same ones #200 used on the phone, so the two twins read as one component. */
  background: var(--text-primary);
  color: var(--text-inverse);
  font-size: var(--fs-md);
  line-height: 20px;
  font-weight: var(--fw-extrabold);
  letter-spacing: -0.01em;
  padding: 9px 14px;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-popover);
  /* Kept from the chip it replaces: the hint is torn down by the capture-phase `pointerdown` that
     adds `comparison-rank-touched` before `startRankDrag` binds in the bubble phase, so a tap
     "through" it can never reach a drag it is still covering. Giving it `auto` would change
     behaviour; the ask was a restyle. */
  pointer-events: none;
  z-index: 4;
}

/* The tail, on the handle so its tip is pinned to the thing it points at rather than to the cell. */
body.nifhr-screen.comparison-rank-active:not(.comparison-rank-touched) [data-screen-id="nIfHr"] .compare-marta .compare-rank-handle::before {
  content: "";
  position: absolute;
  bottom: 100%;
  /* The handle is 30px wide, the tail 16px, so 7px puts the point on the handle's centre line. */
  left: 7px;
  right: auto;
  width: 16px;
  height: 8px;
  background: var(--text-primary);
  /* Points DOWN, unlike the phone twin's, because this card sits above its target. */
  clip-path: polygon(0 0, 100% 0, 50% 100%);
  pointer-events: none;
  z-index: 5;
}

/* ==========================================================================================
ENTRY #270d - DESKTOP ONLY (>=901px): the scheduling segment's "locked in" cards melt onto the
REAL kanban cards at the end of the run.

⚠️ HOST-BUILT, NOT A CLONE OF THE GRAFT'S CARD, and cloning was ruled out by measurement rather
than taste. The graft's `.update-card` lives in a shadow root and is styled by the graft's own
stylesheet, so a `cloneNode` into this layer arrives unstyled. Cloning the REAL kanban card is no
better: its rules are scoped `body.nifhr-screen [data-screen-id="nIfHr"] .kanban.import-board
.candidate-card`, which depend on ancestors the clone would not have. So the flying card is built
here and wears the destination's look, taken from the real card's own computed style: radius 12px,
1px solid --border, padding 9px 10px, flex with gap 5px, a 32px avatar and a 34px score pill.

⚠️ WIDTH IS FIXED AT THE TARGET'S AND THE TRANSFORM CARRIES THE SIZE. Animating `width` would
relayout the text on every frame; a uniform `scale` does not, and the two boxes are close enough
(317 source, 333 target) that the scale barely reads. Never animate the box, animate the transform.
========================================================================================== */
.wmode-lockcard {
  position: fixed;
  left: 0;
  top: 0;
  width: 333px;
  box-sizing: border-box;
  transform-origin: 0 0;
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 9px 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: 0 18px 40px rgba(17, 24, 39, 0.18);
  opacity: 0;
  pointer-events: none;
}

.wmode-lock-avatar {
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  border-radius: var(--radius-pill);
  display: grid;
  place-items: center;
  color: var(--text-inverse);
  font: 800 13px/1 Inter, -apple-system, system-ui, sans-serif;
}

.wmode-lock-name {
  flex: 1 1 auto;
  min-width: 0;
  font: 700 13px/1.14 Inter, -apple-system, system-ui, sans-serif;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.wmode-lock-score {
  width: 34px;
  height: 34px;
  flex: 0 0 auto;
  border-radius: var(--radius-pill);
  display: grid;
  place-items: center;
  background: var(--success-bg);
  color: var(--success-text);
  font: 700 13px/1 Inter, -apple-system, system-ui, sans-serif;
}

/* ==========================================================================================
ENTRY #270e - DESKTOP ONLY: the invited cards keep the wash INSIDE their border during the
scheduling watch.

⚠️ THE PURPLE OUTSIDE THE BORDER WAS NEVER THE WASH. The wash is a `background-image` gradient,
which cannot paint outside the border box. It was the app's own SELECTED treatment at
`body.nifhr-screen.candidate-move-step [data-screen-id="nIfHr"] .kanban.import-board
[data-select-target][data-selected="true"]`, which sets a 2px `--brand` border and TWO purple
spread rings (`0 0 0 1px` at 0.95 and `0 0 0 5px` at 0.13) plus a violet drop shadow. Measured on
the real card: `1px solid rgb(229,231,235)` with `box-shadow: none` before the watch, `2px solid
rgb(124,58,237)` with both rings at the park.

⚠️ (0,8,1) BEATS THE RULE IT CORRECTS AT (0,7,1) ON SPECIFICITY, NOT ON SOURCE ORDER, and that is
deliberate. Counted out: `.nifhr-screen`, `.nifhr-watch-active`, `[data-screen-id]`, `.kanban`,
`.import-board`, `[data-select-target]`, `[data-selected]` and `[data-wmode-invited]` make eight,
plus the `body` element. Carrying `[data-selected="true"]` here is what buys the extra point; drop
it and the two rules TIE, at which point this only wins because it happens to sit later in the file
and it silently breaks the next time either block moves.
========================================================================================== */
body.nifhr-screen.nifhr-watch-active [data-screen-id="nIfHr"] .kanban.import-board [data-select-target][data-selected="true"][data-wmode-invited] {
  border-color: var(--border);
  border-width: var(--border-w);
  box-shadow: none;
}

/* ==========================================================================================
   ENTRY #273: THE UNIVERSAL-INBOX PROOF. Marta's reply bubble unfolds in place into the same
   message as Outlook renders it.

   ⚠️ THE PANE IS SIZED AND FADED FROM JAVASCRIPT, NOT BY A CSS TRANSITION, and that is required
   rather than preferred. This segment is a SEEKABLE film driven by one clock: every visible thing
   in it is written per frame by `renderMessengerScript`, so a state that lived in a CSS transition
   would be correct while playing forward and wrong at every seeked frame, which is how the whole
   segment is verified. `renderMessengerEmailUnfold` owns `height`, `width`, `max-width` and both
   opacities; nothing here declares a transition on any of them.

   ⚠️ `overflow: hidden` ON THE INNER IS LOAD-BEARING. The pane keeps a FIXED pixel width for the
   whole unfold (written inline) while the stack around it grows from the bubble's 344px, so the
   pane never reflows mid-tween. Let it size to a percentage instead and every line of the quoted
   header re-wraps on every frame, which reads as text boiling rather than a card opening.

   ⚠️ THE COLOURS BELOW ARE DELIBERATELY NOT HIRELLO TOKENS. This is a picture of somebody else's
   application, and painting it in `--brand` purple would destroy the only thing it is here to say.
   They are scoped to `.msg-outlook` so they cannot leak, and they are named after the Fluent ramp
   they came from. Precedent in this file: the `#0EA5E9` on the email read receipt.
   ========================================================================================== */
body.nifhr-screen .msg-unfold {
  /* The stack's own 344px is overridden inline for the whole unfold. Both properties have to move:
     `.message-email-stack` declares `width` AND `max-width`, so growing one alone clamps at 344. */
  position: relative;
}

body.nifhr-screen .msg-unfold-inner {
  position: relative;
  overflow: hidden;
}

body.nifhr-screen .msg-unfold-inner > .message-bubble.email {
  /* Stays in flow: it is what gives the inner its natural height before the unfold starts, and what
     `renderMessengerEmailUnfold` measures as the fold-back target. */
  width: 100%;
  max-width: 100%;
}

body.nifhr-screen .msg-outlook {
  position: absolute;
  left: 0;
  top: 0;
  /* Measured while invisible, so it must never be `display: none`: a display-none node reports a
     zero box and the unfold would have no height to grow to. Same law as the card flight's landing
     box at #272q. */
  opacity: 0;
  pointer-events: none;
  overflow: hidden;
  border: 1px solid var(--ol-border);
  border-radius: 10px;
  background: var(--ol-surface);
  box-shadow: 0 10px 30px rgba(17, 24, 39, 0.14);
  font-family: var(--ol-font);
  color: var(--ol-text);

  --ol-blue: #0078D4;          /* Fluent communication blue: the title bar, the command icons */
  --ol-quote-bar: #C7E0F4;     /* the blue rule down a quoted reply, Outlook's own treatment */
  --ol-canvas: #FAF9F8;        /* neutralLighterAlt: chrome against white content */
  --ol-surface: #FFFFFF;
  --ol-border: #EDEBE9;
  --ol-text: #323130;
  --ol-text-2: #605E5C;
  --ol-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
}

body.nifhr-screen .msg-outlook-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--space-xs) 0 10px;
  height: 30px;
  border-bottom: 1px solid var(--ol-border);
  background: var(--ol-blue);
}

body.nifhr-screen .msg-outlook-app {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--text-inverse);
  font-size: var(--fs-sm);
}

body.nifhr-screen .msg-outlook-app b { font-weight: var(--fw-semibold); }

body.nifhr-screen .msg-outlook-app svg {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

body.nifhr-screen .msg-outlook-win {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  color: rgba(255, 255, 255, 0.86);
}

body.nifhr-screen .msg-outlook-win svg {
  width: 10px;
  height: 10px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.1;
}

body.nifhr-screen .msg-outlook-cmd {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: 0 var(--space-sm);
  height: 34px;
  border-bottom: 1px solid var(--ol-border);
  background: var(--ol-canvas);
}

body.nifhr-screen .msg-outlook-cmd > span {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--ol-text);
  font-size: var(--fs-sm);
}

body.nifhr-screen .msg-outlook-cmd svg {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: var(--ol-blue);
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

body.nifhr-screen .msg-outlook-read { padding: var(--space-sm) 14px 14px; }

body.nifhr-screen .msg-outlook-subject {
  margin: 0 0 10px;
  color: var(--ol-text);
  font-size: var(--fs-xl);
  font-weight: var(--fw-semibold);
  line-height: 1.3;
}

body.nifhr-screen .msg-outlook-from {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--ol-border);
}

body.nifhr-screen .msg-outlook-avatar {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  /* The one place the candidate palette is right: this is still Marta, and #74's law is that a
     candidate colour comes from `--cand-*` and nowhere else. */
  background: var(--cand-marta);
  color: var(--text-inverse);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: 0.02em;
}

body.nifhr-screen .msg-outlook-who { flex: 1 1 auto; min-width: 0; }

body.nifhr-screen .msg-outlook-who p {
  margin: 0;
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
  color: var(--ol-text-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

body.nifhr-screen .msg-outlook-who b {
  color: var(--ol-text);
  font-size: 13px;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen .msg-outlook-when {
  flex: 0 0 auto;
  color: var(--ol-text-2);
  font-size: 11.5px;
  white-space: nowrap;
}

body.nifhr-screen .msg-outlook-body {
  padding-top: 11px;
  font-size: 13px;
  line-height: 1.55;
}

body.nifhr-screen .msg-outlook-body > p { margin: 0; }

body.nifhr-screen .msg-outlook-quote {
  margin-top: var(--space-sm);
  padding: var(--space-xs) 0 2px 11px;
  border-left: 2px solid var(--ol-quote-bar);
  color: var(--ol-text-2);
}

body.nifhr-screen .msg-outlook-quote p { margin: 0; }

/* The From/Sent/To/Subject block, in Outlook's literal field order. This is the single most
   recognisable thing in the pane: without it the card reads as a generic email mock. */
/* ⚠️ THE `p.` IS NOT DECORATION. `body.nifhr-screen .msg-outlook-quote p` two rules up is (0,2,2)
   and would beat a bare `.msg-outlook-qhead` at (0,2,1) on specificity whatever the source order,
   so the margin below would silently never apply. Qualifying with the element makes this (0,3,2)
   and it wins on the count rather than on where it happens to sit in the file. */
body.nifhr-screen .msg-outlook-quote p.msg-outlook-qhead {
  margin-bottom: var(--space-xs);
  color: var(--ol-text);
  font-size: var(--fs-sm);
  line-height: 1.65;
}

/* ==========================================================================================
   ENTRY #274: THE INBOUND FILE, THE INLINE READER, AND SHARING IT WITHOUT LEAVING THE INBOX.

   ⚠️ THE READER ITSELF IS NOT REBUILT. `.messenger-pdf-drawer` already exists and already slides
   (shared closed/open rules with the document and location drawers). This block adds only the three
   things that did not exist: an INCOMING file card, a Share control in the reader's head, and the
   collaborator picker that opens BESIDE the reader rather than on top of it.
   ========================================================================================== */

/* --- The incoming file card ---------------------------------------------------------------
   ⚠️ `.message-document-stack` is `margin: 0 0 10px auto`, i.e. permanently outgoing. Marta's file
   needs the mirror or her own attachment renders right-aligned as if we had sent it to ourselves.
   This is the third time this exact mirror has been needed: `.message-email-stack.incoming` and
   `.message-quest-stack.incoming` are the other two, and all three say the same two declarations. */
body.nifhr-screen .message-document-stack.incoming {
  margin-left: 0;
  margin-right: auto;
}

/* The inbound bubble is the SAME component as the outbound one, deliberately: it is the same file
   card, and the only thing that differs is who sent it. Colour comes off the surface, not the brand,
   so it reads as received rather than sent. */
body.nifhr-screen .message-document-stack.incoming .message-document-bubble {
  border-color: var(--border);
  background: var(--surface);
}

/* The "Shared with" strip that appears on the card once the file has gone to the panel. It is the
   PAYOFF of the whole act, so it lives on the card in the thread rather than in a toast that
   disappears: the point being made is that the record stays with the conversation. */
/* ⚠️⚠️ ENTRY #318 (2026-08-11, her annotation): "These being bunched up like this is eye cancer
   put them responsive as fuck across the width of the component". The strip was rendering as a
   narrow vertical column, roughly one word per line.
   THE CAUSE WAS NOT A WIDTH, A `white-space` OR A MISSING `min-width: 0`, and reaching for any of
   those would have changed nothing. `.message-document-bubble` is `display: grid` with exactly two
   tracks, `44px minmax(0, 1fr)`. Its first three children fill row 1 col 1, row 1 col 2 and (via
   an explicit `grid-column: 2`) row 2 col 2. This strip is appended AFTERWARDS by
   `applyShareToCard()` in nifhr.html and declared no column of its own, so ordinary sparse
   auto-placement dropped it into the only free cell left: row 2, column 1, the 44-PIXEL ICON
   TRACK. The text then reflowed to fit 44px, exactly as authored.
   `grid-column: 1 / -1` gives it its own full-width row across both tracks. The avatars stay
   right-aligned on their own `margin-left: auto`.
   ⚠️ Blast radius is nil: this class is instantiated in exactly one place in the codebase, on the
   one inbound document card, so the rule needs no further scoping. */
body.nifhr-screen .message-document-shared {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  margin-top: var(--space-xs);
  padding-top: var(--space-xs);
  border-top: 1px solid var(--border);
  color: var(--text-secondary);
  font-size: 11.5px;
  font-weight: var(--fw-semibold);
}

body.nifhr-screen .message-document-shared svg {
  width: 13px;
  height: 13px;
  flex: 0 0 auto;
  fill: none;
  stroke: var(--success-text, var(--brand));
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
}

body.nifhr-screen .message-document-shared-avs {
  display: inline-flex;
  align-items: center;
  margin-left: auto;
}

/* Overlapped avatars, the house stacked-avatar treatment. The negative margin is cancelled on the
   first child so the row does not start with a phantom indent. */
body.nifhr-screen .message-document-shared-avs .messenger-avatar {
  width: 22px;
  height: 22px;
  margin-left: -6px;
  border: 2px solid var(--surface);
  font-size: 9px;
}

body.nifhr-screen .message-document-shared-avs .messenger-avatar:first-child { margin-left: 0; }

/* --- The Share control in the reader's head ------------------------------------------------
   ⚠️ IT GOES IN THE HEAD, NOT A FOOTER, AND THAT IS A GRID DECISION AS WELL AS A DESIGN ONE.
   `.messenger-pdf-drawer` is the only one of the four drawers with TWO grid tracks
   (`auto minmax(0, 1fr)`): it has no footer track because it was built as a pure read-only preview.
   Putting the control in the head means this block adds no third track and cannot disturb the
   body's `minmax(0, 1fr)`, which is what lets the pages scroll. */
body.nifhr-screen .pdf-drawer-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
}

body.nifhr-screen .pdf-share-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 40px;
  padding: 0 var(--space-md);
  border: 1px solid color-mix(in srgb, var(--brand) 26%, var(--border));
  border-radius: var(--radius-pill);
  background: var(--brand-tint);
  color: var(--brand);
  font-size: 13px;
  font-weight: var(--fw-bold);
  cursor: pointer;
  transition: background var(--motion-ui) var(--ease-standard), border-color var(--motion-ui) var(--ease-standard);
}

/* ⚠️ `[data-watch-hover]` SITS ON THE SAME RULE AS `:hover`, never in a watch-only rule of its own.
   The film cannot produce a real `:hover`, and two rules would let the scripted rollover and a real
   pointer's drift into two different looks. Same law as the menu options at #272. */
body.nifhr-screen .pdf-share-btn:hover,
body.nifhr-screen .pdf-share-btn:focus-visible,
body.nifhr-screen .pdf-share-btn[data-watch-hover] {
  border-color: var(--brand);
  background: color-mix(in srgb, var(--brand) 16%, var(--surface));
}

/* ⚠️⚠️ ENTRY #283, USER CALL ON AN ANNOTATED FRAME: *"This share icon and share text needs to be
   centred inside the circle"*. IT IS THE ONLY HEADER BUTTON WITH A LABEL AS WELL AS AN ICON, and
   that is the whole defect. `body.nifhr-screen .pdf-drawer-head button` (:6985) makes every button in
   this header a 52x52 icon circle, `display: grid; place-items: center`, and it OUTRANKS the pill
   rule above on specificity, (0,2,2) against (0,2,1), so the pill never applied. The close button
   beside it is icon-only and fits; this one is not.
   ⚠️ WHY IT SPILLED TO THE RIGHT RATHER THAN SYMMETRICALLY, measured rather than eyeballed: the pill
   rule's `padding: 0 16px` still won (`:6985` sets no padding), so inside a 52px border-box the
   content area is 20px wide while "Share" at 13px/700 is about 40px. A grid column wider than its
   container starts at the content-box LEFT edge and overflows one way, which put the icon's centre
   8.4px right of the button's centre and the label outside the circle entirely.
   ⚠️ THE CIRCLE IS KEPT ON PURPOSE. The user asked for the contents centred INSIDE it, not for the
   button to become a pill, and it sits next to an identical 52px close button, so changing the shape
   would break that pairing to fix a spill. The padding goes (meaningless on a fixed-size circle) and
   the icon and label are sized to FIT, which is what lets `place-items: center` actually centre them.
   ⚠️ Specificity is deliberate: `.pdf-drawer-head button.pdf-share-btn` is (0,3,2) against `:6985`'s
   (0,2,2), and it sits LATE in the file, so it wins on both counts rather than by source order alone. */
/* ⚠️⚠️ ENTRY #331 SUPERSEDES THE BLOCK ABOVE, USER CALL ON AN ANNOTATED FRAME: *"Remove the share
   text label not needed"*. #283's reasoning is kept above rather than deleted, because it is what
   explains this rule: it sized the icon DOWN to 18px and the label to 9px with a 2px gap so that
   both would fit inside the 52px circle. THE LABEL IS THE ONLY REASON ANY OF THAT EXISTED.
   ⚠️ SO THE 18px GOES WITH IT, and that is completing the ask rather than widening it: the icon was
   never authored at 18px, it was SHRUNK to make room. Deleting the `.pdf-drawer-head
   button.pdf-share-btn svg` override lets `.pdf-drawer-head button svg` apply again at 22px, which
   is the size the close button beside it already draws, and #283's own text names that pairing as
   the thing worth protecting ("it sits next to an identical 52px close button").
   ⚠️ THE PADDING AND GAP STILL HAVE TO BE ZEROED HERE and this rule cannot simply be deleted: the
   pill rule earlier in this file is (0,2,1) and declares `padding: 0 16px` and `gap: 7px`, which
   `body.nifhr-screen .pdf-drawer-head button` at (0,2,2) does not declare at all and therefore
   cannot override. Left alone, a 22px icon would be centred inside a 20px content box, and a grid
   track wider than its container starts at the content-box LEFT edge and overflows one way. That is
   #283's original spill, re-created by removing its fix. */
body.nifhr-screen .pdf-drawer-head button.pdf-share-btn {
  gap: 0;
  padding: 0;
}

body.nifhr-screen .pdf-share-btn svg {
  width: 15px;
  height: 15px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --- The collaborator picker: a BOTTOM SHEET SPANNING THE READER ------------------------------
   ⚠️⚠️ THIS PANEL IS NOT IN THE MUTUALLY-EXCLUSIVE DRAWER SET, AND THAT IS THE WHOLE POINT OF THE
   BEAT. The four existing drawers each close the other three when they open, because each one
   REPLACES the others. This one opens WITH the reader and the document has to stay on screen
   while you choose who to send it to: the user's own words were that they never break out of the
   messenger. It closes when the reader closes.
   ⚠️⚠️ IT COSTS NOTHING ON THE HORIZONTAL AXIS, AND THAT IS WHY THE READER COULD GO BACK TO FULL
   WIDTH. As a side-by-side column it had to be paid for out of the chat column's width, and #278
   paid for it by pinning the reader to 396px. Measured before this entry: the pair fitted at 1440
   (396 + 300 = 696 inside an 860 column) and did NOT fit below it, spilling over the messages list
   by 252px at 1024x768 and 375px at 901x800, at which widths the coach card also came to rest on
   Raj's row. Bottom-anchored, the picker occupies zero horizontal space, so no width has to trade
   the reader against it and the spill cannot happen at any viewport.
   ⚠️ THE WIDTH EXPRESSION MIRRORS THE READER'S, IT IS NOT A COINCIDENCE THEY MATCH. The user's ask
   was that the picker span the reader's width, so it must carry the reader's own `33vw`/`460px`
   floor. `max(33vw, 460px)` is the standing way this file writes that (it is what every coach-card
   offset in the drawer beats already uses); a bare `33vw` would be narrower than the reader at
   every viewport below ~1394px, which is where the floor does the work.
   ⚠️ ONE ABOVE THE READER, DECLARED RATHER THAN INHERITED FROM DOM ORDER. It now OVERLAPS the
   reader instead of sitting beside it, so which one paints on top is load-bearing rather than
   incidental. It stays below the raised coach card (2147483021) and the Demo-sections launcher. */
body.nifhr-screen .messenger-share-drawer {
  position: fixed;
  inset: auto 0 0 auto;
  z-index: 2147483011;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  width: max(33vw, 460px);
  height: auto;
  max-height: min(70dvh, 540px);
  border-top: 1px solid var(--border);
  border-left: 1px solid var(--border);
  /* ⚠️ ENTRY #322, USER CALL ON AN ANNOTATED FRAME: *"make left right corners like a fucking
     bottomsheet using only our tokens and nothing hardcoded"*. Both top corners on the sheet
     token. The right border exists so the top-right curve does not terminate a border stub: the
     sheet is flush to the viewport's right edge, so the border itself sits off screen except
     where the curve pulls in. */
  border-right: 1px solid var(--border);
  border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
  background: var(--surface);
  box-shadow: 0 -22px 58px rgba(17, 24, 39, 0.18);
  color: var(--text-primary);
  transform: translateY(104%);
  opacity: 0;
  pointer-events: none;
  transition:
    transform var(--motion-drawer) var(--ease-in, cubic-bezier(.4, 0, 1, 1)),
    opacity 0s linear var(--motion-drawer);
}

/* Two directions declared separately, exit on `--ease-in`, entrance on the standard curve, and
   `visibility` is deliberately NOT used here: this panel animates `transform` and `opacity` only,
   so there is nothing to delay. #272m and #272s both shipped a drawer that could not animate
   because one direction was missing; this declares both from the start. */
body.nifhr-screen.messenger-share-open .messenger-share-drawer {
  transform: translateY(0);
  opacity: 1;
  pointer-events: auto;
  transition:
    transform var(--motion-drawer) var(--ease-out),
    opacity var(--motion-ui) var(--ease-standard);
}

/* ⚠️⚠️ ENTRY #318 (2026-08-11, her annotation on the open picker): "Darken the background to the
   main screen and then the drawer background using common ui ux design principles and our tokens
   nothing hard coded when this bottomsheet is showing."
   TWO SURFACES, NOT ONE, because the reader is not the app: a modal sheet is conventionally lit
   against everything it is modal OVER, and here that is both the page behind the reader AND the
   reader's own document. The sheet itself stays undimmed. Both use `--scrim-dim`, the ENTRY #111
   "ONE MODAL/SPOTLIGHT DIM" token, and both fade on the existing motion tokens: nothing here is a
   typed number.
   ⚠️ THE STATE HOOK IS `.messenger-share-open` ALONE, not the pdf-open class as well. The two are
   always true together (the picker is only reachable from inside the open reader, and closing the
   reader force-closes the picker), so `AND`-ing them would add specificity that asserts nothing
   and would quietly stop working if the picker is ever opened from somewhere else.
   ⚠️ `--scrim-dim-coach` (0.55 + blur) was NOT used: it is deliberately scoped to the coach
   modals, and a blur here would be a backdrop-filter over a drawer that is itself animating,
   which is the T151 compositor-starve shape. Flat alpha, no filter.
   ⚠️ AND THE PAGE DIM IS `::after`, NOT `::before`. `body.nifhr-screen::before` is already taken,
   twice, by the unconditional board-focus/walkthrough dim at z 9000; reusing it would have fought
   a rule that other beats switch on and off. */
body.nifhr-screen::after {
  content: '';
  position: fixed;
  inset: 0;
  /* One BELOW the reader (2147483010) and two below the picker (2147483011), so it darkens the
     app shell behind them and neither of the two panels it is modal for. */
  z-index: 2147483009;
  background: var(--scrim-dim);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--motion-ui) var(--ease-standard);
}

body.nifhr-screen.messenger-share-open::after {
  opacity: 1;
}

/* ⚠️⚠️ ENTRY #319 (her annotation on the parked messenger frame): "When this bottomsheet is active
   then dim the rest of the screen." The END sheet of the messenger watch segment ("Everything in
   one thread") had nothing behind it dimmed at all.
   IT REUSES #318's DIM ELEMENT rather than adding a second one: one `::after` on the body, two
   states that raise it. A second full-viewport layer would have to be ordered against this one for
   no benefit, and the two can never be up together (the share picker belongs to the interactive
   messenger, this sheet to the watch layer's parked end frame).
   ⚠️ THE Z-INDEX ALREADY WORKS OUT AND IT IS WORTH SAYING WHY. The dim sits at 2147483009 and the
   watch layer is raised to 2147483020 for this segment, so the sheet stays lit without a single
   carve-out. Measured at the END beat: every other child of that layer is `opacity: 0` and the
   scrim is `display: none`, so the sheet really is the only thing above the dim.
   ⚠️ SCOPED TO THE MESSENGER SEGMENT, DELIBERATELY, AND SAID OUT LOUD. The same END sheet closes
   the screening, references and scheduling segments too, and those park on a signature frame the
   viewer is meant to keep looking at, with pixel guards calibrated against it. She pointed at this
   one. Widening it is a one-line change if she wants it.
   ⚠️ This does NOT reverse the standing "dont dim anything on this screen" instruction for the
   messenger segment (entries #278/#280/#281): that governs `.wmode-scrim`, the clock-driven
   spotlight, whose two carved exception windows both close at 66.14 while this sheet appears at
   89.49. Nothing here touches the scrim. */
body.nifhr-screen.screening-step-messenger.wmode-end-open::after {
  opacity: 1;
}

/* The reader's own dim. It is a child of the reader rather than another viewport-sized layer so
   it cannot ever cover the picker: the reader creates its own stacking context (it is
   `position: fixed` with a z-index AND a transform), so no number written in here can climb out
   of it, which is the property that makes this safe rather than a race. */
body.nifhr-screen .messenger-pdf-drawer::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 5;
  background: var(--scrim-dim);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--motion-ui) var(--ease-standard);
}

body.nifhr-screen.messenger-share-open .messenger-pdf-drawer::after {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  body.nifhr-screen::after,
  body.nifhr-screen .messenger-pdf-drawer::after {
    transition: none;
  }
}

body.nifhr-screen .share-drawer-head {
  padding: 20px 20px 14px;
  border-bottom: 1px solid var(--border);
}

body.nifhr-screen .share-drawer-head p {
  margin: 0 0 3px;
  color: var(--brand);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

body.nifhr-screen .share-drawer-head h2 {
  margin: 0;
  font-size: 17px;
  font-weight: var(--fw-bold);
  line-height: 1.25;
}

body.nifhr-screen .share-collab-list {
  display: grid;
  gap: var(--space-xs);
  align-content: start;
  min-height: 0;
  overflow: auto;
  padding: 14px var(--space-md);
}

/* ⚠️ THE ROW IS THE HOUSE SELECTABLE-ROW CONTRACT, NOT A LOOKALIKE. `.document-file-row` plus the
   CSS-drawn `.document-check` tick is how every other pick-then-send list in this screen works
   (the library drawer, the location drawer, the questionnaire drawer), and the standing rule is to
   reuse the shared class contract rather than invent a second one that drifts. The only thing this
   adds is an avatar column, because these rows are people rather than files. */
body.nifhr-screen .share-collab-row {
  display: grid;
  grid-template-columns: 20px 34px minmax(0, 1fr);
  align-items: center;
  gap: 11px;
  width: 100%;
  padding: 9px 11px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  text-align: left;
  cursor: pointer;
  transition: border-color var(--motion-ui) var(--ease-standard), background var(--motion-ui) var(--ease-standard);
}

body.nifhr-screen .share-collab-row:hover,
body.nifhr-screen .share-collab-row:focus-visible,
body.nifhr-screen .share-collab-row[data-watch-hover] {
  border-color: color-mix(in srgb, var(--brand) 42%, var(--border));
  background: var(--brand-tint);
}

body.nifhr-screen .share-collab-row.is-selected {
  border-color: var(--brand);
  background: var(--brand-tint);
}

body.nifhr-screen .share-collab-row .messenger-avatar {
  width: 34px;
  height: 34px;
  font-size: var(--fs-sm);
}

body.nifhr-screen .share-collab-copy { min-width: 0; }

body.nifhr-screen .share-collab-copy strong {
  display: block;
  font-size: 13px;
  font-weight: var(--fw-semibold);
  line-height: 1.3;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

body.nifhr-screen .share-collab-copy small {
  display: block;
  color: var(--text-secondary);
  font-size: 11.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ⚠️ ENTRY #322: the share rows wear the Selection Panel's PHOTOGRAPH avatars (assets/panel-*.png
   inline per row), the same treatment .sp-face carries in the collaborator segment: cover, headroom
   at the top, and NO initials (font-size 0; the spans are empty anyway, this is belt and braces for
   the strip clones). The --mm-c token stays as the backing colour behind the transparent edges. */
body.nifhr-screen .share-collab-photo {
  background-size: cover;
  background-position: center top;
  font-size: 0;
}

body.nifhr-screen .share-drawer-foot {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 14px var(--space-md) 18px;
  border-top: 1px solid var(--border);
}

body.nifhr-screen .share-drawer-foot span {
  color: var(--text-secondary);
  font-size: var(--fs-sm);
}

body.nifhr-screen .share-drawer-foot button {
  margin-left: auto;
  height: 40px;
  padding: 0 18px;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: var(--text-inverse);
  font-size: 13px;
  font-weight: var(--fw-bold);
  cursor: pointer;
}

/* ⚠️ `aria-disabled`, NOT `disabled`, and a JS early return behind it. `disabled` greys the label
   AND drops the control out of the tab order, and this project has already settled that shape once
   (#235). The look is the MD3-inactive treatment the rest of the screen uses. */
body.nifhr-screen .share-drawer-foot button[aria-disabled="true"] {
  background: color-mix(in srgb, var(--text-primary) 12%, var(--surface));
  color: var(--text-tertiary);
  cursor: default;
}

/* ⚠️⚠️ THE READER IS NO LONGER NARROWED, AND DELETING THAT PIN WAS THE OTHER HALF OF MAKING THE
   PICKER A BOTTOM SHEET. #278 pinned this reader to `396px; min-width: 0` for the marta-portfolio
   document because the picker sat BESIDE it and the pair had to fit the chat column. The user's
   frame at this entry was that the reader reads too narrow against the other three drawers, and it
   did: every sibling is `33vw` with a `460px` floor and this one alone was 396 flat.
   ⚠️ THE TWO HALVES COULD NOT SHIP SEPARATELY. Widening the reader while the picker was still a
   column would have restored the exact 177px spill over the messages list that the pin was paid to
   stop, so the pin could only come out once the picker stopped competing for horizontal space.
   ⚠️ THE PIN CARRIED A LATENT MOBILE DEFECT TOO, which is why its deletion is a fix rather than a
   revert. It was un-media-queried and its `[data-pdf-open]` attribute beat the `(max-width: 900px)`
   full-width rule on specificity, (0,3,1) against (0,2,1), so on a phone this one document opened
   at a flat 396px instead of the `calc(100vw - 24px)` sheet every other document gets. Removing it
   restores the reader to the shared expression at every viewport. */

/* ⚠️ THE SHEET MIRRORS THE READER'S MOBILE WIDTH, AND THIS RULE HAS TO LIVE HERE RATHER THAN BESIDE
   THE READER'S OWN `(max-width: 900px)` BLOCK. That block sits far earlier in the file, and a media
   query grants no specificity: at equal specificity the LATER rule wins, so a mobile override
   written up there would lose to this panel's base rule below and silently never apply. */
@media (max-width: 900px) {
  body.nifhr-screen .messenger-share-drawer {
    width: calc(100vw - 24px);
  }
}

/* ==========================================================================
   ENTRY #318 (2026-08-11, USER CALL), rj0wj COMPANY DRAWER: THE GREEN PANEL STANDS DOWN WHILE A
   SECTION IS OPEN. Her annotation, pointing at the "63% / Make your edits" panel: "When clicked to
   expand this section then hide the Make your edits green panel on desktop and mobile and tablet
   views."
   ⚠️ NO MEDIA QUERY, DELIBERATELY, AND THAT IS THE LITERAL ASK: she named all three tiers, so the
   rule is unscoped and a viewport gate would be the bug. (The existing `.celebration-compact-title`
   is already revealed by STATE and never by viewport, for the same reason, recorded at #121.)
   ⚠️ `display: none`, NOT `visibility: hidden`. The house rule that "hide it but do not move
   anything" means `visibility` applies when the ask is to leave the layout alone; here the whole
   point is to give the expanded section the room back, and `visibility` would leave a blank band
   at the top of a STICKY stack, which reads as a defect rather than as a design. Nothing
   transitions on this panel's own box (only its `padding`, 320ms), so there is no animation being
   cut short.
   ⚠️ ITS ONE REAL CONSEQUENCE IS HANDLED IN THE PAGE, NOT HERE: `stampToastCover()` in rj0wj.html
   used to read this panel's rect to park both toasts, and a `display: none` box measures zero. It
   now anchors on the first laid-out section in the sticky stack instead, so the toasts land on the
   company hero card while this is down. See the note on that function.
   ⚠️ `body.company-basics-expanded` is stamped REVERSIBLY by the Company basics toggle. It is not
   `body.company-basics-has-opened`, which is next to it in the same handler and is a one-way latch.
   ⚠️ Only ONE section in that accordion is genuinely expandable; the other four are inert
   `aria-disabled` buttons that never open. A rule keyed on "any section open" would be describing
   something this drawer does not have.
   ========================================================================== */
body.company-basics-expanded .company-celebration {
  display: none;
}

/* ==========================================================================
   ENTRY #326 — A THREAD OUTSIDE THE SELECTED ROLE
   Written at the END of the file on purpose: this OVERRIDES `.messenger-thread`'s own
   `display: grid` (declared at the messenger block, and re-declared by the #147 tail block), and
   an override lives after what it corrects.
   ⚠️ `display: none`, NOT `visibility: hidden`. The house rule that "hide it but do not move
   anything" is for asks where the layout must be left alone; here the entire point of the beat is
   that the remaining threads CLOSE UP, so a filtered inbox is visibly shorter than the full one.
   ⚠️ A CLASS, NOT `[hidden]`. The UA's `[hidden] { display: none }` is a single type selector and
   loses to `body.nifhr-screen .messenger-thread { display: grid }`, so a `hidden` property flip
   here would do nothing at all and would read exactly like a filter that never fired.
   ⚠️ Scoped to `.messenger-thread`, which is the DESKTOP inbox only. The mobile universal inbox
   builds `.mm-thread` rows from its own roster() and is deliberately not filtered.
   ========================================================================== */
body.nifhr-screen .messenger-thread.is-scope-hidden {
  display: none;
}

/* ============ ENTRY #340: the pause primer's CLICK HERE / TAP HERE target ============
   Her frame, on the pause-coach step: *"I want a section that is as large as this callout use our
   same tokens though and I want the animated hands we use in the floating card chooser screen to
   have one hand here that clicks this section and use the token for the cyan but have this panel
   here 39% md3 opacity not the text"*, and the correction that fixed its lifetime: *"the panel
   shows as soon as the many parts of this demo bottomsheet shows ... and as soon as they do the
   panel disappears showing the red pause element as normal"*.

   ⚠️ THE VISIBILITY IS A ONE-SHOT, NOT A STATE. `body.pause-coach-active` opens with the sheet and
   closes 900ms AFTER the resume, while `html.hf-cine-held` goes false on the resume itself, so the
   obvious compound selector is true again for that 900ms and the panel would flash back on under a
   departing sheet. `pause-cue-taken` is latched in rj0wj's own `watchForPause()` observer, in the
   `held && !pauseShown` branch that already exists precisely because it never re-arms.

   ⚠️ THE 39% IS ON THE BACKGROUND, NEVER ON THE ELEMENT: her words were "39% md3 opacity not the
   text", and `opacity` here would take the label down with it. 0.39 is this project's own
   MD3-inactive value, written as a literal 18 times above. `color-mix` keeps `--cue-teal` as the
   single source of the hue rather than restating it as an rgba triplet.

   ⚠️ SIZE DERIVED FROM HER FRAME, NOT PICKED. The red stripe is a known 65.5px tall and measured
   72px in her screenshot, so that image is at 0.91 scale; her drawn 585x232 is about 532x211 CSS
   px, and 532 of her ~1820px viewport is 29vw. The 232px floor is what keeps the stack clear of
   the 65.5px band that lands across this panel's centre a moment later. */
.pause-cue { display: none; }
body.pause-coach-active:not(.pause-cue-taken) .pause-cue {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  /* Above the sheet (2147483031), below the toast (2147483034) and the red stripe (2147483035).
     It never coexists with that stripe, but the ordering is stated so a future reader does not
     have to re-derive it. */
  z-index: 2147483033;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  width: clamp(260px, 29vw, 620px);
  min-height: 232px;
  border-radius: var(--radius-xl);
  background: color-mix(in srgb, var(--cue-teal) 39%, transparent);
  /* A SIGN, NOT A CONTROL. The shared pause module reads the click off `document`, so the demo
     still freezes; taking pointer events here would only risk becoming the target of a gesture
     the module deliberately routes past chrome. */
  pointer-events: none;
}
/* ⚠️ THE HAND IS THE CHOOSER DECK'S TAP HAND, RE-DECLARED RATHER THAN REUSED BY CLASS. Borrowing
   `.jc-gesture-tap` would drag the deck's own state with it: `body.jc-sheet-active .jc-gesture`
   sets `animation-play-state: paused`, and by this point in the walkthrough the deck's body classes
   have already been through their own lifecycle. The sprite, the 50-cell step count, the TAP row
   (`background-position-y: 100%`; row 0 is the two SWIPE hands, which would read as the wrong
   gesture entirely) and the 1.67s duration are copied verbatim from `.jc-gesture`/`.jc-gesture-tap`
   above. If that sprite is ever re-cut, both places move together. */
.pause-cue-hand {
  width: clamp(34px, 10vw, 46px);
  height: clamp(34px, 10vw, 46px);
  flex: 0 0 auto;
  background-image: url("assets/gesture-hands.png");
  background-repeat: no-repeat;
  background-size: 5000% 200%;
  background-position-y: 100%;
  animation: jcGestureFilm 1.67s steps(50) infinite;
}
/* The label is the DEMO PAUSED treatment declaration for declaration, because she asked for "the
   same text as the DEMO PAUSED text". Its caps come from the literal string exactly as the stripe's
   do: there is no text-transform on either. The shadow is the stripe's, which is what lets white
   copy sit on a translucent panel over an unknown page behind it. */
.pause-cue-label {
  font-size: 13px;
  font-weight: var(--fw-extrabold);
  letter-spacing: .14em;
  line-height: 1;
  color: var(--text-inverse);
  text-shadow: 0 2px 6px rgba(17, 24, 39, .78), 0 1px 2px rgba(17, 24, 39, .92);
}
/* (ENTRY #382) THE SENTENCE THAT USED TO BE THE BOTTOM SHEET'S FIRST LINE.
   ⚠️ IT TAKES THE LABEL'S TREATMENT, NOT THE SHEET'S. On the sheet it was `#C7CBD4` at
   `text-align: start` on a solid dark panel; here it sits on a 39%-translucent teal panel over an
   unknown page, so it needs the same white-plus-shadow contract `.pause-cue-label` already spells
   out three rules above, and centring, because every other child of this flex column is centred.
   ⚠️ THE PADDING IS ON THE TEXT, NEVER ON `.pause-cue`. The panel's own box is derived from her
   #340 frame (29vw, 232px floor) and `#340A` reads it; padding the panel would move a measurement
   she specified. `var(--space-md)` is the declared token, not a number picked to look right.
   ⚠️ `--fs-lg` RATHER THAN THE SHEET'S `--fs-xl`: at the panel's 260px floor the longer measure
   needs the smaller step to keep the sentence off the hand above it. */
.pause-cue-copy {
  padding-inline: var(--space-md);
  font-size: var(--fs-lg);
  font-weight: var(--fw-medium);
  line-height: 1.35;
  text-align: center;
  text-wrap: balance;
  color: var(--text-inverse);
  text-shadow: 0 2px 6px rgba(17, 24, 39, .78), 0 1px 2px rgba(17, 24, 39, .92);
}
/* (ENTRY #382b) THE #381 SOLID PIE COUNTDOWN, PARKED IN THE PANEL'S BOTTOM-RIGHT CORNER.
   Her words: *"even bottom and right margin position"*, so ONE token drives both insets and they
   cannot drift apart the way two numbers would.
   ⚠️ `var(--space-md)`, A DECLARED TOKEN, NOT A NUMBER PICKED TO LOOK RIGHT. It is the same 16px
   the sentence above uses for its `padding-inline`, so the disc sits on the copy's own margin line.
   ⚠️ `position: absolute` WORKS WITHOUT ADDING `position: relative` TO `.pause-cue`, because that
   element is already `position: fixed`, which is itself a containing block. Do not "fix" this by
   adding a relative: the panel's fixed centring is what puts it on the viewport at all.
   ⚠️ TWO SELECTORS DEEP ON PURPOSE. The module's own `.hf-pie` base rule is (0,1,0) and is injected
   into `<head>` AFTER this stylesheet's <link>, so a one-class rule here would LOSE a tie. It sets
   no `position` today and there is no conflict, but a tie that resolves on source order is a trap
   waiting for the module's next edit, and this costs nothing.
   ⚠️ The panel is `pointer-events: none` and so is `.hf-pie`; the disc is a sign, not a control. */
/* (ENTRY #382f) THE #381 PIE ON THE JOB-AD COACHMARK, bottom-right, on the card's own padding line.
   ⚠️ ABSOLUTE AND A SIBLING, so the card's box is unchanged: `smoke.spec.js` and `config.spec.js`
   both do tail-alignment maths off `.tooltip`'s rect, and `smoke.spec.js` counts its children by
   tag and class. See the JS comment beside `mountJobTourPie()`.
   ⚠️ `.tooltip` is `position: absolute` already (it is placed by `placeTour()`), so it is its own
   containing block and needs no `position: relative` adding. */
.coachmark-overlay .tooltip [data-job-tour-pie] {
  position: absolute;
  right: var(--space-md);
  bottom: var(--space-md);
}
.pause-cue [data-pause-cue-pie] {
  position: absolute;
  right: var(--space-md);
  bottom: var(--space-md);
}
/* ⚠️⚠️ (ENTRY #382d) THE STRIPE'S PIE MUST KEEP RUNNING WHILE THE FREEZE HOLDS EVERYTHING ELSE
   STILL, AND WITHOUT THIS RULE IT DOES NOT MOVE AT ALL. Her report: "Doesn't animate stays like
   this", with the disc full.
   ⚠️ THE MECHANISM, MEASURED NOT GUESSED. `hirello-cinematic-hold.js:187` ships
   `html.hf-cine-held *:not(.hf-cine-pause){animation-play-state:paused !important;}`, i.e. the
   freeze pauses EVERY animation on the page and exempts only the stripe ELEMENT, not its
   descendants. The pie is a descendant, so the thing it counts down is the thing that stops it.
   Sampled every 500ms of real time while held: playState `paused`, clock 0ms, dashoffset 0, five
   samples running.
   ⚠️ THIS IS THE MODULE'S OWN PRECEDENT, NOT A NEW IDEA. Line 196 of that file carves out exactly
   the same exemption for its own pause icon
   (`html.hf-cine-held .hf-cine-pause svg.hf-cine-pause-icon{animation-play-state:running
   !important;}`). A descendant of the stripe that must keep moving needs its own rule; there is no
   third option short of editing a module thirteen pages load.
   ⚠️ `!important` IS REQUIRED ON BOTH SIDES AND SPECIFICITY DECIDES IT. The module's rule is
   `!important` too, so this wins only by being more specific: (0,4,1) against (0,2,1). Do not
   "simplify" this selector.
   ⚠️ AND A GUARD FOR THIS MUST SAMPLE WALL-CLOCK TIME. `duration`, `iterations`, `direction`,
   `fill` and even a SEEKED drain are all true of a paused animation; only the clock advancing
   proves it runs. The first version of `#382G` asserted all five and went green on a frozen disc. */
html.hf-cine-held .hf-cine-pause [data-stripe-pie] .hf-pie-fill {
  animation-play-state: running !important;
}
@media (prefers-reduced-motion: reduce) {
  /* Hold the tap hand on its own ripple-peak frame, the value the chooser deck's reduced-motion
     block already uses for this same sprite row. */
  .pause-cue-hand { animation: none; background-position-x: 28.5714%; }
}

/* =============================================================================
   (ENTRY #346) THE SPEED EXPLAINER'S TRACK, INVITE CARD AND PAYOFF SHEET
   The fourth primer card used to DESCRIBE the speed button with a looping bar as a
   stand-in. The user's call is that it SHOW it: a track across the viewport with a
   walking man on it at normal speed, an ordinary car on the first press and a racing
   car on the second, so the effect on their viewing experience is visible rather than
   asserted.

   ⚠️ THE WRAP IS SEAMLESS BY DUPLICATION, WHICH IS A NEW PATTERN HERE AND IS DELIBERATE.
   Every other repeating horizontal motion in this stylesheet (jcGestureDriftL :16353,
   jcShimmer :14414, x1riqt-trail-cue :3779) returns to its start WHILE INVISIBLE, and
   :16353 documents that as the house answer to "loops back without sudden position
   jarring". That idiom cannot be used here: it buys its seamlessness with dead time, and
   the user's ask is explicitly that there be none of it. Two runners exactly one viewport
   apart, with the lane travelling one viewport, means the half of the subject that has
   left the right edge is already drawn at the left. Nothing is ever waiting to come round.

   ⚠️ EVERY LENGTH BELOW IS DERIVED, NOT PICKED BY EYE.
     --speed-figure-h is the speed FAB's own measured size (43px, 35px at <=900px), taken
       from #345's sampling on four pages at three viewports. The thing being taught and
       the thing demonstrating it then read at one scale.
     the car and racing-car heights are real-world proportions against a person: a car is
       about 1.5m to a person's 1.75m (0.857) and a low sports car about 1.25m (0.714).
       Their widths follow from each file's own viewBox via aspect-ratio, so no width is
       stated here at all and re-cutting the art cannot desync them.
     the launcher's dim is 0.35, the alpha this stylesheet already dims with (--scrim-dim,
       :69).
   ============================================================================= */
.speed-stage {
  --speed-figure-h: 43px;
  position: fixed;
  inset: 0;
  /* Above the company-coach scrim (1290) so it reads on the dim, below the speed FAB and
     the sheet (2147483030 / 2147483031) so it can never cover the control it is about. */
  z-index: 2147483020;
  pointer-events: none;
  overflow: hidden;   /* a runner parked one viewport to the left must never make the page scroll */
  color: var(--text-inverse);
}
.speed-stage[hidden] { display: none; }

/* left/right rather than a width, so a scrollbar cannot knock it off: the .hf-cine-pause law. */
.speed-track {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
  background: currentColor;
}
.speed-lane {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  will-change: transform;
}
/* A zero-sized anchor sitting exactly on the track; the subject hangs off its bottom edge,
   so every subject stands ON the line regardless of its own height. */
.speed-runner {
  position: absolute;
  left: 0;
  top: 50%;
  display: block;
  width: 0;
  height: 0;
}
/* THE SECOND COPY, exactly one viewport to the left. -100% resolves against .speed-lane,
   which is exactly the viewport, so this is exact at every width with nothing measured in
   JS and nothing to recompute on resize or rotate. */
/* ⚠️⚠️ THREE COPIES, AND THE THIRD IS NOT SPARE. Two are enough only while each subject is
   LEFT-ALIGNED to its runner. Once the subjects are centred (see the block below, which fixes a
   real jump), each one straddles its own origin, so at the loop restart the lead copy is half off
   the LEFT edge and the trailing copy is still a whole viewport away: measured 48.0px of ink
   against a 90.4px subject for the frames right after the wrap, and `#346C` reddened on it. With
   copies at -100%, 0 and +100% the covered width is exactly one subject at every position.
   ⚠️ nth-child, NOT the adjacent-sibling selector this replaced: `.speed-runner + .speed-runner`
   matches the SECOND and THIRD alike, which would park both to the left and leave the gap open. */
.speed-runner:nth-child(2) { left: -100%; }
.speed-runner:nth-child(3) { left: 100%; }

/* ⚠️⚠️ CENTRED ON THE RUNNER, NOT LEFT-ALIGNED TO IT, AND THAT IS A FIX.
   The three subjects are inherently different widths (measured at 1440: walker 30.8, car 74.0,
   racing car 90.4, because a person is not the width of a car). Pinning their LEFT edges made a
   swap throw the rest of the shape rightwards: measured 43.2px of right-edge movement on the very
   first press, with the left edge motionless, which reads as the icon lurching across the track at
   the exact moment the viewer is looking for the difference the press made. Centring cannot remove
   the size change, but it makes the swap symmetrical and holds the PERCEIVED position still: the
   worst edge movement halves, and the centre does not move at all. */
.speed-walker,
.speed-racer {
  position: absolute;
  left: 0;
  bottom: 0;
  transform: translateX(-50%);
  display: none;
}
/* One subject at a time, chosen by data-subject alone, mirroring how the speed module picks
   its face from data-rate: ONE place maps a state to a piece of art. display, not opacity,
   so a hidden subject can never be hit-tested or read out.
   ⚠️⚠️ (Entry #461) TWO SUBJECTS, AND THE RACER MOVED FROM INDEX 2 TO INDEX 1. She removed the
   middle playback speed, so the speed module's step index never reaches 2. Left mapped at three,
   index 1 would have drawn the ordinary car for the FASTEST state and the racing car would have
   become art the lesson can never reach: the fastest state would have stopped looking like it.
   Her call was that the fastest keeps the racer, so the ordinary car's selector, art and sizing
   are deleted rather than left unreachable.
   ⚠️⚠️ THIS BLOCK EXISTS TWICE: here, and in the CSS bundle inside `hirello-controls-primer.js`.
   The four pages that link this stylesheet read it from HERE; the other seven read it from there.
   Both copies change in the same edit, or the same lesson looks different depending on which page
   a viewer met it on. Changing one and not the other is exactly how a pause-stripe countdown
   stayed frozen on seven pages for about seventy entries.
   ⚠️ AND IT IS A POSITIONAL TWIN of `SUBJECT_TRAVEL_S` in that same file: same number of steps,
   same order, or the art and the travel speed describe different states. */
.speed-stage[data-subject="0"] .speed-walker,
.speed-stage[data-subject="1"] .speed-racer { display: block; }

/* The sprite sheet is GENERATED, NOT HAND-MADE: `python3 harness/build-walk-sprite.py`
   reads harness/walk-src/walk-cycle.png and writes assets/walking-man.png, and it PRINTS
   the three constants below as its CSS contract. Re-run it and re-read them rather than
   editing them by hand.
   ⚠️ 120% IS NOT A TYPO FOR 100%. background-position-x is a percentage of
   (container - image), so cell k shows at k/(cells-1) = k/5, and a steps(6) run must end at
   6/5 = 120%. Ending at 100% skews every frame and never reaches the last cell. This is the
   same law the chooser deck's gesture sprite documents at :16290. */
.speed-walker {
  height: var(--speed-figure-h);
  aspect-ratio: 86 / 120;
  background: url("assets/walking-man.png") no-repeat 0 0 / 600% 100%;
  animation: speedWalk 0.45s steps(6) infinite;
}
@keyframes speedWalk {
  from { background-position-x: 0%; }
  to   { background-position-x: 120%; }
}

/* MASKED, NOT DRAWN. The car file is `fill="currentColor"`, which an external SVG used as
   a background-image cannot resolve against this page - it would paint it black on a black
   stage. As a mask the file's alpha is the shape and `background: currentColor` supplies the
   colour, so the .svg file stays the single source of truth for the art AND follows the
   stage's colour.
   ⚠️ (#461) This rule was shared with the ordinary car, which is gone. `assets/car.svg` is left
   on disk on purpose, so restoring the middle state is a revert rather than an asset hunt. */
.speed-racer {
  background: currentColor;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;
}
.speed-racer {
  height: calc(var(--speed-figure-h) * 0.714);
  aspect-ratio: 63.73 / 21.64;
  -webkit-mask-image: url("assets/racing-car.svg");
          mask-image: url("assets/racing-car.svg");
}

@media (max-width: 900px) {
  /* The FAB's own <=900px size, same source as the base value. */
  .speed-stage { --speed-figure-h: 35px; }
}

/* THE INVITE CARD. It is a `.fab-coach` in full, so the dark card, the type and the tail
   geometry are the ones already on screen twice in this same primer, and nothing about the
   card's look is redeclared here beyond the three things below. */
.speed-invite-coach [data-speed-invite-lead] {
  font-weight: var(--fw-extrabold);
  color: var(--text-inverse);
}
.speed-invite-coach p:last-of-type { margin-bottom: 0; }
/* (ENTRY #382e) THE #381 PIE, PARKED IN THIS CARD'S BOTTOM-RIGHT.
   ⚠️ THE INSETS ARE THE CARD'S OWN PADDING TOKEN, not numbers picked to look right:
   `.fab-coach` is `padding: 14px var(--space-md) var(--space-md)`, so `var(--space-md)` on both
   sides puts the disc exactly on the text's right edge and the card's own bottom line.
   ⚠️ ABSOLUTE ON PURPOSE. `placeSpeedInviteCard()` measures this card's offsetWidth/offsetHeight to
   aim its right-pointing tail at a FAB one `--space-sm` away, and clamps the width against the room
   available; a disc in the flow would change both and re-aim a calibrated card.
   ⚠️ OVERLAP WITH THE COPY IS A REAL RISK AND IS MEASURED, NOT ASSUMED: see the guard, which
   compares the disc's rect against the body paragraph's own client rects at each viewport. */
.speed-invite-coach [data-speed-invite-pie] {
  position: absolute;
  right: var(--space-md);
  bottom: var(--space-md);
}
/* ⚠️ THE TAIL POINTS RIGHT, NOT UP, AND THAT IS THE WHOLE REASON THIS CARD SITS BESIDE THE
   BUTTON RATHER THAN UNDER IT. The speed FAB and the chapter launcher are the same size and
   share a right edge, one --space-sm apart (#345), so a card hanging below the speed FAB
   covers the launcher: measured at 1440, it covered it almost entirely while every number in
   the placement read correct. Beside-and-pointing-right clears the launcher by construction
   at every viewport, and leaves it visibly dimmed as the user asked rather than hidden under
   a card.
   Same clip-path idiom as .fab-coach::before (:18432), rotated a quarter turn, and identical
   to the one .messenger-feature-coach[data-tail="right"] already uses (:19332). Equal
   specificity to the rule it overrides, and later in source, which is what carries it.
   `left: auto` is required: without it the inherited `left: var(--fab-tail-x, 40px)` wins over
   `right` and the tail sits INSIDE the card. */
.speed-invite-coach::before {
  top: var(--speed-tail-y, 40px);
  left: auto;
  right: -13px;
  width: 15px;
  height: 18px;
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}

/* .coach-cta carries the house pill styling; this only does layout, exactly as
   .fab-coach-cta and .company-coach-cta do. */
.speed-coach-cta {
  display: inline-flex;
  width: 100%;
  /* ⚠️ (#354) HER ASK: "Add our normal spacing token between text and the CTA button." `--space-md`
     is the middle step of the scale declared at `:root` (4 / 8 / 12 / 16 / 24 / 32), i.e. the
     project's normal spacing, and it is the nearest step to the 14px LITERAL its sibling
     `.company-coach-cta` still carries. A token, not a number: `:root` was grepped first and this
     value was NOT invented. If that sibling is ever tokenised too, both land on the same step. */
  margin-top: var(--space-md);
}

/* THE CHAPTER FAB IS DIMMED FOR THE LENGTH OF THIS STEP (user's call), so the speed button is
   the only lit control while the card is pointing at it. Dimmed, not hidden: the four existing
   launcher rules (:15704, .jc-sheet-active, .job-card-chooser-active, :18885) all take it off
   screen entirely, and this step is teaching where the controls LIVE, so it should stay
   legible. The launcher's own injected block in hirello-shell.js sets only `top`, so there is
   no competing declaration for either property here. */
body.speed-coach-active .demo-sections-launcher {
  opacity: 0.35;
  pointer-events: none;
  transition: opacity 200ms ease;
}

@media (prefers-reduced-motion: reduce) {
  /* The step is skipped outright under reduced motion (openSpeedCoach bails to phase B before
     any of this exists), so this can only ever be reached if the stage is rendered some other
     way. Freeze on a full stride rather than the passing pose, which is the frame that still
     reads as "walking" standing still - the same signature-frame law as :16390 and :25891. */
  .speed-walker { animation: none; background-position-x: 20%; }
  .speed-lane { display: none; }
  body.speed-coach-active .demo-sections-launcher { transition: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   (ENTRY #347) THE PLAYBACK-SPEED FAB IS NOW PERMANENT ON rj0wj, SO IT INHERITS THE CHAPTER
   LAUNCHER'S HIDE RULES. Her words: *"the playback speed fab needs to remain on the screen ... in
   not dimmed normal state the same as the chapter chooser fab"*. `closeSpeedCoach()` used to hide
   it; it no longer does, so from the payoff CTA onwards `.hf-cine-speed` lives beside
   `.demo-sections-launcher` for the rest of the session. "The same as" is the whole ask, and it
   cuts both ways: the two must also DISAPPEAR together.

   ⚠️⚠️ THIS IS NOT A PRECAUTION, IT IS A MEASURED DEFECT. Rendered at 1440 on the beat straight
   after hers (tap the switcher, company profile drawer opens): the drawer occupies x 840-1440 and
   the speed FAB sits at 1377-1420 x 76-119, an overlap of **1849px2, which is the button's ENTIRE
   area**, floating over the drawer's header beside its close X. At 390 the same read gives
   1225px2, again the whole button. That is precisely the collision entry #88 removed the chapter
   launcher for (hirello-app.css:15749-15760, *"the right-side CANDIDATE PROFILE drawer overlaps the
   top-right FAB"*), and it arrived here the moment this button outlived its own step.

   ⚠️ THE SELECTOR LIST IS COPIED FROM THE LAUNCHER'S OWN FIVE RULES, DELIBERATELY WHOLE, rather
   than trimmed to the states rj0wj can reach today. Two of them (`jc-sheet-active`,
   `job-card-chooser-active`) cannot fire on this button as things stand, because the deck and its
   landing sheet both precede the primer and returning to the deck is a NAVIGATION, which takes the
   button with it. They are kept so the pair cannot drift: a future entry that shows the deck
   in-document would otherwise reintroduce this exact overlap against a rule that reads as complete.

   ⚠️ NOT COPIED, AND THAT IS DELIBERATE: the four `fab-coach-*` rules and
   `body.workspace-tour-active.fab-coach-done` (hirello-app.css:18378, :18392, :18869, :18885).
   Those belong to the tutorial that TEACHES the chapter launcher: they hide it before its coaching
   card, grow and pulse it during, and keep it inert afterwards. None of that is about this button,
   and inheriting them would hide it during the very steps it has just been introduced in.
   ⚠️ ALSO NOT COPIED: `body.speed-coach-active .demo-sections-launcher`'s 0.35 dim. That rule dims
   the CHAPTER FAB while the speed FAB is the hero; pointing it at the hero would be backwards.

   MEASURED AT HER OWN BEAT, so none of this touches what she asked for: immediately after the
   payoff CTA the body carries `workspace-tour-active jc-coach-done workspace-tour-locked nav-locked
   fab-coach-done pause-coach-done speed-coach-done company-coach-dismissed` and NONE of the classes
   below. `drawer-active` is absent even on a phone, because `workspace-tour-locked` is the
   documented veto in hirello-shell.js's OR. Both FABs are on screen at opacity 1 there.
   -> today-update.md #347
   ═══════════════════════════════════════════════════════════════════════════════════════════════ */
@media (pointer: coarse) and (hover: none) and (max-width: 700px),
       (pointer: coarse) and (hover: none) and (max-height: 540px) {
  body.drawer-active .hf-cine-speed {
    opacity: 0;
    pointer-events: none;
    visibility: hidden;
    transition: opacity 140ms ease, visibility 0s linear 140ms;
  }
}

body.jc-sheet-active .hf-cine-speed,
body.job-card-chooser-active .hf-cine-speed {
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  transition: opacity 140ms ease, visibility 0s linear 140ms;
}

@media (min-width: 901px) {
  body.profile-drawer-open .hf-cine-speed,
  body.company-drawer-open .hf-cine-speed {
    opacity: 0;
    pointer-events: none;
    visibility: hidden;
    transition: opacity 140ms ease, visibility 0s linear 140ms;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   (ENTRY #348) THE SPEED FAB GROWS AND PULSES FOR THE LENGTH OF ITS OWN ACT, THEN GOES BACK TO
   NORMAL. Her words: *"i need the new playback speed button to be pulsing larger like how the
   chapter jumper fab in the act before it also pulses and then stays as normal not dim and normal
   postion and size once its act is over and the employer name field in the act is highlighted"*.

   ⚠️ THE KEYFRAMES ARE THE CHAPTER LAUNCHER'S OWN `fabCoachPulse`, REUSED RATHER THAN COPIED, AND
   THAT IS THE POINT. The ask is that this button pulses LIKE that one, so the two must move
   together: a copy would let them drift apart the first time either is retuned, which is the same
   trap as the FAB_PULSE_MAX one-number law at hirello-app.css:18366. If you retune `fabCoachPulse`
   you are retuning BOTH acts, deliberately.

   ⚠️ `transform-origin: top right`, FOR THE SAME REASON THE LAUNCHER USES IT (:18358). This button
   also sits at `right: 20px` (14px at <=900px), so a centre-origin scale would push its right edge
   off the viewport and add horizontal scroll. Anchoring the top-right corner grows it DOWN AND
   LEFT, which can never leave the screen, and it makes `placeSpeedInviteCard()`'s largest-state
   arithmetic exact: under this origin the unscaled top and right edges are FIXED POINTS, so the JS
   never has to read a scale that a composited animation would misreport anyway.

   ⚠️ NO `pointer-events: none` AND NO ARMED RING, WHICH IS WHERE THIS DELIBERATELY DIVERGES FROM
   `body.fab-coach-armed`. That act makes the launcher loud and INERT because its lesson is "this is
   where the control lives, you may not press it yet". This act's whole lesson is the viewer
   PRESSING this button, so it stays fully live and keeps its own hit-testing. Growing it makes the
   target bigger, which helps a finger.

   ⚠️ IT GOES BACK TO NORMAL BY THE CLASS COMING OFF, not by a second rule. `closeSpeedCoach()`
   removes `speed-coach-active`, so size, position and opacity all revert together on the CTA
   click, which is the "normal position and size once its act is over" half of the ask. The
   entry #347 rules above own the "not dim" half.

   ⚠️ MEASURED CONSEQUENCE, RECORDED RATHER THAN DISCOVERED LATER: at the 1.62 peak the grown button
   reaches down to 145.7px at 1440 and 120.7px at 390, and the chapter launcher's top edge is at
   131 and 111. The two therefore OVERLAP by about 15px and 10px at the peak, for the length of this
   act only. That is accepted rather than unnoticed: the launcher is at opacity 0.35 underneath for
   exactly this window (:26092), so the hero grows over a control that is already receding, and the
   alternative origins are worse (a bottom-right origin grows UP into the app header, a centre
   origin leaves the viewport).
   -> today-update.md #348
   ═══════════════════════════════════════════════════════════════════════════════════════════════ */
body.speed-coach-active .hf-cine-speed {
  transform-origin: top right;
  animation: fabCoachPulse 1500ms ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  /* Same call as the launcher's (:18398): the size change is INFORMATION, so it still grows, and it
     rests at the pulse MAXIMUM rather than at 1.5 so the invite card placed for the largest state
     sits clear of it rather than leaving a visible gap. Defensive in practice, because
     openSpeedCoach() bails to phase B under reduced motion and this class is never added there. */
  body.speed-coach-active .hf-cine-speed {
    animation: none;
    transform: scale(1.62);
  }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   (ENTRY #358) THE SPEED FAB GOES INERT ONCE ITS OWN ACT IS OVER, EXACTLY AS THE CHAPTER LAUNCHER
   DOES. Her ask: *"stop the click ability on the playback speed icon like the chapter selector FAB
   on this explainer screen once the playback speed button explained and the company name
   highlighted then disable click of the playback speed button the same as the chapter selector
   logic."*

   ⚠️ THIS IS `body.workspace-tour-active.fab-coach-done .demo-sections-launcher` (:18885 in the
   superseded numbering) WITH TWO TOKENS SWAPPED, and that is deliberate rather than lazy: she asked
   for "the same as the chapter selector logic", so the shape, the properties and the lifetime are
   copied rather than re-derived. `fab-coach-done` becomes `speed-coach-done`, which is added by
   `closeSpeedCoach()` at the same moment it calls `finishWorkspacePhaseA()` and therefore the same
   moment the company name is highlighted: her two conditions are one class.

   ⚠️ KEYED ON EXISTING STATE, NEVER A NEW CLASS, and that is the half worth copying. Both exits
   from the primer already remove `workspace-tour-active`, so this window closes itself and cannot
   be left stale by a path that forgets to clean up. That is #194's law and #131's self-clearing cue.

   ⚠️ `pointer-events` AND `cursor`, NOT `disabled`: this button belongs to the shared cinematic
   module, which owns its own element and would have to grow an API for a state only rj0wj wants.
   The launcher is inerted the same way for the same reason.

   ⚠️ IT STAYS FULLY VISIBLE AND UNDIMMED. #347 is the entry that made it permanent and her words
   there were "in not dimmed normal state the same as the chapter chooser fab"; inert is not the
   same as faded, and the launcher beside it is inert and undimmed in this exact window too.
   -> today-update.md #358
   ═══════════════════════════════════════════════════════════════════════════════════════════════ */
body.workspace-tour-active.speed-coach-done .hf-cine-speed {
  pointer-events: none;
  cursor: default;
}

/* ==============================================================================================
   (ENTRY #360, 2026-08-20, user call from an annotated screenshot) THE X1RIQT PARSE BANNER'S
   PROGRESS ELEMENT MOVED INTO THE SUB-LINE'S SLOT, and it is laid out as ONE LINE there.
   Her words: "Replace this text with this progress element and make it fit in the current height
   of the row height so we dont have deal with fucking around with the sizings across multiple
   screens."
   ⚠️ THAT LAST CLAUSE IS THE DESIGN CONSTRAINT, not a footnote: this is ONE arrangement at EVERY
   viewport, with no per-tier sizing. It is why the label, the track and the percentage sit on a
   single row instead of the stacked pair they were: stacked, the block is 34px and the banner
   would have GROWN by 10px at 1440 (headline 27 + gap 3 + 34 = 64 against today's 51px row).
   On one line it is ~18px, so the row is no taller than it was and the tablet/phone tiers, which
   used to stack the whole progress block BELOW the copy, get shorter instead.
   ⚠️ `display: contents` ON `.progress-label` IS WHY THE MARKUP DID NOT HAVE TO BE RESTRUCTURED.
   Its two children become flex items of `.progress-wrap` directly, so `[data-progress-label]`,
   `.progress-label strong` and every existing rule still match the same nodes. `order` then puts
   the track between them, which a DOM move would otherwise have been needed for.
   ⚠️ SCOPED TO `.drop-copy`, so the at-rest `.progress-wrap` rules (width: 280px, column) stay
   exactly as they are for any other consumer, and the `<=900px` `width: 100%` override is
   consistent with the width set here rather than fighting it.
   ============================================================================================== */
/* ⚠️ WITHOUT THESE TWO, THE BAR IS AS WIDE AS THE HEADLINE AND NOTHING ELSE. `.drop-processing`
   is `justify-content: space-between` and now has ONE child, so `.drop-left` shrink-wrapped to
   the headline's 180px and the `width: 100%` below resolved against that. MEASURED at 1440x900:
   180.08px before these rules, the full banner column after. */
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .drop-left,
[data-screen-id="x1RIQT"] .import-demo[data-demo-state="processing"] .drop-copy {
  flex: 1 1 auto;
  align-self: stretch;
}
[data-screen-id="x1RIQT"] .drop-copy .progress-wrap {
  width: 100%;
  flex-direction: row;
  align-items: center;
  gap: var(--space-sm);
}
[data-screen-id="x1RIQT"] .drop-copy .progress-label {
  display: contents;
}
[data-screen-id="x1RIQT"] .drop-copy .progress-track {
  order: 2;
  flex: 1 1 auto;
  min-width: 0;
}
[data-screen-id="x1RIQT"] .drop-copy .progress-label strong {
  order: 3;
  flex: 0 0 auto;
}

/* ==============================================================================================
   (ENTRY #362, 2026-08-20, user call from an annotated screenshot of the screening watch film)
   "The rule is not being followed here where when a drawer is open visible active then instantly
   hide the FAB buttons."
   THREE GAPS IN ONE RULE, all the same shape: a right-anchored, full-height panel covering the
   top-right corner where both FABs live, with no rule naming it.
   ⚠️ THE RULE IS NAMED-CLASS-PER-DRAWER ON PURPOSE and this entry does not change that. Widening
   to `body.drawer-active` is the fix that looks obvious and is wrong: its only consumers are
   coarse-pointer gated, and un-gating it kills the FAB at load through `job-card-modal-active`
   and reddens #75. Entry #88's comment says so, #325 re-learned it, and this entry keeps to it.

   (1) `watch-builder-open` — the "New questionnaire" panel of nifhr's screening watch film. It is
       built by nifhr-watch-mode.js rather than by nifhr.html, so it had no body class at all; the
       class it now sets is written from the same scrubbed scalar as its own visibility. NOT
       media-gated because the panel structurally cannot exist below 901px (the whole watch route
       is gated on `desktop.matches`), so a gate here would be decoration.
   (2) `screening-sequence-open` — nifhr's screening drawer. It was in `DRAWER_SIGNALS` and so
       reached the phone rule, and had NO desktop rule of any kind for either FAB. Gated to
       >=901px because below that this drawer is not a right-hand panel at all: it becomes a 42dvh
       BOTTOM SHEET (hirello-app.css `inset: auto 0 0 0`), which never touches the FAB's corner,
       and hiding the escape hatch for a sheet that does not cover it would be the #72 regression.
   (3) the five messenger panels, for `.hf-cine-speed` ONLY. The launcher has had them since #325;
       #347 copied the launcher's rules to the speed FAB and DELIBERATELY left this group out. Her
       rule as stated covers any drawer, so that exclusion is overruled here rather than left as a
       difference between two buttons that sit in the same corner and look like a pair.
   ⚠️ ALL OF IT USES THE SAME DECLARATION TRIAD AND THE SAME 140ms FADE as every rule it joins.
   `visibility` is what makes it "instantly" unavailable rather than merely invisible, and
   `pointer-events: none` is what stops the hidden button eating a click.
   ============================================================================================== */
body.watch-builder-open .demo-sections-launcher,
body.watch-builder-open .hf-cine-speed {
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  transition: opacity 140ms ease, visibility 0s linear 140ms;
}

@media (min-width: 901px) {
  body.screening-sequence-open .demo-sections-launcher,
  body.screening-sequence-open .hf-cine-speed {
    opacity: 0;
    pointer-events: none;
    visibility: hidden;
    transition: opacity 140ms ease, visibility 0s linear 140ms;
  }
}

body.messenger-document-library-open .hf-cine-speed,
body.messenger-location-open .hf-cine-speed,
body.messenger-questionnaire-open .hf-cine-speed,
body.messenger-pdf-preview-open .hf-cine-speed,
body.messenger-share-open .hf-cine-speed {
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  transition: opacity 140ms ease, visibility 0s linear 140ms;
}

/* ==============================================================================================
   (ENTRY #364, 2026-08-20, user call from an annotated screenshot of the screening watch payoff)
   THE SCREENED-NOT-REJECTED CARD. "Now add a tailed coaching card with tail pointing up to Priya
   when the flag has been set."
   ⚠️ TAIL UP MEANS THE CARD IS BELOW HER. Literal spatial words win: the tail is on the card's TOP
   edge and points UP at Priya's row, so the card hangs underneath it. (My own scout brief said
   "tail down, card above" and that was my error, not hers; caught by re-reading her sentence.)
   ⚠️ EVERY PAINT VALUE IS COPIED FROM `.messenger-feature-coach`, DELIBERATELY AND WHOLE, because
   this is the same family of dark tailed coach card and the demo already has two. It is NOT
   reusing that class: `nifhr.html`'s own placer owns `[data-feature-coach]` and
   `nifhr-watch-mode.js` already does a bare `document.querySelector('.messenger-feature-coach')`
   for a z-index toggle, so a second element wearing that class would make both ambiguous. A
   separate class in this file's own `wmode-` idiom, with the family's values, is the safe half of
   "make it look like the others".
   ⚠️ THE TAIL X IS A CUSTOM PROPERTY the placer writes in CARD-LOCAL coordinates, so clamping the
   card against the viewport edge slides the card without the tail losing Priya.
   ⚠️ `pointer-events: none` is written explicitly because `.wmode-layer > *` sets `auto` on every
   direct child, and this card must never take a click meant for the board underneath it.
   ============================================================================================== */
.wmode-coach {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 4;
  width: 320px;
  max-width: calc(100vw - 24px);
  padding: var(--space-md) 18px 18px;
  border-radius: var(--radius-lg, 18px);
  border: 1px solid rgba(148, 163, 184, 0.14);
  background:
    radial-gradient(circle at 18% 14%, rgba(124, 58, 237, 0.28), rgba(124, 58, 237, 0) 34%),
    #0f1726;
  color: #f8fafc;
  box-shadow:
    0 24px 60px rgba(15, 23, 42, 0.42),
    0 0 0 1px rgba(124, 58, 237, 0.16);
  pointer-events: none;
  animation: wmodeCoachIn 260ms var(--ease-standard, cubic-bezier(0.2, 0, 0, 1)) both;
}
.wmode-coach::before {
  content: "";
  display: block;
  position: absolute;
  top: -13px;
  left: var(--wmc-tail-x, 40px);
  width: 18px;
  height: 15px;
  background: #0f1726;
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
  pointer-events: none;
}
.wmode-coach h3 {
  margin: 0;
  color: #f8fafc;
  font-size: 13px;
  font-weight: var(--fw-extrabold);
  letter-spacing: 0.6px;
  line-height: 1.28;
}
.wmode-coach p {
  margin: 7px 0 0;
  color: rgba(248, 250, 252, 0.78);
  font-size: 13px;
  font-weight: var(--fw-medium);
  line-height: 1.45;
}
@keyframes wmodeCoachIn {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .wmode-coach { animation: none; }
}

/* ==============================================================================================
   (ENTRY #367) THE "DRAG AND DROP BUILDER" CARD: the same dark family as #364's, with the tail on
   the RIGHT edge because it sits to the LEFT of the questionnaire builder and points at it.
   ⚠️ ONLY THE TAIL MOVES. Everything else is inherited from `.wmode-coach`, so the two cards
   cannot drift apart in paint: one place owns the surface, the shadow, the type and the colours.
   ⚠️ `--wmc-tail-y` IS WRITTEN IN CARD-LOCAL COORDINATES by the placer, so clamping the card
   against the viewport slides the card and leaves the tail on the panel.
   ⚠️ The base `::before` is re-pointed rather than added to: a card must never carry two tails, and
   `.wmode-coach`'s own rule already draws one on the top edge.
   ============================================================================================== */
.wmode-coach-right::before {
  top: var(--wmc-tail-y, 40px);
  left: auto;
  right: -13px;
  width: 15px;
  height: 18px;
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}
.wmode-coach-right h3 { margin-bottom: 2px; }
.wmode-coach-right p + p { margin-top: 10px; }

/* ==============================================================================================
   ENTRY #378 · THE WATCH-MODE "New reference received" PUSH BANNER
   ----------------------------------------------------------------------------------------------
   User: *"I want this notification the same size and layout style and position in from top of
   screen like the one from the start your walkthrough notification for all views"*.

   ⚠️⚠️ THIS EXISTS BECAUSE THE GRAFTED BANNER PHYSICALLY CANNOT REACH THE TOP OF THE SCREEN, and
   that is a measurement, not a preference. In the desktop watch route `references-mobile.html` is
   mounted inside a shadow root whose chain is
   `#refPush -> #interviewAnimationStage (scale 0.3611) -> #__graftRoot -> hirello-cinematic.wmode-stage
   (position: fixed, scale 0.878) -> .wmode-layer`, and `#__graftRoot` is `overflow: hidden` at a
   fixed 390x844. A `position: fixed` probe injected beside the source banner at 1440x900 landed at
   (1031, 135) rendered 31.7x6.3, not at the viewport corner and not at its authored 100x20: a
   transformed ancestor is the containing block for a fixed descendant, so nothing authored inside
   the graft can be pinned to the real screen or drawn at real pixel sizes. The source's own
   `#refPushDock` cancels the 0.3611 and the 0.878 still remains.
   So the desktop route draws its OWN banner here, in `.wmode-layer`, which is a body-level sibling
   of `.app-shell` with no transform anywhere above it, and hides the grafted one. Same reasoning as
   entry #300's questionnaire drawer, which had to be host-side for the identical reason.

   ⚠️ EVERY NUMBER IS `.job-notif-push`'s (this same file, the rj0wj block), because that banner IS
   the named reference. Tokens where rj0wj uses tokens; the two must move together.
   ⚠️ (#399) READ THAT AS "AT EVERY WIDTH EITHER OF THEM CAN BE SEEN AT", WHICH IS 901 AND ABOVE.
   `.job-notif-push` gained a `max-width: 900px` tier at #399 (a realistic phone banner, her call)
   and this block DELIBERATELY did not. The route that draws it, `nifhr-watch-mode.js`, is gated
   `(min-width: 901px)` at its line 67, so this banner never renders below 901 and there is nothing
   for that tier to match here. Parity is intact; do not "restore" it by copying the mobile block
   in. -> today-update.md #399
   ⚠️ `.wmode-layer > *` sets `pointer-events: auto` on every direct child, so this rule states
   `none` rather than relying on a default.
   ⚠️ It carries `hidden` at rest and `#249C` filters this layer's children on `!el.hidden`, which
   is what keeps it out of that guard's "nothing else is drawn beside the phone" child list, exactly
   as the prologue coach card does.
   ============================================================================================== */
.wmode-push {
  /* ⚠️ NO z-index, DELIBERATELY, AND THE FIRST VERSION HAD ONE. Nothing else in `.wmode-layer`
     carries a z-index, so its children stack by SOURCE ORDER, and this node is appended after the
     phone and before the hand. A `z-index: 6` here put the banner above `.wmode-cursor` (z auto)
     regardless of order, and the hand tapping it vanished: measured at 1440x900, the cursor
     reported opacity 1 at (704, 75), inside the banner's box, and was not in the render. */
  position: absolute; top: 16px; left: 0; right: 0; margin-inline: auto;
  max-width: min(560px, calc(100vw - 32px));
  display: grid; grid-template-columns: auto 1fr; align-items: start; gap: var(--space-md);
  padding: 20px 22px 21px;
  border: 0; border-radius: 34px;
  background: var(--surface);
  box-shadow: 0 8px 26px rgba(17, 24, 39, .16), 0 1px 3px rgba(17, 24, 39, .10);
  pointer-events: none;
  will-change: transform, opacity;
}
.wmode-push-icon {
  width: 62px; height: 62px; border-radius: 15px;
  background: var(--surface) url("mq50ezgz-hirelloLogo-BgVy4PP3.png") center / 68% no-repeat;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .10);
}
.wmode-push-body { min-width: 0; padding-top: 1px; }
.wmode-push-head { display: flex; align-items: baseline; gap: 6px; margin-bottom: var(--space-2xs); }
.wmode-push-app { font-size: 17px; font-weight: var(--fw-650); letter-spacing: .01em; color: var(--text-secondary); }
.wmode-push-now { margin-left: auto; font-size: 17px; font-weight: var(--fw-medium); color: var(--text-tertiary); }
.wmode-push-title { font-size: 22px; font-weight: var(--fw-bold); line-height: 1.25; letter-spacing: -.006em; color: #1F2937; overflow-wrap: anywhere; }
.wmode-push-text { margin-top: 1px; font-size: 20px; line-height: 1.32; font-weight: 450; color: var(--text-muted); text-wrap: pretty; overflow-wrap: anywhere; }

/* ==============================================================================================
   ENTRY #380 · THE "Ranking submitted." CONFIRMATION: SHEET WIDTH, AND A TICK THAT DRAWS ITSELF
   ----------------------------------------------------------------------------------------------
   Two calls on one annotated frame of `nifhr.html?scene=rank` in its submitted state:
   *"Make this as wide as the bottomsheet width for all views"* and *"Animate the tick in the circle
   being drawn like a normal ux pattern until drawn then dont loop"*.
   ============================================================================================== */

/* ⚠️⚠️ THE WIDTH COMES FROM THE SHEET'S OWN MEASURED BOX, published as `--rank-done-w` by
   `measureRankDone()` beside the height it already published. MEASURED before the change, Chromium
   at deviceScaleFactor 1, confirmation card against `.rank-done-card`:
     1440x900   1060 against  560   <- the 500px overhang she drew red lines around
      834x1112   770 against  834
      390x844    366 against  390
   ⚠️ SO THIS IS A DESKTOP FIX AND THE OTHER TWO TIERS ARE DELIBERATELY LEFT WHERE THEY ARE. Below
   901px the sheet is FULL BLEED and the confirmation is a floating rounded card inside the
   overlay's own padding, so it is already at the widest its container allows; `min(..., 100%)` is
   what expresses that, and it is also the guard against the card being pushed wider than the
   padded overlay and clipped. Saying it out loud rather than silently doing one of the three.
   ⚠️ THE MODAL, NOT `.compare-success`. The white box the viewer sees is the modal (it carries the
   1px border: measured modal left 190 against the panel's 191), so pinning the inner panel would
   leave a 2px collar wider than the sheet.
   ⚠️ The fallback is the sheet's own desktop value, so a state that somehow shows this card with no
   sheet open renders at the intended width rather than at `auto`. */
body.nifhr-screen.comparison-submitted.rank-done-open [data-screen-id="nIfHr"] .nifhr-compare-overlay {
  /* ⚠️ THE INLINE PADDING IS RELEASED FOR THIS STATE ONLY, and it is what lets the card reach the
     sheet's width on the tiers where that sheet is FULL BLEED. The block padding is untouched: the
     bottom one carries `--rank-done-h` and is what centres the card in the room above the sheet. */
  padding-inline: 0;
}

/* ⚠️⚠️ `100vw`, NOT `100%`, AND THE FIRST VERSION USED `100%` AND WAS WRONG. The overlay is a
   `place-items: center` grid, so the modal's grid area is sized by the modal and a percentage
   resolves against a box that is itself content-sized: measured 270px at 1440x900, where the sheet
   is 560 and the card had been 1060. A percentage against a shrink-to-fit container is not a
   fraction of the screen. */
body.nifhr-screen.comparison-submitted [data-screen-id="nIfHr"] .nifhr-compare-modal {
  width: min(var(--rank-done-w, 560px), 100vw);
}

/* ⚠️⚠️ THE TICK DRAWS ONCE AND STAYS DRAWN. `animation-fill-mode: both` plus the default
   `animation-iteration-count: 1` is the whole of "until drawn then dont loop": there is no
   `infinite` here and there must never be one. It re-runs each time the panel enters, because
   `.compare-success` goes `display: none` -> `flex` on `body.comparison-submitted`, which restarts
   a CSS animation; that is correct, a viewer who comes back to this beat should see it drawn again.
   ⚠️ `stroke-dasharray` IS IN USER UNITS, NOT CSS PIXELS. The mark is `<svg viewBox="0 0 24 24"
   width="30">` and `path.getTotalLength()` measured 22.63 in that space, so 24 covers the whole
   stroke with a little to spare. Reading the 30 off the width attribute would leave a visible stub.
   ⚠️ THE REDUCED-MOTION BRANCH MUST RESET THE OFFSET, not just drop the animation. Without the
   `stroke-dashoffset: 0` below, a viewer with the preference set gets a green circle with NO TICK
   IN IT, because the resting state of this pattern is an invisible stroke. */
@keyframes compareCheckPop {
  from { transform: scale(0.62); opacity: 0; }
  60%  { transform: scale(1.06); opacity: 1; }
  to   { transform: scale(1); opacity: 1; }
}
@keyframes compareCheckDraw {
  to { stroke-dashoffset: 0; }
}
body.nifhr-screen [data-screen-id="nIfHr"] .compare-success-check {
  animation: compareCheckPop 380ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
body.nifhr-screen [data-screen-id="nIfHr"] .compare-success-check svg path {
  stroke-dasharray: 24;
  stroke-dashoffset: 24;
  animation: compareCheckDraw 420ms cubic-bezier(0.65, 0, 0.35, 1) 240ms both;
}
@media (prefers-reduced-motion: reduce) {
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-success-check,
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-success-check svg path {
    animation: none;
  }
  body.nifhr-screen [data-screen-id="nIfHr"] .compare-success-check svg path {
    stroke-dashoffset: 0;
  }
}

/* ============================================================================
   ENTRY #393 (2026-08-23, user call, annotated screenshot) - x1riqt, DESKTOP ONLY (>=901px).
   "On desktop view only (leave mobile & tablet views as they are!) then I want a countdown timer
   element here for 5s after the bottomsheet auto dismisses (same colour token here as the
   john-smith-resume.pdf text element for this one only) and if reaches zero through no interaction
   from the viewer then I need the hand to click and hold and drag into the drop zone which then arms
   and its dropped as if the viewer had did this."

   The disc itself is drawn by `hirello-pie-timer.js`, which injects its own CSS and reads three
   custom properties. All this block does is make ROOM for it inside the pile card.

   ⚠️ WHY THE NAME BOX HAS TO SHRINK, MEASURED at 1440x900 before a line was written: `.stack-front`
   is a 356px flex row with 14px side padding and a 12px `column-gap`. Its two children are the 44px
   `.file-icon` and the text wrapper, whose width is set by `b`'s fixed 260px. That leaves the card
   content edge only 10.27px past `b`'s right edge, and a third flex child needs 16px for the disc
   plus the 12px gap. So the disc does not fit at 260 and the row would have overflowed its own card.

   ⚠️ AND WHY 236 IS SAFE RATHER THAN A GUESS: the FILENAME'S GLYPH RUN is 183.56px, measured with a
   `Range` over the text node rather than off `b`'s box, which is 262.24px and says nothing about
   where the last glyph is. At 236 the run still has 52.44px of slack, so the ellipsis cannot engage.
   The value is DERIVED from the two things it has to clear (the disc's own 16px and `--space-xs`),
   never typed as a number.
   ⚠️ The attribute is written by the JS only while the disc is actually in the card, so the moment
   the countdown is cancelled or spent the name box goes back to its full 260px on its own.
   ------------------------------------------------------------------------ */
@media (min-width: 901px) {
  [data-screen-id="x1RIQT"] .stack-front[data-x1-drop-countdown] {
    --x1-stack-name-w: calc(260px - 16px - var(--space-xs));
  }
  /* The disc is the row's third flex child, so the row's existing 12px `column-gap` spaces it and
     it needs no margin of its own. This is the case #392's `spaceDiscFrom()` deliberately skips:
     that helper only adds `--space-xs` where the host's computed `column-gap` is `normal` or `0px`,
     and here it is 12px. Nothing to do but let the disc sit in the row. */
  [data-screen-id="x1RIQT"] .stack-front > [data-x1-countdown-disc] {
    flex: 0 0 auto;
    align-self: center;
  }

  /* ⚠️⚠️ THE HAND WAS PAINTED UNDER THE CARD IT WAS DRAGGING, AND IT WAS FOUND BY LOOKING AT THE
     RENDERED FRAME, NOT BY ANY NUMBER. Every value read correct: the coach layer `is-visible`,
     opacity 1, the fingertip on the right coordinate every frame. It was invisible anyway.

     Cause, and it is entry #155's note above happening a second time in the next state along. The
     coach is `z-index: 12` INSIDE `.import-demo`, and the real drag's ghost is appended to `<body>`
     at `z-index: 10000`. #155 already had to give up `.import-demo`'s `isolation: isolate` to let
     the hand out into the root stacking context, but it did so ONLY for `[data-demo-state="selected"]`,
     which is the state the idle loop runs in. The moment the drag starts the state becomes
     grabbing/dragging/drop-active, `.import-demo` re-isolates (:3194), and NO z-index on a
     descendant can climb out of an ancestor's stacking context. So the hand went back under the card.

     Same fix, same two rules, scoped to the AUTO-DRAG only: a human drag never sets this attribute
     and is byte-identical to before. 10001 is one above the ghost and deliberately below the
     `.drag-helper` pill at 10020, so "OK drop here!" still reads on top of everything.
     ⚠️ Giving up the isolation here is safe for the same reason it was at #155: the only things it
     lets into the root context are the pile (21, and `opacity: 0` throughout a drag) and the drop
     zone (2/4), both of which the ghost's 10000 still beats. */
  [data-screen-id="x1RIQT"] .import-demo[data-x1-auto-drag="flying"] {
    isolation: auto;
    z-index: auto;
  }
  [data-screen-id="x1RIQT"] .import-demo[data-x1-auto-drag="flying"] .drag-coach {
    z-index: 10001;
  }

  /* ⚠️⚠️ ENTRY #394 WROTE A `z-index: 10001` RULE FOR nifhr's MOVE COACH HERE AND DELETED IT AGAIN
     AFTER MEASURING IT INERT. It is recorded rather than silently dropped, because the reasoning
     that produced it is correct for the SIBLING screen and will look correct to the next reader.
     The theory was #393's: the curated-move ghost carries an inline `zIndex: '10000'`
     (hirello-board.js:240 and :302) while `.move-coach` is 9100 (:15501), both in the root stacking
     context, so the hand should paint under the card it drags. On x1riqt that was a real, visible
     defect. HERE IT IS NOT, because the two barely overlap: the grab point is the CENTRE of the
     three ticked cards while the grabbed card is the TOP one, so `pointerOffset` draws the ghost
     stack mostly ABOVE the fingertip. Measured across the whole flight, max overlap of the ghost
     with the hand's BOX (not its glyph, which hangs below-left inside that box): 0.0% at 901,
     3.5% at 1440, 7.2% at 1920. A rule that changes nothing is worse than no rule, because the next
     reader believes it was needed: #286 measured an `align-items` line inert the same way and
     deleted it rather than shipping it. If the grab point ever moves to the top card, re-measure
     before assuming this is still true. */
}

/* ══════════════════════════════════════════════════════════════════════════════════════════════
   (#404) THE TWO DRAWER TOASTS BECOME DARK TAILED COACHING CARDS, ON EVERY VIEW.

   Her annotated frame, verbatim: *"Turn both of the 2 variations of these toasts into our dark
   coaching cards with the tail pointing up or down according to view so it would never be clipped
   or truncated and always 100% visible onscreen in the viewport and when one triggered then the pie
   countdown will be 3s duration but if they click somewhere else that would trigger one then
   instantly close the other one if on timer and the new one will have 3s timer and so on until the
   master timer on the main CTA button runs out"*.

   ⚠️⚠️ THIS SUPERSEDES #195's POSITIONING, WHICH WAS ALSO HER CALL, AND THAT IS THE POINT OF THE
   "so" IN HER SENTENCE. #195 parked these two at the bottom right on desktop and, below 901px,
   stamped them over the drawer's sticky top panel via `--toast-cover-*`. A card in a fixed corner
   can never be "clipped or truncated", so a tail added to that would point at nothing and the
   clause explaining WHY the tail flips would be meaningless. These are now anchored to the field
   the viewer actually clicked, which is what a tail means, and the flip is what keeps them on
   screen. `#195B` pinned the old arrangement and goes red on this; the guard changes, not the
   product, and #195's reasoning is quoted in it rather than deleted.

   ⚠️ THE PAINT IS COPIED FROM `.messenger-feature-coach`, VALUE FOR VALUE, because that is this
   project's canonical dark tailed coach card and the demo already has several derivatives of it.
   A new dark that is nearly the same would be a fourth opinion.

   ⚠️ SPECIFICITY: every rule below matches the chain it corrects exactly and wins on SOURCE ORDER,
   which is this file's documented convention for overrides. That is also how these beat the
   `max-width: 900px` block from #195 further up: a media query adds no specificity.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */
.demo-detail-toast,
.company-source-toast {
  /* JS writes `left`/`top` inline every time it places the card, so the corner anchors from #195
     must be released or they would fight it. */
  right: auto;
  bottom: auto;
  border: 1px solid rgba(148, 163, 184, 0.14);
  border-radius: var(--radius-lg, 18px);
  background:
    radial-gradient(circle at 18% 14%, rgba(124, 58, 237, 0.28), rgba(124, 58, 237, 0) 34%),
    #0f1726;
  color: #f8fafc;
  box-shadow:
    0 24px 60px rgba(15, 23, 42, 0.42),
    0 0 0 1px rgba(124, 58, 237, 0.16);
  /* ⚠️ THE ENTRANCE IS A LIFT, NOT #195's SLIDE FROM OFF-SCREEN RIGHT. That slide existed to carry
     the card over the sticky panel; an anchored card that starts off-screen is the exact thing her
     "always 100% visible" rules out for the first frames of its own entrance. */
  transform: translateY(6px);
  transition: opacity 180ms ease, transform 180ms ease;
}
.demo-detail-toast.is-visible,
.company-source-toast.is-visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
/* The copy inherits the card's own colour; the two coloured `strong` treatments from the light
   toasts would be near-invisible on this surface. */
.demo-detail-toast strong,
.company-source-toast strong {
  color: #f8fafc;
}
.demo-detail-toast span,
.company-source-toast span {
  color: rgba(226, 232, 240, 0.82);
}
.demo-toast-close,
.company-source-toast-close {
  color: rgba(226, 232, 240, 0.72);
  background: rgba(148, 163, 184, 0.14);
  border-color: transparent;
}
/* ⚠️ THE TAIL, GEOMETRY COPIED FROM `.messenger-feature-coach[data-tail]`: 18x15, the -13px corner
   offset, the same two clip paths, and a FLAT `#0f1726` rather than the card's gradient, because a
   tail sampling that gradient bands against the corner it touches. `--toast-tail-x` is written by
   the placer so the point sits over the field's centre even after the card has been clamped. */
.demo-detail-toast[data-tail]::before,
.company-source-toast[data-tail]::before {
  content: "";
  display: block;
  position: absolute;
  width: 18px;
  height: 15px;
  background: #0f1726;
  pointer-events: none;
  left: var(--toast-tail-x, 40px);
}
.demo-detail-toast[data-tail="up"]::before,
.company-source-toast[data-tail="up"]::before {
  top: -13px;
  clip-path: polygon(0 100%, 50% 0, 100% 100%);
}
.demo-detail-toast[data-tail="down"]::before,
.company-source-toast[data-tail="down"]::before {
  bottom: -13px;
  clip-path: polygon(0 0, 100% 0, 50% 100%);
}
/* ⚠️ THE DISC NEEDS ROOM THAT THE SHARED MODULE DELIBERATELY DOES NOT RESERVE. `hirello-pie-timer`
   is `position: absolute` on purpose so a host's box stays byte-identical; the documented answer is
   for the HOST to make room at its own call site, which is what this padding is.
   ⚠️ 34 IS NOT A CHOSEN NUMBER: it is the disc's 16px inset plus its own 18px size, so the band is
   exactly the box the disc occupies. If either moves, this moves with it.
   ⚠️⚠️ KEYED ON THE CARDS THEMSELVES, NOT ON `[data-tail]`, AND THAT IS A CORRECTION. Scoping it to
   the tail attribute looked tighter and was wrong: the no-anchor path REMOVES that attribute, so a
   card shown while its section is still collapsed would have lost the reservation and dropped the
   disc onto its own last line. These two elements exist only in this one role and always carry a
   disc, so the reservation belongs to them unconditionally. */
.demo-detail-toast,
.company-source-toast {
  padding-bottom: 34px;
}

/* ═══ ENTRY #412 (2026-08-25) — the Submit countdown disc takes the button's colour ═══════════
   Her ask, on an annotated `?scene=rank&at=compare` frame with the arrow on the disc inside
   "Submit my ranking": *"Make this our normal pie timer colour token not this colour token"*.

   ⚠️⚠️ THE DISC IS A `<span>`, AND A DESCENDANT-SPAN RULE CAUGHT IT. `.compare-modal-foot span`
   above sets `color: var(--text-secondary)` for the foot's label spans, and
   `HirelloPieTimer.create()` builds the disc as `<span class="hf-pie">`. That rule is (0,3,2) and
   the module's own `.hf-pie` base rule is (0,1,0), so the foot won and the disc's inline
   `--hf-pie-colour: currentColor` resolved against GREY instead of the button's `--text-inverse`.
   Measured: button `color` rgb(255,255,255), disc `color` rgb(107,114,128), at 1440, 834 and 390
   alike. Every other countdown disc in the demo sits on a button no such rule reaches, which is
   why this is the only one that looked wrong.

   ⚠️ `inherit`, NOT A LITERAL AND NOT A TOKEN NAMED HERE. The whole contract of the module's
   `currentColor` default is that a disc takes its host's colour, so the correction is to restore
   inheritance rather than to name a second colour that would then need updating whenever the
   button's does. It follows the disabled state for free, because that is `opacity` on the button.

   ⚠️ SPECIFICITY, COUNTED RATHER THAN ASSUMED: this is (0,4,1) against the foot rule's (0,3,2), so
   it wins on specificity and does not depend on being last in the file.

   ⚠️ THE FOOT'S OTHER TWO SPAN RULES ARE DELIBERATELY NOT TOUCHED. They set `font-size` and, at
   the phone tier, `flex: 1`, and a stretched disc looked like the obvious second half of this bug.
   MEASURED INSTEAD OF ASSUMED: the disc reads 16x16 with `flex-grow: 0` at 1440, 834 and 390, so
   there is nothing there to fix and the theory was wrong. */
body.nifhr-screen [data-screen-id="nIfHr"] .compare-modal-foot .hf-pie {
  color: inherit;
}

/* ═══ ENTRY #416 (2026-08-25) — the Next steps drawer is the star, so its backdrop recedes ═══════
   Her ask, on an annotated `?scene=messenger` frame with the drawer open: *"the background here
   needs to use our medium blurring token and if we have a darker dimming token use that as the
   star of the show at this point is the content in the drawer"*.

   ⚠️ TWO TOKENS, ONE OF WHICH ALREADY EXISTED AND ONE OF WHICH DID NOT. `--scrim-dim-coach` IS the
   project's darker dim and is used verbatim, which is what she asked for. There was no medium
   blur, so `--blur-scrim-md` is derived from `--blur-scrim` and declared beside it rather than a
   number being invented here. Superseded treatment is DESCRIBED not retyped: this scrim carried
   the standard lighter dim and no blur at all.
   ⚠️ THE NAME `--scrim-dim-coach` IS NOW WIDER THAN ITS OWN COMMENT CLAIMS. That comment says it is
   "Scoped DELIBERATELY to this coach"; this is a second consumer, chosen because she asked for the
   darker token BY VALUE rather than for a new one, and duplicating 0.55 into a second name would be
   two sources of truth for one colour. Recorded here rather than left for the next reader to trip on.
   ⚠️ `-webkit-` FIRST, matching the two existing backdrop-filter sites in this sheet.
   ⚠️ THIS RULE IS SHARED WITH `collaborator-invite.html`, which carries the same `.ns-scrim` markup
   and links this stylesheet. That is deliberate: it is the same component, and a drawer that is the
   star on one page and not on the other would be the inconsistency, not the fix. Said out loud
   because she annotated only the nifhr frame.
   ⚠️ BELOW 901px THIS IS INVISIBLE BY CONSTRUCTION and no special case is added for it: the panel
   becomes `inset: 0; width: 100%; height: 100dvh`, an opaque full-viewport screen, so there is no
   background left to blur. Landscape tablets and up get the drawer and do show it. */
.ns-scrim {
  background: var(--scrim-dim-coach);
  -webkit-backdrop-filter: blur(var(--blur-scrim-md));
  backdrop-filter: blur(var(--blur-scrim-md));
}

/* ============ ENTRY #466: the Next-steps drawer's video header ============
   Her annotation: "Video plays here bleed edge to edge - start muted and the only controls are
   start pause audio on/off".

   ⚠️⚠️ `.ns-panel--video` IS THE WHOLE REASON THIS IS SAFE, and it is a MODIFIER rather than an
   edit to the base `.ns-panel` rule on purpose. The base rule declares
   `grid-template-rows: auto minmax(0,1fr) auto`, three tracks for three in-flow children (head,
   body, foot). `.ns-pay` and `.ns-pay-scrim` are `position: absolute` and consume no track, so
   they do not count. `nifhr.html` now has a FOURTH in-flow child and needs a fourth track.
   `collaborator-invite.html` has its own `#nsPanel`, draws every `.ns-*` rule from this same
   sheet, defines none of its own (measured: 0 `.ns-*` selectors in its two inline <style> blocks),
   and has NO video. Widening the BASE template would hand that page four tracks for three
   children, making its `.ns-body` an `auto` row and its FOOTER the flexible one, which is a
   silent layout break on a page this entry never touched. Scope it, do not widen it.

   ⚠️ THE BLEED NEEDS NO NEGATIVE MARGIN AND MUST NOT USE ONE. As a grid row on `.ns-panel` the
   figure spans the panel's full inline size at every width, with no padding to escape. The
   obvious alternative, parking it as the first child of `.ns-body`, would have to negate
   `.ns-body`'s inline padding, which is a flat 24px at >=901px but
   `max(var(--space-lg), calc((100% - var(--drawer-min-w)) / 2))` at <=900px: viewport-dependent,
   and its `100%` resolves against a different containing block when read from a child, so a
   hardcoded -24px would bleed correctly on desktop and be wrong on every phone. It would also put
   a `100vw`/negative-margin overflow risk in front of guard `#371A`, which asserts
   `documentElement.scrollWidth <= clientWidth`.

   ⚠️ NO `aspect-ratio` FALLBACK IS NEEDED but the ratio IS declared rather than left to the
   file's intrinsic size, so the row reserves its height BEFORE the metadata loads and the option
   cards below do not jump when it arrives. The source is 1920x1080. */
.ns-panel--video { grid-template-rows: auto auto minmax(0, 1fr) auto; }

/* ⚠️⚠️ `width: 100%` IS LOAD BEARING AND WAS ADDED AFTER A MEASURED DEFECT, NOT DEFENSIVELY.
   Without it the figure's width is INDEFINITE, so `aspect-ratio` derives WIDTH FROM HEIGHT the
   moment any height constraint applies, and the box shrinks sideways instead of cropping. It was
   measured at 834x1112 with a `max-height: 34dvh` in place: the row was 378.06px tall, so the
   figure came out 378 * 16/9 = 672.1px wide against a panel of 834, i.e. flush left with 161.88px
   of bare panel down the right-hand side. `object-fit: cover` did NOT save it, because that crops
   INSIDE the element's box and the box itself had collapsed. With a definite width the ratio only
   ever computes height, which is the direction this needs. */
.ns-video {
  position: relative;
  margin: 0;
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--text-primary);
  border-bottom: var(--border-w) solid var(--border);
  overflow: hidden;
}
.ns-video-el {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The two controls she named, and nothing else, on a scrim disc so they stay legible whatever
   frame is behind them.
   ⚠️⚠️ TOP-right, NOT bottom-right, AND THAT IS A COLLISION FIX FOUND BY LOOKING AT THE RENDER,
   NOT BY A MEASUREMENT. They were written bottom-right first, which is where a video control
   normally goes. This particular video has BURNT-IN SUBTITLES along its bottom edge, so the two
   discs sat squarely on the caption text and covered the end of every line. No geometry assertion
   would have caught it: the buttons were exactly where they were told to be, the video was exactly
   the right size, nothing overflowed. It is only visible in a rendered frame. Top-right is clear
   at every tier (the only thing up there is the HIRELLO watermark, which is top-LEFT).
   ⚠️ If the video is ever replaced with one WITHOUT burnt-in subtitles, this can go back to the
   bottom; check the new file's lower third before moving it. */
.ns-video-ctl {
  position: absolute;
  right: var(--space-sm);
  top: var(--space-sm);
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  z-index: 1;
}
.ns-video-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* ⚠️ DERIVED FROM --control-h, NOT TYPED. `--control-h-sm` DOES NOT EXIST: this was written as
     `var(--control-h-sm, 36px)` and the 36px FALLBACK was silently doing all the work, which is
     an invented number wearing a token's clothes. Guard #371C caught it. --control-h is 50px, so
     0.72 gives the 36px disc this needs and it now MOVES if the house control height moves. */
  width: calc(var(--control-h) * 0.72);
  height: calc(var(--control-h) * 0.72);
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: rgba(2, 6, 23, .58);
  color: var(--surface);   /* #371C: was a raw hex; --surface IS #FFFFFF */
  cursor: pointer;
  /* ⚠️ `--blur-scrim-sm` DOES NOT EXIST EITHER, same invented-fallback shape. --blur-scrim-md is
     calc(var(--blur-scrim) * 3) = 6px, which is the value that fallback was hardcoding. */
  -webkit-backdrop-filter: blur(var(--blur-scrim-md));
  backdrop-filter: blur(var(--blur-scrim-md));
}
.ns-video-btn:hover { background: rgba(2, 6, 23, .74); }
/* #371C: was two 20px literals. --fs-2xl is 18px and is what the panel's other icon rules use
   (.ns-save svg takes --fs-xl, .ns-cta-btn svg takes --fs-md), so this joins that scale. */
/* ⚠️⚠️ ENTRY #467: `display: block` WAS REMOVED FROM THIS RULE AND THAT IS THE WHOLE BUG FIX.
   She reported it: *"Just use the speaker icon x when in mute not both icons like you have now -
   should only be one icon state either muted or not muted"*. MEASURED before touching anything:
   the mute button painted TWO svgs (`visiblePerButton` read [1, 2] at 390x844).
   THE CAUSE IS SPECIFICITY, NOT LOGIC. `.ns-video-btn svg` is one class plus one type, (0,1,1).
   The base hiding rule `.ns-video-i-loud { display: none }` is one class, (0,1,0). A tie goes to
   the later rule, but this is not a tie: (0,1,1) simply outranks (0,1,0), so `display: block` won
   and the hidden icon painted anyway.
   ⚠️ THE PLAY BUTTON LOOKED CORRECT PURELY BY LUCK, which is why it read as a mute-only bug. Its
   active state rule carries a second class (`.ns-video[data-playing="1"] .ns-video-i-play`), so it
   scores (0,2,0) and DOES outrank the block. The moment the video is PAUSED that rule stops
   matching and the play button would have painted both icons too. One fix covers both.
   ⚠️ AND MY OWN GUARD MISSED IT: `#466A` asserted the `data-muted`/`data-playing` ATTRIBUTES, which
   were correct the whole time. The attribute is the mechanism; what paints is the outcome. `#467A`
   now counts the icons that actually render.
   Dropping `display` here is safe: an svg inside a flex button is blockified as a flex item
   anyway, so nothing about the layout depends on the declaration that was breaking it. */
.ns-video-btn svg { width: var(--fs-2xl); height: var(--fs-2xl); }

/* ⚠️ WHICH GLYPH SHOWS IS DRIVEN BY THE ELEMENT'S REAL STATE, NOT BY A CLASS THE SCRIPT REMEMBERS.
   `data-playing` and `data-muted` are written by the controller from `video.paused` and
   `video.muted` themselves, so a state change that did NOT come from these buttons (the browser
   pausing a backgrounded tab, the video ending) still lands on the right icon. A class toggled
   only on click would drift out of step with the element and lie. */
.ns-video-i-pause,
.ns-video-i-loud { display: none; }
.ns-video[data-playing="1"] .ns-video-i-play { display: none; }
.ns-video[data-playing="1"] .ns-video-i-pause { display: block; }
.ns-video[data-muted="0"] .ns-video-i-muted { display: none; }
.ns-video[data-muted="0"] .ns-video-i-loud { display: block; }

/* ⚠️⚠️ THERE IS DELIBERATELY NO HEIGHT CAP ON THIS VIDEO, AND THE CAP THAT WAS HERE WAS DELETED
   AFTER MEASURING THAT IT NEVER HELPED. A `max-height: 34dvh` at <=900px was written first, on the
   reasoning that a 16:9 video would crowd the option cards off a short phone. The real numbers say
   otherwise. Panel height minus the header (68.6px) and the footer (83px), against the video's
   natural 16:9 height at that width:
     834x1112  video 469  -> body 591   (the cap would have forced 378 and cropped for nothing)
     390x844   video 219  -> body 473   (both option cards visible without scrolling)
     320x568   video 180  -> body 236   (tight, scrolls, which is what .ns-body's overflow is for)
   So the cap bound at exactly one tier, the one with the MOST room, and there it did harm: it was
   the height constraint that collapsed the figure's width (see the note on `width: 100%` above)
   and it would have cropped a talking head to 2.2:1. Natural ratio at every tier is both simpler
   and correct. If a shorter screen ever does need a cap, cap the HEIGHT with the width already
   definite, and set `object-position` so the crop does not take the top of the speaker's head. */

/* Reduced motion does NOT hide the video or block playback: it is content she asked for, not
   decoration, and it never autoplays anyway. Only the control's own hover transition is dropped,
   for consistency with the rest of this block. */
@media (prefers-reduced-motion: reduce) {
  .ns-video-btn { transition: none; }
}


/* ============ ENTRY #467: the drawer's second pass, from her annotated screenshot ============ */

/* ---- The "Recommended" chip, moved to the card's top-right and filled -----------------------
   Her words: *"Move from here to an inline chip like this (white icon and text too thanks)"*.
   ⚠️ THIS IS DELIBERATELY THE `.ns-free` TREATMENT, not a new look. That badge occupied this exact
   slot in `.ns-opt-head` until #466 deleted it, and it was already a filled success pill with
   white text, so reusing its shape keeps the card's top-right visually where it has always been.
   ⚠️ THE SVG NEEDS NO RULE OF ITS OWN: `.ns-chip svg` has no `color`, so the icon inherits
   `currentColor` from this rule and turns white with the text. That is the "white icon AND text"
   half of her ask, and it is why this is one declaration rather than two. */
.ns-chip--solid {
  flex-shrink: 0;
  border-radius: var(--radius-pill);
  background: var(--success);
  color: var(--surface);
  padding: var(--space-2xs) var(--space-xs);
  font-size: var(--fs-2xs);
  letter-spacing: var(--ls-caps);
}

/* ---- The price: stacked, right-aligned, at the card's trailing edge --------------------------
   ⚠️⚠️ BOTH OF THESE ARE MODIFIERS AND NEITHER TOUCHES THE BASE CLASS, WHICH IS NOT FUSSINESS.
   `collaborator-invite.html` draws every `.ns-*` rule from this same stylesheet and still keeps
   CHIPS in its `.ns-opt-foot` and its `.ns-price` in the card BODY above the description. Putting
   `justify-content: flex-end` on `.ns-opt-foot` would shove that page's chips to the right, and
   putting `flex-direction: column` on `.ns-price` would restack its price, on a page this entry
   never touched and nobody would have looked at. */
.ns-opt-foot--price { justify-content: flex-end; }
.ns-price--stack {
  flex-direction: column;
  align-items: flex-end;
  gap: 0;
  line-height: var(--lh-tight);
}

/* ---- The two option cards grow to fill what the deleted Invite card left ---------------------
   Her words: *"Remove this panel not needed in any view and then increase the height to be equal
   of the two selection panels above to fill its space left behind"*. Two requirements in one
   sentence: FILL the space, and be EQUAL to each other.
   ⚠️ `flex: 1 1 0` ON THE CARDS IS WHAT MAKES THEM EQUAL, and the `0` basis is the load-bearing
   part. With `flex: 1 1 auto` they would each grow by the same AMOUNT from different starting
   heights, so the card with the longer description would stay permanently taller: filled, but not
   equal. A zero basis makes the flex line distribute the whole height, so both land identical
   regardless of how much copy each holds.
   ⚠️ `min-height: min-content` STOPS THE OTHER FAILURE. A flex item with a zero basis is free to
   shrink below its own content, so on a short phone the two cards would clip their own text rather
   than let the body scroll. This floors them at their content and hands the overflow to
   `.ns-body`, which is already `overflow: auto`.
   ⚠️ SCOPED BY `.ns-body--fill`, on nifhr's body only. collaborator-invite's `.ns-body` holds
   three more cards BELOW its options; growing its `.ns-next` would push them off the panel. */
/* ⚠️⚠️ THE OPTIONS BECOME A GRID HERE, NOT A FLEX COLUMN, AND FLEX WAS TRIED FIRST AND MEASURED
   FAILING. `flex: 1 1 0` plus `min-height: min-content` on each card reads as if it must equalise
   them, and it does not: the zero basis distributes the free space evenly, but the content floor
   then raises whichever card holds more copy ABOVE its equal share. Option A's title wraps to more
   lines than option B's, so the measured gap was 2.0px at 1440, 2.0px at 834, 44.0px at 390 and
   131.0px at 320 - filled, and visibly unequal exactly where the screen is smallest.
   Dropping the content floor is not the fix either: a flex item with a zero basis and no floor
   shrinks below its own text and clips it.
   `grid-auto-rows: 1fr` sizes the rows against EACH OTHER rather than each against its own
   content, which is the difference that matters, and the row still cannot go under its content. */
.ns-body--fill > .ns-next { flex: 1 1 auto; }
.ns-body--fill .ns-opts {
  display: grid;
  grid-auto-rows: 1fr;
  align-content: stretch;
  flex: 1 1 auto;
}

/* ---- The video's countdown bar --------------------------------------------------------------
   *"Give me a countdown bar like our cinematics here at bottom of video"*.
   ⚠️⚠️ EVERY VALUE HERE IS COPIED FROM `.hf-cine-timer` IN `hirello-cinematic-timer.js`, WHICH IS
   THE BAR SHE MEANS BY "our cinematics". Read off that module rather than eyeballed: 6px tall,
   full width, `--cand-priya` teal, and the TRACK is that same colour at opacity .2 painted by a
   `::before`. The opacity goes on the pseudo-element and never on the bar, or it would fade the
   fill along with the track, which is the mistake that rule exists to prevent.
   ⚠️ NO `border-radius`, DELIBERATELY. The rounded pill is the OTHER drain bar in this codebase,
   `.jc-card-timer` on the rj0wj deck, which is inset inside a card. The cinematic one is a hard
   edge-to-edge strip, and this sits on a full-bleed video, so it takes the cinematic treatment.
   ⚠️ NO `transition` ON THE FILL, also deliberate and also copied. The width is rewritten every
   animation frame; a transition would chase the value it is already being given and lag behind.
   ⚠️ POSITIONED ABSOLUTE TO THE FIGURE, NOT FIXED TO THE VIEWPORT, which is the one thing that
   differs from the module. `.hf-cine-timer` is viewport-fixed with a 4px lift to dodge an iOS
   bottom-chrome bug; this one belongs to the video's own box, so it sits flush at `bottom: 0`. */
.ns-video-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 6px;
  overflow: hidden;
  z-index: 1;
  pointer-events: none;
}
.ns-video-bar::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--cand-priya);
  opacity: .2;
}
.ns-video-bar > i {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  background: var(--cand-priya);
}

/* ⚠️ Both drain bars in this codebase hide themselves under reduced motion (the cinematic module
   refuses to install at all; the deck's bar uses `display: none`). This follows the CSS-side
   precedent so the behaviour matches the rest of the project rather than inventing a third answer. */
@media (prefers-reduced-motion: reduce) {
  .ns-video-bar { display: none; }
}


/* ============ ENTRY #468: her four numbered callouts, plus the chip correction ============ */

/* ---- THE CHIP SITS ON THE CARD'S TOP BORDER, NOT INSIDE THE CARD ---------------------------
   Her words: *"WHERE THE FUCK IS THE RECOMMENDED CHIP BEING INLINE WITH THE PANEL BORDER"*.
   #467 put it in `.ns-opt-head`, on the title's line, INSIDE the card. It belongs straddling the
   card's top-right border, which is why it is now a child of `.ns-opt` and absolutely positioned
   against the card box rather than laid out in the head's flex row.
   ⚠️ `translateY(-50%)` IS WHAT PUTS IT ON THE LINE rather than above it: the chip's own centre
   lands on the border, so the border appears to pass through it. `top: 0` alone would hang it
   below the border, inside the card, which is the thing being corrected. */
.ns-opt { position: relative; }
.ns-chip--onborder {
  position: absolute;
  top: 0;
  right: var(--space-md);
  transform: translateY(-50%);
  z-index: 1;
  white-space: nowrap;
}

/* ---- CALLOUT 2: the price never moves, and the sub text can never reach it -------------------
   Her words: *"note the position which will never change on any view and the size and how the sub
   text will next interfere with this pricing area"*.
   ⚠️⚠️ A GRID WITH A FIXED SECOND TRACK IS WHAT MAKES BOTH TRUE AT ONCE, and it is why the
   description and the price now share a row element instead of being stacked siblings. Stacked,
   the description's own wrapping decided where the price ended up, so the price moved whenever the
   text rewrapped. As `1fr auto` the price owns its own column: the description physically cannot
   occupy that space at any width, and `align-items: end` pins the price to the row's bottom edge,
   which with equal-height cards is the same place on every view.
   ⚠️ `min-width` ON THE PRICE COLUMN stops it collapsing when the description is long, which would
   reintroduce exactly the movement she is describing. */
.ns-opt-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: end;
  gap: var(--space-sm);
  flex: 1 1 auto;
}
/* ⚠️⚠️ NO `min-width` HERE, AND THE FIRST ATTEMPT HAD ONE AND WAS MEASURED FAILING. A 5.5rem
   floor on this column looked like cheap insurance against the price wrapping; what it actually
   did was take 88px away from the description at every width, and at 390 that left the sub text a
   144px column. Her callout 3 requires the sub text on exactly THREE lines at every view, and 144px
   could not reach three lines above the fitter's readable floor: it measured 5 lines at 390 and 4
   at 1440. The price is `white-space: nowrap`, so it defends its own width without a floor, and the
   column it needs is the width of "$120" and nothing more. */
.ns-opt-foot--price { justify-content: flex-end; }
.ns-price--stack { white-space: nowrap; }

/* ⚠️ THE PRICE TYPE IS FIXED, NOT FLUID, WHICH IS THE OTHER HALF OF "the size ... will never
   change". The title and the sub text below are deliberately size-fitted per viewport; the price
   is deliberately NOT, or it would be the one number on the screen that changes size as the
   window moves, which is what she is telling me not to do. */
.ns-price--stack b { font-size: var(--fs-3xl); }
.ns-price--stack s { font-size: var(--fs-md); }

/* ---- CALLOUTS 1 and 3: one line for the title, three for the sub text ------------------------
   *"Make both panel title text responsive as fuck so always appears on one line no more no less no
   truncation or clipping on any view"* and *"...both panel sub text ... always appears on 3 lines
   no more no less ... left aligned"*.
   ⚠️⚠️ THE LINE COUNTS ARE MEASURED AND SET IN JS, NOT ATTEMPTED HERE, AND THE REASON IS THAT CSS
   CANNOT DO IT. `clamp()` scales type against the VIEWPORT, not against how a specific sentence
   happens to wrap in a specific column, so it lands on the right line count only by luck and
   breaks on the next copy change. A `text-overflow: ellipsis` would satisfy "one line" by
   TRUNCATING, which she explicitly ruled out. The controller binary-searches a font size until the
   real laid-out line count is exactly right, per element, per resize.
   ⚠️ WHAT CSS OWNS HERE IS ONLY WHAT MAKES THAT MEASURABLE: a UNITLESS line-height, so line count
   is exactly scrollHeight / (fontSize * lh) at any size, and `overflow: visible` so a wrong fit
   shows as visibly wrong rather than being silently clipped into looking correct. */
/* ⚠️⚠️ ENTRY #470 REMOVED `white-space: nowrap` FROM THE TITLE, AND IT HAD TO GO. It was correct
   while the requirement was ONE line: wrapping was then a defect. The requirement is now exactly
   TWO rows, so the title MUST be allowed to wrap, and `nowrap` would have made two rows physically
   unreachable while the fitter searched for them and silently gave up.
   ⚠️ `overflow: visible` STAYS AND IS THE HONEST CHOICE. Her instruction says no truncation or
   clipping, so a title that does not fit must LOOK wrong rather than be quietly cut to size. */
.ns-opt-head b {
  line-height: 1.25;
  overflow: visible;
}
.ns-opt-desc {
  line-height: 1.45;
  text-align: left;         /* callout 3, said explicitly */
  overflow: visible;
}

/* ⚠️⚠️ ENTRY #468: NARROW SCREENS GIVE THE SUB TEXT ITS WIDTH BACK BY TIGHTENING THE NESTED
   PADDING, AND THIS IS WHAT MAKES HER "3 LINES ON ANY VIEW" REACHABLE AT A READABLE SIZE.
   Measured at 390 before this rule: the sub text sat in a 177px column and could not reach three
   lines above the 8px floor, so it rendered FOUR. The cause is three boxes of padding nested
   inside each other, each reasonable alone: `.ns-body` 24, `.ns-next` 14, `.ns-opt` 16, which is
   108px of a 390px screen spent on gutters before a word is drawn. Tightening the inner two on
   phones returns ~24px to the column, which is the difference between a 4-line 8px block and a
   3-line ~9.5px one.
   ⚠️ SCOPED TO `.ns-body--fill`, so collaborator-invite's copy of this panel is untouched, and
   bounded at 480 so the tablet and desktop tiers keep the roomier design spacing. */
@media (max-width: 480px) {
  .ns-body--fill .ns-next { padding: var(--space-sm); }
  .ns-body--fill .ns-opt { padding: var(--space-sm); }
  .ns-body--fill .ns-opt-row { gap: var(--space-2xs); }
}

/* ⚠️⚠️ ENTRY #468: AT 360px AND BELOW THE PRICE DROPS UNDER THE SUB TEXT, AND THIS IS THE ONE
   PLACE HER LAYOUT INSTRUCTION AND HER LINE-COUNT INSTRUCTION CANNOT BOTH BE MET SIDE BY SIDE.
   MEASURED, not guessed. At 320x568 with the price beside the text, the sub text's column is
   ~150px and three lines of this copy needs roughly 6px type, which is not readable by anyone.
   Holding the 8px floor instead renders FOUR lines, which breaks her "no more no less". Dropping
   the price beneath gives the sub text the card's full width, and three lines lands at ~10px.
   ⚠️ THE PRICE'S RIGHT EDGE AND ITS SIZE DO NOT MOVE, which is the part of her callout 2 that
   carries the meaning: it stays hard against the card's trailing edge, at the same type size, and
   the sub text still cannot reach into it, because it is no longer beside it at all. What changes
   is only that it sits under the text rather than next to it, on screens narrower than any phone
   she tests on (her iPhone is 390, the moto is wider).
   ⚠️ 360 IS THE BOUND ON PURPOSE: 390 measured clean side-by-side (3 lines at 9.31px), so this
   tier must not reach it. Do not widen this bound without re-measuring 390. */
@media (max-width: 360px) {
  .ns-body--fill .ns-opt-row { grid-template-columns: 1fr; }
  .ns-body--fill .ns-opt-foot--price { justify-content: flex-end; }
}

/* ============ ENTRY #469 ============
   *"Remove the surrounding border around the panels and resize the panels proportions and
   responsiveness as a result"* and *"ensure at all times at all views the prices are in this
   position this size and the sub text never interferes with it - equal margin right and bottom of
   each price group"*, with arrows dragging each price down to its card's bottom-right corner.

   ---- 1. THE WRAPPER AROUND THE TWO CARDS IS GONE ------------------------------------------
   `.ns-next` was a bordered, padded, surface-coloured box holding both options: a third nested
   frame inside `.ns-body` inside `.ns-panel`. Its heading was deleted at #466 and nothing has
   lived in it since except the two cards, so it was drawing a border around a border.
   ⚠️ ITS PADDING WAS ALSO THE THING SQUEEZING THE SUB TEXT. Removing it hands the cards the full
   body column at every width, which is the "resize the panels proportions and responsiveness as a
   result" half of her instruction: the fitter re-runs against the wider column and the copy comes
   up in size on its own rather than needing a second rule.
   ⚠️ SCOPED TO `.ns-body--fill`. collaborator-invite.html draws `.ns-next` from this same sheet and
   still puts a heading and its own options inside it; stripping the base rule would flatten a page
   this entry never touched. */
.ns-body--fill > .ns-next {
  border: 0;
  background: transparent;
  padding: 0;
  border-radius: 0;
}

/* ---- 2. THE PRICE SITS IN THE CARD'S BOTTOM-RIGHT CORNER, EQUAL MARGIN BOTH SIDES ----------
   ⚠️⚠️ THE MARGINS ARE EQUAL BECAUSE THEY ARE THE CARD'S OWN PADDING, NOT BECAUSE A NUMBER WAS
   TYPED TWICE. `.ns-opt` already pads itself equally on all four sides, so a price flush to the
   bottom-right of the card's content box is inset by exactly that padding on the right AND the
   bottom, at every viewport, and it tracks the padding if that ever changes. Typing a `right` and
   a `bottom` literal would be two numbers that have to be kept equal by hand, and the phone tier
   already changes the padding, so they would silently diverge there.
   ⚠️⚠️ `align-items: stretch` ON THE CARD IS THE LOAD-BEARING LINE AND IT IS EASY TO MISS WHY.
   `.ns-opt` is `align-items: flex-start`, so `.ns-opt-body` was only as tall as its own content
   and its bottom edge sat wherever the text happened to end, NOT at the card's bottom. That is
   why the price was floating in the middle of the card: it was bottom-aligned inside a box that
   did not reach the bottom. Stretching the body, then letting the desc/price row grow into it,
   puts the row's bottom edge on the card's content-box bottom, which is what the arrows in her
   screenshot are dragging it to.
   ⚠️ THE RADIO MUST OPT OUT OF THE STRETCH or it grows into a full-height pill. */
.ns-body--fill .ns-opt { align-items: stretch; }
.ns-body--fill .ns-opt > .ns-radio { align-self: flex-start; }
.ns-body--fill .ns-opt-body { flex: 1 1 auto; }
/* ⚠️⚠️ `align-items: start` ON THE ROW, `align-self: end` ON THE PRICE ONLY. The first version
   put `align-items: end` on the row, which bottom-aligned BOTH cells: the price landed correctly
   in the card's bottom-right corner and DRAGGED THE SUB TEXT DOWN WITH IT, leaving a hole between
   the title and the copy. Only the price is supposed to fall to the bottom; the sub text belongs
   directly under the title where it was. Two different alignments in one row means the row cannot
   carry a single `align-items`, so the price opts out of the row's alignment on its own. */
.ns-body--fill .ns-opt-row { flex: 1 1 auto; align-items: start; }
.ns-body--fill .ns-opt-foot--price { align-self: end; }

/* ============ ENTRY #474: the success step becomes a full-drawer screen ============
   *"Change the success to be full drawer coverage screen only thing they can click or tap is the
   open my dashboard button"*.

   ⚠️⚠️ THE SHEET ITSELF IS RE-POSITIONED, NOT A NEW ELEMENT. `.ns-pay` is already
   `position: absolute; left/right/bottom: 0` with `max-height: 88%` and a rounded top: a bottom
   sheet. Making the success step full-drawer is therefore a change to the SHEET's box on that one
   step, not a second overlay stacked on top. A second element would have to duplicate the sheet's
   own z-index, transition and aria-modal handling and would leave two things claiming to be the
   dialog.
   ⚠️ KEYED ON `[data-ns-pay-at="done"]`, WHICH `payStep()` WRITES. The step used to exist only as a
   `hidden` flag on each step div, which CSS on the parent cannot see. That attribute is the whole
   reason this rule can exist.
   ⚠️ `inset: 0` AND `max-height: none` TOGETHER. The `max-height: 88%` on the base rule is what
   makes it a sheet; leaving it would cap this screen at 88% of the drawer and show a strip of the
   cards underneath, which is exactly the "coverage" she is asking for and would not get. */
.ns-pay[data-ns-pay-at="done"] {
  inset: 0;
  max-height: none;
  border-radius: 0;
  border-top: 0;
  box-shadow: none;
  padding: var(--space-lg);
}
/* The grab handle is a bottom-sheet affordance and reads as "drag me down to dismiss", which is
   the opposite of what this screen is. It is hidden rather than left as a decoration. */
.ns-pay[data-ns-pay-at="done"] .ns-pay-grab,
.ns-pay[data-ns-pay-at="done"] .ns-pay-head { display: none; }

/* ⚠️ THE STEP IS THE FLEX COLUMN THAT FILLS THE SCREEN, and `margin-top: auto` on the CTA is what
   pins it to the bottom without positioning it absolutely. Absolute positioning would take it out
   of flow and let the copy above it run underneath on a short viewport. */
.ns-pay[data-ns-pay-at="done"] .ns-pay-step--done {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-sm);
}
.ns-pay[data-ns-pay-at="done"] .ns-pay-tick {
  align-self: center;
  margin-top: auto;          /* centres the tick+heading block in the space above the copy */
  width: calc(var(--control-h) * 2);
  height: calc(var(--control-h) * 2);
}
.ns-pay[data-ns-pay-at="done"] .ns-pay-tick svg {
  width: 50%;
  height: 50%;
}
/* ⚠️⚠️ `--fs-3xl` IS NAMED DIRECTLY HERE, AND IT USED TO READ `var(--fs-4xl, var(--fs-3xl))`.
   `--fs-4xl` DOES NOT EXIST in this stylesheet (measured: zero declarations), so the fallback was
   doing all the work and the rule only LOOKED like it was reaching for a bigger step. That is the
   same shape as the invented `--control-h-sm` and `--blur-scrim-sm` that guard #371C caught at
   #467: a token name nobody declared, with a real value hiding behind the comma. It also matters
   for the rule below, which has to be exactly one step under this one: a phantom token gives no
   answer to "one step down from what?". The scale here is 2xs 10, xs 11, sm 12, md 14, lg 15,
   xl 16, 2xl 18, 3xl 24. */
.ns-pay[data-ns-pay-at="done"] .ns-pay-done-title {
  align-self: center;
  text-align: center;
  font-size: var(--fs-3xl);
}
/* ⚠️ ONE TOKEN STEP DOWN FROM THE HEADING, and expressed as the neighbouring token rather than as
   a calc off it. Her instruction was "one point size down token from payment successful size
   token": `--fs-3xl` is 24 and the step below it on this scale is `--fs-2xl` at 18. A
   `calc(var(--fs-3xl) - 1px)` would satisfy the words and miss the point, and it would type a
   pixel literal into a `.ns-*` rule, which #371C fails on.
   ⚠️ CENTRED at #475, superseded from left-aligned: her earlier mock showed these two lines ranged
   left under a centred heading, and this one shows them centred with it. */
.ns-pay[data-ns-pay-at="done"] .ns-pay-done-copy {
  text-align: center;
  font-size: var(--fs-2xl);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  margin: 0;
}
.ns-pay[data-ns-pay-at="done"] .ns-pay-done-copy:first-of-type { margin-top: var(--space-lg); }
.ns-pay[data-ns-pay-at="done"] .ns-pay-cta { margin-top: auto; }
