:root {
  --ckv-navy: #1b3a5c;
  --ckv-green: #2f8f4e;
  --ckv-bg: #f5f7fa;
}

body {
  background-color: var(--ckv-bg);
  color: #22303f;
}

/* Generic click-to-sort table headers (added v1.444, sortable-tables.js) -
   same up/down-triangle visual language ae_dashboard.php's own
   (previously one-off) sort headers already used, now shared site-wide. */
th.sortable-col { cursor: pointer; user-select: none; }
th.sortable-col::after { content: ''; display: inline-block; width: 0.7em; }
th.sortable-col.sort-asc::after { content: '\25B2'; font-size: .7em; margin-left: .25em; }
th.sortable-col.sort-desc::after { content: '\25BC'; font-size: .7em; margin-left: .25em; }

.ckv-navbar .navbar-brand { letter-spacing: .3px; }
.ckv-navbar .nav-link.active { font-weight: 600; color: var(--ckv-navy) !important; }

/* ---------- Sidebar (added v1.449) - ADMIN/CKV/AE/AGENCY only, replacing
   the old top navbar for those roles; CANDIDATE keeps .ckv-navbar above
   unchanged (mobile-heavy, and their own menu is already short). ---------- */
.ckv-app-shell { display: flex; min-height: 100vh; }

.ckv-sidebar {
  position: fixed;
  top: 0; left: 0; bottom: 0;
  width: 240px;
  background: var(--ckv-navy);
  color: #e8eef5;
  overflow-y: auto;
  z-index: 1030;
  transition: transform .25s ease;
  display: flex;
  flex-direction: column;
}
.ckv-sidebar-brand {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: 1rem;
  color: #fff;
  font-weight: 700;
  text-decoration: none;
  border-bottom: 1px solid rgba(255,255,255,.12);
}
.ckv-sidebar-brand:hover { color: #fff; }
.ckv-sidebar-brand img { max-height: 32px; }
.ckv-sidebar-nav { padding: .5rem 0 1rem; flex: 1 1 auto; }
.ckv-sidebar-link {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: .6rem;
  padding: .55rem 1rem;
  color: rgba(255,255,255,.85);
  text-decoration: none;
  font-size: .92rem;
  border-left: 3px solid transparent;
}
.ckv-sidebar-link:hover { background: rgba(255,255,255,.08); color: #fff; }
.ckv-sidebar-link.active {
  background: rgba(255,255,255,.12);
  color: #fff;
  font-weight: 600;
  border-left-color: var(--ckv-green);
}
.ckv-sidebar-group-toggle { justify-content: space-between; }
.ckv-sidebar-chevron { transition: transform .2s ease; font-size: .75rem; }
.ckv-sidebar-group-toggle[aria-expanded="true"] .ckv-sidebar-chevron { transform: rotate(180deg); }
.ckv-sidebar-subnav .ckv-sidebar-sublink { padding-left: 2.6rem; font-size: .84rem; }
.ckv-sidebar-divider { margin-top: .5rem; padding-top: .5rem; border-top: 1px solid rgba(255,255,255,.12); }
.ckv-sidebar-section-label {
  padding: .25rem 1rem .35rem;
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: rgba(255,255,255,.5);
}
.ckv-sidebar-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.4); z-index: 1025; display: none; }

