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

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

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

/* ---------- 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; }
.cal-event.type-Shift { background: #e5e0fb; color: #4b2e9e; }
/* 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; }
/* 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; }
/* 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); }
}

/* 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 */
}
