/* =============================================
   GigFinder – Members Area Styles
   ============================================= */

/* Members / employers / admin shared body. The background image lives here
   so all three areas pick it up; the semi-transparent white overlay keeps
   content cards / tables readable. Tune the alpha (0 = full image,
   1 = solid white) to change wash intensity. */
.members-body {
  background:
    linear-gradient(rgba(255,255,255,0.80), rgba(255,255,255,0.80)),
    url('/images/background/background-04.png') center center / 100% 100% no-repeat fixed,
    var(--bg-alt);
  min-height: 100vh;
}

/* Admin pages use the exact public-pages backdrop effect (.brand-bg in
   styles.css) -- same dark vignette, same cover fit, same fixed
   attachment -- only the image URL differs. body.admin-body bumps
   specificity above the shared .members-body rule that also applies
   here; !important on every sub-property is belt-and-suspenders so the
   white-wash .members-body shorthand cannot leak through any sub-prop
   (color, image, attachment) even if a later rule mutates one piece. */
/* Loose text on dark-backdrop pages (admin + members + employers
   areas) -- page headers and intro paragraphs need light type so
   they read against the dark navy backdrop. Card-internal text
   keeps its dark default. These live in members.css (not admin.css)
   because /members/*.asp and /employers/*.asp only load styles.css
   + members.css. */