.ckv-main-wrap {
  flex: 1 1 auto;
  min-width: 0;
  margin-left: 240px;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  transition: margin-left .25s ease;
}
.ckv-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  padding: .5rem 1rem;
  background: #fff;
  border-bottom: 1px solid #e3e7ec;
}
.ckv-topbar-link {
  display: inline-flex;
  align-items: center;
  padding: .35rem .5rem;
  color: #22303f;
  text-decoration: none;
  border-radius: .375rem;
  font-size: 1.05rem;
}
.ckv-topbar-link:hover { background: #f0f2f5; }
.ckv-topbar-link.active { color: var(--ckv-navy); }
.ckv-main-content { flex: 1 1 auto; }

/* Desktop: the toggle hides the sidebar entirely and gives its width back
   to the content ("collapse out of view"), rather than shrinking to
   icons - simpler to reason about, and persisted via localStorage
   (header.php's own inline pre-paint script) so it stays collapsed across
   pages until toggled back. */
body.ckv-sidebar-collapsed .ckv-sidebar { transform: translateX(-240px); }
body.ckv-sidebar-collapsed .ckv-main-wrap { margin-left: 0; }

/* Mobile: the sidebar is always an off-canvas drawer over the content,
   never eating its width - same collapsed-by-default feel the old
   navbar-toggler had on small screens. */
@media (max-width: 991.98px) {
  .ckv-sidebar { transform: translateX(-240px); }
  .ckv-main-wrap { margin-left: 0; }
  body.ckv-sidebar-mobile-open .ckv-sidebar { transform: translateX(0); }
  body.ckv-sidebar-mobile-open .ckv-sidebar-backdrop { display: block; }
}

/* ---------- Login ---------- */
.login-shell {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, var(--ckv-navy), #274d74);
}
.login-card {
  width: 100%;
  max-width: 420px;
  border: none;
  border-radius: 16px;
  box-shadow: 0 20px 50px rgba(0,0,0,.25);
}

/* ---------- Candidate list ---------- */
.headshot-thumb {
  width: 48px; height: 48px; object-fit: cover; border-radius: 50%;
  border: 2px solid #fff; box-shadow: 0 0 0 1px #dfe4ea;
}
.candidate-row:hover { background-color: #eef4ff; cursor: pointer; }

/* ---------- Profile page ---------- */
.profile-hero {
  background: linear-gradient(135deg, var(--ckv-navy), #2d5c8a);
  border-radius: 16px;
  color: #fff;
  padding: 2rem;
}
.profile-headshot {
  width: 140px; height: 140px; object-fit: cover; border-radius: 50%;
  border: 4px solid #fff; box-shadow: 0 6px 18px rgba(0,0,0,.35);
  background: #fff;
}
.role-pill {
  font-size: .85rem;
  letter-spacing: .5px;
  text-transform: uppercase;
}
.ckv-card {
  border: none;
  border-radius: 14px;
  box-shadow: 0 2px 12px rgba(20, 30, 45, .06);
}
.ckv-card .card-header {
  background: #fff;
  border-bottom: 1px solid #eef1f5;
  border-radius: 14px 14px 0 0 !important;
  font-weight: 600;
  color: var(--ckv-navy);
}
table.requirements-table th { color: #6b7787; font-weight: 600; font-size: .8rem; text-transform: uppercase; letter-spacing: .4px; }
table.requirements-table td { vertical-align: middle; }

.status-pill { font-size: .78rem; padding: .35em .7em; }

/* Review action buttons */
.review-actions .btn { min-width: 130px; }

/* Calendar */
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.cal-day { background: #fff; border-radius: 10px; min-height: 100px; padding: 6px; border: 1px solid #eef1f5; }
.cal-day.today { border-color: var(--ckv-green); box-shadow: inset 0 0 0 2px var(--ckv-green); }
.cal-day .cal-date { font-size: .75rem; color: #8a95a3; font-weight: 600; }
.cal-event { font-size: .72rem; background: #eaf2ff; color: #1b3a5c; border-radius: 6px; padding: 2px 5px; margin-top: 3px; display: block; text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-event.type-Interview { background: #fdeecb; color: #7a4e00; }
.cal-event.type-Orientation { background: #e2f6e8; color: #1e6b39; }
.cal-event.type-Expiration { background: #fbe1e1; color: #8c1f1f; }
/* The candidate name was already built into _label (calendar.php) -
   "6:45p-11:15p Bahija Khabar (RN)" - but the base .cal-event rule above
   clips everything to one line with an ellipsis, so on a normal-width day
   cell the name silently gets cut off and only the time survives. Fixed
   directly, 2026-09-25 - "no names on the calendar view... put the names,
   instead of making entries wider, make them taller" - let a Shift chip
   wrap onto a second line instead of truncating, so the full label
   (including who) is always visible without a hover. Scoped to
   type-Shift only, not every .cal-event - Interview/Orientation/
   Expiration chips keep their existing compact single-line look. */
.cal-event.type-Shift { background: #e5e0fb; color: #4b2e9e; white-space: normal; overflow: visible; text-overflow: clip; line-height: 1.25; }
/* Shift for a candidate not marked "Added to Roster" (v1.65) - flagged, not
   hidden, so a real worked shift never silently disappears just because the
   roster status hasn't caught up yet; the dashed amber border calls out that
   the mismatch itself needs a look. */
.cal-event.not-roster { border: 1px dashed #b8860b; background: #fdf3d9; color: #7a5c00; }
/* Confirmed live inside CKV Hub but not yet reflected in a ConnectTeam
   import (v1.467) - a facility that confirms shifts entirely through the
   Hub (no ConnectTeam sync of its own, e.g. Messiah) would otherwise never
   see the candidate's name here at all. Same solid-green as the existing
   .confirmed-shift chip, so "confirmed" reads consistently across both
   calendar.php and candidate.php. */
.cal-event.live-confirmed { border: 1px solid #1e6b39; }
/* Shaded per-designation boxes within a day cell (v1.68, requested directly:
   "a shaded block... that shows CNA, then another that shows RN/LPN") - a
   distinct tinted container per role, sitting around that day's .cal-event
   entries for that role, so who's covering what role each day is visible as
   a shape/pattern, not just a flat undifferentiated list. */
.cal-role-group { border-radius: 8px; padding: 3px 4px 4px; margin-top: 4px; }
.cal-role-group.role-CNA { background: #e7f3fb; }
.cal-role-group.role-Nurse { background: #eafaf0; }
.cal-role-label { font-size: .65rem; font-weight: 700; text-transform: uppercase; letter-spacing: .3px; color: #5a6b7a; margin-bottom: 2px; }
/* Pay rate next to the role label (v1.179) - deliberately NOT bold/uppercase
   like .cal-role-label itself, so "CNA $26" reads as a label plus a fact
   about it, not two equally-weighted headings. */
.cal-role-rate { font-weight: 400; text-transform: none; letter-spacing: normal; opacity: .75; }
/* Outer facility-shaded wrapper (v1.147, requested directly: "another
   grouping on the calendar that wraps around facility... aka shaded
   wrapper") - one neutral bordered box per facility, sitting AROUND that
   facility's own .cal-role-group boxes (or, on pages with no role
   grouping at all, directly around its plain .cal-event chips). Only
   rendered on a day that actually has more than one facility's shifts in
   play (an admin's unfiltered view, or a candidate qualified for 2+
   facilities) - a day with just one facility looks exactly as it always
   has, no added box. Deliberately neutral/gray, not tinted like the CNA/
   Nurse boxes - it has to read as a DIFFERENT kind of grouping than the
   role boxes nested inside it, not another color in the same family. */
.cal-facility-group { border: 1px solid #ccd4da; border-radius: 8px; padding: 4px 4px 5px; margin-top: 4px; background: #f4f6f7; }
.cal-facility-label { font-size: .65rem; font-weight: 700; text-transform: uppercase; letter-spacing: .3px; color: #46545f; margin-bottom: 2px; }
/* Open/unfilled shift (v1.69) - a real coverage gap, not a confirmed person,
   so it's styled to read as an empty slot rather than a name: dashed border,
   no fill color, muted text - sits in the same CNA/RN-LPN box as everyone
   else covering that day, but visually unmistakable as "nobody yet." */
.cal-event.open-shift { background: transparent; border: 1px dashed #9aa5b1; color: #6b7787; }

/* Mobile month grid (v1.214) - "the calendar for candidates mobile
   friendly... on PC its awesome." Phone-width screens already default to
   Day view (day-view.js, v1.167) with real touch targets, but Month view
   itself - reachable any time someone taps "Month" - never got a mobile
   pass: 7 fixed-width columns of truncated .72rem text is unreadable
   below ~500px. This doesn't change Month view above the breakpoint at
   all - only narrows the day cells and loosens the chip text once there's
   no room for 7 comfortable columns. */
@media (max-width: 576px) {
  .cal-grid { gap: 3px; }
  .cal-day { min-height: 64px; padding: 4px 3px; border-radius: 7px; }
  .cal-day .cal-date { font-size: .68rem; }
  .cal-event { font-size: .62rem; padding: 3px 4px; white-space: normal; line-height: 1.15; }
  .cal-role-label, .cal-facility-label { font-size: .58rem; }
}

.avatar-placeholder {
  display: flex; align-items: center; justify-content: center;
  background: #dfe6ee; color: #7c8b9c; font-weight: 700;
}

/* ---------- Candidate self-upload portal ---------- */
.doc-card { position: relative; border: none; border-radius: 14px; box-shadow: 0 2px 12px rgba(20, 30, 45, .06); height: 100%; }
.doc-card .card-body { display: flex; flex-direction: column; }
.doc-card .doc-card-criteria { flex-grow: 1; }
/* Uploaded state (v1.45) - a bigger, unmistakable "done" cue than the small
   status badge alone: the whole card tints green and gets a corner checkmark
   badge, while Replace stays a normal button (uploading again is still one
   click, this is purely a visual state). */
.doc-card.doc-card-complete { background: #eef8f1; border: 1px solid var(--ckv-green); box-shadow: 0 2px 12px rgba(47, 143, 78, .12); }
.doc-card-checkmark {
  position: absolute;
  top: -10px;
  right: -10px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--ckv-green);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.1rem;
  box-shadow: 0 2px 6px rgba(0, 0, 0, .2);
}

/* ---------- Admin quick-upload (drag and drop) ---------- */
.qu-dropzone {
  border: 2px dashed #cdd5e0;
  border-radius: 10px;
  padding: 1rem;
  text-align: center;
  font-size: .85rem;
  color: #6b7787;
  cursor: pointer;
  transition: border-color .15s, background-color .15s;
}
.qu-dropzone:hover { border-color: var(--ckv-navy); background: #f5f8fc; }

/* ---------- Shifts calendar drag-and-drop ---------- */
.shift-chip { cursor: grab; }
.shift-chip:active { cursor: grabbing; }
.roster-chip:active { cursor: grabbing; }
.shift-chip.shift-dragover { outline: 2px solid var(--ckv-green); outline-offset: 1px; background: #eefaf1 !important; }
.cal-day.shift-dragover { outline: 2px solid var(--ckv-green); outline-offset: -2px; background: #eefaf1; }
/* Shift chip now shows who's actually working it, not just the posted role
   (fixed directly, 2026-09-25 - "no names on the calendar view, just RN or
   CNA, i need a usable view"), so it needs to wrap onto multiple lines
   instead of the plain .cal-event's single-line ellipsis truncation -
   scoped to .shift-chip specifically so calendar.php's own single-line
   Interview/Orientation/Expiration chips are untouched. Background reflects
   status at a glance: green once every opening on the shift is Confirmed,
   amber while any opening is still Awaiting Confirmation, and the existing
   dashed .open-shift style (calendar.php) covers a shift nobody's claimed
   at all yet. */
.cal-event.shift-chip { white-space: normal; overflow: visible; text-overflow: clip; padding: 4px 6px; }
/* .cal-event-time/.cal-event-name (the two-tier "time header, then one line
   per assigned name" layout) aren't scoped to .shift-chip alone - schedule.php's
   own staff-facing calendar chip (fixed the same pass, .type-Shift not
   .shift-chip) reuses the identical structure, so these style by the inner
   class names directly rather than requiring a specific parent. */
.cal-event-time { font-weight: 700; }
.cal-event-name { font-size: .68rem; opacity: .9; }
.cal-event.shift-chip.confirmed-shift { background: #e6f3ea; color: #1e6b39; }
.cal-event.shift-chip.awaiting-shift { background: #fdf3d9; color: #7a5c00; }
/* Claims Kanban board (v1.180) - same drag-and-drop visual language as the
   shift-chip/cal-day pair above, applied to a drop zone that's a whole
   scrollable column instead of a single grid cell. */
.claim-column { background: #f4f6f7; border-radius: 8px; padding: 8px; min-height: 120px; max-height: 70vh; overflow-y: auto; }
.claim-card { cursor: grab; }
.claim-card:active { cursor: grabbing; }
.claim-column.claim-dragover { outline: 2px solid var(--ckv-green); outline-offset: -2px; background: #eefaf1; }
.qu-dropzone.qu-dragover { border-color: var(--ckv-green); background: #eefaf1; color: #1e6b39; }
.qu-dropzone.qu-error { border-color: #dc3545; background: #fdecee; color: #8c1f1f; }

/* ---------- "Needs Confirmed" (added v1.84, moved here v1.89 so both
   calendar.php and candidate.php's Calendar widget can share it without
   duplicating the CSS) - deliberately loud: solid red, bold, all-caps, and
   a slow pulse via box-shadow so it can't be mistaken for a routine
   calendar entry at a glance. ---------- */
.needs-confirm-chip {
  display: block; background: #dc3545; color: #fff; font-weight: 700;
  letter-spacing: .02em; cursor: pointer; animation: needsConfirmPulse 1.6s ease-in-out infinite;
  line-height: 1.3; padding-top: 3px; padding-bottom: 3px; border-radius: 6px;
}
.needs-confirm-chip:hover { background: #c82333; color: #fff; }
/* Second line - who/when, so the shift can be identified at a glance while
   it's still pulsing, not just after clicking in. */
.needs-confirm-chip span:last-child { font-weight: 500; letter-spacing: normal; }
@keyframes needsConfirmPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(220, 53, 69, .55); }
  50% { box-shadow: 0 0 0 5px rgba(220, 53, 69, 0); }
}
/* Whole-row variant for candidates.php's list (added v1.89, widened v1.90
   from an inset glow to a real outer one - "make the whole thing pulsing,
   may add a couple pixel around it so you can see the effect"). A plain
   box-shadow on a <tr> gets clipped by adjacent rows under the default
   collapsed table border model, so .candidates-table switches to a
   separated model with a little vertical gap between every row - that gap
   is what gives the glow room to actually show above and below the row,
   not just at the edges. */
.candidates-table { border-collapse: separate; border-spacing: 0 6px; }
.candidates-table tbody tr:not(.needs-confirm-row) { box-shadow: none; }
.needs-confirm-row { animation: needsConfirmRowPulse 1.6s ease-in-out infinite; }
@keyframes needsConfirmRowPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(220, 53, 69, .55); }
  50% { box-shadow: 0 0 0 4px rgba(220, 53, 69, .55); }
}

/* Candidate-facing "claim an open shift" calendar chip (shift_available.php) -
   the bonus note used to only reach the title tooltip, so a candidate had to
   click/hover to learn a shift paid extra before deciding whether to claim
   it. Fixed directly, 2026-09-25 - "add the bonus $50 in the description so
   they dont have to click it to know what it is." Same wrap-not-truncate
   treatment as the other calendar chips fixed this session - role+time on
   its own line, the bonus note (when present) on a second line below,
   always visible without a hover. */
.claim-chip { white-space: normal; text-overflow: clip; }

/* Bonus shift - "make the bonus stand out like a video slot machine would"
   (v1.166). Gold gradient + a pulsing glow (same box-shadow-pulse pattern
   as .needs-confirm-chip above, gold instead of red) plus a diagonal
   shimmer sweep, the "shiny jackpot" part a plain pulse doesn't give on
   its own. Used on calendar.php's open-shift boxes and
   shift_available.php's claimable chips - anywhere a shift's own bonus
   note gets shown. */
.bonus-shift {
  position: relative; overflow: hidden;
  background: linear-gradient(135deg, #ffd700, #ffb300 50%, #ffd700);
  color: #4a3300 !important; font-weight: 700;
  animation: bonusGlow 1.4s ease-in-out infinite;
}
.bonus-shift::after {
  content: ''; position: absolute; top: 0; left: -60%; width: 40%; height: 100%;
  background: linear-gradient(115deg, transparent, rgba(255,255,255,.75), transparent);
  animation: bonusShimmer 2.2s ease-in-out infinite;
}
@keyframes bonusGlow {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 179, 0, .7); }
  50% { box-shadow: 0 0 6px 3px rgba(255, 179, 0, .7); }
}
@keyframes bonusShimmer {
  0% { left: -60%; }
  100% { left: 140%; }
}
@media (prefers-reduced-motion: reduce) {
  .bonus-shift, .bonus-shift::after { animation: none; }
}

/* ---------- Print (candidate profile doubles as the CKV summary sheet) ---------- */
/* Collapsible card-header toggle (e.g. All Documents on candidate.php,
   collapsed by default) - chevron flips to point up when expanded. */
[data-bs-toggle="collapse"] { cursor: pointer; }
[data-bs-toggle="collapse"] .bi-chevron-down { transition: transform .15s ease; }
[data-bs-toggle="collapse"][aria-expanded="true"] .bi-chevron-down { transform: rotate(180deg); }

@media print {
  .d-print-none { display: none !important; }
  /* A collapsed section must still show up on the printable candidate
     summary sheet - candidate.php doubles as that sheet (see CSS.md). */
  .collapse { display: block !important; height: auto !important; }
  /* Same principle for the Credentials/Schedule tabs (added v1.94) - only
     the active tab-pane would otherwise print, since Bootstrap's own CSS
     hides inactive ones with a plain display:none this block overrides.
     The tab nav itself is already .d-print-none in the markup (a row of
     clickable tab buttons means nothing on paper), same as everywhere else
     interactive-only chrome gets hidden for print. */
  .tab-pane { display: block !important; opacity: 1 !important; }
  body { background: #fff !important; }
  main { padding: 0 !important; }
  .profile-hero {
    background: #fff !important;
    color: #000 !important;
    border: 1px solid #ccc;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  .profile-hero .badge, .profile-hero .opacity-75 { color: #000 !important; }
  .ckv-card { box-shadow: none !important; border: 1px solid #ddd !important; }
  .col-lg-8, .col-lg-4 { width: 100% !important; max-width: 100% !important; flex: 0 0 100% !important; }
  a[href]:after { content: none !important; } /* some browsers auto-append URLs when printing */
}

/* Dashboard (v1.323) - plain Bootstrap cards + these few rules, no new
   library/page weight. A subtle lift on hover (every tile is a link)
   and a big tabular number are the whole "cool looking" budget. */
.dash-tile { transition: transform .12s ease, box-shadow .12s ease; }
.dash-tile:hover { transform: translateY(-2px); box-shadow: 0 6px 18px rgba(20, 30, 45, .1); }
.dash-stat { font-size: 2.1rem; font-weight: 700; color: var(--ckv-navy); font-variant-numeric: tabular-nums; line-height: 1.1; }
.dash-delta { font-size: .95rem; font-weight: 600; vertical-align: middle; }
.dash-tile-urgent { border-left: 4px solid #dc3545; }