.admin-body .m-page-header h1,
.member-area-bg .m-page-header h1,
.employer-area-bg .m-page-header h1 { color: #fff; }
.admin-body .m-page-header p,
.member-area-bg .m-page-header p,
.employer-area-bg .m-page-header p  { color: rgba(255, 255, 255, 0.85); }

/* Settings page tabs (Job Preferences / Background / Job Match
   Preferences) sit on the dark backdrop, not inside a card. Lift
   their muted-text color to white-ish and replace the light-gray
   bottom rule with a translucent-white hairline so the tab bar
   still reads as a tab bar against the dark backdrop. Active tab
   keeps its purple underline -- the underline color is already
   high-contrast on dark. */
/* Brand blue shift: redefine the brand tokens for the logged-in app areas so
   every var(--primary...) usage (buttons, option pills, wizard controls, focus
   rings, etc.) resolves to blue WITHOUT touching the global :root -- so the
   public/marketing site keeps the current palette. Hardcoded hex literals in
   individual page files are swept separately. */
.member-area-bg,
.employer-area-bg {
  --primary:       #1E40AF;
  --primary-dark:  #172554;
  --primary-light: #EFF6FF;
  --primary-line:  #BFDBFE;   /* hairline/border on --primary-light surfaces */
  --primary-fill:  #DBEAFE;   /* stronger tint -- filled chips / pills        */
  --primary-line-strong: #93C5FD;  /* stronger border -- selected/hover cards */
  --accent:        #2563EB;

  /* Connect-green counterpart to --btn-grad / --btn-grad-hover (the blue ramp
     in styles.css). Same 135deg geometry and the same dark-to-light direction,
     built from the section switcher's green (#15803D) so the Contract/Project
     tab reads as the green twin of the Jobs tab rather than a flat chip. */
  --btn-grad-cx:       linear-gradient(135deg, #14532D 0%, #16A34A 100%);
  --btn-grad-cx-hover: linear-gradient(135deg, #15803D 0%, #22C55E 100%);
}

/* ══════════════════════════════════════════════════════════════════════════
   CONTRACT MODE (body.m-mode-cx, set by /includes/member_mode_inc.asp)

   The member area runs both audiences out of /members/ and swaps MODE rather
   than folder, so the whole palette swap happens here. Same three-layer shape
   as css/employers-connect.css, which does this for the employer Connect
   section -- keep the two in step:

     1. The Connect palette as --cx-* properties, declared across the member
        area. Declaring them changes nothing on its own.
     2. A re-point of the SHARED brand tokens (--primary, --btn-grad,
        --card-header-grad, ...) onto that palette, in contract mode only. Every
        component already built on var(--primary) / var(--btn-grad) turns green
        with no further edits.
     3. Green counterparts for the components whose blue is HARDCODED in this
        file (the sweep the old note here asked for). When one of those is
        retired in favour of a token, delete its counterpart below too.
   ══════════════════════════════════════════════════════════════════════════ */

/* 1. Connect palette. Values mirror employers-connect.css position for position
      so the two sections stay identical. */
.member-area-bg {
  --cx-primary:        #15803D;  /* master color -- solid fills, borders, links */
  --cx-primary-dark:   #14532D;  /* pressed/hover of the master color          */
  --cx-primary-deep:   #166534;  /* gradient start                              */
  --cx-primary-bright: #16A34A;  /* accent / gradient end                       */
  --cx-accent:         #22C55E;  /* lighter accent -- hover gradient end        */
  --cx-tint:           #DCFCE7;  /* filled chips, banner surfaces               */
  --cx-tint-soft:      #F0FDF4;  /* row hovers, focus fills                     */
  --cx-tint-line:      #BBF7D0;  /* hairlines on tinted surfaces                */

  --cx-grad:        linear-gradient(135deg, #14532D 0%, #15803D 100%);
  --cx-grad-bright: linear-gradient(135deg, #166534 0%, #16A34A 100%);
  --cx-grad-hover:  linear-gradient(135deg, #15803D 0%, #22C55E 100%);
}

/* 2. Re-point the shared brand tokens. This is what turns every tokenized
      component (buttons, card headers, focus rings, option pills) green. */
.member-area-bg.m-mode-cx {
  --primary:          var(--cx-primary);
  --primary-dark:     var(--cx-primary-dark);
  --primary-light:    var(--cx-tint-soft);
  --primary-line:     var(--cx-tint-line);
  --primary-fill:     var(--cx-tint);
  --primary-line-strong: var(--cx-tint-line);
  --accent:           var(--cx-primary-bright);
  --card-header-grad: var(--cx-grad);
  --btn-grad:         var(--cx-grad-bright);
  --btn-grad-hover:   var(--cx-grad-hover);
  --btn-grad-shadow:  rgba(22, 163, 74, .28);
}

/* 3. Green counterparts for the hardcoded blues in this file. --------------- */

/* Menu row: active tab wash + underline (the two-level nav). */
.member-area-bg.m-mode-cx .m-tab:not(.m-tab--static):hover,
.member-area-bg.m-mode-cx .m-tab.active {
  color: var(--cx-primary);
  background: var(--cx-tint-soft);
  border-color: var(--cx-tint-line);
}
.member-area-bg.m-mode-cx .m-nav-tabs .m-tab.active {
  background: var(--cx-tint-soft);
  color: var(--cx-primary);
  box-shadow: inset 0 -4px 0 var(--cx-primary);
}

/* Dashboard stats hero: the master gradient. */
.member-area-bg.m-mode-cx .m-stats-hero { background: var(--cx-grad); }

/* Account cluster + avatar edit button. The profile pill itself is kept
   section-agnostic (neutral dark-gray outline, see .m-profile-btn), so its
   Connect-mode hover matches the base neutral hover rather than going green. */
.member-area-bg.m-mode-cx .m-profile-btn:hover {
  border-color: #374151;
  background: #F3F4F6;
}
.member-area-bg.m-mode-cx .pf-avatar-edit-btn { color: var(--cx-primary); }
.member-area-bg.m-mode-cx .pf-avatar-edit-btn:hover { background: var(--cx-tint-soft); }

/* Photo modal. */
.member-area-bg.m-mode-cx .pf-photo-preview {
  background: var(--cx-tint-soft);
  border-color: var(--cx-tint-line);
}
.member-area-bg.m-mode-cx .pf-photo-preview-initials { color: var(--cx-primary); }

/* Match cards + "search all" pill. */
.member-area-bg.m-mode-cx .m-match-card:hover { background: var(--cx-tint-soft); }
.member-area-bg.m-mode-cx .m-search-all-pill { background: var(--cx-primary); }
.member-area-bg.m-mode-cx .m-search-all-pill:hover { background: var(--cx-primary-dark); }

/* Job cards: focus rings, work-model pill, the "..." cut marker, and the
   category-check "?" badge. (The bookmark control is the paperclip now, which
   takes its color from the header strip it sits on and needs no mode variant.) */
.member-area-bg.m-mode-cx .mj-card-top-deadline { color: var(--cx-primary); }
.member-area-bg.m-mode-cx .mj-card:focus-visible { outline-color: var(--cx-primary); }
/* In Contract mode the work-model pill (e.g. Remote) is styled IDENTICALLY to
   the Contract type pill (.mj-pill--type), so the two pills on a contract card
   read as one set. Values mirror .mj-pill--type below. */
.member-area-bg.m-mode-cx .mj-pill--work {
  background: #D1FAE5;
  color: #065F46;
  border-color: #6EE7B7;
}
.member-area-bg.m-mode-cx .mj-card-desc-cut { color: var(--cx-primary); }
.member-area-bg.m-mode-cx .mj-card-pills .cc-trigger:hover { background: var(--cx-primary); }

.member-area-bg .pf-tab-bar,
.employer-area-bg .pf-tab-bar,
.member-area-bg .rt-tabs { border-bottom-color: rgba(255, 255, 255, 0.18); }
.member-area-bg .pf-tab,
.employer-area-bg .pf-tab,
.member-area-bg .rt-tab { color: rgba(255, 255, 255, 0.75); }
/* Member + employer active/hover tabs: white text + underline on the dark
   backdrop (replaces the old light-blue #BFDBFE); inactive tabs stay at the
   dimmed rgba white above so the active one still stands out. */
.member-area-bg .pf-tab:hover,
.member-area-bg .pf-tab--active,
.member-area-bg .rt-tab:hover,
.member-area-bg .rt-tab--active,
.employer-area-bg .pf-tab:hover,
.employer-area-bg .pf-tab--active { color: #FFFFFF; }
.member-area-bg .pf-tab--active,
.member-area-bg .rt-tab--active,
.employer-area-bg .pf-tab--active { border-bottom-color: #FFFFFF; }

/* Resume-tools upload/saved cards (.signup-panel reused inside .rt-grid)
   keep their purple stroke on the public signup page but drop it on the
   dark member-area backdrop -- the panel's drop-shadow + lavender fill
   already define the edge. .rt-side-card (the right "saved resumes"
   card) gets the same treatment so the two cards read consistently. */
.member-area-bg .signup-panel,
.member-area-bg .rt-side-card,
.employer-area-bg .signup-panel,
.employer-area-bg .rt-side-card { border: none; }

/* Thin hairline at the top of the dark-backdrop footers across all
   three dark areas (admin, members, employers). */
.admin-body .m-footer,
.member-area-bg .m-footer,
.employer-area-bg .m-footer { border-top: 1px solid rgba(255, 255, 255, 0.15); }

/* White cards on the dark backdrop don't need their light-gray
   stroke -- the drop-shadow already defines the edge, and the
   border read as visual noise once the page went dark. Same
   call we made on the search-jobs / job-detail cards earlier. */
.member-area-bg .m-section,
.employer-area-bg .m-section { border: none; }

body.admin-body,
body.member-area-bg,
body.employer-area-bg {
  background-color: #111827 !important;
  background-image:
    /* Neutral darken layer -- pure black at low alpha so the image gets
       darker without picking up additional purple saturation. Matches
       the layer stack on .brand-bg / .hero / body.home-bg in styles.css.
       This is the layer that's load-bearing for the perceived darkness;
       without it, the surface reads noticeably lighter even with
       identical vignette + image. */
    linear-gradient(rgba(0, 0, 0, 0.35), rgba(0, 0, 0, 0.35)),
    linear-gradient(
      to bottom,
      rgba(17, 24, 39, 0.72) 0%,
      rgba(17, 24, 39, 0.68) 55%,
      rgba(17, 24, 39, 0.82) 100%
    ),
    /* BLACK AND WHITE PHOTO, 2026-08-27. A flat achromatic grey blended onto
       the photo with `saturation` (see background-blend-mode below) -- that
       mode takes the SATURATION of this layer (grey = zero) and the hue +
       luminosity of the photo beneath, i.e. a desaturation. NOT
       `filter: grayscale()`, which would apply to <body> AND EVERY
       DESCENDANT and drain the colour out of the whole app.
       Canonical note, including how to tune the strength, is on .brand-bg in
       styles.css. The two rules below override background-image only, so they
       inherit this size / blend list -- add or remove a layer in all three or
       the lists fall out of step. */
    linear-gradient(rgba(128, 128, 128, 1), rgba(128, 128, 128, 1)),
    url('/images/background/background-members.png') !important;
  background-position: center !important;
  /* One value per layer -- a short list silently misapplies sizing. */
  background-size: auto, auto, auto, cover !important;
  background-blend-mode: normal, normal, saturation, normal !important;
  background-repeat: no-repeat !important;
  background-attachment: fixed !important;
}

/* Employer area (/employers/* -- INCLUDING the Connect section) uses the
   Connect backdrop image at the SAME brightness as the member area (black .22 /
   navy .58-.54-.66), lighter than the shared dark rule above (black .35 /
   navy .72-.68-.82). All three layers are re-stated because background-image is
   a single-list property. Same (0,1,1) specificity as the shared rule above, so
   it wins on source order (it comes after). */
body.employer-area-bg {
  background-image:
    linear-gradient(rgba(0, 0, 0, 0.22), rgba(0, 0, 0, 0.22)),
    linear-gradient(
      to bottom,
      rgba(17, 24, 39, 0.58) 0%,
      rgba(17, 24, 39, 0.54) 55%,
      rgba(17, 24, 39, 0.66) 100%
    ),
    /* Grey `saturation` layer -- keeps this list the same length as the
       shared rule's background-size / background-blend-mode above. */
    linear-gradient(rgba(128, 128, 128, 1), rgba(128, 128, 128, 1)),
    url('/images/background-connect.png') !important;
}

/* Members area (/members/*.asp) uses the GigFinder Connect backdrop: same image
   AND the same layer alphas as .connect-page .brand-bg in connect.css, so the
   two sections read at identical darkness. Lighter than the shared rule above
   (black .35 -> .22, navy .72/.68/.82 -> .58/.54/.66).
   All three layers are re-stated because background-image is a single list
   property: naming just the url() would drop the black wash and the navy
   vignette and leave the photo bare. Employers and admin keep the darker
   treatment on background-members.png.
   Must come AFTER the shared rule above -- same specificity (0,1,1), so this
   wins on source order. */
body.member-area-bg {
  background-image:
    linear-gradient(rgba(0, 0, 0, 0.22), rgba(0, 0, 0, 0.22)),
    linear-gradient(
      to bottom,
      rgba(17, 24, 39, 0.58) 0%,
      rgba(17, 24, 39, 0.54) 55%,
      rgba(17, 24, 39, 0.66) 100%
    ),
    /* Grey `saturation` layer -- keeps this list the same length as the
       shared rule's background-size / background-blend-mode above. */
    linear-gradient(rgba(128, 128, 128, 1), rgba(128, 128, 128, 1)),
    url('/images/background-connect.png') !important;
}

/* ── Members Nav ── */

/* Nav geometry, matching the taller full-bleed bar on the public end and
   GigFinder Connect: --m-nav-h drives BOTH the .m-nav-inner height and the
   .m-main top offset, so the bar and the content padding can never drift
   apart. --m-nav-max of `none` lets the logo sit at the far left and the
   right-hand cluster at the far right.
   Applies to the member, employer, and /admin42 areas alike -- all three
   render this nav and carry the "members-body" class. */
.members-body {
  --m-nav-h: 88px;
  --m-nav-max: none;
  /* Geometry of the page content column (.m-content). Lives here as tokens so
     the nav can line things up with the content edge without re-stating the
     numbers -- the section switcher (.m-secnav) right-aligns to exactly this
     box. Change the width in one place and the two stay in step. */
  --m-content-max: 1160px;
  --m-content-pad: 24px;

  /* m-content-width -- the opt-in WIDE measure. Say "put it on m-content-width".

     1160px is the STANDARD measure and stays the default for every page. Some
     pages want more room: job-detail spends a fixed 380px of its width on the
     sidebar, which left the description ~716px and reading cramped. Rather
     than widening the standard measure (that would move every member,
     employer and admin screen at once) the wide measure is declared here as a
     second named value, and pages ADOPT it one at a time.

     To put a page on it: set memberContentWidth = True before including
     members_header_inc.asp. The header stamps .m-content-width on the body and
     the rule below re-points --m-content-max, so .m-content and the section
     switcher (.m-secnav) both move together and stay on one edge. Never set
     max-width on .m-content directly -- that slides the content out from under
     the switcher.

     ADOPTED BY (swept 2026-08-18): every /members/ and /employers/ screen that
     renders .m-content, WITH THREE EXCEPTIONS -- the dashboards, which keep the
     standard measure: /members/index.asp, /employers/index.asp and
     /employers/connect/index.asp (plus /members/dashboard_ring.asp, the
     dashboard style sandbox). /admin42/ was left on the standard measure too;
     the sweep covered the member and employer areas only.

     Popups are unaffected BY CONSTRUCTION, not by omission: --m-content-max is
     read by exactly two rules, .m-content and .m-secnav. No modal, popup pane
     or overlay consumes it, so none of them can move when this value changes.
     Keep it that way -- if a popup ever needs a width, give it its own token.

     A new member/employer page defaults to the STANDARD measure and has to opt
     in. That is deliberate: the two are a real choice per screen, not a default
     to inherit. Add adopted pages to this note as they go. */
  --m-content-width: 1320px;
}

/* The opt-in itself. Two classes, so it beats the one-class .members-body rule
   above at specificity regardless of source order. */
.members-body.m-content-width {
  --m-content-max: var(--m-content-width);
}

/* Areas that carry the section switcher (.m-secnav). Setting --m-secnav-h is
   the opt-in: it reserves the strip's band at the top of the bar and drives
   every offset that depends on it. The strip's height is ADDED to the 88px row
   (88 -> 120) rather than compressing it, so nothing in the existing bar moves.

   /admin42 also carries .members-body but renders no strip, so it deliberately
   sits this out and keeps the 88px bar. */
.member-area-bg,
.employer-area-bg {
  --m-secnav-h: 32px;
  --m-nav-h: 120px;
}

.m-nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 1000;
  background: var(--white);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
}

.m-nav-inner {
  display: flex;
  align-items: center;
  height: var(--m-nav-h);
  /* Top padding reserves the section-switcher band, so the logo/menu/profile
     row still centers in its original 88px height. Zero where no strip is
     opted in (/admin42), which leaves the bar exactly as it was. */
  padding: var(--m-secnav-h, 0px) 24px 0;
  max-width: var(--m-nav-max);
  margin: 0 auto;
  gap: 0;
}

/* ── Bar gutters + the tight band ─────────────────────────────────────────
   Symmetric gutters on purpose: an asymmetric one (a margin on a side column)
   would shift the centered tab list off true center by half its width.

   EVERY rule here MUST carry --m-secnav-h as the padding shorthand's top value.
   Writing `padding: 0 52px` silently resets the section-switcher band that
   .m-nav-inner reserves, collapsing the whole bar back to one row. Same goes
   for any other rule that re-declares padding on .m-nav-inner.

   Width budget (employer, the heavier of the two): 104px gutters + 393px
   wordmark + ~430px menu (5 tabs) + ~280px account cluster is about 1200px. So:
     >= 1321px  full 52px gutters, full wordmark -- the roomy default
     1201-1320  narrow wordmark, still 52px gutters and no tightening. Width
                isn't the constraint here; CENTERING is. The 393px wordmark
                can't shrink, so it would overrun its half of the free space and
                shove the menu (and the strip above it) off center. Threshold
                lives on the <picture> in the headers -- keep them in step.
     921-1200   narrow wordmark (-163px) plus the tightening below (-124px),
                which brings the floor to ~915px
     <= 920px   hamburger (see the collapse block near the bottom of this file) */
@media (min-width: 921px) {
  .m-nav-inner { padding: var(--m-secnav-h, 0px) 28px 0; }

  /* Pull the account cluster in off the right edge. A relative offset, NOT
     padding or a margin: the side columns are flex:1 1 0, so any box change on
     this one grows it past the logo column and drags the centered menu off true
     center by half the amount (the gutter comment above). `right` shifts it
     visually with zero layout effect, so the menu stays put.

     Modest here because the bar is already tight in the 921-1200 band; the
     >=1201px rule below opens it up once there's room. */
  .m-nav-right { right: 8px; }
}

@media (min-width: 1201px) {
  .m-nav-inner { padding: var(--m-secnav-h, 0px) 52px 0; }
  .m-nav-right { right: 16px; }
}

/* The tight band: everything still fits, but only once the fat is trimmed. The
   narrow wordmark does the heavy lifting; these claw back the rest. */
@media (min-width: 921px) and (max-width: 1200px) {
  /* 14px -> 10px of side padding, across 5 menu tabs. */
  .m-tab { padding: 7px 10px; }

  /* The employer bar overrides this gap to a generous 40px (three items:
     envelope, tokens, profile). 22px still reads as separated. */
  .employer-area-bg .m-nav-right { gap: 22px; }

  /* Section-switcher tabs give back a little padding so the pair can't reach
     the account cluster's left edge on the narrow end of this band. */
  .m-sectab { padding: 0 12px; }
}

/* Logo and the right-hand cluster are equal-width flex columns (same grow, same
   0 basis), so the tab list between them lands on the TRUE center of the bar
   rather than merely centered in the leftover space beside a wide right
   cluster. Neither side can shrink below its content: the default
   min-width:auto floors them, so on narrow bars they fall back to content width
   and the tabs simply take what's left. */
.m-nav-logo {
  display: flex;
  align-items: center;
  flex: 1 1 0;
  margin-right: 0;
}
/* Slightly under the Connect wordmark (connect.css .connect-logo-img), in the
   same 88px bar. The img is a flex child of .m-nav-logo, so it needs
   flex-shrink:0 or it gets squeezed below 48px whenever the bar runs tight;
   max-width:none defeats the global `img { max-width: 100% }` in styles.css,
   which otherwise clamps the 2546px-wide source and distorts it against the
   fixed height. */
/* The wordmark is wrapped in <picture> (narrow mark swaps in below 1200px), so
   the flex child of .m-nav-logo is the <picture>, not the <img>. It has to
   carry the flex behaviour or it collapses to an inline box. */
.m-nav-logo-pic {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}
.m-nav-logo-img { height: 48px; width: auto; max-width: none; flex-shrink: 0; }

/* Desktop tab list: centered in the bar. */
.m-nav-tabs {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  flex: 0 1 auto;
}

.m-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  border-radius: var(--radius-sm);
  font-size: .875rem;
  font-weight: 500;
  /* Unselected nav menu options: gray-600 instead of the lighter gray-500
     (var(--text-muted)) so inactive tabs read a little darker across the
     member, employer, and connect navs. Active/hover set their own color. */
  color: #4B5563;
  transition: var(--transition);
  white-space: nowrap;
  text-decoration: none;
}
.m-tab:hover  { color: var(--primary); background: var(--primary-light); }
.m-tab.active { color: var(--primary); font-weight: 600; background: var(--primary-light); }

/* A tab rendered as inert copy rather than a link (see the Contract Projects
   gate in members_header_inc.asp). Same type and spacing as a real tab, but no
   pointer affordance and no hover highlight, so it never reads as clickable. */
.m-tab--static,
.m-tab--static:hover { cursor: default; background: none; color: var(--text-muted); }

/* ── Member + employer nav tabs: blue active/hover state ───────────────────
   Scoped to .member-area-bg (/members/*.asp) and .employer-area-bg
   (/employers/*.asp). /admin42 (.admin-body) keeps the brand purple.

   The transparent border on every tab is load-bearing: without it, adding a
   1px border only to the active tab would widen that tab by 2px and shove the
   whole centered row sideways as the user navigates. */
.member-area-bg .m-tab,
.employer-area-bg .m-tab { border: 1px solid transparent; }

/* :not(.m-tab--static) keeps the inert Contract Projects span from lighting up
   on hover. This selector is (0,3,0) and would otherwise outrank the
   .m-tab--static:hover rule above and repaint it. */
.member-area-bg .m-tab:not(.m-tab--static):hover,
.member-area-bg .m-tab.active,
.employer-area-bg .m-tab:not(.m-tab--static):hover,
.employer-area-bg .m-tab.active {
  color: #1D4ED8;
  /* One step lighter than the blue-100 (#DBEAFE) this used to carry. The border
     does the work of marking the active tab; the fill is just a soft wash. */
  background: #EFF6FF;
  border-color: #BFDBFE;
}

/* Contract Projects carries the light-blue outline permanently, active or not,
   so it reads as its own destination. Applies to both the linked (test user)
   and inert (everyone else) renderings. The left margin sets it apart from
   Search Jobs, on top of the .m-nav-tabs 4px gap. */
.member-area-bg .m-tab--connect {
  border-color: #93C5FD;
  margin-left: 18px;
  /* Positioning context for the "Coming Soon!" pill. */
  position: relative;
  /* .m-tab is inline-flex, so the pill would otherwise be laid out as a flex
     item beside the label instead of floating over the corner. */
  overflow: visible;
}

/* Tiny "Coming Soon!" pill riding the tab's top-right corner. Same blue chip
   treatment as .m-mode-pill. pointer-events:none so it never eats the click
   on the linked (test-user) version of the tab. */
.member-area-bg .m-tab-soon {
  position: absolute;
  top: -9px;
  right: -8px;
  z-index: 3;
  pointer-events: none;
  padding: 1px 7px;
  border-radius: 999px;
  font-size: .55rem;
  font-weight: 700;
  line-height: 1.6;
  letter-spacing: .03em;
  text-transform: uppercase;
  white-space: nowrap;
  /* Lighter than the tab's active fill (#EFF6FF) so the pill still reads as a
     distinct chip when this tab is the active one, rather than dissolving into
     the tab's own wash. */
  background: #F8FBFF;
  color: #2563EB;
  border: 1px solid #DBEAFE;
}

.m-tab-ai-icon {
  width: 15px;
  height: 15px;
  object-fit: contain;
  flex-shrink: 0;
}

/* ── Section switcher: tabs docked to the nav's top edge ───────────────────
   Two tabs hanging down from the top edge, RIGHT-ALIGNED to the content column
   below: "Full & Part-Time Jobs" and "Contract/Project Marketplace". They swap
   the whole app SECTION, so they sit above the section's own menu rather than
   inside it (they replaced the shaded cross-section pill that used to trail
   the menu row).

   Opt-in: the strip only takes up space where --m-secnav-h is set (see the
   .member-area-bg / .employer-area-bg block near the top of this file).

   Layout: .m-nav-inner takes the strip's height as padding-top, so the
   logo/menu/profile row still centers in its ORIGINAL 88px band instead of
   being pushed off-center.

   The strip itself is lifted OUT of that flex row and absolutely positioned
   against .m-nav (already position:fixed, so it's the containing block). It
   reproduces .m-content's box exactly -- same max-width, same side padding,
   same auto margins -- and pushes the tabs to its far end, so their right edge
   lands on the content column's right edge at every viewport width. Doing it
   with a matching box rather than a calc() off 100vw also sidesteps the
   scrollbar (100vw includes it; a fixed bar's width does not).

   Being absolute is also what keeps the menu centered: out of flow, the strip
   isn't a flex item and so doesn't consume a share of the row. It spans the
   full column, so it's pointer-transparent -- only the tabs take clicks. */
.m-secnav {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  margin: 0 auto;
  max-width: var(--m-content-max);
  padding: 0 var(--m-content-pad);
  height: var(--m-secnav-h, 32px);
  display: flex;
  justify-content: flex-end;
  align-items: stretch;
  gap: 6px;
  pointer-events: none;
}

/* Tabs descend FROM the top edge, so they're square across the top and rounded
   along the bottom, with no top border to read as attached to the edge. */
/* Section-switcher icons are OFF. The two labels ("Full & Part-Time Jobs" and
   "Contract/Project Marketplace") already say what each half is, and the
   glyphs were competing with them for room in a bar that is tight at mid
   widths.

   Done as one rule rather than by deleting the markup: the two headers carry
   NINE copies of this svg between them, spread across the solo/locked/live
   conditional variants, and stripping them one by one is a lot of surface to
   get wrong for a purely visual change. The sizing rules further down are left
   alone -- they set width/height only, so they cannot override this. Delete
   this rule to bring the icons back. */
.m-sectab-ico { display: none; }

.m-sectab {
  display: inline-flex;
  align-items: center;
  /* Equal-width pair. "Contract/Project Marketplace" is the longer label, so the
     min-width is sized to clear it and the shorter "Full & Part-Time Jobs" grows
     to match rather than sitting as a stubby chip beside it. Centered text, and
     min-width (not width) so neither tab can ever clip its own label. */
  justify-content: center;
  min-width: 250px;
  pointer-events: auto;
  padding: 0 18px;
  border: 1px solid #BFDBFE;
  border-top: 0;
  border-radius: 0 0 3px 3px;
  font-size: .8rem;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
  /* Inactive: the reverse of the active fill -- light ground, blue type. Same
     135deg ramp as the active tab but barely there (white -> blue-50), just
     enough to keep it from reading as a flat white chip next to its filled
     sibling. Held in a var so the locked tab can restore it on hover without
     restating the ramp. */
  --sectab-fill: linear-gradient(135deg, #FFFFFF 0%, #EFF6FF 100%);
  background: var(--sectab-fill);
  color: #1E40AF;
}

/* Active + hover: the standard gradient-button fill (--btn-grad on :root in
   styles.css), so these tabs ride the same navy -> blue ramp as every other
   gradient control on the site instead of a one-off flat blue. */
.m-sectab:hover,
.m-sectab:focus-visible,
.m-sectab--active {
  background: var(--btn-grad);
  border-color: #1E40AF;
  color: #fff;
}

/* Hover lifts to the lighter end of the same ramp -- including on the tab
   that's already active, so it still answers to the pointer. Comes after the
   rule above so it wins for the hover case. NOTE: gradients don't interpolate,
   so this swaps rather than fades; that matches the site's other gradient
   buttons. */
.m-sectab:hover,
.m-sectab:focus-visible {
  background: var(--btn-grad-hover);
}

/* Employer marketplace tab while the feature is under construction: still reads
   as a live tab (it pops the "coming soon" notice) rather than a dead control. */
.m-sectab--soon { cursor: pointer; }

/* Locked section tab (member Contract/Project Marketplace): the section isn't
   open to this account, so the tab is inert copy -- no link, no pointer, and no
   notice. It has to actively cancel the :hover/:focus rule above, which would
   otherwise light it up like a live tab; restoring --sectab-fill keeps it
   looking exactly like a dimmed inactive tab. Same specificity as that rule
   (0,2,0), so it wins on source order. */
.m-sectab--locked {
  cursor: default;
  opacity: .5;
}
.m-sectab--locked:hover,
.m-sectab--locked:focus-visible {
  background: var(--sectab-fill);
  border-color: #BFDBFE;
  color: #1E40AF;
}

/* Nav avatar 48 -> 40px wherever the strip is present, which takes the profile
   pill from 58px to 50px (avatar + 4px padding + 1px border, top and bottom).
   That's what lets the account cluster sit on the bar's true centerline: the
   tabs right-align to the content column and so hang directly over the tokens
   and part of the pill, and a 58px pill dead-centered in a 120px bar has only
   (120-58)/2 = 31px of headroom against a 32px strip -- it would cut in by 1px.
   At 50px the headroom is 35px and the pill clears. Scoped to .m-profile-btn so
   other .m-avatar uses in the app are untouched. */
.member-area-bg .m-profile-btn .m-avatar,
.employer-area-bg .m-profile-btn .m-avatar {
  width: 40px;
  height: 40px;
}

/* Bar-level chrome (logo far left, right-hand cluster far right): center these
   on the WHOLE bar, not just the row under the strip.

   .m-nav-inner's padding-top means align-items:center lands every item in the
   88px band beneath the strip, which leaves these sitting visibly low in a
   120px bar. Nudge them up by half the strip height to put them back on the
   bar's true centerline. A relative offset rather than a negative margin on
   purpose: it's a pure visual shift, so it can't perturb the flex sizing of
   the row, and the profile dropdown / popups still anchor off their buttons.

   Only the LOGO does this. It spans the bar's full left column and reads as
   chrome for the whole bar, so it wants the true centerline. The menu and the
   account cluster share the lower row instead (see below).

   Falls back to 0px, so areas that haven't opted into the strip (members,
   /admin42 -- no --m-secnav-h) get no shift at all. */
.m-nav-logo {
  position: relative;
  top: calc(var(--m-secnav-h, 0px) / -2);
}

/* Menu + account cluster (envelope / tokens / profile) share one line, nudged
   DOWN from where align-items:center puts them so they sit clear of the tabs
   above and read as their own row rather than crowding the strip.

   The two carry DIFFERENT divisors on purpose, and it is not a mistake:
   align-items:center already puts them on a common centerline, but the profile
   pill is 50px tall against the menu tab's ~35px, so its extra bulk hangs lower
   and the pair reads as misaligned even when it is geometrically correct. The
   cluster therefore takes half the menu's nudge (4px vs 8px) as an optical
   correction. Raise its divisor to lift it further; drop to /4 to put them back
   on a true shared centerline.

   Both stay keyed to --m-secnav-h so they self-cancel on mobile and in /admin42
   -- a plain `-4px` here would wrongly shift the cluster on bars with no strip. */
.m-nav-tabs {
  position: relative;
  top: calc(var(--m-secnav-h, 0px) / 4);
}
.m-nav-right {
  position: relative;
  top: calc(var(--m-secnav-h, 0px) / 8);
}

/* ══════════════════════════════════════════════════════════════════════════
   TWO-LEVEL NAV -- member area + employer area

   Row 1: logo | section switcher (segmented control) | account cluster
   Row 2: the current section's own menu, centered, on a full-width hairline

   Replaces the single-row bar with the switcher hanging off its top edge. The
   rules above (--m-secnav-h, .m-secnav's absolute strip, the optical nudges on
   .m-nav-tabs / .m-nav-right) are what this overrides; they still apply to
   /admin42, which carries .members-body but NEITHER area class and so keeps the
   original one-row bar. That's also why every selector here is doubled rather
   than written once against .members-body.

   Both rows live in ONE flex container: the tab list is pushed last and given a
   full-width basis so it WRAPS onto a second line. No markup restructuring, so
   every id, popup anchor and active-tab branch stays where it was.

   --m-nav-h must remain the TOTAL bar height: .m-main feeds it to padding-top,
   so the page content clears the nav. Change a row height, change the total.
   Desktop only -- below 921px the hamburger takes over and the bar is one row.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 921px) {
  .member-area-bg,
  .employer-area-bg {
    --m-secnav-h: 0px;   /* the hanging strip band is retired */
    --m-row1-h: 96px;
    --m-row2-h: 60px;
    --m-nav-h: 156px;    /* row1 + row2 -- keep in step with the two above */
  }

  /* The bar's own bottom border would draw a 1px line straight under the active
     tab's underline. The shadow still separates the bar from the page. */
  .member-area-bg .m-nav,
  .employer-area-bg .m-nav { border-bottom: 0; }

  .member-area-bg .m-nav-inner,
  .employer-area-bg .m-nav-inner {
    height: auto;
    flex-wrap: wrap;
    align-items: center;
  }

  /* Row 1. Logo and account cluster are flex:1 1 0 columns, so the switcher
     between them lands on the bar's true center. The logo sets the row height. */
  .member-area-bg .m-nav-logo,
  .employer-area-bg .m-nav-logo {
    order: 1;
    display: flex;
    align-items: center;
    min-height: var(--m-row1-h);
  }
  .member-area-bg .m-secnav,
  .employer-area-bg .m-secnav { order: 2; }
  .member-area-bg .m-nav-right,
  .employer-area-bg .m-nav-right { order: 3; top: 0; }
  .member-area-bg .m-nav-tabs,
  .employer-area-bg .m-nav-tabs { order: 4; }

  /* -- Row 1 center: section switcher as a segmented control ----------------
     Was an absolutely-positioned strip reproducing the content column's box;
     now an in-flow pair of joined buttons. Each wears the color of the section
     it points AT, so the pair reads as two brands. */
  .member-area-bg .m-secnav,
  .employer-area-bg .m-secnav {
    position: static;
    flex: 0 0 auto;
    max-width: none;
    height: auto;
    margin: 0;
    padding: 0;
    gap: 0;
    pointer-events: auto;
    border-radius: 10px;
    box-shadow: 0 1px 2px rgba(17, 24, 39, .06);
  }
  .member-area-bg .m-secnav .m-sectab,
  .employer-area-bg .m-secnav .m-sectab {
    height: 44px;
    min-width: 0;          /* cancels the equal-width min above */
    padding: 0 18px;
    gap: 10px;             /* icon -> label */
    border: 1px solid #E5E7EB;
    border-radius: 0;
    background: #fff;
    color: #374151;
    font-size: .95rem;
    font-weight: 600;
  }
  /* Joined ends: round the outside corners, collapse the shared edge. */
  .member-area-bg .m-secnav > :first-child,
  .employer-area-bg .m-secnav > :first-child { border-radius: 10px 0 0 10px; }
  .member-area-bg .m-secnav > :last-child,
  .employer-area-bg .m-secnav > :last-child { border-radius: 0 10px 10px 0; margin-left: -1px; }
  .m-sectab-ico { width: 20px; height: 20px; flex-shrink: 0; }

  /* Un-selected: plain white chip, gray type -- in BOTH sections. */
  .member-area-bg .m-secnav .m-sectab:not(.m-sectab--active),
  .employer-area-bg .m-secnav .m-sectab:not(.m-sectab--active) {
    background: #fff;
    border-color: #E5E7EB;
    color: #374151;
  }
  .member-area-bg .m-secnav .m-sectab:not(.m-sectab--active):hover,
  .member-area-bg .m-secnav .m-sectab:not(.m-sectab--active):focus-visible,
  .employer-area-bg .m-secnav .m-sectab:not(.m-sectab--active):hover,
  .employer-area-bg .m-secnav .m-sectab:not(.m-sectab--active):focus-visible {
    background: #F9FAFB;
    border-color: #D1D5DB;
    color: #111827;
  }
  /* Employer-only: darken the switcher's resting frame a step (#E5E7EB ->
     #D1D5DB) and drop the container's soft shadow. Scoped to .employer-area-bg
     so the member switcher keeps its lighter frame + shadow. The :not(--active)
     rule (0,4,0) needs its own override to beat the earlier same-specificity
     rule above; the active halves keep their own colored borders. */
  .employer-area-bg .m-secnav { box-shadow: none; }
  .employer-area-bg .m-secnav .m-sectab { border-color: #D1D5DB; }
  .employer-area-bg .m-secnav .m-sectab:not(.m-sectab--active) { border-color: #D1D5DB; }
  /* Selected: gradient fill in that section's color -- the blue ramp for Jobs,
     its green counterpart for Contract/Project. These override the generic
     .m-sectab--active rule earlier in the file (which is blue-gradient for both,
     regardless of section), so each half keeps its own identity. Hover lifts to
     the lighter end of the same ramp, matching the buttons elsewhere. */
  .member-area-bg .m-secnav .m-sectab--jb.m-sectab--active,
  .employer-area-bg .m-secnav .m-sectab--jb.m-sectab--active {
    background: var(--btn-grad); border-color: #1E40AF; color: #fff;
  }
  .member-area-bg .m-secnav .m-sectab--jb.m-sectab--active:hover,
  .member-area-bg .m-secnav .m-sectab--jb.m-sectab--active:focus-visible,
  .employer-area-bg .m-secnav .m-sectab--jb.m-sectab--active:hover,
  .employer-area-bg .m-secnav .m-sectab--jb.m-sectab--active:focus-visible {
    background: var(--btn-grad-hover);
  }

  .member-area-bg .m-secnav .m-sectab--cx.m-sectab--active,
  .employer-area-bg .m-secnav .m-sectab--cx.m-sectab--active {
    background: var(--btn-grad-cx); border-color: #15803D; color: #fff;
  }
  .member-area-bg .m-secnav .m-sectab--cx.m-sectab--active:hover,
  .member-area-bg .m-secnav .m-sectab--cx.m-sectab--active:focus-visible,
  .employer-area-bg .m-secnav .m-sectab--cx.m-sectab--active:focus-visible,
  .employer-area-bg .m-secnav .m-sectab--cx.m-sectab--active:hover {
    background: var(--btn-grad-cx-hover);
  }
  /* Locked (member Connect, not open to this account): inert, dimmed. */
  .member-area-bg .m-secnav .m-sectab--locked,
  .employer-area-bg .m-secnav .m-sectab--locked { opacity: .55; cursor: default; }

  /* -- Row 2: the section's own menu ----------------------------------------
     The flex basis is what wraps it onto its own line, and it must include BOTH
     gutters -- a plain 100% ends at the content box, leaving the hairline a
     gutter short of the bar's right edge. */
  .member-area-bg .m-nav-tabs,
  .employer-area-bg .m-nav-tabs {
    flex: 0 0 calc(100% + 56px);
    position: static;
    top: auto;
    display: flex;
    justify-content: center;
    align-items: stretch;
    gap: 26px;
    height: var(--m-row2-h);
    border-top: 1px solid #E5E7EB;
    margin: 0 -28px;
    padding: 0 28px;
  }
  .member-area-bg .m-nav-tabs li,
  .employer-area-bg .m-nav-tabs li { display: flex; }
  .member-area-bg .m-nav-tabs .m-tab,
  .employer-area-bg .m-nav-tabs .m-tab {
    display: inline-flex;
    align-items: center;
    height: 100%;
    margin: 0;
    padding: 0 18px;
    border: 0;
    border-radius: 0;
    background: none;
    color: #4B5563;
    font-weight: 600;
  }
  .member-area-bg .m-nav-tabs .m-tab:hover,
  .employer-area-bg .m-nav-tabs .m-tab:hover {
    background: #F9FAFB;
    color: #111827;
  }
  /* Active: tinted panel + a thick underline. inset shadow rather than a border,
     so the tab's height doesn't change between states. */
  .member-area-bg .m-nav-tabs .m-tab.active,
  .employer-area-bg .m-nav-tabs .m-tab.active {
    background: #EFF6FF;
    color: #1E40AF;
    box-shadow: inset 0 -4px 0 #1E40AF;
  }
}

/* The gutters widen at 1201px (28 -> 52), so the row-2 bleed follows: margins,
   padding AND the flex basis all move together. */
@media (min-width: 1201px) {
  .member-area-bg .m-nav-tabs,
  .employer-area-bg .m-nav-tabs {
    flex: 0 0 calc(100% + 104px);
    margin: 0 -52px;
    padding: 0 52px;
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   TWO-LEVEL NAV -- narrow-desktop behaviour (921px and up)

   WHY THIS EXISTS. Row 2 (the menu) only reaches its own line BECAUSE
   .m-nav-inner is flex-wrap:wrap -- the tab list carries a >100% basis and is
   pushed onto the next line. The wrap is therefore load-bearing, and it cuts
   both ways: the moment ROW 1 (logo | switcher | account cluster) overflows
   its line, the account cluster wraps too and the bar silently becomes three
   rows tall. --m-nav-h stays 156px, .m-main feeds that to padding-top, and the
   fixed bar then sits ON TOP of the page content instead of above it. That is
   the overlap this block prevents; the height sync in members_footer_inc.asp
   is the second line of defence if anything ever wraps anyway.

   Row 1 at full size wants roughly: 104px gutters + 393px wordmark + ~480px
   switcher + ~395px cluster (Contract mode, invitations + tokens pill) = about
   1370px. So it does NOT fit a 1200px bar, and it does not even fit at 1321px,
   where the full wordmark swaps back in. Two things fix it:

     1. Everything scales CONTINUOUSLY (clamp on the switcher + wordmark)
        rather than in steps. Step-wise tightening is what left the gaps that
        broke this -- each band fit at its top edge and overflowed at its floor.
     2. The logo column is made genuinely elastic, so it absorbs whatever slack
        is left. Its wordmark is decoration; the switcher and the account
        cluster are controls, so the logo is the right thing to give.

   With those, row 1's true minimum is switcher + cluster + gutters (~730px at
   the 921px floor), which clears the collapse point with room to spare. */
@media (min-width: 921px) {
  /* Elastic logo column. The base rules pin the wordmark at height:48px /
     flex-shrink:0 / max-width:none, which is what made the column rigid: a
     fixed height on an 8.2:1 (or 4.8:1) image IS a fixed width. Here it scales
     off width instead, with max-height as the ceiling, so it can never be the
     thing that overflows the line. */
     The logo is the ONLY elastic thing in row 1, and that is the whole
     strategy: the row must never wrap between 921px and the hamburger, because
     --m-nav-h is a fixed constant and a third row puts the bar on top of the
     page. So the switcher and the account cluster hold their shape, and the
     wordmark absorbs every pixel of shortfall. It can shrink to nothing before
     anything else gives, which is the correct trade -- a small logo is a
     cosmetic loss, a wrapped bar is a broken page. */
  .member-area-bg .m-nav-logo,
  .employer-area-bg .m-nav-logo     { min-width: 0; }
  .member-area-bg .m-nav-logo-pic,
  .employer-area-bg .m-nav-logo-pic { min-width: 0; flex-shrink: 1; }
  .member-area-bg .m-nav-logo-img,
  .employer-area-bg .m-nav-logo-img {
    height: auto;
    width: auto;
    max-width: 100%;
    /* 40px at 921px -> 48px at 1440px and up. */
    max-height: clamp(40px, 1.54vw + 25.8px, 48px);
    flex-shrink: 1;
  }

  /* Account cluster keeps flex:1 1 0 -- that equal-column split with the logo
     is what centers the switcher between them, and min-width:auto already
     floors the column at its content width, so it is never the thing that gets
     crushed. What it does NOT keep is the `right` offset from the gutter block
     above: that rule slides the cluster 8-16px LEFT for optical breathing room,
     and on a tight bar it slides straight over the switcher. The two-level bar
     has its own spacing and does not need the nudge.

     (Once the logo has shrunk to nothing the equal split gives out and the
     switcher drifts left of true center. That is the intended degradation --
     the alternative is the wrap.) */
  .member-area-bg .m-nav-right,
  .employer-area-bg .m-nav-right { right: 0; }

  /* Clearance around the section switcher. The cluster's own items sit on a
     tight gap now, and left to itself the whole cluster slides right up against
     the Contract/Project Marketplace half of the switcher once the logo has
     given up its slack -- the two read as one run of controls.

     Done as a column-gap on the ROW, not as a margin on either neighbour. A
     margin sits on one side of the switcher and pushes it off the bar's true
     center by half its width; a row gap applies equally on both sides of it, so
     the switcher stays centered between the logo and the cluster exactly as
     before. row-gap stays 0 -- the tab list wraps to line 2 through this same
     container, and a row gap there would open a hole between the two rows. */
  .member-area-bg .m-nav-inner,
  .employer-area-bg .m-nav-inner {
    column-gap: clamp(16px, 2.3vw - 5px, 28px);
    row-gap: 0;
  }

  /* Switcher: same segmented control, sized off the viewport. Values are the
     existing desktop ones at 1440px and up, tightened smoothly below that.
     This is the other half of the no-wrap guarantee -- at full size the pair is
     ~480px, which is most of a 921px bar on its own. */
  .member-area-bg .m-secnav .m-sectab,
  .employer-area-bg .m-secnav .m-sectab {
    height:      clamp(40px,  0.77vw + 32.9px,  44px);
    padding:  0  clamp(10px,  1.54vw -  4.2px,  18px);
    gap:         clamp(6px,   0.77vw -  1.1px,  10px);
    font-size:   clamp(.8rem, 0.46vw +  .54rem, .95rem);
  }
  .member-area-bg .m-secnav .m-sectab-ico,
  .employer-area-bg .m-secnav .m-sectab-ico {
    width:  clamp(17px, 0.58vw + 11.7px, 20px);
    height: clamp(17px, 0.58vw + 11.7px, 20px);
  }
}

/* Below 1200px the bar is tight enough that the token pills drop their labels
   and keep the coin + count -- "Apply Tokens" on the member bar, "Job Tokens"
   on the employer one. That is ~85-95px back on the widest configuration each
   bar has. The count is the number that matters either way, and the member
   pill's info-pin copy still explains what it is. Last resort before the logo
   starts eating the shortfall. */
@media (min-width: 921px) and (max-width: 1200px) {
  .member-area-bg   .m-app-tokens-word  { display: none; }
  .employer-area-bg .m-nav-tokens-label { display: none; }
}

/* Right cluster. Mirrors .m-nav-logo's flex so the two side columns stay equal
   and the tabs sit dead center; justify-content pins its contents to the right
   edge (margin-left:auto would fight the equal-column split). */
.m-nav-right {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  margin-left: 0;
  flex: 1 1 0;
}

/* ── Member account cluster: ONE spacing scale ─────────────────────────────
   The member bar's right-hand cluster (invitations ticket, Apply Tokens pill,
   envelope, profile) had accumulated four different spacing mechanisms, each
   added when a different item led the row:

     .m-nav-right          gap: 10px
     .m-app-tokens-wrap    margin-right: 54px
     .m-nav-inv + .m-app-tokens-wrap   margin-left: 40px
     .m-nav-inv + .m-nav-msg           margin-left: 14px, margin-right: 24px
     .m-nav-msg            transform: translate(-30px, 2px)

   They compose differently in every configuration -- Jobs vs Contract mode,
   with invitations vs without -- so the real gaps landed anywhere from 24px to
   40px, and none of them were the 10px the flex gap advertises. The -30px pull
   is the worst of them: a transform does not affect layout, so it opens a 30px
   VISUAL hole on the envelope's right that no amount of gap tuning can see.

   All of it is replaced by a single flex gap that scales with the viewport:
   14px at the 921px collapse point, 24px at 1440px and up. That fixes both
   complaints at once -- the resting spacing tightens, and it keeps tightening
   as the bar narrows instead of holding fixed margins while everything else
   shrinks around them.

   This pair is the ONLY knob for cluster spacing now. Tune the two numbers
   together and everything follows; the same expression is repeated once more
   below for the employer Jobs bar, so change both or they drift apart.

   Scoped to .member-area-bg: the employer cluster is a different set of items
   on its own (already deliberate) 40px rhythm. The vertical translate(0, 2px)
   nudges are kept -- those are optical alignment, not spacing. */
.member-area-bg .m-nav-right { gap: clamp(14px, 1.93vw - 3.75px, 24px); }
.member-area-bg .m-nav-msg,
.member-area-bg .m-nav-inv + .m-nav-msg {
  transform: translate(0, 2px);
  margin-left: 0;
  margin-right: 0;
}
.member-area-bg .m-app-tokens-wrap,
.member-area-bg .m-nav-inv + .m-app-tokens-wrap {
  margin-left: 0;
  margin-right: 0;
}

/* Employer bar, Full & Part-Time (Jobs) section only: same scale as the member
   cluster above. Its spacing was already clean -- one 40px flex gap in
   employers_header_inc.asp, with the shared -30px envelope pull cancelled --
   but 40px is a lot between three items and it was fixed, so the cluster read
   as increasingly stranded as the bar narrowed. Nothing else needs undoing
   here: the invitations adjacency rules don't match on this bar (the ticket is
   followed by the tokens pill, never the envelope).

   :not(.emp-connect-bg) is the section gate -- employers_header_inc.asp adds
   that class to <body> in the Connect section, which keeps its own 40px
   rhythm. (0,3,0) so it outranks both the inline 40px rule and the 22px
   narrow-band override, wherever they sit in source order. */
.employer-area-bg:not(.emp-connect-bg) .m-nav-right {
  gap: clamp(14px, 1.93vw - 3.75px, 24px);
}

/* Profile button + dropdown */
.m-profile-wrap { position: relative; }

/* Tiny effective-mode pill, pinned to the top-right corner of the profile
   dropdown so it overlaps the button without affecting nav layout. Tier
   colors: Free = neutral gray, Plus = brand purple, Premium = gold. */
.m-mode-pill {
  position: absolute;
  top: -6px;
  right: -4px;
  z-index: 3;
  pointer-events: none;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: .58rem;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  line-height: 1.5;
  border: 1px solid transparent;
  box-shadow: 0 1px 3px rgba(0,0,0,.15);
  white-space: nowrap;
}
/* Neutral chip: like the profile pill it overlaps, the package pill does NOT
   follow the section color scheme (blue in Jobs, green in Connect). Light gray
   fill + dark gray text and outline keep it section-agnostic. All three tiers
   share one look -- the label text is what distinguishes Basic / Plus /
   Premium, not the color. */
.m-mode-pill--free,
.m-mode-pill--plus,
.m-mode-pill--premium { background: #F9FAFB; color: #374151; border-color: #4B5563; }

/* "Premium" is the widest label, so nudge it further right to keep it from
   overhanging the avatar's left edge. */
.m-mode-pill--premium { right: -12px; }

.m-profile-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 16px 4px 8px;
  /* Neutral chip: the profile pill deliberately does NOT follow the section
     color scheme (blue in Jobs, green in Connect). Light gray fill + a dark
     gray outline keep it section-agnostic. Hover deepens both a step so the
     button still gives feedback. */
  background: #F9FAFB;
  border: 1px solid #4B5563;
  border-radius: 40px;
  cursor: pointer;
  transition: var(--transition);
}
.m-profile-btn:hover {
  border-color: #374151;
  background: #F3F4F6;
}

.m-avatar {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  border: 1px solid #E5E7EB;
  flex-shrink: 0;
}

.m-avatar--initials {
  background: var(--primary);
  color: #fff;
  font-size: .85rem;
  font-weight: 700;
  letter-spacing: .03em;
  display: flex;
  align-items: center;
  justify-content: center;
  border-color: #E5E7EB;
}

.m-caret {
  color: var(--text-muted);
  transition: transform var(--transition);
  flex-shrink: 0;
  margin-left: 10px;
}
.m-profile-wrap.open .m-caret { transform: rotate(180deg); }

.m-profile-menu {
  display: none;
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 210px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  overflow: hidden;
  z-index: 300;
}
.m-profile-wrap.open .m-profile-menu { display: block; }

.m-profile-menu-info {
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.m-profile-menu-info strong { font-size: .875rem; color: var(--text-dark); }
.m-profile-menu-info span   { font-size: .78rem;  color: var(--text-muted); }

.m-profile-menu-divider { height: 1px; background: var(--border); }

.m-profile-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  font-size: .875rem;
  color: var(--text-mid);
  font-weight: 500;
  transition: var(--transition);
}
.m-profile-menu-item:hover { background: var(--bg-alt); color: var(--primary); }

/* Hamburger (mobile only) */
.m-hamburger {
  display: none;
  flex-direction: column;
  justify-content: space-between;
  width: 22px;
  height: 16px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
}
.m-hamburger span {
  display: block;
  height: 2px;
  background: var(--text-mid);
  border-radius: 2px;
  transition: var(--transition);
}
.m-hamburger.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.m-hamburger.open span:nth-child(2) { opacity: 0; }
.m-hamburger.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* Mobile nav drawer */
.m-mobile-nav {
  display: none;
  flex-direction: column;
  border-top: 1px solid var(--border);
  background: var(--white);
}
.m-mobile-nav.open { display: flex; }

.m-mobile-tab {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 24px;
  font-size: .9rem;
  font-weight: 500;
  color: var(--text-mid);
  border-bottom: 1px solid var(--border);
  transition: var(--transition);
}
.m-mobile-tab:last-child { border-bottom: none; }
.m-mobile-tab:hover,
.m-mobile-tab.active { color: var(--primary); background: var(--primary-light); }

/* Inert drawer tab. Must come after the :hover rule above and carry both
   classes: .m-tab--static alone ties on specificity and loses on source order. */
.m-mobile-tab.m-tab--static,
.m-mobile-tab.m-tab--static:hover { cursor: default; background: none; color: var(--text-muted); }

/* Drawer counterpart of .m-sectab--soon: the Contract/Project Marketplace tab
   while that section is under construction. It is a span, not a link, so it
   needs the pointer back, but it keeps the normal tab color and hover wash
   because it really does do something (it pops the "coming soon" notice). */
.m-mobile-tab.m-tab--soon { cursor: pointer; }

/* ── AI Settings page (matching_settings.asp) ── */
.ms-hero,
.pf-hero {
  /* Brand blue (--card-header-grad on :root in styles.css), replacing the
     legacy purple ramp. Shared by the member profile / settings heroes and
     the employer profile / account-settings heroes. */
  background: var(--card-header-grad);
  border-radius: var(--radius) var(--radius) 0 0;
  padding: 36px 40px;
  margin-bottom: 0px;
  min-height: 170px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
}

/* Tabbed account pages (member + employer settings, profile, and the billing
   tab that lives inside profile) read as a self-contained panel: a tab bar
   follows after a 16-24px gap, so nothing butts up underneath. They carry the
   same radius on the bottom as on the top.

   A modifier rather than a change to the base .ms-hero/.pf-hero rule, which is
   still shared by search-jobs and matching_settings. Those are untouched --
   add this class to either if their hero should float too.
   (jobcriteria_hold.asp was listed here until 2026-08-04; it was deleted as
   unreferenced dead code during the PCI sweep.) */
.ms-hero--rounded,
.pf-hero--rounded {
  border-radius: var(--radius);
}

.ms-hero-inner {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 20px;
}
.ms-hero-icon {
  width: 48px; height: 48px;
  object-fit: contain; flex-shrink: 0;
  filter: brightness(0) invert(1);
  margin-top: 3px;
}
.ms-hero-title {
  font-size: clamp(1.3rem, 2.5vw, 1.75rem);
  font-weight: 800;
  color: #fff;
  margin: 0 0 10px;
  letter-spacing: -.02em;
}
.ms-hero-desc {
  font-size: .925rem;
  color: rgba(255,255,255,.82);
  margin: 0;
  line-height: 1.7;
}
.ms-controls     { display: grid; grid-template-columns: 1fr 1fr; gap: 40px; margin-bottom: 8px; }

@media (max-width: 700px) {
  .ms-controls   { grid-template-columns: 1fr; gap: 28px; }
}

@media (max-width: 600px) {
  .ms-hero        { padding: 24px 20px; }
  .ms-hero-inner  { gap: 14px; }
}

/* ── AI Matching Card (profile page, above tabs) ── */
.pf-ai-card {
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: 36px;
  box-shadow: 0 4px 24px rgba(30, 64, 175, .14);
  border: 1px solid rgba(30, 64, 175, .22);
}

.pf-ai-header {
  display: flex;
  align-items: flex-start;
  gap: 18px;
  padding: 28px 32px;
  background: var(--card-header-grad);
}

.pf-ai-icon {
  width: 40px;
  height: 40px;
  object-fit: contain;
  flex-shrink: 0;
  filter: brightness(0) invert(1);
  margin-top: 2px;
}

.pf-ai-header-text h2 {
  font-size: 1.2rem;
  font-weight: 800;
  color: #fff;
  margin: 0 0 7px;
  letter-spacing: -.01em;
}

.pf-ai-header-text p {
  font-size: .875rem;
  color: rgba(255, 255, 255, .82);
  margin: 0;
  line-height: 1.65;
  max-width: 680px;
}

.pf-ai-body {
  background: var(--white);
  padding: 28px 32px 24px;
}

.pf-ai-controls {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 36px;
  margin-bottom: 8px;
}

.pf-ai-col-title { font-size: .95rem; font-weight: 700; color: var(--text-dark); margin: 0 0 4px; }
.pf-ai-col-desc  { font-size: .84rem; color: var(--text-muted); margin: 0 0 4px; line-height: 1.55; }

@media (max-width: 680px) {
  .pf-ai-controls { grid-template-columns: 1fr; gap: 24px; }
  .pf-ai-header   { padding: 22px 20px; gap: 14px; }
  .pf-ai-body     { padding: 20px 20px 18px; }
}

/* ── Profile identity hero ── */
.pf-id-card {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 24px;
}
.pf-avatar-wrap {
  position: relative;
  flex-shrink: 0;
  width: 78px;
  height: 78px;
}

.pf-id-avatar {
  width: 78px;
  height: 78px;
  border-radius: 50%;
  background: rgba(255,255,255,.18);
  border: 2px solid rgba(255,255,255,.35);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.8rem;
  font-weight: 700;
  color: #fff;
  letter-spacing: .02em;
  overflow: hidden;
}

.pf-avatar-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
  display: block;
}

.pf-avatar-edit-btn {
  position: absolute;
  bottom: 0;
  right: 0;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: #fff;
  border: 1.5px solid rgba(255,255,255,.6);
  color: #1E40AF;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 1px 4px rgba(0,0,0,.25);
  transition: background .15s, transform .15s;
  padding: 0;
}
.pf-avatar-edit-btn:hover {
  background: #EFF6FF;
  transform: scale(1.1);
}

/* -- Save-error modal (settings.asp) --
   The "Save Settings" button posts every panel, so a validation failure can
   come from a tab the member is not looking at. The small red line beside the
   button was missed entirely in that case, and the save looked like it had
   silently done nothing. Errors now come up in this popup; the inline line
   stays as the quiet confirmation channel for a successful save.
   Esc or the buttons close it. Clicking the backdrop deliberately does not. */
.pf-err-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,.5);
  z-index: 3200;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.pf-err-overlay--open { display: flex; }

.pf-err-modal {
  background: #fff;
  border-radius: 12px;
  box-shadow: 0 20px 60px rgba(0,0,0,.2);
  width: 100%;
  max-width: 440px;
  overflow: hidden;
}
.pf-err-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 16px 22px;
  background: #FEF2F2;
  border-bottom: 1px solid #FECACA;
}
.pf-err-icon {
  flex: 0 0 auto;
  display: inline-flex;
  color: #B91C1C;
}
.pf-err-title {
  font-size: 1rem;
  font-weight: 700;
  color: #991B1B;
}
.pf-err-body {
  padding: 20px 22px;
  font-size: .98rem;
  line-height: 1.55;
  color: #1F2937;
}
.pf-err-foot {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 0 22px 20px;
}

/* -- Photo modal -- */
.pf-photo-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,.5);
  z-index: 3000;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.pf-photo-overlay--open { display: flex; }

.pf-photo-modal {
  background: #fff;
  border-radius: 12px;
  box-shadow: 0 20px 60px rgba(0,0,0,.2);
  width: 100%;
  max-width: 380px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.pf-photo-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 22px 16px;
  background: var(--card-header-grad);
}

.pf-photo-title {
  font-size: 1rem;
  font-weight: 700;
  color: #fff;
}

.pf-photo-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  background: rgba(255,255,255,.15);
  border: none;
  border-radius: 6px;
  color: #fff;
  cursor: pointer;
  transition: background .15s;
}
.pf-photo-close:hover { background: rgba(255,255,255,.3); }

.pf-photo-body {
  padding: 24px 22px 16px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

.pf-photo-preview-wrap {
  display: flex;
  justify-content: center;
}

.pf-photo-preview {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  overflow: hidden;
  background: #EFF6FF;
  border: 3px solid #93C5FD;
  display: flex;
  align-items: center;
  justify-content: center;
}

.pf-photo-preview img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.pf-photo-preview-initials {
  font-size: 2rem;
  font-weight: 700;
  color: #1E40AF;
}

.pf-photo-actions {
  display: flex;
  gap: 10px;
  align-items: center;
}

.pf-photo-choose-btn,
.pf-photo-delete-btn {
  width: 130px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  box-sizing: border-box;
  line-height: 1;
}

.pf-photo-choose-btn {
  background: transparent;
  border: 1.5px solid #93C5FD;
  color: #1E40AF;
  cursor: pointer;
}
.pf-photo-choose-btn:hover {
  background: #EFF6FF;
  border-color: #1E40AF;
}

.pf-photo-delete-btn {
  background: transparent;
  border: 1.5px solid #fca5a5;
  color: #dc2626;
}
.pf-photo-delete-btn:hover {
  background: #fef2f2;
  border-color: #dc2626;
}

.pf-photo-hint {
  font-size: .75rem;
  color: var(--text-muted);
  margin: 0;
  text-align: center;
}

.pf-photo-fb {
  font-size: .82rem;
  min-height: 1.2em;
  text-align: center;
}
.pf-photo-fb--err { color: #dc2626; }

.pf-photo-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  padding: 14px 22px;
  border-top: 1px solid #e5e7eb;
  background: #f9fafb;
}
.pf-id-info {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.pf-id-name {
  font-size: 1.35rem;
  font-weight: 700;
  color: #fff;
  letter-spacing: -.01em;
}
.pf-id-email {
  font-size: .875rem;
  color: rgba(255,255,255,.75);
}
.pf-id-date-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  background: rgba(255,255,255,.1);
  border: 1px solid rgba(255,255,255,.18);
  border-radius: 8px;
  padding: 8px 14px;
  width: 140px;
  height: 68px;
  flex-shrink: 0;
  box-sizing: border-box;
  white-space: nowrap;
}
.pf-id-date-label {
  font-size: .7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: rgba(255,255,255,.55);
  text-align: center;
}
.pf-id-date-val {
  font-size: .85rem;
  font-weight: 600;
  color: #fff;
  text-align: center;
}
.pf-id-date-val--mode {
  font-size: .9rem;
  font-weight: 700;
  letter-spacing: .01em;
}
.pf-id-date-val--word {
  font-size: .9rem;
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
}
.pf-id-date-sub {
  font-size: .65rem;
  font-weight: 500;
  color: rgba(255,255,255,.7);
  text-align: center;
  letter-spacing: .02em;
  white-space: nowrap;
  margin-top: 2px;
}
/* Expired-plan accent: red label + value. The hero uses a purple
   gradient background, so a darker red-tint reads as a warning
   without clashing with the light-on-purple pattern. */
.pf-id-date-item--warn {
  background: rgba(220, 38, 38, .22);
  border-color: rgba(252, 165, 165, .4);
}
.pf-id-date-item--warn .pf-id-date-label,
.pf-id-date-item--warn .pf-id-date-val,
.pf-id-date-item--warn .pf-id-date-sub { color: #fecaca; }
.pf-id-badge-wrap {
  flex-shrink: 0;
  margin-left: auto;
}
.pf-id-badge {
  display: inline-block;
  padding: 7px 16px;
  border-radius: 999px;
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .01em;
}
.pf-id-badge--active {
  background: rgba(134,239,172,.18);
  color: #86EFAC;
  border: 1px solid rgba(134,239,172,.35);
}
.pf-id-badge--setup {
  background: rgba(255,255,255,.12);
  color: rgba(255,255,255,.8);
  border: 1px solid rgba(255,255,255,.25);
}

@media (max-width: 600px) {
  .pf-hero    { padding: 24px 20px; }
  .pf-id-card { flex-wrap: wrap; }
  .pf-id-date-item { flex: 1; }
  .pf-id-badge-wrap { width: 100%; }
}

/* ── Profile Page ── */
.pf-tab-bar       { display: flex; gap: 0; border-bottom: 2px solid var(--border);
                    margin-bottom: 28px; }
.pf-tab           { padding: 10px 22px; font-size: .9rem; font-weight: 600;
                    font-family: var(--font); background: none; border: none;
                    color: var(--text-muted); cursor: pointer;
                    border-bottom: 3px solid transparent; margin-bottom: -2px;
                    transition: color 0.15s, border-color 0.15s; }
.pf-tab:hover     { color: var(--primary); }
.pf-tab--active   { color: var(--primary); border-bottom-color: var(--primary); }

.pf-panel         { display: none; }
.pf-panel--active { display: block; }

.pf-section-title { font-size: 1rem; font-weight: 700; color: var(--text-dark); margin: 0 0 4px; }
.pf-section-desc  { font-size: .875rem; color: var(--text-muted); margin: 0 0 20px; }

/* ── Links & Resources (settings): one row per network, led by its logo ──
   The logo sits beside the label+input pair rather than above it, so the four
   rows read as a list of networks. align-items:center keeps the mark on the
   row's optical center; the icon can't shrink when the input takes the width. */
.pf-link-list { display: flex; flex-direction: column; gap: 18px;
                max-width: 640px; margin: 18px 0 26px; }
.pf-link-row  { display: flex; align-items: center; gap: 14px; }
.pf-link-ico  { width: 28px; height: 28px; flex: 0 0 28px; object-fit: contain; }
.pf-link-field { flex: 1 1 auto; min-width: 0; }
/* The label is a .pf-section-desc, whose 20px bottom margin would push the input
   away from it; inside a link row it's a caption, so tighten it right up. */
.pf-link-field .pf-section-desc { display: block; margin: 0 0 4px; }

.pf-readonly-row  { display: flex; flex-wrap: wrap; gap: 32px; margin-bottom: 20px;
                    padding: 14px 18px; background: var(--bg-alt);
                    border: 1px solid var(--border); border-radius: var(--radius-sm); }
.pf-readonly-item { display: flex; flex-direction: column; gap: 3px; }
.pf-readonly-label{ font-size: .72rem; font-weight: 700; color: var(--text-muted);
                    text-transform: uppercase; letter-spacing: .06em; }
.pf-readonly-val  { font-size: .9rem; color: var(--text-dark); font-weight: 500; }

.pf-opts          { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 4px; }
.pf-opt           { padding: 8px 20px; border: 1.5px solid var(--border);
                    border-radius: 40px; font-size: .875rem; font-weight: 500;
                    color: var(--text-mid); background: var(--white);
                    cursor: pointer; transition: var(--transition); font-family: var(--font); }
.pf-opt:hover     { border-color: var(--primary); color: var(--primary); background: var(--primary-light); }
.pf-opt--sel      { border-color: var(--primary); background: var(--primary); color: #fff; }
.pf-opt--sel:hover{ background: var(--primary-dark); border-color: var(--primary-dark); }

.pf-salary-wrap   { max-width: 580px; }

.pf-save-row      { display: flex; align-items: center; justify-content: flex-end;
                    gap: 14px; margin-top: 20px; padding-top: 16px;
                    border-top: 1px solid var(--border); }
.pf-feedback      { font-size: .85rem; font-weight: 500; min-width: 80px; text-align: right; }
.pf-feedback--ok  { color: var(--success); }
.pf-feedback--err { color: #DC2626; }

.pf-section--coming { opacity: .65; }
.pf-coming-soon   { font-size: .875rem; color: var(--text-muted); font-style: italic; margin: 8px 0 0; }

/* ── Hero expiry: expired state ── */
.pf-id-date-item--expired .pf-id-date-label { color: #fca5a5; }
.pf-id-date-item--expired .pf-id-date-val   { color: #fca5a5; }

/* ── Billing expiry block ── */
.pf-expiry-block {
  display: inline-flex;
  flex-direction: column;
  gap: 4px;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px 24px;
  margin-bottom: 20px;
}
.pf-expiry-block--expired {
  background: #fef2f2;
  border-color: #fecaca;
}
.pf-expiry-label {
  font-size: .72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--text-muted);
}
.pf-expiry-date {
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--text-dark);
}
.pf-expiry-block--expired .pf-expiry-date { color: #b91c1c; }
.pf-expiry-note {
  font-size: .8rem;
  font-weight: 500;
  margin-top: 4px;
}
.pf-expiry-note--soon    { color: #b45309; }
.pf-expiry-note--expired { color: #b91c1c; }

.pf-renew-link {
  display: inline-block;
  margin-top: 2px;
  font-size: .68rem;
  font-weight: 600;
  color: rgba(255,255,255,.75);
  text-decoration: underline;
  letter-spacing: .02em;
  line-height: 1;
}
.pf-renew-link:hover { color: #fff; }

/* ── Main layout ── */
.m-main {
  /* --m-nav-h-measured is written by the height sync in members_footer_inc.asp
     from the bar's ACTUAL rendered height; --m-nav-h is the per-breakpoint
     constant it corrects, and the fallback when JS hasn't run.

     The two must stay SEPARATE variables. Below 921px .m-nav-inner sizes itself
     off --m-nav-h, so writing the measurement back into that same variable made
     the bar feed its own input: each observed height set a new height, which the
     observer saw, which set a new height. The page grew a pixel at a time and
     never stopped. Measuring one variable and consuming another breaks the
     cycle -- nothing the sync writes can change what it measures. */
  padding-top: var(--m-nav-h-measured, var(--m-nav-h));
  min-height: 100vh;
}

.m-content {
  max-width: var(--m-content-max);
  margin: 0 auto;
  padding: 40px var(--m-content-pad);
}

/* Page header */
.m-page-header { margin-bottom: 32px; }
/* Centered variant -- opt-in per page (Resume Tools, My Applications,
   Search Contracts, Settings). The default stays left-aligned for the
   other member/employer/admin pages that share this header. */
.m-page-header--center { text-align: center; }
.m-page-header h1 {
  font-size: clamp(1.4rem, 2.5vw, 1.9rem);
  margin-bottom: 4px;
}
.m-page-header p { font-size: .95rem; color: var(--text-muted); }

/* Stats grid */
.m-stats-hero {
  /* Brand blue dashboard panel (navy -> blue), identical on the member and
     employer dashboards. Matches the employer stats hero + --card-header-grad. */
  background: linear-gradient(135deg, #172554 0%, #1E40AF 100%);
  border-radius: var(--radius);
  padding: 36px 40px;
  margin-bottom: 28px;
  min-height: 170px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
}
.m-stats-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
  width: 100%;
}

.m-stat-card {
  background: rgba(255,255,255,.15);
  border: 1px solid rgba(255,255,255,.22);
  border-radius: var(--radius);
  padding: 22px 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 8px;
  min-height: 160px;
  transition: var(--transition);
}
.m-stat-card:hover { background: rgba(255,255,255,.22); border-color: rgba(255,255,255,.4); }

.m-stat-icon { font-size: 1.4rem; }
.m-stat-num  { font-size: 1.9rem; font-weight: 700; color: #fff; line-height: 1; }
.m-stat-num--word { font-size: 1.15rem; letter-spacing: .02em; text-transform: uppercase; }
/* Larger variant of .m-match-card__score for the dashboard stat tile that
   displays the member's average match rating. Renders as a hollow ring
   on the dark tile (no white center disc); the unfilled portion uses a
   translucent white so it reads against the dark backdrop. The ring is
   painted by a masked ::before pseudo so the parent's text content stays
   unmasked and remains legible inside the cutout. */
.m-stat-ring {
  flex: 0 0 60px !important;
  width: 60px !important;
  height: 60px !important;
  font-size: .82rem !important;
  font-weight: 700 !important;
  color: #ffffff !important;
  background: transparent !important;
  box-shadow: none !important;
}
.m-stat-ring::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background:
    conic-gradient(var(--score-color) calc(var(--pct) * 1%), rgba(255, 255, 255, .18) 0);
  -webkit-mask: radial-gradient(circle closest-side, transparent 68%, #000 69%);
          mask: radial-gradient(circle closest-side, transparent 68%, #000 69%);
}
.m-stat-label {
  font-size: .75rem;
  color: rgba(255,255,255,.7);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
}

/* Content sections */
.m-section {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  /* Sitewide raised-panel treatment -- matches the shadow on the
     /employers/applications.asp split panes so every .m-section reads
     as a card lifted off the page background. */
  box-shadow: 0 2px 6px rgba(15,23,42,.12);
  padding: 28px;
  margin-bottom: 24px;
}

.m-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 20px;
}
.m-section-header h2 { font-size: 1.1rem; font-weight: 700; display: flex; align-items: center; gap: 6px; flex-wrap: nowrap; }

/* Variant: header rendered as a flush colored strip across the top of
   the card. Pull the header out to the card edges via negative margins
   matching .m-section's padding, then re-pad the strip itself.
   overflow:hidden on the section clips the match-list (which uses
   negative bottom margin to sit flush with the bottom edge) to the
   section's border-radius so the bottom corners stay rounded. */
.m-section--strip-header {
  overflow: hidden;
}
.m-section--strip-header > .m-section-header {
  background: #6B7280;
  color: #fff;
  margin: -28px -28px 0;
  padding: 14px 28px;
  border-radius: var(--radius) var(--radius) 0 0;
}
/* Inside the dark strip, all child text/labels read on white. The info-pin
   is included: it defaults to the brand blue (#1E40AF in styles.css), which
   disappears against the strip. Same for the pins on the blue stats panel. */
.m-section--strip-header > .m-section-header h2,
.m-section--strip-header > .m-section-header .m-thresh-control,
.m-section--strip-header > .m-section-header .m-thresh-label,
.m-section--strip-header > .m-section-header .m-thresh-label strong,
.m-section--strip-header > .m-section-header .info-pin,
.m-stats-hero .info-pin {
  color: #fff;
}
/* Inline "X% Threshold" link inside the title strip routes to the AI Match
   Notifications tab on settings. Subtle dotted underline so it reads as a
   link without competing with the heading itself. */
.m-section--strip-header > .m-section-header .m-thresh-link {
  color: inherit;
  text-decoration: underline dotted rgba(255,255,255,0.55);
  text-underline-offset: 2px;
}
.m-section--strip-header > .m-section-header .m-thresh-link:hover {
  text-decoration: underline solid #fff;
}
/* "(AI-Score Matched)" subtitle treatment: smaller + muted gray on the
   dark strip so it reads as a parenthetical clarifier next to the bigger
   "Job Recommendations" title. */
.m-section--strip-header > .m-section-header .m-section-header__sub {
  font-size: .82rem;
  font-weight: 500;
  color: #D1D5DB;
}
/* Slider track gets a slightly lighter gray so the brand thumb still pops. */
.m-section--strip-header > .m-section-header .m-thresh-slider {
  background: #9CA3AF;
}
/* "Displaying X of Y" badge on the right end of the strip header so the
   user knows the slider is filtering a larger pool. */
.m-section-header__count {
  font-size: .85rem;
  color: #E5E7EB;
  white-space: nowrap;
}
/* Secondary line under "Displaying X of Y" -- only rendered when the
   employer has more applications than the dashboard's 300-row cap, so
   they know the list is showing the most recent slice rather than the
   whole pool. Sits right-aligned beneath the count, smaller + lighter. */
.m-section-header__cap-note {
  display: block;
  margin-top: 2px;
  font-size: .72rem;
  font-weight: 600;
  color: #C7D2FE;
  text-align: right;
}
/* Pulse dots: lighten so they remain visible on the dark strip. */
.m-section--strip-header > .m-section-header .m-dot-pulse span {
  background: #F3F4F6;
}
.m-section--strip-header > .m-section-header .m-dot-pulse--paused span {
  background: #D1D5DB;
}
@media (max-width: 480px) {
  .m-section--strip-header > .m-section-header {
    margin: -20px -16px 0;
    padding: 12px 16px;
  }
}

/* Left cluster inside the strip header: title sits next to the threshold
   slider so adjusting the cutoff feels like part of the section title.
   Pulse indicator stays on the right via the parent's space-between. */
.m-section-header__left {
  display: flex;
  align-items: center;
  gap: 32px;
  flex-wrap: wrap;
}

/* Inline threshold control (label + slider + saved-flash). */
.m-thresh-control {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-size: 1rem;
  color: #374151;
  white-space: nowrap;
}
.m-thresh-label strong {
  color: var(--primary);
  font-weight: 700;
  font-size: 1.05rem;
}
.m-thresh-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 160px;
  height: 6px;
  border-radius: 999px;
  background: #D1D5DB;
  outline: none;
  cursor: pointer;
}
.m-thresh-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--primary);
  border: 2px solid #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,.2);
  cursor: pointer;
}
.m-thresh-slider::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--primary);
  border: 2px solid #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,.2);
  cursor: pointer;
}
/* Show the focus ring only for keyboard navigation (:focus-visible) so a
   mouse drag/click doesn't leave the purple outline lingering on the
   slider until the user clicks away. */
.m-thresh-slider:focus { outline: none; }
.m-thresh-slider:focus-visible {
  outline: 2px solid rgba(124, 58, 237, .35);
  outline-offset: 2px;
}
.m-thresh-saved {
  color: #15803d;
  font-weight: 600;
  font-size: .9rem;
  opacity: 0;
  transition: opacity .2s;
  min-width: 50px;
}
.m-thresh-saved.show {
  opacity: 1;
}

@media (max-width: 600px) {
  .m-thresh-slider { width: 110px; }
  .m-thresh-control { gap: 8px; font-size: .92rem; }
  .m-thresh-label strong { font-size: .98rem; }
}

/* ------------------------------------------------------------------
   Dashboard AI match listing cards. One row per ai_match_ratings hit
   for this member; client-side filter on the threshold slider toggles
   visibility via [hidden]. Whole row is the link to job-detail.
------------------------------------------------------------------ */
.m-match-list {
  display: flex;
  flex-direction: column;
  gap: 0;
  overflow-y: auto;
  overflow-x: hidden;
  /* Pull the list to the card's outer edges so rows extend full-width.
     Negative margins offset the .m-section padding (28px desktop / 16px
     mobile via the breakpoint below). The -28px on the bottom also pulls
     the last card flush with the section's bottom rounded corner so we
     don't show a white strip below the final row. */
  margin: 0 -28px -28px;
}
@media (max-width: 480px) {
  .m-match-list { margin: 0 -16px -16px; }
}

/* Body wrapper for the AI matches card. Caps at viewport math so the
   section never pushes past the footer, but shrinks to content when
   fewer cards are rendered -- otherwise an empty white strip shows
   between the last card and the section's bottom edge. */
.m-match-section-body {
  max-height: min(calc(100vh - 540px), 380px);
  /* Floor height so the section doesn't bounce around as the threshold
     filter shows/hides cards (or as the empty / wait / no-above states
     swap in). Cards still size to content -- any extra space appears as
     a white gap below the last card on purpose. */
  min-height: 360px;
  display: flex;
  flex-direction: column;
}
.m-match-section-body > .m-empty-state {
  flex: 1 1 auto;
  /* min-height keeps the empty / wait / no-above states visible even when
     the section itself has no explicit height (we removed that earlier so
     the cards list could shrink-to-fit without leaving a white margin). */
  min-height: 380px;
  justify-content: center;
}
.m-match-section-body > .m-match-list {
  /* Size to card content, capped by the section's max-height. Without
     this the list would stretch to fill the section and leave a white
     gap below the last card when there are fewer cards than fit. */
  flex: 0 1 auto;
  min-height: 0;
}
.m-match-card {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  /* Left padding 28px aligns the score badge column with the strip
     header's "Recent AI Matched Listings" text. Right padding 36px
     pulls the salary/chip/posted/arrow cluster off the card edge. */
  padding: 12px 36px 12px 28px;
  /* Fixed row height pegged to the natural height of rows that include
     a location (those wrap meta to two lines: title + city/state + employer),
     so every card sits at that uniform height regardless of whether the
     individual row has a location. flex-shrink:0 prevents the parent flex
     column from compressing rows with less content (no city/state) more
     than rows with more content. */
  height: 77px;
  flex-shrink: 0;
  background: #fff;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transition: background .15s;
}
/* Tiny GF flag at the top-left of the row for partner_id=1 jobs. */
.m-match-card__partner-flag {
  display: inline-block;
  width: 14px;
  height: 14px;
  margin-left: 7px;
  vertical-align: -2px;
  opacity: 0.6;
  pointer-events: none;
}
/* Stripe alternation is driven by JS so hidden (filter-skipped) rows
   don't break the visual rhythm. JS toggles .is-alt on every other
   currently-visible card. */
.m-match-card.is-alt { background: #F8F9FB; }
/* Hover tint on the brand blue, not the legacy lavender. Light enough that
   the zebra stripe (.is-alt) still reads underneath it. */
.m-match-card:hover  { background: #EFF6FF; }
.m-match-card[hidden] { display: none; }

/* Hide / un-hide control: tiny circle pinned to the row's top-right. It
   lives inside the card <a>, so its JS handler stops the anchor navigating. */
/* Sits where the old carat arrow did: pinned to the row's right edge,
   vertically centered. z-index + its own click handler (which stops the
   event) keep this hit zone separate from the row-anchor navigation, so
   clicking the circle never opens the job. */
.m-match-card__hide {
  position: absolute;
  top: 50%;
  right: 32px;
  transform: translateY(-50%);
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: #858D9A;
  color: #fff;
  line-height: 1;
  cursor: pointer;
  opacity: .6;
  transition: opacity .15s, background .15s, color .15s;
}
/* Invisible hit halo: extends the clickable area ~9px past the visible circle
   so taps near the icon land on the button (which stops the event) instead of
   the row link -- a safer, more forgiving target without growing the circle. */
.m-match-card__hide::before {
  content: "";
  position: absolute;
  inset: -9px;
  border-radius: 50%;
}
.m-match-card:hover .m-match-card__hide { opacity: 1; }
.m-match-card__hide:hover { background: #4B5563; color: #fff; }
.m-match-card__hide:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  opacity: 1;
}
/* Restore (+) variant on already-hidden rows: brand-purple tint so it reads
   as a distinct "bring this back" affordance, and stays fully visible. */
.m-match-card__hide--restore {
  background: #E5E7EB;
  color: #9CA3AF;
  opacity: 1;
}
.m-match-card__hide--restore:hover { background: #D1D5DB; color: #6B7280; }

/* Faded look for member-hidden rows. Grayscale + reduced opacity is applied
   to every part EXCEPT the restore control, so the "+" stays crisp and
   clickable (a parent opacity would otherwise clamp the child's). */
.m-match-card--hidden > :not(.m-match-card__hide) {
  filter: grayscale(1);
  opacity: .55;
}
/* On hidden rows, push the normally-dark job title and salary to a light
   gray too so the whole row reads as muted/dismissed. */
.m-match-card--hidden .m-match-card__title,
.m-match-card--hidden .m-match-card__salary {
  color: #9CA3AF;
}

/* Thin section headers ("TODAY" / "YESTERDAY" / "MORE") sit between
   match-card rows and group them by matched-on calendar day. Same
   uppercase/letter-spaced typography as .mj-card-stat-label so it reads
   as a quiet divider rather than a competing heading. */
.m-match-list__hdr {
  padding: 6px 28px;
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: #6B7280;
  background: #F9FAFB;
  border-top: 1px solid #E5E7EB;
  border-bottom: 1px solid #E5E7EB;
}
/* The first header sits flush at the top of the list -- skip the
   redundant top border so it doesn't double up with the card surface. */
.m-match-list__hdr:first-child { border-top: 0; }
.m-match-list__hdr[hidden]     { display: none; }

/* Standalone notice shown when the member has hit their daily AI-match
   cap. Free + Plus only -- Premium never sees this. Lives OUTSIDE the AI
   Matched Listings card (sibling of .m-section), styled as a soft purple
   banner so it reads as its own callout rather than a card row. */
.m-match-cap-note {
  margin: -8px 0 24px;
  padding: 10px 20px;
  background: #EDE9FE;
  border: 1px solid #DDD6FE;
  border-radius: var(--radius);
  font-size: .85rem;
  color: #4B5563;
  line-height: 1.4;
  text-align: center;
}
.m-match-cap-note strong { color: #1F2937; }
.m-match-cap-note__cta {
  display: inline-block;
  margin-left: 8px;
  color: var(--primary);
  font-weight: 600;
  text-decoration: none;
}
.m-match-cap-note__cta:hover { text-decoration: underline; }

/* Applicant identity block sits in the cluster on the employer dashboard's
   Application Recommendations card. Two stacked lines: applicant name on
   top (prominent), applied-when below in muted gray. Left-aligned so both
   lines share a single starting edge for cleaner reading. */
.m-match-card__applicant-block {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  white-space: nowrap;
  line-height: 1.25;
}
.m-match-card__applicant {
  font-weight: 700;
  font-size: 1rem;
  color: #111827;
  line-height: 1.2;
}
.m-match-card__applicant-when {
  font-size: .76rem;
  color: #9CA3AF;
  line-height: 1.2;
}
.m-match-card__applicant-when--empty {
  color: #C7CACF;
  font-style: italic;
}
/* Applicant's home location ("City, State") sits as its own column
   directly to the right of the applicant-block (name + Applied stack),
   so the location reads as part of the same identity unit instead of
   being pushed to the far right of the cluster. */
.m-match-card__applicant-from {
  font-size: .9rem;
  color: #4B5563;
  white-space: nowrap;
  padding-left: 18px;
}

/* Lifecycle pill. Pinned to the right side of the row via
   margin-left:auto so the cluster's name/location/avatar stay grouped
   on the left while the current lifecycle stage ("Viewed", "Contacted",
   etc.) reads as a separate status indicator on the right edge -- just
   before the row arrow. Neutral gray to keep it from competing visually
   with the AI score ring. */
.m-match-card__lc-pill {
  margin-left: auto;
  margin-right: 48px;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border-radius: 999px;
  background: #FFFFFF;
  border: 1px solid #E5E7EB;
  color: #4B5563;
  font-size: .75rem;
  font-weight: 600;
  white-space: nowrap;
  letter-spacing: .01em;
}
/* Terminal "Withdrawn" state. Light-red fill + dark-red frame/text so
   the row reads at-a-glance that the applicant pulled their
   application -- distinct from the neutral white pill used for active
   lifecycle stages. Applied wherever the base .m-match-card__lc-pill
   is rendered (member + employer application lists, dashboard). */
.m-match-card__lc-pill--withdrawn {
  background: #FEE2E2;
  border-color: #B91C1C;
  color: #991B1B;
}
/* "Under Review" -- light-green in-progress pill. Member-facing only;
   employer lifecycle stages don't use this status string. */
.m-match-card__lc-pill--review {
  background: #DCFCE7;
  border-color: #16A34A;
  color: #166534;
}
/* Lifecycle tone pills -- mirror the employer status-picker option tones
   (ar-lc-status-opt--pos/--neg) so the applications-list pills use the exact
   same color coding as the picker popup. pos = progressing (green),
   neg = terminal-negative (red). Driven by AppLifecycleTone(). */
.m-match-card__lc-pill--pos {
  background: #F0FDF4;
  border-color: #BBF7D0;
  color: #166534;
}
.m-match-card__lc-pill--neg {
  background: #FEF2F2;
  border-color: #FECACA;
  color: #991B1B;
}

/* Applicant-card variant: shrink the body's fixed slot so the cluster
   (applicant name + applied-when) sits closer to the title rather than
   floating far to the right after a 360px-wide body. */
.m-match-card--applicant .m-match-card__body {
  flex: 0 0 240px;
}
/* Extra breathing room between the ring score and the applicant/match body
   on the dashboard strip and the employer applicants strip. The parent's
   14px gap reads tight against the circular ring; a small margin pushes
   the title column out a touch without disturbing tight contexts like the
   search-jobs or job-detail badges that reuse .m-match-card__score. */
.m-match-card--applicant .m-match-card__score,
.m-match-list .m-match-card__score {
  margin-right: 6px;
}

/* Applicant avatar inside the cluster. Mirrors the .ap-avatar treatment
   on /employers/applications.asp so the chip looks identical across both
   surfaces -- circular 40px, brand-purple gradient + initials when no
   members.photo is on file. */
.m-match-card__avatar {
  flex: 0 0 40px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  object-fit: cover;
  background: #E5E7EB;
  display: inline-block;
}
.m-match-card__avatar--initials {
  background: linear-gradient(135deg, #4F46E5, #7C3AED);
  color: #fff;
  font-size: .9rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .02em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* "Search All Jobs" CTA pill rendered below the match list. Centered
   horizontally; brand purple per the rest of the site's primary CTAs.
   Negative margin-bottom pulls the page's bottom edge up so the pill
   doesn't sit far above the footer. */
.m-search-all-cta {
  display: flex;
  justify-content: center;
  padding: 18px 0 0;
  margin-bottom: -24px;
}
.m-search-all-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 22px;
  border-radius: 999px;
  background: #1E40AF;
  color: #fff;
  font-weight: 600;
  font-size: .9rem;
  text-decoration: none;
  transition: background .15s;
}
.m-search-all-pill:hover { background: #172554; }
.m-search-all-pill svg  { flex: 0 0 auto; }
/* Nudge only the label text down 1px so it optically centers with the arrow. */
.m-search-all-label { position: relative; top: 1px; }

/* Ring-style match score. Renders as a circular conic-gradient progress
   ring with the score number reading inside a white center cutout.
   Callers pass the score percentage via inline `style="--pct:N"`. The
   band color (good/mid/low/unrated) comes from the modifier classes
   below, exposed as the `--score-color` custom property so the same
   value drives both the conic fill and the number's text color. */
.m-match-card__score {
  --pct: 0;
  --score-color: #9CA3AF;
  position: relative;
  flex: 0 0 42px;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-weight: 700;
  font-size: .78rem;
  color: var(--score-color);
  /* Two-layer background paints the white center disk ON TOP of the
     conic ring without a pseudo. Text content of the element renders
     above the background automatically, so the score number is always
     legible (positioned ::before would otherwise paint above text and
     hide it). */
  background:
    radial-gradient(circle closest-side, #ffffff 85%, transparent 86%),
    conic-gradient(var(--score-color) calc(var(--pct) * 1%), #E5E7EB 0);
  box-shadow: 0 1px 3px rgba(17, 24, 39, .08);
}
/* Tiny AI badge pinned to the upper-right of the score pill. Opt-in via
   the --with-ai modifier so listing surfaces (dashboard match cards,
   search-jobs cards) stay clean -- the badge only shows on standalone
   pills (e.g. the top of /members/job-detail.asp) where it can identify
   the rating as AI-generated without competing with neighboring icons. */
.m-match-card__score--with-ai::after {
  content: "";
  position: absolute;
  top: -5px;
  right: -5px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #fff url("/images/ai.png") center/11px no-repeat;
  border: 1.5px solid #fff;
  box-shadow: 0 1px 2px rgba(0,0,0,.2);
  pointer-events: none;
}
/* Score-tier colors mirror the search-jobs badge palette for consistency
   across surfaces: green for strong matches, amber for marginal, red for
   low. Same thresholds (>=80 / >=60 / <60) drive both. */
.m-match-card__score--good     { --score-color: #16A34A; }
.m-match-card__score--mid      { --score-color: #F59E0B; }
.m-match-card__score--low      { --score-color: #DC2626; }
.m-match-card__score--unrated  { --score-color: #9CA3AF; }

/* Score pill -----------------------------------------------------------------
   Labelled lockup around a .m-match-card__score dial: the words "MATCH SCORE"
   and the dial itself sitting together on one plate, so the number is not a
   bare percentage in a circle that never says what it is scoring.

   Introduced on /members/job-detail.asp's snapshot card and brought here so the
   search-jobs and search-contracts listing cards can carry the same treatment.
   THE TWO GROUNDS ARE DIFFERENT AND THIS IS DELIBERATELY NOT A COPY:
   job-detail's lockup sits on a saturated dark ramp, so its plate is
   translucent white and its label is white; it also gives the dial a white
   collar because a green arc on a green ramp has no edge. It stays scoped as
   .jd-snap-match in that page's own style block.
   THIS one sits on a white listing card, where translucent white is invisible
   and the collar is pointless. Same geometry, light palette.

   Padding is asymmetric on purpose: the dial is a circle and brings its own
   optical margin, so it needs less room on its side than the text does. */
.m-score-pill {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 6px 4px 10px;
  border-radius: 999px;
  background: #F3F4F6;
  cursor: pointer;
  transition: background .15s;
}
.m-score-pill:hover { background: #E5E7EB; }
.m-score-pill:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
/* Quiet, small, and uppercase: it is a caption on the number, not a heading.
   The 1px right shift is optical -- letter-spacing hangs a gap off the end of
   the last letter, which reads as the text sitting a shade left of centre.
   Offset rather than padding so it cannot change the pill's width. */
.m-score-pill__label {
  position: relative;
  left: 1px;
  font-size: .66rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: #6B7280;
  white-space: nowrap;
}
/* Dial inside the pill: a size down from the standalone 42px, because here it
   is one half of a lockup rather than the whole thing. The shared box-shadow is
   dropped -- on a plate this light it reads as smudge rather than lift. */
.m-score-pill .m-match-card__score {
  flex: 0 0 34px;
  width: 34px;
  height: 34px;
  font-size: .64rem;
  box-shadow: none;
}
/* The listing cards are dense; below the tablet breakpoint the words cost more
   width than they are worth and the dial goes back to standing alone. */
@media (max-width: 760px) {
  .m-score-pill { padding: 0; background: none; }
  .m-score-pill:hover { background: none; }
  .m-score-pill__label { display: none; }
}

/* Top-rated (>=90%) gold star overlay -----------------------------------------
   A small gold star pinned to a rating badge whose score is 90% or higher.
   The render sites add the --ace modifier class (server-side, and via JS in the
   employer review popup) whenever the score clears 90. Pure CSS overlay -- no
   animation. Shared across the member and employer surfaces, which both load
   this stylesheet. Default geometry targets the 42px badge; the overrides below
   handle the 38px / 60px / 96px variants. The star rides ::after, except on the
   job-detail badge -- it already uses ::after for its AI sub-badge, so there the
   star rides ::before at the top-left instead (an element has only one ::after).
   --------------------------------------------------------------------------- */
.m-match-card__score--ace,
.je-aim-score--ace,
.ar-aimatch-score--ace { position: relative; }

.m-match-card__score--ace:not(.m-match-card__score--with-ai)::after,
.je-aim-score--ace::after,
.ar-aimatch-score--ace::after,
.m-match-card__score--with-ai.m-match-card__score--ace::before {
  content: "\2605";              /* solid star */
  position: absolute;
  top: -7px;
  right: 1px;
  font-size: 17px;
  line-height: 1;
  color: #FBBF24;               /* gold */
  text-shadow: 0 1px 2px rgba(0,0,0,.25);
  pointer-events: none;
}
/* job-detail badge: star to the top-LEFT so it clears the AI sub-badge. */
.m-match-card__score--with-ai.m-match-card__score--ace::before { right: auto; left: 1px; }
/* 38px employer applications-list badge */
.je-aim-score--ace::after { top: -6px; font-size: 15px; }
/* 60px dashboard average-rating ring */
.m-stat-ring.m-match-card__score--ace::after { top: -10px; right: 2px; font-size: 24px; }
/* 96px employer review-popup modal score */
.ar-aim-modal-score.ar-aimatch-score--ace::after { top: -14px; right: 4px; font-size: 36px; }

/* Strong-match (>=85%) one-shot attention pulse. The server tags qualifying
   rows with .m-match-card--hot; the dashboard's load script adds .is-pulsing
   to the visible ones and strips it on animationend, so the green pulse plays
   exactly once when the dashboard first renders. */
@keyframes mMatchHotPulse {
  0%   { box-shadow: 0 0 0 0 rgba(22,163,74,.6); }
  70%  { box-shadow: 0 0 0 9px rgba(22,163,74,0); }
  100% { box-shadow: 0 1px 3px rgba(17,24,39,.08); }
}
/* Pulse only the circular score badge, not the whole row. animationend
   bubbles from the badge up to the card, so the load script's listener
   still strips .is-pulsing cleanly. The score badge sits above neighbors
   for the ring to read fully. */
.m-match-card--hot.is-pulsing .m-match-card__score {
  animation: mMatchHotPulse 0.6s ease-out 2;
  position: relative;
  z-index: 1;
}

.m-match-card__body {
  /* Fixed body width so the cluster after it always starts at the same
     horizontal column across rows. Combined with a fixed-width salary
     slot below, this aligns the chip pills' start position row-to-row. */
  flex: 0 0 360px;
  min-width: 0;
}
.m-match-card__title {
  font-weight: 600;
  font-size: .98rem;
  color: #111827;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.m-match-card__emp {
  color: #9CA3AF;
  display: inline-block;
  max-width: 180px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: bottom;
}
.m-match-card__meta {
  font-size: .82rem;
  color: #6B7280;
  margin-top: 1px;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.m-match-card__employer {
  color: #1F2937;
  font-weight: 600;
}
.m-match-card__sep {
  color: #D1D5DB;
}
.m-match-card__loc {
  color: #6B7280;
}
/* Right-side cluster after the body: salary (bold text), work-model and
   job-type as light pills, posted-date as muted text, then a right caret.
   Each piece is its own flex item so the row reads as separated cells. */
.m-match-card__salary {
  /* Fixed-width slot so the pills that follow start at a consistent
     horizontal position across rows regardless of how long any one
     salary string is. Wide enough for a dual six-figure range; any
     longer string (e.g. non-USD currency + " / yr" suffix) clips with
     an ellipsis rather than bleeding over the chips beside it. */
  flex: 0 0 180px;
  font-weight: 500;
  font-size: .95rem;
  color: #111827;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding-right: 6px;
}
.m-match-card__chip {
  flex: 0 0 auto;
  padding: 4px 12px;
  border-radius: 999px;
  background: #F9FAFB;
  border: 1px solid #E5E7EB;
  color: #1F2937;
  font-size: .78rem;
  font-weight: 600;
  white-space: nowrap;
}
/* Personal-state icons (bookmark, applied check) that live in the cluster
   grid's third column. Always render so the grid slot stays occupied and
   the dates column past it stays aligned. */
.m-match-card__flags {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  line-height: 1;
}
.m-match-card__flag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
}
.m-match-card__flag--bm  { color: #16A34A; }
.m-match-card__flag--app { color: #16A34A; }
.m-match-card__posted {
  flex: 0 0 auto;
  font-size: .76rem;
  color: #9CA3AF;
  white-space: nowrap;
}
/* Stacks "Job Posted: ..." over "Matched: ..." in the right cluster so the
   row stays compact horizontally. The wrapper carries the leading margin
   that the inner spans used to provide individually. */
.m-match-card__dates {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  text-align: right;
  gap: 2px;
  /* margin-left: auto pins the dates to the cluster's right edge so
     they land in the same column row-to-row regardless of how wide the
     work_model / job_type chips are above them. */
  margin-left: auto;
  margin-right: 10px;
  /* Nudge the whole posted/matched block left, off the right edge. margin
     alone gets absorbed by the grid cell's auto-margin/stretch resolution
     (no visible shift), so use a transform -- it's not subject to that. */
  transform: translateX(-34px);
  line-height: 1.2;
}
.m-match-card__dates .m-match-card__posted {
  display: block;
  width: 100%;
  text-align: right;
}
/* Right-side cluster wrapper. CSS grid. The dates ride the trailing 1fr
   track so they stay pinned at a fixed spot just left of the row's "x"
   regardless of how wide the salary/chips are. The salary is the only
   flexible track -- minmax(0,180px) lets it shrink and ellipsize under
   pressure (it's the long, variable string) instead of forcing the grid
   to overflow and shove the dates rightward. Columns:
     minmax(0,180px)  salary slot (shrinks + ellipsizes; matches .m-match-card__salary)
     auto             chips slot  (natural width: 0/1/2 pills)
     50px             flags slot  (two 14px SVGs + gap + buffer)
     1fr              dates slot  (absorbs slack; dates pin right). */
.m-match-card__cluster {
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: minmax(0, 180px) auto 50px 1fr;
  align-items: center;
  gap: 14px;
  min-width: 0;
}
/* Chips run inside their cluster cell as their own flex line so 0/1/2
   chips all left-align inside the fixed-width chips slot. */
.m-match-card__chips {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

@media (max-width: 480px) {
  .m-match-card { padding: 10px 12px; gap: 10px; }
  .m-match-card__score { flex: 0 0 36px; width: 36px; height: 36px; font-size: .68rem; }
  .m-match-card__title { font-size: .92rem; }
  /* On narrow screens, drop the desktop fixed widths -- body grows again
     and salary sizes to its own content. Cluster reverts from grid to
     flex so the empty chips/dates cells don't reserve dead space. */
  .m-match-card__body   { flex: 1 1 auto; }
  .m-match-card__salary { flex: 0 0 auto; font-size: .9rem; }
  .m-match-card__cluster {
    display: flex;
    grid-template-columns: none;
  }
  /* Hide the chips/dates text to keep the row compact. Flags still show. */
  .m-match-card__chips,
  .m-match-card__chip,
  .m-match-card__posted,
  .m-match-card__dates { display: none; }
}

.m-section--activity { min-height: 260px; }
.m-see-all { font-size: .85rem; color: var(--primary); font-weight: 500; }


.m-gear-spin {
  display: inline-block;
  animation: mGearSpin 10s linear infinite;
  color: var(--text-muted);
  line-height: 1;
}

.m-gear-lg {
  font-size: 1.6rem;
  position: relative;
  top: -10px;
}

.m-searching-wrap {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 3px;
}

.m-searching-label {
  font-size: .65rem;
  font-weight: 500;
  color: #9CA3AF;
  letter-spacing: .05em;
  text-transform: uppercase;
}

.m-dot-pulse {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.m-dot-pulse span {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--text-muted);
  animation: mDotFade 2.4s ease-in-out infinite;
}

.m-dot-pulse span:nth-child(2) { animation-delay: .2s; }
.m-dot-pulse span:nth-child(3) { animation-delay: .4s; }

.m-dot-pulse--paused span {
  animation: none;
  opacity: .2;
  transform: scale(.85);
}

/* Static dots: present and fully visible, just not pulsing. Used for Basic-mode
   accounts, which receive no automatic AI matching, so the "actively searching"
   pulse would be misleading. Keeps the normal size/opacity (unlike --paused). */
.m-dot-pulse--static span {
  animation: none;
}

@keyframes mDotFade {
  0%, 80%, 100% { opacity: .2; transform: scale(.85); }
  40%           { opacity: 1;  transform: scale(1); }
}

@keyframes mGearSpin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

.m-threshold-badge {
  font-weight: 700;
  color: #B0B7C3;
}

.m-ai-sup {
  position: relative;
  top: -4px;
  margin-left: -3px;
}
.m-see-all:hover { text-decoration: underline; }

/* Empty state */
.m-empty-state {
  text-align: center;
  padding: 0 20px 40px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
.m-empty-state[hidden] { display: none; }
.m-empty-icon { font-size: 2.8rem; }
.m-empty-state h3 { font-size: 1.05rem; color: var(--text-dark); }
.m-empty-state p  { font-size: .95rem; color: var(--text-muted); max-width: 320px; }
.m-empty-state .btn { margin-top: 6px; }
.m-empty-state--centered { padding: 0 20px 40px; justify-content: center; min-height: 380px; }
.m-searching-badge { font-size: 1rem; padding: 10px 22px; color: #16a34a; border-color: #16a34a; }
.m-empty-state p.m-empty-searching { font-size: .95rem; color: var(--text-muted); margin: 0; }
.m-empty-state p.m-matching-wait-msg {
  font-size: .98rem;
  color: var(--text-muted);
  max-width: 560px;
  line-height: 1.55;
  margin: 0;
  padding: 14px 20px;
  background: rgba(79,70,229,.035);
  border: 1px solid #e5e7eb;
  border-radius: 10px;
}
.m-matching-wait-msg a { color: #4F46E5; font-weight: 600; text-decoration: underline; }
.m-matching-wait-msg a:hover { color: #7C3AED; }

/* Standalone "browse the catalog" line under the Basic-mode panel -- plain
   muted text (no tinted box) so it reads as a secondary note, not a second card. */
.m-basic-off-browse { margin: 0; font-size: .92rem; color: var(--text-muted); }
.m-basic-off-browse a { color: #4F46E5; font-weight: 600; text-decoration: underline; }
.m-basic-off-browse a:hover { color: #7C3AED; }
.m-matching-wait-icon { color: #4F46E5; opacity: .85; animation: m-wait-pulse 2.4s ease-in-out infinite; }
@keyframes m-wait-pulse { 0%,100% { opacity: .55; transform: scale(1); } 50% { opacity: 1; transform: scale(1.06); } }

/* Colorful "matching off / upgrade to launch" rocket for the Basic-mode panel.
   Own class (not .m-matching-wait-icon) so the brand-blue tint + fade don't
   wash out its fills; gentle bob keeps it lively without looping the eye. */
.m-basic-off-icon {
  transform-origin: center;
  animation: m-basic-off-bob 3.2s ease-in-out infinite;
  /* Pull the robot down toward the message; the panel's 14px flex gap
     otherwise leaves it floating too far above the text. */
  margin-bottom: -8px;
}
@keyframes m-basic-off-bob {
  0%, 100% { transform: translateY(0) rotate(-3deg); }
  50%      { transform: translateY(-5px) rotate(3deg); }
}
@media (prefers-reduced-motion: reduce) {
  .m-basic-off-icon { animation: none; }
}
.m-matching-wait-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  max-width: 600px;
  text-align: center;
}

/* ── Nav Bell ── */
.m-nav-bell {
  /* Alerts bell hidden site-wide; envelope now occupies this slot.
     Remove this line to restore the bell. */
  display: none;
  position: relative;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  background: none;
  border: none;
  cursor: pointer;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--text-mid);
  transition: var(--transition);
  flex-shrink: 0;
  margin-right: 10px;
}
.m-nav-bell:hover { color: var(--primary); }
/* Quiet state = no unread alerts. Bell is still fully clickable (opens
   the popup's empty state); the muted color just signals there's nothing
   urgent waiting. */
.m-nav-bell--quiet { color: #C8CDD6; }
.m-nav-bell--quiet:hover { color: #9CA3AF; }
.m-nav-bell:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.m-nav-bell .m-alert-badge {
  position: absolute;
  top: 4px;
  right: 0;
  width: 18px;
  height: 18px;
  font-size: .62rem;
}

/* ── Nav Messages (envelope) ── */
/* Mirrors .m-nav-bell so the envelope and bell read as a matched pair.
   No margin-right: the .m-nav-right flex gap handles spacing to the bell. */
.m-nav-msg {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  background: none;
  border: none;
  cursor: pointer;
  font-size: 1.7rem;
  line-height: 1;
  /* Envelope frame reads black (stroke inherits currentColor). Changed from
     the old #374151 dark gray 2026-08-04 at the owner's request. Keep this in
     step with .m-nav-inv in /includes/invitations_popup_inc.asp: the envelope
     and the invitations ticket are one row of controls and must read as a set.
     This is the RESTING colour only; the nothing-unread state is the separate
     .m-nav-msg--quiet rule below and is still #9CA3AF. */
  color: #000000;
  transition: var(--transition);
  flex-shrink: 0;
  transform: translate(-30px, 2px);
}
/* Bump the SVG up a touch from its 38x27 attribute size, and thin the stroke.
   Set here (not on the markup) so both headers grow/thin together. */
.m-nav-msg-ico { width: 44px; height: 32px; stroke-width: 1.2; }
.m-nav-msg:hover { color: var(--primary); }
/* "Quiet" = nothing unread. The envelope now stays BLACK in this state too
   (owner's call, 2026-08-04) -- it used to drop to #9CA3AF to match its "0"
   badge. So the icon no longer changes colour at all between states, and with
   the count badge since removed the ONLY unread signal on the envelope is the
   pulsing halo (.m-nav-msg--alert, further down). Keep this in step with
   .m-nav-inv--quiet in /includes/invitations_popup_inc.asp.

   The class is still load-bearing even though the colour matches the resting
   rule: messages_popup_inc.asp reads and writes it as the client-side record
   of "nothing unread". Do not delete it just because the colours match. */
.m-nav-msg--quiet { color: #000000; }
.m-nav-msg:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* Contract-mode application-token pill (members_header_inc.asp, mhInConnect
   only): solid Connect-green rounded pill, coin icon + count + "token(s)", on
   one line. Leads the right cluster, to the left of the envelope.
   The envelope (.m-nav-msg) keeps its tuned -30px pull in BOTH modes so it does
   not jump position between Jobs and Contract; margin-right here gives the pill
   clearance from that pulled-in envelope (10px gap - 30px pull => +30 to clear,
   plus a little breathing room). */
/* Wrapper holds the pill + its info-pin as one unit and carries the cluster
   spacing. Nudged down 2px to sit level with the envelope/profile; relative
   offset keeps it out of flow so neighbours don't shift. margin-right gives
   clearance from the envelope's -30px pull (see .m-nav-msg). */
.m-app-tokens-wrap {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  position: relative;
  top: 2px;
  margin-right: 54px;
}
.m-app-tokens {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 18px 8px 16px;
  border-radius: 999px;
  background: var(--cx-primary);
  color: #fff;
  line-height: 1;
  white-space: nowrap;
  /* The whole pill opens the Apply Tokens info modal (no separate "i" pin). */
  cursor: pointer;
}
.m-app-tokens:hover { filter: brightness(1.08); }
.m-app-tokens:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
/* Low balance (fewer than 2 tokens): orange warning fill. */
.m-app-tokens--low { background: #EA580C; }
.m-app-tokens-ico { flex: 0 0 auto; }
.m-app-tokens-count {
  font-size: .82rem;
  font-weight: 800;
  letter-spacing: .01em;
}
.m-app-tokens-word {
  font-size: .72rem;
  font-weight: 600;
  opacity: .92;
  /* Plain label now -- the PILL is the click target (info modal), so the
     word carries no link styling of its own. */
  color: inherit;
}

/* NOTE: the unread COUNT BADGE is gone (removed 2026-08-04, owner's call).
   Both header bars now render the envelope with the camera marker in that
   top-right corner and no number at all -- unread is signalled purely by the
   pulsing halo below. The old .m-msg-badge / .m-msg-badge--zero rules were
   deleted with it; the count still reaches the client as data-unread on the
   button, which messages_popup_inc.asp reads to drive the halo live.
   The invitations ticket badge is a SEPARATE control and keeps its number. */

/* ── Camera marker on the envelope ───────────────────────────────────────
   Black-framed circle carrying the same vendor-neutral camera glyph used in
   the messages popup toolbar. Sits in the envelope's TOP-RIGHT corner, the
   slot the count badge used to occupy (top:1px / right:-5px, 18x18 -- kept
   near enough that the cluster spacing did not have to be retuned). Border
   and glyph both inherit currentColor, so the marker follows the envelope
   through resting, hover and quiet with no rules of its own -- do not give it
   a fixed colour or it will stop matching. */
.m-nav-msg-cam {
  position: absolute;
  right: -5px;
  top: 1px;
  width: 19px;
  height: 19px;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  background: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  pointer-events: none;
}
/* Explicit display:block because of the global img/svg block rule elsewhere in
   this file; without it the glyph picks up inline baseline space and sits low. */
.m-nav-msg-cam svg { width: 11px; height: 11px; display: block; }

/* Live or imminent meeting: solid green marker, white ring, white camera.
   Raised by msgMeetingSoon in members|employers/messages_inc.asp -- a meeting
   this viewer is a party to whose door is ALREADY OPEN (it opens 15 minutes
   before the scheduled start) and which is not yet 30 minutes past its start.
   So "active now, or starting within 15 minutes" is exactly this flag; do not
   invent a second query for it.

   Setting color here is what turns BOTH the ring and the glyph white in one
   go: the border is `1.5px solid currentColor` and the svg strokes with
   currentColor. It also pins the marker against .m-nav-msg:hover, which would
   otherwise drag the inherited colour to --primary.

   The green is #16A34A (--cx-primary-bright) hard-coded rather than var()'d:
   this marker renders on the plain employer bar too, which does not always
   load the Connect palette. */
.m-nav-msg-cam--live {
  background: #16A34A;
  border-color: #fff;
  color: #fff;
  /* The white ring needs separation from the white header bar behind it, or
     the marker reads as a floating green dot with a bite out of it. */
  box-shadow: 0 0 0 1px rgba(22, 163, 74, .55);
}

/* ── Attention: pulsing red halo ─────────────────────────────────────────
   REPLACED THE ENVELOPE WIGGLE 2026-08-04. The halo carries two signals at
   once, which the wiggle could not: --alert is emitted when there is something
   unread OR when a video meeting's door is open right now (msgMeetingSoon in
   members|employers/messages_inc.asp). That is why --alert is a separate class
   from --quiet rather than being derived from the unread count.

   Red is deliberate: it means "needs you now". With the count badge removed
   this halo is the ONLY unread signal on the envelope, so it now runs a BRIGHT
   SOLID #DC2626 (2026-08-04, tuned live: #EF4444/.55 -> #B91C1C/.65 ->
   #FF2D2D/.8 -> fully opaque #FF2D2D -> darkened to #DC2626 here) -- vivid
   enough to read on the light bar without a number behind it.
   The ring is drawn with box-shadow, not an outline or border, so it adds no
   layout and cannot disturb the cluster's flex gap or the button's translate().

   The button paints no background, so the shadow reads as a filled disc behind
   the envelope rather than an outline. That is what the NEGATIVE start spread
   is for: -16px pulls the disc in to roughly 12px, a small dot sitting under
   the envelope glyph, and each pulse grows out from there. The 11px end spread
   is unchanged, so it still reaches the same outer size as before. Do not push
   the start past -22px: at half the 44px box the shadow collapses to nothing
   and the first frames of the pulse simply vanish. */
.m-nav-msg--alert {
  border-radius: 50%;
  animation: m-msg-halo 1.9s ease-out infinite;
}

/* Alpha runs straight from full to nothing across the expansion. A hold at
   full opacity through the first third was tried 2026-08-04 and taken back
   out: it made the pulse read as a beat rather than a ripple. */
@keyframes m-msg-halo {
  0%   { box-shadow: 0 0 0 -16px rgba(220, 38, 38, 1); }
  70%  { box-shadow: 0 0 0 11px  rgba(220, 38, 38, 0); }
  100% { box-shadow: 0 0 0 -16px rgba(220, 38, 38, 0); }
}

/* Respect users who ask for less motion. The halo still RENDERS at rest so the
   signal is not lost entirely, it simply stops animating. */
@media (prefers-reduced-motion: reduce) {
  .m-nav-msg--alert { animation: none; box-shadow: 0 0 0 2px rgba(220, 38, 38, 1); }
}

/* ── Header cluster divider ───────────────────────────────────────────────
   Hairline between the notification icons and the profile control in the
   member bar's right cluster. Decorative only (aria-hidden in the markup).
   It is a flex item, so the cluster's own gap supplies the space on both
   sides -- do not add margins here or the two halves stop matching. */
.m-nav-sep {
  flex: 0 0 auto;
  width: 1px;
  height: 26px;
  background: #D1D5DB;
  /* Same 2px optical nudge the envelope and ticket carry. */
  position: relative;
  top: 2px;
}

/* ── Alerts Stat Card ── */
.m-stat-card--alerts {
  cursor: pointer;
  position: relative;
}
.m-stat-card--alerts:hover {
  box-shadow: var(--shadow-md);
  border-color: var(--primary);
}
.m-stat-card--alerts:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

.m-alert-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  background: #EF4444;
  color: #fff;
  font-size: .65rem;
  font-weight: 700;
  border-radius: 50%;
  vertical-align: super;
  margin-left: 2px;
  line-height: 1;
}

/* ── Alerts Popup ── */
.m-alerts-overlay {
  position: fixed;
  inset: 0;
  z-index: 2000;
  background: rgba(0, 0, 0, .45);
  align-items: center;
  justify-content: center;
  padding: 24px;
  animation: mSetupFadeIn .2s ease;
}
.m-alerts-overlay--out {
  animation: mSetupFadeOut .24s ease forwards;
}

.m-alerts-modal {
  background: var(--white);
  border-radius: var(--radius-lg, 16px);
  box-shadow: var(--shadow-xl, 0 20px 60px rgba(0,0,0,.18));
  max-width: 640px;
  width: 100%;
  /* Tall by default so the empty state has room to breathe and a few
     alerts don't make the popup feel cramped. Caps at 80% of viewport so
     it never bleeds off the screen on short windows. */
  min-height: 560px;
  height: 560px;
  max-height: 80vh;
  overflow: hidden;          /* clip the purple bar to the rounded corners */
  animation: mSetupSlideIn .2s ease;
  display: flex;
  flex-direction: column;
}
.m-alerts-overlay--out .m-alerts-modal {
  animation: mSetupSlideOut .24s ease forwards;
}

/* Standard purple bar: matches the .ap-pane-header pattern used on
   /employers/applications. Bell + "Alerts" + count pill on the left,
   white X on the right. */
.m-alerts-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 20px;
  height: 56px;
  background: var(--card-header-grad);
  color: #fff;
  flex-shrink: 0;
}

.m-alerts-header h2 {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: .01em;
  color: #fff;
  margin: 0;
}

.m-alerts-header-icon { font-size: 1.15rem; line-height: 1; }

/* Count pill, translucent-on-blue to match the applications pane chip. */
.m-alerts-count {
  display: inline-flex;
  align-items: center;
  font-size: .78rem;
  font-weight: 600;
  color: rgba(255,255,255,.95);
  background: rgba(255,255,255,.18);
  border: 1px solid rgba(255,255,255,.32);
  padding: 2px 10px;
  border-radius: 999px;
  letter-spacing: .02em;
  line-height: 1.2;
}

/* White-on-purple X, lightens on hover. */
.m-alerts-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  background: rgba(255,255,255,.0);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: #fff;
  cursor: pointer;
  transition: background .15s, border-color .15s;
  flex-shrink: 0;
}
.m-alerts-close:hover {
  background: rgba(255,255,255,.15);
  border-color: rgba(255,255,255,.35);
  color: #fff;
}
.m-alerts-close:focus-visible {
  outline: 2px solid rgba(255,255,255,.7);
  outline-offset: 2px;
}

.m-alerts-list {
  list-style: none;
  padding: 12px 0;
  margin: 0;
  flex: 1 1 auto;
  overflow-y: auto;
}

.m-alert-item {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 18px 28px;
  font-size: 1.1rem;
  color: var(--text-mid);
  line-height: 1.55;
  border-bottom: 1px solid var(--border);
}
.m-alert-item:last-child { border-bottom: none; }

.m-alert-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #EF4444;
  flex-shrink: 0;
  margin-top: 8px;
}

.m-alert-text {
  flex: 1;
}

.m-alert-dismiss {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  transition: var(--transition);
  margin-top: 2px;
}
.m-alert-dismiss:hover {
  background: #FEF2F2;
  border-color: #FECACA;
  color: #EF4444;
}
.m-alert-dismiss:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

.m-alert-item--removing {
  opacity: 0;
  transform: translateX(12px);
  transition: opacity .22s ease, transform .22s ease;
}

@media (max-width: 520px) {
  .m-alerts-modal { border-radius: 16px 16px 0 0; }
  .m-alerts-overlay { align-items: flex-end; padding: 0; }
}

/* ── Members Footer ── */
.m-footer {
  background: var(--text-dark);
  color: rgba(255, 255, 255, .7);
  margin-top: 60px;
}

.m-footer-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  max-width: 1160px;
  margin: 0 auto;
  padding: 32px 24px;
  flex-wrap: wrap;
}

.m-footer-logo {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

.m-footer-logo-img {
  height: 28px;
  width: auto;
  display: block;
  filter: brightness(0) invert(1);
  opacity: .9;
}

.m-footer-nav {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 2px;
}

.m-footer-nav a {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 12px;
  font-size: .825rem;
  font-weight: 500;
  color: rgba(255, 255, 255, .65);
  border-radius: var(--radius-sm);
  transition: var(--transition);
  white-space: nowrap;
}

.m-footer-nav a:hover {
  color: var(--white);
  background: rgba(255, 255, 255, .08);
}

.m-footer-nav-ai .m-tab-ai-icon {
  filter: brightness(0) invert(1);
  opacity: .7;
}

.m-footer-nav-divider {
  width: 1px;
  height: 16px;
  background: rgba(255, 255, 255, .15);
  margin: 0 8px;
  flex-shrink: 0;
}

.m-footer-bottom {
  border-top: 1px solid rgba(255, 255, 255, .08);
}

.m-footer-bottom-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  max-width: 1160px;
  margin: 0 auto;
  padding: 16px 24px;
  font-size: .78rem;
  flex-wrap: wrap;
}

.m-footer-legal {
  display: flex;
  gap: 20px;
}

.m-footer-legal a,
.m-footer-bottom-inner a {
  color: rgba(255, 255, 255, .5);
  transition: color var(--transition);
}

.m-footer-legal a:hover,
.m-footer-bottom-inner a:hover {
  color: var(--white);
}

@media (max-width: 600px) {
  .m-footer-inner {
    flex-direction: column;
    align-items: flex-start;
    gap: 20px;
    padding: 28px 16px;
  }

  .m-footer-bottom-inner {
    flex-direction: column;
    align-items: flex-start;
    padding: 14px 16px;
  }

  .m-footer-nav {
    gap: 2px;
  }
}

/* ── Profile Setup Popup ── */
.m-setup-overlay {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 2000;
  background: rgba(0, 0, 0, .5);
  align-items: center;
  justify-content: center;
  padding: 24px;
  animation: mSetupFadeIn .22s ease;
}

.m-setup-overlay--out {
  animation: mSetupFadeOut .26s ease forwards;
}

@keyframes mSetupFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes mSetupFadeOut {
  from { opacity: 1; }
  to   { opacity: 0; }
}

.m-setup-modal {
  position: relative;
  background: var(--white);
  border-radius: var(--radius-lg, 16px);
  box-shadow: var(--shadow-xl, 0 20px 60px rgba(0,0,0,.18));
  max-width: 460px;
  width: 100%;
  padding: 40px 36px 32px;
  text-align: center;
  animation: mSetupSlideIn .22s ease;
}

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

.m-setup-overlay--out .m-setup-modal {
  animation: mSetupSlideOut .26s ease forwards;
}

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

.m-setup-close {
  position: absolute;
  top: 14px;
  right: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  transition: var(--transition);
}
.m-setup-close:hover {
  background: var(--bg-alt);
  border-color: var(--border);
  color: var(--text-dark);
}

.m-setup-icon-wrap {
  margin-bottom: 16px;
}

.m-setup-icon {
  font-size: 2.6rem;
  line-height: 1;
}

.m-setup-modal h2 {
  font-size: 1.35rem;
  font-weight: 700;
  color: var(--text-dark);
  margin-bottom: 12px;
}

.m-setup-modal > p {
  font-size: .925rem;
  color: var(--text-muted);
  line-height: 1.6;
  margin-bottom: 28px;
}

.m-setup-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 20px;
}

.m-setup-actions .btn {
  text-align: center;
  justify-content: center;
}

.m-setup-note {
  font-size: .78rem;
  color: var(--text-muted);
  margin-bottom: 0;
}

/* "Still needed" checklist: unchecked-to-do rings, a small count pill, and
   hairline dividers so it reads as a set of tasks to tick off rather than a
   flat list. Header markup lives in index.asp / settings.asp; the <li> rows
   come from setupMissingHtml in alerts_inc.asp. */
.m-setup-missing {
  text-align: left;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0;
  margin: -8px 0 24px;
  overflow: hidden;
}

.m-setup-missing-hd {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 11px 14px 9px;
}

.m-setup-missing-label {
  font-size: .72rem;
  font-weight: 700;
  color: var(--text-dark);
  text-transform: uppercase;
  letter-spacing: .05em;
  margin: 0;
}

.m-setup-missing-count {
  flex: 0 0 auto;
  font-size: .7rem;
  font-weight: 700;
  color: var(--primary);
  background: var(--primary-light);
  padding: 2px 9px;
  border-radius: 999px;
  white-space: nowrap;
}

.m-setup-missing ul {
  list-style: none;
  margin: 0;
  padding: 4px 14px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.m-setup-missing li {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: .88rem;
  color: var(--text-mid, var(--text-muted));
  line-height: 1.4;
}

.m-setup-missing li::before {
  content: "\203A";
  flex: 0 0 auto;
  width: 12px;
  text-align: center;
  color: var(--primary);
  font-weight: 800;
  font-size: 1.05rem;
  line-height: 1;
}

/* Page-level variant (settings.asp): full-width banner rather than the
   tightened-into-the-modal spacing the popup uses. */
.pf-setup-banner {
  margin: 16px 0 0;
}

@media (max-width: 520px) {
  .m-setup-modal {
    padding: 32px 24px 28px;
  }
}

/* ── Responsive ── */
@media (max-width: 900px) {
  .m-stats-hero { padding: 28px 24px; }
  .m-stats-grid { grid-template-columns: repeat(2, 1fr); }
}

/* Collapse point. Raised from 768px to 920px: with the Settings tab added, the
   bar needs ~1200px at full size and still ~915px even fully tightened (narrow
   wordmark, reduced gutters, tighter tab padding and cluster gap -- see the
   921-1200px block above). Below that it can only overflow, so it goes to the
   hamburger instead of cramming. */
@media (max-width: 920px) {
  .m-nav-tabs  { display: none; }
  .m-secnav    { display: none; }
  .m-hamburger { display: flex; }
  .m-nav-logo  { margin-right: auto; }

  /* Match the public bar's mobile height. */
  .members-body { --m-nav-h: 76px; }

  /* The menu collapses to the hamburger here and the strip hides with it, so
     the switcher areas hand the bar back its normal mobile height. Zeroing
     --m-secnav-h also drops .m-nav-inner's top padding and cancels the
     centering nudge on the logo / account cluster, all of which key off it. */
  .member-area-bg,
  .employer-area-bg { --m-secnav-h: 0px; --m-nav-h: 76px; }

  /* The GigFinder Jobs wordmark is 8.2:1, so a fixed height blows past the bar
     width once the badge, right cluster and hamburger take their share. Cap the
     height at Connect's 40px, but drive the size off width so it can never
     overflow a narrow bar. flex-shrink stays 0 from the base rule; max-width
     does the limiting instead. */
  .m-nav-logo-img { height: auto; width: auto; max-width: 40vw; max-height: 40px; }
}

@media (max-width: 480px) {
  /* Same shorthand caveat as the 769px rule: keep --m-secnav-h as the top
     value. It resolves to 0 here (the strip is hidden below 768px), so this is
     identical to `0 16px` today -- but it can't silently eat the strip band if
     the mobile treatment ever changes. */
  .m-nav-inner { padding: var(--m-secnav-h, 0px) 16px 0; }
  .m-content   { padding: 20px 16px; }
  .m-stats-hero { padding: 20px 16px; min-height: unset; }
  .m-stats-grid { gap: 12px; }
  .m-stat-card  { padding: 16px 14px; min-height: 140px; }
  .m-stat-num   { font-size: 1.5rem; }
  .m-stat-num--word { font-size: 1rem; }
  .m-section    { padding: 20px 16px; }
}


/* =============================================
   Profile Setup Wizard  (/members/profile_wizard.asp)
   ============================================= */

/* Overlay backdrop — hidden until pwOpen() adds .pw-open */
.pw-page {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 2000;
  background: rgba(15, 23, 42, 0.82);
  align-items: center;
  justify-content: center;
  padding: 24px;
  animation: pwFadeIn 0.22s ease;
}
.pw-page.pw-open {
  display: flex;
}
.pw-page.pw-closing {
  animation: pwFadeOut 0.24s ease forwards;
}

@keyframes pwFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes pwFadeOut {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* Wizard card */
.pw-modal {
  background: var(--white);
  border-radius: 20px;
  box-shadow: 0 28px 90px rgba(0, 0, 0, 0.30);
  width: 100%;
  max-width: 860px;
  height: 82vh;
  max-height: 780px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: pwSlideIn 0.22s ease;
}
.pw-closing .pw-modal {
  animation: pwSlideOut 0.24s ease forwards;
}

@keyframes pwSlideIn {
  from { transform: translateY(18px); opacity: 0; }
  to   { transform: translateY(0);    opacity: 1; }
}
@keyframes pwSlideOut {
  from { transform: translateY(0);    opacity: 1; }
  to   { transform: translateY(18px); opacity: 0; }
}

/* ── Header ── */
.pw-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 24px 32px 22px;
  background: var(--card-header-grad);
  flex-shrink: 0;
}

.pw-header-left {
  display: flex;
  align-items: center;
  gap: 16px;
}

.pw-wand {
  font-size: 2.6rem;
  line-height: 1;
  flex-shrink: 0;
}

.pw-title {
  font-size: 1.4rem;
  font-weight: 800;
  color: #fff;
  margin: 0;
  line-height: 1;
  position: relative;
  top: 2px;
  letter-spacing: -.02em;
}

.pw-review-badge {
  display: inline-block;
  padding: 5px 11px;
  background: #F59E0B;
  color: #fff;
  font-size: .72rem;
  font-weight: 800;
  letter-spacing: .14em;
  border-radius: 4px;
  line-height: 1;
  box-shadow: 0 0 0 2px rgba(245,158,11,.28);
  text-transform: uppercase;
  position: relative;
  top: 1px;
}

.pw-review-note {
  margin: 0 0 18px;
  padding: 12px 14px;
  background: #FFFBEB;
  border: 1px solid #FCD34D;
  border-left: 4px solid #F59E0B;
  border-radius: 6px;
  color: #78350F;
  font-size: .9rem;
  line-height: 1.5;
}

.pw-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  background: rgba(255,255,255,.14);
  border: 1px solid rgba(255,255,255,.22);
  border-radius: 50%;
  color: #fff;
  cursor: pointer;
  transition: var(--transition);
  flex-shrink: 0;
  text-decoration: none;
}
.pw-close svg { stroke-width: 2.2; }
.pw-close:hover {
  background: rgba(255,255,255,.28);
  border-color: rgba(255,255,255,.4);
}
.pw-close:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

/* ── Progress bar ── */
.pw-progress-wrap {
  height: 5px;
  background: var(--border);
  flex-shrink: 0;
}

.pw-progress-fill {
  height: 100%;
  background: var(--success);
  transition: width 0.45s ease;
  width: 16.67%; /* default: step 1 of 6 */
}

/* ── Scrollable body ── */
.pw-body {
  flex: 1;
  overflow-y: auto;
  min-height: 0;
  scroll-behavior: smooth;
}

/* ── Step panels ── */
.pw-step { display: none; }

.pw-step--active {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  min-height: 100%;
  padding: 36px 40px 28px;
  box-sizing: border-box;
}

.pw-step-intro  { margin-bottom: 28px; }

.pw-step-title {
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--text-dark);
  margin: 0 0 7px;
}

.pw-step-desc {
  font-size: .9rem;
  color: var(--text-muted);
  margin: 0;
  line-height: 1.6;
}

/* ── Form layout ── */
.pw-form {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.pw-row {
  display: grid;
  gap: 16px;
}
.pw-row--2 { grid-template-columns: 1fr 1fr; }
.pw-row--3 { grid-template-columns: 1fr 1fr 1fr; }

/* ---- Password field treatment, shared with /signup.asp and
   /members/resume_setup.asp so every account-setup path looks and behaves the
   same. Those two carry their own copies inline because neither loads this
   file; if you change one, change all three.

   NOTE ON THE PREFIX: pw- in the surrounding rules means PROFILE WIZARD, not
   password. .pw-wrap / .pw-eye below are the password ones. Confusing, but
   renaming the wizard prefix would touch far more than this. ---- */

/* Full width error line ABOVE a field pair. Unlike a per-field span it wraps,
   so it can carry a whole sentence explaining the rule that failed, and it
   collapses to nothing when empty so it costs no space until it speaks.
   Placed above rather than below because the password row tends to be the last
   thing on its pane, and a message underneath lands below the fold where the
   user sees only a red border with nothing explaining it. */
.fld-err-block {
  color: #EF4444;
  font-size: .82rem;
  font-weight: 600;
  line-height: 1.35;
  margin: 0 0 8px;
  padding: 7px 10px;
  border-left: 3px solid #EF4444;
  border-radius: 4px;
  background: #FEF2F2;
}
.fld-err-block:empty { display: none; }

/* Show/hide toggle inside a password input. The wrapper is what the button
   positions against, so the input keeps its existing styling untouched.
   padding-right keeps typed text clear of the button. */
.pw-wrap { position: relative; }
.pw-wrap input { padding-right: 44px; }
.pw-eye {
  position: absolute;
  top: 50%;
  right: 7px;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: #9CA3AF;
  cursor: pointer;
  line-height: 0;
}
.pw-eye:hover  { color: #4B5563; background: #F3F4F6; }
.pw-eye:focus-visible { outline: 2px solid #1E40AF; outline-offset: 1px; }
/* Explicit display: the global img/svg block rule must not decide this, and
   the two icons swap on .is-on rather than being rebuilt in JS. */
.pw-eye svg { display: block; }
.pw-eye .pw-eye-off { display: none; }
.pw-eye.is-on .pw-eye-on  { display: none; }
.pw-eye.is-on .pw-eye-off { display: block; }
.pw-eye.is-on { color: #1E40AF; }

.pw-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.pw-label {
  font-size: .8rem;
  font-weight: 600;
  color: var(--text-mid);
  letter-spacing: .025em;
  text-transform: uppercase;
}

.pw-optional {
  font-weight: 400;
  color: var(--text-muted);
  text-transform: none;
  letter-spacing: 0;
  font-size: .78rem;
}

.pw-readonly-tag {
  display: inline-block;
  font-size: .68rem;
  font-weight: 500;
  background: #F1F5F9;
  color: #94A3B8;
  border: 1px solid #E2E8F0;
  border-radius: 4px;
  padding: 1px 6px;
  margin-left: 5px;
  vertical-align: middle;
  text-transform: none;
  letter-spacing: 0;
}

.pw-input {
  width: 100%;
  padding: 10px 14px;
  font-size: .9rem;
  font-family: var(--font);
  color: var(--text-dark);
  background: var(--white);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-sm);
  outline: none;
  transition: border-color var(--transition), box-shadow var(--transition);
  appearance: none;
}
.pw-input:focus {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-light);
}
.pw-input--readonly {
  background: #F8FAFC;
  color: #9CA3AF;
  cursor: not-allowed;
}
.pw-select {
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M2 4L6 8L10 4' stroke='%236B7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 34px;
}

.pw-divider {
  height: 1px;
  background: var(--border);
  margin: 20px 0;
}

/* ── Choice cards (SELECT ONE) ── */
.pw-choices {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
  margin-top: 4px;
}

.pw-choices--3 .pw-choice {
  flex: 1;
  min-width: 180px;
}

.pw-choices--wrap {
  flex-wrap: wrap;
  justify-content: center;
}
.pw-choices--wrap .pw-choice {
  flex: 0 1 calc(33.33% - 14px);
  min-width: 180px;
}

.pw-choice {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 32px 20px 28px;
  background: var(--white);
  border: 2px solid var(--border);
  border-radius: 16px;
  cursor: pointer;
  text-align: center;
  transition: border-color 0.2s, background 0.2s, transform 0.18s, box-shadow 0.2s;
  font-family: var(--font);
}
.pw-choice:hover {
  border-color: var(--primary);
  background: var(--primary-light);
  transform: translateY(-3px);
  box-shadow: 0 8px 28px rgba(79, 70, 229, 0.12);
}
.pw-choice:focus-visible {
  outline: 3px solid var(--primary);
  outline-offset: 2px;
}
.pw-choice--selected {
  border-color: var(--primary);
  background: var(--primary-light);
  box-shadow: 0 0 0 4px rgba(79, 70, 229, 0.12),
              0 8px 28px rgba(79, 70, 229, 0.14);
}

.pw-choice-icon {
  color: var(--text-muted);
  transition: color 0.2s;
  line-height: 1;
}
.pw-choice:hover .pw-choice-icon,
.pw-choice--selected .pw-choice-icon { color: var(--primary); }

.pw-choice-title {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text-dark);
  line-height: 1.2;
}

.pw-choice-desc {
  font-size: .82rem;
  color: var(--text-muted);
  line-height: 1.55;
}

/* Step 3 (Full-Time & Part-Time Jobs): shrink cards so the salary slider below
   is visible without scrolling on initial render. */
#pwStep3 .pw-choices { gap: 12px; }
#pwStep3 .pw-choice {
  padding: 14px 14px 12px;
  gap: 6px;
  border-radius: 12px;
}
#pwStep3 .pw-choice-icon svg { width: 30px; height: 30px; }
#pwStep3 .pw-choice-title { font-size: .92rem; }
#pwStep3 .pw-choice-desc  { font-size: .74rem; line-height: 1.4; }

/* ── Footer nav ── */
.pw-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 32px;
  border-top: 1px solid var(--border);
  background: #FAFBFC;
  flex-shrink: 0;
}

/* Step dots */
.pw-step-dots {
  display: flex;
  align-items: center;
  gap: 8px;
}

.pw-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--border);
  transition: background 0.25s, transform 0.25s;
}
.pw-dot--active {
  background: var(--primary);
  transform: scale(1.4);
}
.pw-dot--done {
  background: var(--primary);
  opacity: 0.35;
}

/* Prev / Next buttons */
.pw-nav-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 10px 22px;
  font-size: .88rem;
  font-weight: 600;
  font-family: var(--font);
  border-radius: var(--radius-sm);
  border: 1.5px solid var(--border);
  cursor: pointer;
  transition: var(--transition);
  line-height: 1;
}
.pw-nav-btn:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

.pw-nav-btn--prev {
  background: var(--white);
  color: var(--text-mid);
}
.pw-nav-btn--prev:hover:not(:disabled) {
  border-color: var(--primary);
  color: var(--primary);
  background: var(--primary-light);
}
.pw-nav-btn--prev:disabled {
  opacity: 0.38;
  cursor: not-allowed;
}

.pw-nav-btn--next {
  background: var(--primary);
  color: var(--white);
  border-color: var(--primary);
}
.pw-nav-btn--next:hover:not(:disabled) {
  background: var(--primary-dark);
  border-color: var(--primary-dark);
}
.pw-nav-btn--next:disabled,
.pw-nav-btn--saving {
  opacity: 0.6;
  cursor: not-allowed;
}

/* Saving spinner */
.pw-spinner {
  display: inline-block;
  width: 13px;
  height: 13px;
  border: 2px solid rgba(255,255,255,0.35);
  border-top-color: #fff;
  border-radius: 50%;
  animation: pwSpin 0.65s linear infinite;
  flex-shrink: 0;
}
@keyframes pwSpin {
  to { transform: rotate(360deg); }
}

/* ── Toast notification ── */
.pw-toast {
  position: fixed;
  bottom: 32px;
  left: 50%;
  transform: translateX(-50%) translateY(14px);
  background: #1E293B;
  color: #fff;
  font-size: .875rem;
  font-weight: 500;
  font-family: var(--font);
  padding: 12px 24px;
  border-radius: 40px;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.20);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.24s ease, transform 0.24s ease;
  z-index: 9999;
  white-space: nowrap;
  max-width: calc(100vw - 48px);
}
.pw-toast--show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
.pw-toast--error   { background: #DC2626; }
.pw-toast--success { background: var(--success); }

/* ── Step sub-section (combined steps) ── */
.pw-step-section       { margin-top: 26px; padding-top: 0; }
.pw-step-section-title { font-size: 1.05rem; font-weight: 700; color: var(--text-dark); margin: 0 0 6px; }

/* ── Salary Slider (Step 5) ── */
.pw-salary-wrap        { display: flex; flex-direction: column; gap: 0; padding: 12px 0 0; }

.pw-salary-slider-wrap { position: relative; padding: 54px 44px 0; margin-bottom: 6px; }

.pw-salary-tooltip {
  position: absolute; top: 0;
  transform: translateX(-50%);
  background: var(--primary); color: #fff;
  font-size: .95rem; font-weight: 700; font-family: var(--font);
  padding: 9px 18px; border-radius: 8px;
  white-space: nowrap; pointer-events: none;
  transition: left 0.06s linear;
}
.pw-salary-tooltip::after {
  content: "";
  position: absolute; bottom: -7px; left: 50%;
  transform: translateX(-50%);
  border: 7px solid transparent;
  border-bottom: none;
  border-top-color: var(--primary);
}

/* Custom range input */
:root { --sal-track: #E2E8F0; }

/* The fill is the element's own background, repainted by PwSlider.place (see
   /includes/slider_inc.asp). The 0% start is a deliberate "not measured yet"
   state: it used to read 19%, which drew a real-looking fill at a value nobody
   had chosen whenever the script had not run yet. */
.pw-salary-slider {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 6px;
  border-radius: 3px; outline: none; cursor: pointer;
  background: linear-gradient(to right, var(--primary) 0%, var(--sal-track) 0%);
}
.pw-salary-slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 24px; height: 24px; border-radius: 50%;
  background: #fff;
  border: 2.5px solid var(--primary);
  box-shadow: 0 0 0 3px rgba(30,64,175,0.15);
  cursor: pointer; transition: box-shadow 0.15s;
}
.pw-salary-slider::-webkit-slider-thumb:hover,
.pw-salary-slider:focus::-webkit-slider-thumb {
  box-shadow: 0 0 0 5px rgba(30,64,175,0.22);
}
.pw-salary-slider::-moz-range-thumb {
  width: 24px; height: 24px; border-radius: 50%;
  background: #fff;
  border: 2.5px solid var(--primary);
  box-shadow: 0 0 0 3px rgba(30,64,175,0.15);
  cursor: pointer;
}
/* MUST stay transparent. Firefox paints ::-moz-range-track OVER the input's own
   background, so giving this a colour hid the gradient completely and every
   slider on the site rendered as an empty track in Firefox no matter what the
   value was. The track colour is the second stop of that gradient instead. */
.pw-salary-slider::-moz-range-track {
  height: 6px; border-radius: 3px; background: transparent;
}
/* Disabled sliders (a gating toggle turned its column off) keep the fill but
   drop the grab cursor, so the control reads as inert rather than broken. */
.pw-salary-slider:disabled { cursor: not-allowed; }
.pw-salary-slider:disabled::-webkit-slider-thumb { cursor: not-allowed; }
.pw-salary-slider:disabled::-moz-range-thumb     { cursor: not-allowed; }

.pw-salary-desc   { font-size: .9rem; color: var(--text-mid); font-weight: 500;
                    margin: 10px 0 0; text-align: center; }

/* ── Job Type Preferences | Contract Marketplace split card ── */
/* Banner heading over BOTH halves of the split. One size up from
   .pf-section-title (1rem), which the two column headings still use, so the
   hierarchy reads section > column rather than two competing titles. The rule
   under it spans the full width and visually ties the halves together, which the
   vertical divider between them otherwise works against. */
.pf-jt-split-title {
  margin: 0 0 8px;
  font-size: 1.18rem;
  font-weight: 700;
  color: var(--text-dark);
  text-align: center;
  letter-spacing: -.01em;
}
/* Section intro, describing BOTH halves. The dividing rule lives on this rather
   than on the title, so the line falls below the whole header block (title +
   intro) and separates it from the two columns -- putting it on the title would
   have stranded the intro on the columns' side of the rule.
   Full width so the rule spans both halves, but the TEXT is capped and centered:
   at the section's full width a three-sentence centered paragraph would run to
   uncomfortably long lines. */
.pf-jt-split-desc {
  margin: 0 0 24px;
  /* Horizontal padding narrows the TEXT while the border-bottom still spans the
     element's full width, so the rule ties both halves together without the
     paragraph running to uncomfortably long centered lines. A max-width would
     have shortened the rule too. */
  padding: 0 8% 16px;
  border-bottom: 1px solid var(--border);
  font-size: .9rem;
  line-height: 1.55;
  color: var(--text-muted);
  text-align: center;
}
@media (max-width: 860px) {
  /* The columns stack here, so reclaim the side padding for the text. */
  .pf-jt-split-desc { padding: 0 0 16px; }
}
.pf-jt-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 36px;
  align-items: start;
}
/* Vertical divider between the two halves, drawn on the right column so it
   collapses cleanly when the layout stacks on narrow screens. */
.pf-jt-col--contract {
  border-left: 1px solid var(--border);
  padding-left: 36px;
}
@media (max-width: 860px) {
  .pf-jt-split { grid-template-columns: 1fr; gap: 28px; }
  .pf-jt-col--contract {
    border-left: none;
    border-top: 1px solid var(--border);
    padding-left: 0;
    padding-top: 28px;
  }
}

/* Connect logo at the top of the contract half. */
.pf-connect-logo-wrap { margin: 0 0 14px; }
.pf-connect-logo      { height: 34px; width: auto; display: block; }

/* Profile-setup wizard step 3 ("Full-Time & Part-Time Jobs"). The participation
   toggle that briefly headed this step was removed 2026-07-28 (it was pinned ON
   and unclickable), taking its #pwStep3 .pw-toggle-row fill with it. The gap
   stays: .pw-choices carries only margin-top:4px, which now leaves the job-type
   cards riding up against the step intro. */
#pwStep3 .pw-choices    { margin-top: 18px; }

/* Profile-setup wizard step 4 reuses the same logo + contract fields. The logo
   stands in for the step title there, so it runs a little larger. */
#pwStep4 .pf-connect-logo-wrap { margin: 0 0 18px; }
#pwStep4 .pf-connect-logo      { height: 44px; }

/* The shared --bg-alt (#E9ECEF) reads heavy against this step's white fields,
   so the filled cells here sit a few shades lighter. Scoped to the step: the
   same classes elsewhere keep the standard tone. The matching
   #pwStep4 .pw-toggle-row rule went with the enrollment toggle (2026-07-28). */
#pwStep4 .pw-input:disabled  { background: #FBFCFD; }

/* The intro carries the whole explanation of the marketplace on this step (no
   step title above it), so it runs one notch up from the .pw-step-desc default
   of .9rem. Field descriptions further down keep the standard size. */
#pwStep4 .pw-step-intro .pw-step-desc { font-size: 1rem; }

/* .pw-step-desc is margin:0 (steps normally carry one paragraph), so the
   intro's stacked paragraphs would run together without this. */
#pwStep4 .pw-step-intro .pw-step-desc + .pw-step-desc { margin-top: 14px; }

/* Pull the enrollment note up under the intro. The shared .pw-step-intro
   margin-bottom (28px) plus .pw-inline-note margin-top (10px) left a 38px
   trench between the intro copy and a note that reads as part of it. Scoped to
   this step: every other step's intro is followed by form sections that want
   the full 28px. */
#pwStep4 .pw-step-intro  { margin-bottom: 12px; }
#pwStep4 .pw-inline-note { margin-top: 0; }

/* Opted-out state: dim the bio/rate/hours controls but keep them readable. */
.pf-contract-fields--off { opacity: .5; }
.pf-contract-fields--off .pw-salary-slider,
.pf-contract-fields--off input,
.pf-contract-fields--off textarea { cursor: not-allowed; }

/* Same treatment for the LEFT half, driven by the "Participate in Full-Time and
   Part-Time jobs" toggle (2026-07-28). That toggle replaced the old Contract Only
   pill; off means members.job_type = 3, so the pills and Desired Salary below it
   no longer apply and go inert. Kept as its own class rather than reusing
   .pf-contract-fields--off, because the two halves hold different controls and
   are likely to diverge. */
/* Breathing room under the participation toggle. .pw-toggle-row is a filled,
   bordered box and .pf-opts carries no top margin, so without this the pills sat
   flush against the bottom edge of that box. Put on the wrapper rather than on
   .pf-opts, which is shared by every pill row on the page (work model, salary
   basis, categories) and should not all gain a gap. */
.pf-std-fields { margin-top: 18px; }
.pf-std-fields--off { opacity: .5; }
.pf-std-fields--off .pw-salary-slider,
.pf-std-fields--off .pf-opt,
.pf-std-fields--off input { cursor: not-allowed; }

/* Tighten the gap between contract sub-sections (bio / rate / hours) to
   conserve vertical space in the split card. */
.pf-contract-fields .pw-step-section { margin-top: 16px; }

/* Short-bio textarea + live character count. */
.pf-contract-bio {
  width: 100%;
  min-height: 96px;
  resize: vertical;
  margin-top: 12px;
  font-family: var(--font);
  line-height: 1.45;
}
.pf-bio-count {
  margin-top: 6px;
  font-size: .75rem;
  color: var(--text-muted);
  text-align: right;
}

/* Weekly-availability from/to inputs. */
.pf-hours-row {
  display: flex;
  align-items: flex-end;
  gap: 14px;
  margin-top: 12px;
}
.pf-hours-field       { display: flex; flex-direction: column; gap: 5px; }
.pf-hours-field label {
  font-size: .65rem; font-weight: 700; letter-spacing: .08em;
  color: var(--text-muted); text-transform: uppercase;
}
.pf-hours-input-wrap  { display: flex; align-items: center; gap: 8px; }
.pf-hours-input-wrap .pw-input { width: 90px; }
.pf-hours-suffix      { font-size: .85rem; color: var(--text-muted); white-space: nowrap; }
.pf-hours-dash        { padding-bottom: 10px; color: var(--text-muted); font-weight: 600; }

.pw-salary-tabs   { display: flex; border: 1.5px solid var(--primary);
                    border-radius: var(--radius-sm); overflow: hidden;
                    align-self: center; }
.pw-salary-tab    { padding: 7px 16px; font-size: .85rem; font-weight: 600;
                    font-family: var(--font); background: none; border: none;
                    color: var(--primary); cursor: pointer;
                    transition: background 0.15s, color 0.15s; }
.pw-salary-tab + .pw-salary-tab { border-left: 1.5px solid var(--primary); }
.pw-salary-tab--active { background: var(--primary); color: #fff; }
.pw-salary-tab:hover:not(.pw-salary-tab--active) { background: var(--primary-light); }

/* ── AI Matching & Notifications (Step 7) ── */
.pw-notify-block       { margin-bottom: 28px; }
.pw-notify-block:last-child { margin-bottom: 0; }

.pw-toggle-list        { display: flex; flex-direction: column; gap: 12px; margin-top: 16px; }

.pw-toggle-row         { display: flex; align-items: center; gap: 16px;
                         padding: 16px 20px; background: var(--bg-alt);
                         border: 1px solid var(--border); border-radius: var(--radius-sm); }
.pw-toggle-info        { flex: 1; }
.pw-toggle-label       { font-size: .9rem; font-weight: 600; color: var(--text-dark); line-height: 1.3; }
.pw-toggle-desc        { font-size: .9rem; color: var(--text-muted); margin-top: 3px; }

.pw-toggle {
  position: relative; flex-shrink: 0;
  width: 46px; height: 26px; border-radius: 13px;
  background: #CBD5E1; border: none; cursor: pointer;
  transition: background 0.2s; padding: 0;
}
.pw-toggle[aria-checked="true"]  { background: var(--primary); }
.pw-toggle:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

.pw-toggle-thumb {
  position: absolute; top: 3px; left: 3px;
  width: 20px; height: 20px; border-radius: 50%;
  background: #fff; transition: transform 0.2s;
  box-shadow: 0 1px 4px rgba(0,0,0,.18);
  pointer-events: none;
}
.pw-toggle[aria-checked="true"] .pw-toggle-thumb { transform: translateX(20px); }

/* Tier-locked toggle: forced OFF, visibly dim, click ignored. */
.pw-toggle--locked,
.pw-toggle[disabled] {
  background: #E5E7EB !important;
  cursor: not-allowed;
  opacity: 0.55;
}
.pw-toggle--locked:focus-visible,
.pw-toggle[disabled]:focus-visible { outline: none; }

/* Pinned toggle: forced ON, not clickable. The opposite of --locked above, so
   it must KEEP the checked color -- only the cursor and a slight dim say it
   cannot be changed. Used for marketplace enrollment under a Contract Only
   job type. */
.pw-toggle--pinned { cursor: not-allowed; opacity: .75; }
.pw-toggle--pinned:focus-visible { outline: none; }

/* Inline explainer inside a wizard step. Only the step 4 enrollment note uses
   it now; the step 3 salary stand-in went with the Contract Only card. */
.pw-inline-note {
  margin: 10px 0 0;
  padding: 10px 14px;
  border-left: 3px solid var(--primary);
  background: var(--bg-alt);
  border-radius: var(--radius-sm);
  font-size: .85rem;
  line-height: 1.45;
  color: var(--text-muted);
}

/* Enrollment note (step 4): green, because it reports something the member has
   GAINED (marketplace access + free Apply Credits) rather than something they
   still owe. Its own modifier instead of restyling .pw-inline-note, so the base
   class stays neutral for any future note. #F0FDF4 is the light-light fill,
   #16A34A the rule, #15803D the text -- the same darker green already used for
   .m-thresh-saved, kept for contrast against that pale a background (--success,
   #10B981, is too light to read as body text here). */
.pw-inline-note--enroll {
  border-left-color: #16A34A;
  background: #F0FDF4;
  color: #15803D;
}
.pw-inline-note--enroll strong { color: #14532D; }

/* ── Completion Screen (Step 8) ── */
.pw-complete        { display: flex; flex-direction: column; align-items: center;
                      justify-content: center; text-align: center;
                      flex: 1; gap: 18px; padding: 8px 0; }

.pw-complete-headline { font-size: 2.4rem; font-weight: 800; color: var(--text-dark);
                        letter-spacing: -.02em; line-height: 1; }

.pw-complete-check  { width: 90px; height: 90px; border-radius: 50%;
                      background: #16A34A; display: flex; align-items: center;
                      justify-content: center; flex-shrink: 0;
                      box-shadow: 0 0 0 10px rgba(22,163,74,0.12); }
.pw-complete-check svg { width: 46px; height: 46px; }

.pw-complete-title  { font-size: 1.6rem; font-weight: 700; color: var(--text-dark);
                      margin: 0; }
.pw-complete-sub    { font-size: 1.05rem; color: var(--text-mid); max-width: 420px;
                      line-height: 1.65; margin: 0; }
.pw-complete-note   { font-size: .9rem; color: var(--text-muted); max-width: 400px;
                      line-height: 1.6; margin: 0; border-top: 1px solid var(--border);
                      padding-top: 18px; }

/* ── Category Picker (Step 4) ── */
.pw-cat-wrap        { display: flex; flex-direction: column; gap: 14px; }

.pw-cat-sel-area    { display: flex; flex-wrap: wrap; gap: 10px; min-height: 52px;
                      align-items: center; padding: 14px 18px;
                      background: var(--bg-alt); border-radius: var(--radius-sm);
                      border: 1px solid var(--border); }
.pw-cat-none        { font-size: .85rem; color: var(--text-muted); }

.pw-cat-browser     { max-height: 280px; overflow-y: auto;
                      border: 1px solid var(--border); border-radius: var(--radius-sm);
                      padding: 8px 16px 16px; }
.pw-cat-group       { margin-bottom: 16px; }
.pw-cat-group:last-child { margin-bottom: 0; }
.pw-cat-group-label { font-size: .72rem; font-weight: 700; color: var(--text-muted);
                      text-transform: uppercase; letter-spacing: .06em;
                      margin-bottom: 8px; }

.pw-cat-pills       { display: flex; flex-wrap: wrap; gap: 7px; }

.pw-cat-pill        { display: inline-flex; align-items: center; gap: 5px;
                      padding: 5px 13px; border: 1.5px solid var(--border);
                      border-radius: 40px; font-size: .83rem; font-weight: 500;
                      color: var(--text-mid); background: var(--white);
                      cursor: pointer; transition: var(--transition);
                      font-family: var(--font); line-height: 1.3; }
.pw-cat-pill:hover  { border-color: var(--primary); color: var(--primary);
                      background: var(--primary-light); }
.pw-cat-pill--sel   { border-color: var(--primary); background: var(--primary-light);
                      color: var(--primary); }
.pw-cat-pill-x      { font-size: .88rem; line-height: 1; opacity: .65; }

.pw-cat-empty       { font-size: .875rem; color: var(--text-muted);
                      padding: 24px 0; text-align: center; }

/* ── Responsive ── */
@media (max-width: 700px) {
  .pw-page.pw-open { padding: 0; align-items: flex-end; }
  .pw-modal        { border-radius: 20px 20px 0 0; height: 94vh; max-height: 94vh; }
  .pw-step--active { padding: 24px 20px 20px; }
  .pw-header  { padding: 18px 20px 16px; }
  .pw-footer  { padding: 14px 20px; }
  .pw-row--2,
  .pw-row--3  { grid-template-columns: 1fr; }
  .pw-choices { flex-direction: column; }
  .pw-choice  { flex-direction: row; text-align: left; padding: 20px; gap: 16px; }
  .pw-choice-icon { flex-shrink: 0; }
}


/* ============================================================
   Job cards -- shared between search-jobs.asp (results list) and
   job-detail.asp (single-job detail view). Each card renders a
   purple-gradient header bar with the job title + posted date,
   followed by a body containing title/employer, pills, location,
   stat strip, and (on the search page only) a clamped description
   excerpt + Details button.
============================================================ */

.mj-cards {
  display: flex;
  flex-direction: column;
  gap: 36px;
  padding: 20px 0;
}
.mj-card {
  display: flex;
  flex-direction: column;
  /* min-width:0 caps the min-content width this card publishes to .mj-cards.
     .mj-cards is a column flex, so its own min-content width is the widest
     child's -- without this, one over-long skill pill or category name deep
     inside a card widened every card on the page. Paired with the max-width +
     ellipsis on .mj-pill / .mj-skill-pill; both halves are required. */
  min-width: 0;
  height: 580px;
  background: #fff;
  border: 1px solid #E5E7EB;
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(15,23,42,.05);
  /* Positioning context for .mj-card-partner-flag, which is pinned to the
     card's bottom-left corner. */
  position: relative;
}
.mj-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  /* Horizontal padding matches .mj-card-body (20px) so the category text in the
     strip lines up with the job title and everything else in the card below it.
     Was 14px, which left the strip text sitting 6px to the left of the column. */
  padding: 18px 20px;
  background: var(--card-header-grad);
  color: #fff;
  flex: 0 0 auto;
}
/* Applied state -- the member already submitted an application for this job.
   Green gradient (Tailwind green-600 -> green-700) so the card visually
   confirms an applied status at a glance. */
.mj-card-header--applied {
  background: linear-gradient(135deg, #16A34A 0%, #15803D 100%);
}
/* Applied state on the search-jobs / search-contracts listings -- purple
   gradient (Tailwind violet-600 -> violet-800) so an already-applied job or
   contract reads as a distinct state at a glance, separate from the green/blue
   section strips. Scoped to those two listings via the --applied-p class;
   job-detail.asp keeps the green .mj-card-header--applied treatment above. */
.mj-card-header--applied-p {
  background: linear-gradient(135deg, #5B21B6 0%, #3B0764 100%);
}
/* Withdrawn state -- the member applied but later withdrew. Gray gradient
   (Tailwind gray-500 -> gray-600) so the strip reads as inactive/neutral
   rather than the active purple or the green applied treatment. */
.mj-card-header--withdrawn {
  background: linear-gradient(135deg, #6B7280 0%, #4B5563 100%);
}
/* Terminal state (job-detail) -- any non-in-flight status (Not Selected,
   Withdrawn, or Job Closed). Gray gradient (same as withdrawn) so the strip
   reads as inactive rather than a loud red bar; the red-vs-neutral
   distinction is carried by the status cells below it. */
.mj-card-header--closed {
  background: linear-gradient(135deg, #6B7280 0%, #4B5563 100%);
}
/* Category text in the header strip. Toned down: smaller and a muted gray-white
   rather than solid #fff, so it reads as a label on the strip instead of
   competing with the job title directly below it. */
.mj-card-title {
  margin: 0;
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: -.01em;
  color: rgba(255, 255, 255, .72);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1 1 auto;
  min-width: 0;
}
.mj-card-title-id {
  font-size: .65rem;
  font-weight: 500;
  opacity: .75;
  margin-left: 4px;
  vertical-align: middle;
}
.mj-card-date {
  font-size: .8rem;
  font-weight: 600;
  color: rgba(255,255,255,.9);
  white-space: nowrap;
  flex: 0 0 auto;
}

/* ── Listing strips (search-jobs / search-contracts) ──────────────────────
   Those two pages dropped the posted date from the strip and centred the
   category across it. Everything here is gated behind --listing so the strips
   on job-detail, the dashboard and the applications list are untouched.

   The category is taken OUT OF FLOW to centre. The strip is a space-between
   flex row, so an in-flow title can only ever centre itself in the gap left of
   whatever else is in the row -- fine on the cards that carry nothing else,
   visibly off-centre on the applied ones that still show an "Applied:" label.
   Absolute + left:50% is the one treatment that is right for both.

   Because it leaves the flow, an APPLIED card's label is the only thing left
   sizing the strip and a card with no label has nothing at all -- hence the
   explicit min-height, which is what the strip measured before (18px padding
   twice, plus a line of text). box-sizing is stated rather than assumed: this
   number is a whole strip height, and it would be 36px short read as content. */
.mj-card-header--listing {
  position: relative;
  box-sizing: border-box;
  min-height: 56px;
}
.mj-card-header--listing .mj-card-title {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  max-width: 76%;
  text-align: center;
  font-size: .95rem;
  /* Up from .72 alpha with the size bump. Centred and larger, it is the strip's
     subject now rather than a label sitting beside one. */
  color: rgba(255, 255, 255, .92);
}
/* Applied cards keep an "Applied:" label on the right. Narrow the centred text
   so a long category ellipsises instead of running under it. The class is set
   in the markup rather than inferred with :has(), which would put a browser
   support question in the way of a card not overlapping itself. */
.mj-card-header--hasmeta .mj-card-title { max-width: 52%; }
/* Sole in-flow item now, and space-between parks a lone item at the START --
   without this the label jumps to the left of the strip. */
.mj-card-header--listing .mj-card-date { margin-left: auto; }
.mj-card-body {
  flex: 1 1 auto;
  /* Same reason as .mj-card above: this is the column flex the pill rows sit in,
     so it has to refuse to grow to its widest child too. */
  min-width: 0;
  /* Bottom padding sets how far the bottom content sits off the card's lower
     edge. Search-result cards (search-jobs / search-contracts) carry a pinned
     .mj-card-footer and override this below to reserve its space; the
     job-detail card (.jd-card-body) has no footer and keeps this 16px. */
  padding: 18px 20px 16px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 14px;
  font-size: .88rem;
  color: var(--text-mid, #475569);
}
/* Search-result cards only: reserve space for the absolutely-pinned
   .mj-card-footer (View Job + Verified Employer) so the description above never
   renders underneath it. Excludes the job-detail card, which has no footer.
   The reserve exceeds the footer's rendered height so there's a clear gap
   between the (clipped) description and the button's top edge. */
.mj-card-body:not(.jd-card-body) {
  padding-bottom: 116px;
}

/* Top row inside the body: title (large, dark) + small posted line on
   the left, then the employer logo right next to it, then the action
   cluster pinned to the right via margin-left:auto on the cluster. */
.mj-card-top {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}
.mj-card-top-text { min-width: 0; flex: 0 1 auto; }
.mj-card-job-title {
  margin: 0;
  font-size: 1.1rem;
  font-weight: 700;
  color: #111827;
  letter-spacing: -.01em;
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mj-card-top-meta {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 3px;
  font-size: .82rem;
  color: #6B7280;
}
.mj-card-employer { color: #6B7280; font-weight: 600; font-size: .95rem; }
.mj-card-top-sep  { color: #9CA3AF; }
.mj-card-top-posted { color: #6B7280; }
/* Guest-mode "Accepting applications until ..." nudge rendered next to
   the Posted date on search-jobs cards + job-detail header. Quiet brand
   blue so it reads as a soft scarcity prompt without competing with the
   posted-on label beside it. */
.mj-card-top-deadline { color: #1E40AF; font-weight: 600; }
.mj-card-top-actions {
  display: flex;
  align-items: flex-start;
  gap: 22px;
  flex: 0 0 auto;
  margin-left: auto;   /* pin to the right edge regardless of how
                          short the title block is */
}

/* "Withdrawn" pill in the card's top-action cluster, just left of the
   bookmark. Light-red treatment mirrors the withdrawn lifecycle pill used
   elsewhere (m-match-card__lc-pill--withdrawn). */
.mj-card-withdrawn-pill {
  flex: 0 0 auto;
  align-self: center;
  display: inline-flex;
  align-items: center;
  padding: 4px 12px;
  border-radius: 999px;
  background: #FEE2E2;
  border: 1px solid #B91C1C;
  color: #991B1B;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* Employer logo (or generic-employer SVG fallback) sandwiched between
   the title block and the action cluster. flex: 0 0 auto and align-self
   stretch so the box matches the height of the title+meta lines without
   shifting the text on its left. */
.mj-card-top-logo {
  flex: 0 0 auto;
  align-self: stretch;
  box-sizing: border-box;   /* keep padding+border inside the stretched
                               height so the box matches the text block
                               exactly, not text-block + padding. */
  width: 110px;              /* landscape-friendly: most employer logos
                                are wider than tall. Height stays pinned
                                to the title+meta rows via stretch. */
  margin-top: 6px;           /* nudge down so the logo box sits closer to
                                the meta line baseline rather than the
                                title cap-height */
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  background: #F9FAFB;
  border: 1px solid #E5E7EB;
  overflow: hidden;
  padding: 4px;
}
.mj-card-top-logo img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}
.mj-card-top-logo svg {
  /* The fallback building icon has a 1:1 viewBox; scale by height so
     it stays square inside the wider landscape box. */
  height: 80%;
  width: auto;
  color: #9CA3AF;
}

.mj-card-airating {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 4px;
  margin: -4px;
  margin-top: 4px;
  color: #4B5563;
  border-radius: 6px;
  flex: 0 0 auto;
  cursor: help;
}
.mj-card-airating-badge {
  position: absolute;
  top: -7px;
  right: -10px;
  min-width: 28px;
  height: 22px;
  padding: 0 6px;
  border-radius: 999px;
  background: #16A34A;
  color: #fff;
  font-size: .72rem;
  font-weight: 700;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid #fff;
  box-shadow: 0 1px 2px rgba(0,0,0,.15);
}
/* Score-based variants. Thresholds match the prototype's matches-table:
   >=80 good (green), >=60 mid (amber), <60 low (red), no record => gray. */
.mj-card-airating-badge--good { background: #16A34A; }
.mj-card-airating-badge--mid  { background: #F59E0B; }
.mj-card-airating-badge--low  { background: #DC2626; }
.mj-card-airating-badge--none { background: #6B7280; }

/* NOTE: .mj-card-bookmark (the old bookmark star) is gone from the member area
   -- search-jobs, search-contracts and job-detail all bookmark with the
   paperclip below. The class survives only inside /contractwork.asp, which
   defines its own copy of these rules locally, so nothing here is left for it.

   Purple "saved!" pulse. JS adds .bm-pulsing to the job-detail Bookmark button
   the moment a job is saved (not on un-save, not on page load) and strips it on
   animationend, so the icon pops once with a brief purple glow. The paperclip
   does NOT use it: it has its own grow-rotate-and-lift transition. */
@keyframes bmSavePulse {
  0%   { transform: scale(1);    filter: drop-shadow(0 0 0 rgba(124,58,237,0)); }
  40%  { transform: scale(1.45); filter: drop-shadow(0 0 7px rgba(124,58,237,.75)); }
  100% { transform: scale(1);    filter: drop-shadow(0 0 0 rgba(124,58,237,0)); }
}
.jd-save-btn.bm-pulsing svg {
  transform-origin: center;
  animation: bmSavePulse .5s ease-out;
}

/* ============================================================
   Paperclip bookmark (search-jobs, search-contracts, job-detail)
   Adopted from the employer Connect contractor grid (.cx-bmk in
   /employers/connect/browse-talent.asp), pointed at the member job bookmark
   endpoint instead. This is THE bookmark control in the member area now; the
   star it replaced is gone from all three pages. ONE button with two looks,
   both anchored to the card's top-left corner:
     OFF: a faint clip sitting inside the header strip, an invitation rather
          than a badge.
     ON:  the same clip grows, rotates and lifts out over the card's top edge
          with a shadow, so it reads as a real paperclip fastened to the card.
   Clicking it in either state flips the bookmark.

   job-detail also keeps its labelled Bookmark button (.jd-save-btn) at the
   bottom of the card. Both carry .js-bookmark-toggle there and are kept in
   step by that page's syncBookmarkButtons.
============================================================ */

/* Opt-in card variant: only the cards that carry a clip go overflow:visible,
   so the clip can hang past the top edge. Everything that previously relied on
   the card to clip it still does: .mj-card-body has its own overflow:hidden,
   the header strip gets the card's top radii below, and the pinned footer
   paints no background of its own. */
.mj-card--clip { overflow: visible; }
/* The clip lives INSIDE the header strip and positions against it, not against
   the card: the card is 580px tall on the listings and taller still on
   job-detail, so a top:50% resting position measured from the card would drop
   the clip into the middle of the white body (where a translucent white icon is
   invisible). The strip is also the thing the clip is visually attached to. */
.mj-card--clip > .mj-card-header {
  position: relative;
  border-radius: 10px 10px 0 0;
}
/* Make room for the resting clip so it never sits on the category text. */
.mj-card--clip > .mj-card-header > .mj-card-title { padding-left: 24px; }

.mj-clip {
  position: absolute;
  /* Centered on the header strip (its offset parent) rather than pinned a fixed
     distance from its top, so it sits on the strip's midline whatever that
     strip's height works out to -- the listing cards and job-detail's taller
     two-line header differ. The translate is folded into the transform, which
     the ON state below overwrites with its rotation as it leaves the strip. */
  top: 50%; left: 14px;
  /* The extra -1px is optical, not geometric: the clip's drawn mass sits low in
     its own box, so a true center reads a hair below the strip's midline. */
  transform: translateY(calc(-50% - 1px)) rotate(0deg);
  width: 17px; height: 17px;
  padding: 0;
  background: none;
  border: 0;
  /* Clearly legible white on the strip, still a step below the strip's own text
     so it reads as an affordance rather than a badge. Goes to full white on
     hover. */
  color: rgba(255, 255, 255, .55);
  cursor: pointer;
  z-index: 6;
  filter: none;
  transition: color .15s ease, transform .15s ease, top .15s ease,
              left .15s ease, width .15s ease, height .15s ease;
}
.mj-clip svg { width: 100%; height: 100%; display: block; }
.mj-clip:hover { color: rgba(255, 255, 255, .95); }
/* Bookmarked: off the strip's top edge (which is the card's top edge) and out
   over the card, rotated, with a shadow under it. */
.mj-clip.is-on {
  top: -7px; left: 16px;
  width: 34px; height: 34px;
  color: #94A3B8;
  transform: rotate(-16deg);
  filter: drop-shadow(0 3px 3px rgba(17, 24, 39, .45));
}
.mj-clip.is-on:hover { color: #6B7280; }
.mj-clip.is-busy { opacity: .45; pointer-events: none; }
/* Guest preview on job-detail: the clip is rendered so the card looks the same
   as it will once they sign up, but it does nothing. No pointer, no hover
   brighten, no tooltip (the button's native title carries the sign-up nudge). */
.mj-clip[disabled] { cursor: default; }
.mj-clip[disabled]:hover { color: rgba(255, 255, 255, .55); }
.mj-clip[disabled]::after { content: none; }
.mj-clip:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
.mj-clip.is-on:focus-visible { outline-color: #1E40AF; }

/* Hover/focus hint. The clip is a bare icon with no label, so spell out what
   clicking it does. A ::after bubble rather than the native title tooltip so it
   appears immediately and matches the card styling. */
.mj-clip::after {
  content: attr(data-tip);
  position: absolute;
  top: calc(100% + 8px); left: 0;
  background: #111827;
  color: #fff;
  font-size: .72rem; font-weight: 600; line-height: 1.2;
  letter-spacing: .01em;
  padding: 6px 10px;
  border-radius: 7px;
  white-space: nowrap;
  box-shadow: 0 4px 12px rgba(17, 24, 39, .28);
  opacity: 0; visibility: hidden;
  transition: opacity .12s ease;
  pointer-events: none;
  z-index: 12;
}
.mj-clip:hover::after,
.mj-clip:focus-visible::after { opacity: 1; visibility: visible; }
/* The bookmarked clip is rotated, so cancel that on the bubble to keep the
   text level. */
.mj-clip.is-on::after { transform: rotate(16deg); transform-origin: top left; }

/* Message-the-employer pill. Shown in the top-right action cluster (in
   place of the AI score + bookmark) once the member has applied to the
   job; envelope icon + "Message Employer" label. Brand-purple gradient
   fill so it reads as the primary post-application action. Opens the
   compose modal on /members/job-detail.asp. */
.mj-card-msg {
  appearance: none;
  position: relative; /* anchor for the .mj-card-msg-dot unread indicator */
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 16px;
  /* White fill + gray outline + gray label -- matches the employer end's
     "Message Applicant" pill (.ar-msg-applicant-btn) so both sides look alike. */
  border: 1px solid #374151;
  border-radius: 999px;
  background: #fff;
  color: #374151;
  font-size: .88rem;
  font-weight: 600;
  letter-spacing: .01em;
  cursor: pointer;
  flex: 0 0 auto;
  box-shadow: 0 1px 2px rgba(17,24,39,.12);
  transition: background .15s, border-color .15s, box-shadow .15s, transform .05s;
}
.mj-card-msg:hover { background: #F3F4F6; box-shadow: 0 3px 8px rgba(17,24,39,.2); }
.mj-card-msg:active { transform: translateY(1px); }
.mj-card-msg svg { flex: 0 0 auto; }
.mj-card-msg:focus-visible {
  outline: 2px solid #1E40AF;
  outline-offset: 2px;
}
/* Unread-messages indicator: small red dot pinned to the button's top-right
   corner. Rendered only when the job has unread incoming messages for the
   member. The white ring lifts it off the button edge. */
.mj-card-msg-dot {
  position: absolute;
  top: -8px;
  right: -5px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #EF4444;
  border: 2px solid #fff;
  box-sizing: border-box;
  pointer-events: none;
}

/* Pill row -- color-coded by data type for fast visual scanning. */
.mj-card-pills { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 6px; min-width: 0; }
/* Force the work-model + job-type pills onto their own line below the
   category pills by giving their wrapper full width within the flex row. */
.mj-card-pills-row2 { flex: 0 0 100%; display: flex; flex-wrap: wrap; gap: 12px; min-width: 0; }
/* display is inline-BLOCK, not inline-flex, specifically so text-overflow works:
   text-overflow applies to block containers and is ignored on a flex container,
   so an inline-flex pill would clip its overflow with no ellipsis. Every .mj-pill
   in the codebase holds a single escaped text node (category / work model / job
   type) with no child elements, so nothing needed the flex context.
   vertical-align keeps it lined up with neighbouring pills the way align-items
   did. See the truncation note on .mj-skill-pill below. */
.mj-pill {
  display: inline-block;
  vertical-align: middle;
  /* 20rem = 320px. Raised from 14rem 2026-07-28: the first cap truncated
     ordinary category names, not just the runaway feed strings it was aimed at.
     min(100%, ...) still stops it exceeding the card on a narrow viewport. */
  max-width: min(100%, 20rem);
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: .76rem;
  font-weight: 600;
  border: 1px solid transparent;
  line-height: 1.4;
  white-space: nowrap;
}
/* Category pill moves off the legacy lavender to the brand blue, matching
   .qa-pill--cat in the Quick Apply popup: same blue family as the work-model
   pill, separated by the darker navy text so the two still read apart. */
/* Category pills are neutral gray (matching .mj-skill-pill), so the colored
   pills next to them -- work model and job type -- carry the signal. */
.mj-pill--cat  { background: #F3F4F6; color: #374151; border-color: #E5E7EB; }
.mj-pill--work { background: #DBEAFE; color: #1E40AF; border-color: #93C5FD; }
.mj-pill--type { background: #D1FAE5; color: #065F46; border-color: #6EE7B7; }

/* Category-fix "?" rendered as a corner badge attached to the last
   category pill on partner-feed jobs. */
.mj-card-pills .mj-pill-wrap {
  position: relative;
  display: inline-flex;
}
.mj-card-pills .cc-trigger {
  position: absolute;
  top: -6px;
  right: -7px;
  width: 14px;
  height: 14px;
  margin: 0;
  font-size: .58rem;
  background: #6B7280;
  border: 2px solid #fff;
  box-shadow: 0 1px 2px rgba(0,0,0,.2);
}
.mj-card-pills .cc-trigger:hover {
  background: #1E40AF;
  transform: scale(1.1);
}

/* Meta row: location pin and (when present) the closing-date indicator. */
.mj-card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  margin: 6px 0;
  font-size: .88rem;
  color: #4B5563;
}
.mj-card-meta-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.mj-card-meta-item svg { color: #6B7280; flex: 0 0 auto; }

/* Shaded stat strip: 3 equal columns. */
.mj-card-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}
.mj-card-stat {
  background: #F9FAFB;
  border: 1px solid #E5E7EB;
  border-radius: 8px;
  padding: 10px 12px;
  min-width: 0;
}
.mj-card-stat-label {
  font-size: .68rem;
  font-weight: 700;
  color: #6B7280;
  text-transform: uppercase;
  letter-spacing: .06em;
  margin-bottom: 4px;
}
.mj-card-stat-value {
  font-size: .92rem;
  font-weight: 600;
  color: #111827;
  line-height: 1.3;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Description excerpt (search-results card only): clamp to a fixed
   height so the Details button below stays pinned to the same spot
   regardless of how long the source text is. */
/* ── Contract specs / Required Skills / View Job / Verified Employer ─────────
   Ported from the .cw-card on /connect/work (CONNECT/work.asp) so the member
   listings (search-jobs.asp and search-contracts.asp) read the same way. The
   specs and the skills pills render only when the job carries that data, so a
   full-time posting with no contract terms simply shows neither. */
.mj-card-specs {
  display: flex;
  flex-wrap: wrap;
  /* row-gap kept tight so the wrapped spec rows (Starts on line 1, Length +
     Hours on line 2) sit close together; column-gap stays 8px between pills. */
  gap: 4px 8px;
  margin: 12px 0 2px;
}
.mj-card-spec {
  font-size: .76rem;
  /* Value color matches .mj-card-stat-value (Salary/Experience cells above). */
  color: #111827;
  /* Background + outline match .mj-card-stat (the Salary/Experience cells). */
  background: #F9FAFB;
  border: 1px solid #E5E7EB;
  border-radius: 8px;
  padding: 5px 10px;
}
.mj-card-spec b {
  /* Label color matches .mj-card-stat-label (Salary/Experience cells above). */
  color: #6B7280;
  font-weight: 600;
  font-size: .66rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  margin-right: 4px;
}

.mj-card-skills-label {
  font-size: .62rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: #9CA3AF;
  margin: 14px 0 5px;
}
/* Clamped to a single row: skills beyond the first line are hidden rather than
   letting one heavily-tagged job stretch its card past its neighbours. */
.mj-card-skills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-content: flex-start;
  max-height: 30px;
  overflow: hidden;
  margin: 0 0 4px;
  min-width: 0;
}
/* TRUNCATION (2026-07-28). Skill and technology names arrive from partner feeds
   and occasionally contain a whole sentence, or a single unbroken token hundreds
   of characters long. With white-space:nowrap and no cap, such a pill has a huge
   MIN-CONTENT width, and flex-wrap cannot break inside it. That min-content width
   propagates up .mj-card-skills -> .mj-card-body -> .mj-card -> .mj-cards, and
   because .mj-cards is a column flex (whose min-content width is its widest
   child's), one bad string stretched every card on the page sideways.
   Two halves to the fix, and both are needed:
     1. cap the pill here and ellipsis the overflow, and
     2. min-width:0 on the card containers below, so a wide child can never
        publish its min-content width upward again.
   The listing templates add title="" carrying the untruncated text, so hovering
   still shows the full skill. Detail screens are unaffected by the card rules. */
.mj-skill-pill {
  display: inline-block;
  vertical-align: middle;
  /* 26rem = 416px. Raised from 18rem 2026-07-28 so real skill names read in
     full and only the genuinely absurd feed strings get clipped. Note the
     trade-off against .mj-card-skills being clamped to a single 30px row: the
     wider a pill is allowed to get, the fewer sibling skills stay visible. */
  max-width: min(100%, 26rem);
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: .78rem;
  line-height: 18px;
  color: #374151;
  background: #F3F4F6;
  border: 1px solid #E5E7EB;
  border-radius: 999px;
  padding: 5px 12px;
  white-space: nowrap;
}

/* Primary action. Bigger and full-width compared with the old inline "Job
   Details" pill -- overrides .mj-card-details-btn, which it is applied
   alongside. The sizing group also covers the applied/withdrawn "View
   Application" variants so all three card-footer buttons render at the SAME
   size; only the gradient differs (their background rules below this one
   still win on source order at equal-or-lower specificity because no
   background is set here). */
.mj-card-details-btn.mj-card-view-btn,
.mj-card-details-btn.mj-card-details-btn--applied,
.mj-card-details-btn.mj-card-details-btn--applied-p,
.mj-card-details-btn.mj-card-details-btn--withdrawn {
  display: block;
  /* .mj-card-actions is a flex row with justify-content:flex-end, so width
     alone would not fill it -- flex-grow is what stretches the button across. */
  flex: 1 1 auto;
  width: 100%;
  /* Nudged down visually only. A relative offset (rather than margin) keeps the
     button out of the flow calculation, so the Verified Employer line beneath it
     does not move with it. */
  position: relative;
  top: 6px;
  padding: 12px 16px;
  border-radius: 10px;
  color: #fff;
  font-size: .95rem;
  font-weight: 600;
  text-align: center;
}
.mj-card-details-btn.mj-card-view-btn {
  /* Section gradient, not the flat Connect green it was ported with: the blue
     ramp on the Jobs listing, the green ramp on the Contract listing. Both come
     from --btn-grad, which .m-mode-cx re-points (see the mode block at the top
     of this file), so the button follows whichever mode the member is in. */
  background: var(--btn-grad);
  transition: background .15s ease;
}
.mj-card-details-btn.mj-card-view-btn:hover { background: var(--btn-grad-hover); }

/* The Verified line sits at the bottom of the absolutely-pinned .mj-card-footer,
   below the action row (footer gap + this margin set the space above it). */
.mj-card-verified {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  margin-top: 5px;
  font-size: .72rem;
  font-weight: 600;
  color: #14A800;
  letter-spacing: .02em;
}
.mj-card-verified svg { flex: 0 0 auto; }
/* Not a verified (partner_id=1) employer: hold the line's space but paint
   nothing, so cards with and without the badge end at the same height and the
   View Job buttons stay level across a row. visibility, NOT display:none --
   display:none would collapse the row and reintroduce the ragged bottom. */
.mj-card-verified--none { visibility: hidden; }

.mj-card-desc {
  position: relative;
  margin: 6px 0 0;
  color: #4B5563;
  font-size: 1.0625rem;
  line-height: 1.65;
  max-height: 9.9em;  /* ~6 lines at line-height 1.65 */
  overflow: hidden;
  /* This is the flexible "slack" in the fixed-height (580px) card: when the
     content above (stats, contract specs, skills) grows -- e.g. the Hours/basis
     spec row on a contract card -- the description must be able to shrink so the
     bottom-pinned action row (margin-top:auto) and the Verified line stay inside
     the card. A flex child's default min-height:auto floors it at content
     height, so without this it refuses to shrink and the View Job button
     overflows the clipped bottom edge. See flex-overflow min-height rule. */
  min-height: 0;
  flex-shrink: 1;
}
.mj-card-desc-cut {
  color: #1E40AF;
  font-weight: 700;
  letter-spacing: .04em;
  margin-left: 2px;
}

/* Fixed bottom stack: View Job button + Verified Employer line. Pinned to the
   card's lower edge and out of the content flow, so it sits in the SAME place
   on every card no matter how much content (stats, contract specs, skills,
   description) is above it. .mj-card is position:relative; .mj-card-body
   reserves matching space via its padding-bottom. */
.mj-card-footer {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  /* Horizontal padding matches .mj-card-body so the button lines up with the
     content column; bottom padding sets the gap off the card's lower edge. */
  padding: 0 20px 16px;
  display: flex;
  flex-direction: column;
  /* Preserve the button -> Verified spacing that .mj-card-body's own gap
     provided when these two lived in that flow. */
  gap: 14px;
}
.mj-card-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
}
/* "Posted directly on this site" marker (the tiny G favicon). HIDDEN for now:
   it is redundant with the Verified Employer line, which is gated on the same
   partner_id=1 condition. Flip display back to block to bring it back -- it is
   pinned to the card's bottom-left corner, clear of the centered Verified line
   (it cannot ride in the action row any more, since the View Job button spans
   that row's full width). */
.mj-card-partner-flag {
  display: none;
  position: absolute;
  left: 14px;
  bottom: 12px;
  width: 14px;
  height: 14px;
  margin: 0;
  opacity: .85;
}
.mj-card-details-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 18px;
  border-radius: 8px;
  background: var(--btn-grad);
  color: #fff;
  font-size: .85rem;
  font-weight: 600;
  text-decoration: none;
  transition: filter .15s, box-shadow .15s;
}
.mj-card-details-btn:hover {
  filter: brightness(1.05);
  box-shadow: 0 4px 12px var(--btn-grad-shadow);
}
/* "View Application" variant: green gradient matching the .va-status-cell
   panels and the .mj-card-header--applied strip on job-detail.asp so the
   applied state reads consistently across surfaces. */
.mj-card-details-btn--applied {
  background: linear-gradient(135deg, #16A34A 0%, #15803D 100%);
}
.mj-card-details-btn--applied:hover {
  box-shadow: 0 4px 12px rgba(22,163,74,.28);
}
/* "View Application" purple variant matching the .mj-card-header--applied-p
   strip on the search-jobs / search-contracts listings. */
.mj-card-details-btn--applied-p {
  background: linear-gradient(135deg, #5B21B6 0%, #3B0764 100%);
}
.mj-card-details-btn--applied-p:hover {
  box-shadow: 0 4px 12px rgba(91,33,182,.32);
}
/* "View Application" withdrawn variant: gray gradient matching the
   .mj-card-header--withdrawn strip so a withdrawn application reads as
   inactive across the whole card rather than the active purple. */
.mj-card-details-btn--withdrawn {
  background: linear-gradient(135deg, #6B7280 0%, #4B5563 100%);
}
.mj-card-details-btn--withdrawn:hover {
  box-shadow: 0 4px 12px rgba(75,85,99,.28);
}

/* ============================================================
   JOB TYPE PILL -- SINGLE SOURCE OF TRUTH
   Full-Time / Part-Time / Contract / Staffing / Job.

   THIS IS THE ONLY PLACE THE JOB-TYPE PILL IS STYLED. Tweak the look here and
   every consumer follows. Do not re-declare these properties in a page's own
   <style> block; add a modifier class here instead.

   Consumers (add to this list when another page adopts it):
     /includes/messages_popup_inc.asp  .m-msgx-conv-kind    conversation rail;
                                       adds absolute positioning + a white ring
     /members/applications.asp         .ma-col-jobtype td   Job Type column

   Lives in members.css rather than styles.css because both the member and the
   employer header load it (members_header_inc.asp:303, employers_header_inc.asp:167)
   while nothing on the public side renders a job-type pill, and because this
   file is cache-busted off its own mtime so edits take effect immediately.

   COLOR keys off the JOB's type, never the reader's mode. Both ramps are
   HARDCODED for that reason: --card-header-grad re-points to the green
   --cx-grad while the member is in Contract mode, so a token here would color
   the pill by the section being browsed instead of what the job actually is,
   and these lists mix both kinds at once. Keep in step with styles.css:70 and
   --cx-grad if either brand ramp changes.

   GEOMETRY is deliberately whole-pixel: integer font-size, fixed height,
   line-height equal to that height, and a radius of exactly height/2. A
   fractional font-size (.66rem) plus a unitless line-height previously landed
   the pill on a partial pixel, and the clamped 999px radius was then recomputed
   against it, which is what made the rounded ends render crackly on some rows
   and clean on others. Keep height, line-height and border-radius in step.
============================================================ */
.gf-jobtype-pill {
  display: inline-block;
  height: 22px;
  padding: 0 10px;
  border-radius: 11px;   /* exactly height/2 */
  background: linear-gradient(135deg, #172554 0%, #1E40AF 100%);
  /* Keeps the fill from painting out under a ring a consumer adds via
     box-shadow on browsers that treat the two as overlapping. */
  background-clip: padding-box;
  color: #FFFFFF;
  border: 0;
  font-size: 11px;       /* integer px, not rem: no fractional text metrics */
  font-weight: 700;
  line-height: 22px;     /* == height, so the label centres on whole pixels */
  letter-spacing: .04em;
  /* Uppercased in CSS, not in the markup, so the accessible text stays
     "Full-Time" rather than "FULL-TIME". */
  text-transform: uppercase;
  white-space: nowrap;
}
/* Contract work takes the Connect green ramp. Text stays white in both, so the
   two pills differ only by hue. */
.gf-jobtype-pill--contract {
  background: linear-gradient(135deg, #14532D 0%, #15803D 100%);
}

/* ============================================================
   MESSAGE FLAG (envelope + state badge) -- SINGLE SOURCE OF TRUTH
   Says whether a conversation exists with the other party, and whether any of
   it is unread. Three states, and the badge is what distinguishes them:

     no badge      no messages exchanged yet
     .gf-msg-check comms exist, everything read     (green circle + white tick)
     .gf-msg-dot   comms exist, something unread    (red dot)

   THIS IS THE ONLY PLACE THE FLAG IS STYLED. Tweak the look here and every
   consumer follows. Do not re-declare these properties in a page's own <style>
   block; add a modifier class here instead.

   Consumers (add to this list when another page adopts it):
     /includes/invitations_popup_inc.asp   invitation rows, next to the pill

   NOT YET MIGRATED, and worth folding in next time either page is touched:
     /employers/applications.asp  .ap-ct-flag  (Contacted column) -- the
                                  original; this component is lifted from it
     /members/applications.asp    .ma-msg-flag (unread column) -- two states
                                  only, no green check

   The consumer owns POSITION (the nudge that centres it in whatever row or
   cell it sits in) and whether it is interactive. Everything visual is here.

   DO NOT PUT PADDING ON .gf-msg-flag. The two badges are absolutely positioned
   against it, and an absolutely positioned child anchors to its containing
   block's PADDING box -- so padding moves the badge out by that much in both
   axes and visibly detaches it from the envelope. To grow the tap target, use a
   transparent ::before overlay with a negative inset instead (see
   .m-inv-msg::before in /includes/invitations_popup_inc.asp). */
.gf-msg-flag {
  position: relative;
  display: inline-block;
  line-height: 0;
  color: #4B5563;
}
.gf-msg-flag svg { width: 20px; height: 18px; display: block; }
/* Clickable variant. Not the default: a flag can be purely informational. */
.gf-msg-flag--btn { cursor: pointer; background: none; border: 0; padding: 0; }
.gf-msg-flag--btn:hover { color: var(--primary); }
.gf-msg-flag--btn:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 3px;
  border-radius: 4px;
}
/* Unread: red dot pinned to the envelope's top-right corner. The white ring is
   what keeps it legible on a banded or tinted row rather than bleeding into it. */
.gf-msg-dot {
  position: absolute;
  top: -3px;
  right: -4px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #EF4444;
  border: 1.5px solid #fff;
  box-sizing: border-box;
  pointer-events: none;
}
/* All-read: the same corner, same white ring, swapping the red fill for a green
   circle with a centred white tick (the svg strokes currentColor). */
.gf-msg-check {
  position: absolute;
  top: -5px;
  right: -6px;
  width: 13px;
  height: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: #16A34A;
  border: 1.5px solid #fff;
  box-sizing: border-box;
  color: #fff;
  line-height: 0;
  pointer-events: none;
}
.gf-msg-check svg { width: 8px; height: 8px; display: block; }

