/* ===========================================================
   IMRAface palette + typography. Anchored on the IMRA Texas navy
   with a brighter lime accent the operator referenced from the
   Suitability Review Kick-off Dashboard. Inter handles every
   surface so the app reads as a single modern geometric sans
   instead of the prior navy-serif + sans mix.
   =========================================================== */
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap");
:root {
  --navy:        #012169;       /* IMRA Texas brand navy — buttons, links */
  --navy-dark:   #011245;
  --navy-light:  #1a3a7a;
  --header-bg:   #131d3b;       /* Dashboard hero bg — darker, cooler navy */
  --header-fg:   #f5f7fb;       /* Light text + chips when over header-bg */
  --green:       #42873f;       /* Existing Safal green */
  --green-dark:  #306b2d;
  --accent-lime: #5cc943;       /* Brighter dashboard accent number color */
  --bg:          #f5f7fb;       /* Slightly cooler page bg */
  --card-bg:     #ffffff;
  --text:        #0f1d3b;       /* Body text reads as the navy family */
  --muted:       #64748b;
}

html, body {
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
h1, h2, h3, h4, h5 {
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-weight: 700;
  color: var(--navy);
  letter-spacing: -0.015em;
}

/* "Big accent number" callout — used in the Quality Reports header
   tiles and elsewhere we want a kick-off-dashboard feel. */
.accent-number {
  color: var(--accent-lime);
  font-weight: 800;
  letter-spacing: -0.025em;
  line-height: 1;
}

/* ===========================================================
   Dark navy hero nav bar. The <header class="nav-dark-hero">
   gets a dashboard-style dark navy background; every nav child
   inherits a light-on-navy color palette so we don't have to
   touch the Tailwind classes on each individual tab button.
   The active tab carries a lime accent underline matching the
   Suitability Review Kick-off Dashboard reference image.
   =========================================================== */
.nav-dark-hero {
  background: var(--header-bg);
  color: var(--header-fg);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.nav-dark-hero h1,
.nav-dark-hero h2,
.nav-dark-hero h3 {
  color: var(--header-fg);
}
.nav-dark-hero p,
.nav-dark-hero span,
.nav-dark-hero label {
  color: var(--header-fg);
}
.nav-dark-hero .tab-btn {
  color: rgba(255, 255, 255, 0.78) !important;
  background: transparent !important;
  border-color: transparent !important;
  font-weight: 500;
  letter-spacing: 0.01em;
  position: relative;
}
.nav-dark-hero .tab-btn:hover {
  color: #ffffff !important;
  background: rgba(255, 255, 255, 0.08) !important;
}
.nav-dark-hero .tab-btn[data-active="true"] {
  color: #ffffff !important;
  background: rgba(92, 201, 67, 0.08) !important;        /* lime tint */
}
/* Active tab gets a lime underline — kick-off dashboard style. */
.nav-dark-hero .tab-btn[data-active="true"]::after {
  content: "";
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: -1px;
  height: 2px;
  background: var(--accent-lime);
  border-radius: 2px;
}
.nav-dark-hero .nav-divider {
  background: rgba(255, 255, 255, 0.15) !important;
}
/* Rose-tinted V1281 tabs get a softer lime-shifted glow on the
   dark bar so they read as part of the same surface but still
   call out the elevated-access tier. */
.nav-dark-hero .text-rose-700 {
  color: rgba(255, 200, 200, 0.85) !important;
}
.nav-dark-hero .text-cyan-700 {
  color: rgba(180, 230, 255, 0.95) !important;
}
.nav-dark-hero .text-emerald-700 {
  color: rgba(190, 255, 200, 0.95) !important;
}
/* Welcome banner sits inside the dark header — flip its label
   muted-gray to a translucent white, and the name to the lime
   accent so it reads as the dashboard's signature color. */
.nav-dark-hero #welcome-name {
  color: var(--accent-lime) !important;
}
.nav-dark-hero #welcome-banner span:not(#welcome-name) {
  color: rgba(255, 255, 255, 0.65) !important;
}

/* Sync cluster — markup carries inline navy colors that drown
   against the dark header. Force everything inside the cluster
   to light-on-navy with !important so the inline rules lose. The
   countdown value keeps its lime alarm-clock glow. */
.nav-dark-hero #sync-cluster span,
.nav-dark-hero #sync-cluster .text-gray-500,
.nav-dark-hero #sync-cluster .text-gray-700,
.nav-dark-hero #sync-cluster [style*="color"] {
  color: rgba(255, 255, 255, 0.85) !important;
}
.nav-dark-hero #sync-cluster .uppercase {
  color: rgba(255, 255, 255, 0.7) !important;
}
.nav-dark-hero #sync-countdown-value {
  color: var(--accent-lime) !important;
}
.nav-dark-hero #sync-now-btn {
  background: rgba(255, 255, 255, 0.08) !important;
  border-color: rgba(255, 255, 255, 0.35) !important;
  color: #ffffff !important;
}
.nav-dark-hero #sync-now-btn:hover {
  background: rgba(255, 255, 255, 0.18) !important;
}

/* Brand logo button — wide rectangle (180x80) so the IMRA seal /
   IMRAface character render LARGE in the upper-left corner.
   position:relative + absolutely-positioned images means the two
   logos overlap perfectly: only one is visible at a time (.hidden
   removes the other from flow), and during the easter-egg spin
   there's no flex side-by-side moment where both render in
   adjacent rectangles. */
.brand-logo-btn {
  position: relative;
  width: 180px;
  height: 80px;
  border-radius: 16px;
  background: #ffffff;
  display: inline-block;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(255, 255, 255, 0.25);
  cursor: pointer;
  overflow: hidden;
}
.brand-logo-img {
  position: absolute;
  top: 4px;
  left: 4px;
  right: 4px;
  bottom: 4px;
  width: calc(100% - 8px);
  height: calc(100% - 8px);
  object-fit: contain;
  transition: transform 600ms ease;
}

/* Nav dropdown clusters — keep V1281's 13 tabs on a single header
   row by collapsing the Internal Tools and Admin groups behind
   small trigger buttons. The menu renders as a little popup-modal
   card hanging below the trigger; the deep shadow + rounded card
   match the existing modal styling so it reads as an overlay, not
   a flat dropdown. */
.nav-dropdown-menu {
  background: #ffffff;
  border: 1px solid rgba(0, 0, 0, 0.08);
  border-radius: 16px !important;
  padding: 10px !important;
  min-width: 18rem !important;
  box-shadow:
    0 24px 48px -12px rgba(0, 0, 0, 0.45),
    0 12px 24px -6px rgba(0, 0, 0, 0.25);
}
.nav-dark-hero .nav-dropdown-menu {
  /* Light card on dark hero — the buttons inside keep their own
     colored text (gray/rose), so we revert to the light palette
     inside the menu rather than inheriting the navy overrides
     that affect inline tabs. */
  background: #ffffff;
  border-color: rgba(0, 0, 0, 0.08);
}
.nav-dropdown-menu .tab-btn {
  padding: 0.5rem 0.75rem !important;
  border-radius: 10px !important;
  font-size: 0.875rem;
  font-weight: 500;
}
.nav-dark-hero .nav-dropdown-menu .tab-btn {
  /* Re-light the tab buttons that live inside the white menu
     card so they don't inherit the dark-hero's white-on-navy
     overrides and become invisible against white. */
  color: #374151 !important;        /* gray-700 */
  background: transparent !important;
}
.nav-dark-hero .nav-dropdown-menu .tab-btn:hover {
  color: #111827 !important;        /* gray-900 */
  background: #f3f4f6 !important;   /* gray-100 */
}
.nav-dark-hero .nav-dropdown-menu .tab-btn[data-active="true"] {
  color: #111827 !important;
  background: rgba(92, 201, 67, 0.18) !important;  /* lime tint */
}
.nav-dark-hero .nav-dropdown-menu .text-rose-700 {
  color: #be123c !important;        /* rose-700 */
}
.nav-dark-hero .nav-dropdown-menu .tab-btn.text-rose-700:hover {
  background: #fff1f2 !important;   /* rose-50 */
}
/* Phase 88 — match the dropdown trigger's idle styling to the
   tab buttons (.tab-btn) so "Tools" and "Admin" read as bright as
   their tab-button siblings on the navy nav. Without this, the
   trigger leaned on Tailwind's text-gray-700, which faded into
   the navy background and read as a disabled control. */
.nav-dark-hero .nav-dropdown-trigger {
  color: rgba(255, 255, 255, 0.78) !important;
  background: transparent !important;
  border-color: transparent !important;
  font-weight: 500;
  letter-spacing: 0.01em;
}
.nav-dark-hero .nav-dropdown-trigger:hover {
  color: #ffffff !important;
  background: rgba(255, 255, 255, 0.08) !important;
}
.nav-dropdown-trigger[aria-expanded="true"] {
  background: rgba(255, 255, 255, 0.12) !important;
  color: #ffffff !important;
}
/* Tailwind's .hidden and .inline-flex have equal specificity; the
   trigger carries both classes (hidden until perms reveal it) and
   we don't want the CDN's class ordering to decide. Force hidden
   to win when it's present. */
.nav-dropdown-trigger.hidden {
  display: none !important;
}

/* Phase 71 — My Dashboard light-up tiles. Three flavors (rose
   for open writing issues, indigo for cross-alignment notes, amber
   for "due in <5 days"). Each only renders when its flag is true,
   so the row stays clean when there's no pending work — the
   presence of a tile is the signal. */
.mydash-tile {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.25rem 0.625rem;
  border-radius: 0.5rem;
  border: 1px solid transparent;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
  animation: mydash-tile-glow 2.8s ease-in-out infinite;
}
.mydash-tile--rose {
  background: linear-gradient(135deg, #fda4af 0%, #e11d48 100%);
  color: #ffffff;
  border-color: #be123c;
}
.mydash-tile--indigo {
  background: linear-gradient(135deg, #a5b4fc 0%, #4f46e5 100%);
  color: #ffffff;
  border-color: #3730a3;
}
.mydash-tile--amber {
  background: linear-gradient(135deg, #fde68a 0%, #d97706 100%);
  color: #ffffff;
  border-color: #b45309;
}
@keyframes mydash-tile-glow {
  0%, 100% { filter: brightness(1); }
  50%      { filter: brightness(1.08); }
}

/* Phase 71 — Phase 4 sign-off action button. Two states:
   .fr-signoff-locked  → rose-filled with a lock icon spelling out
                          which gates still need to clear. The COACH
                          should feel why they can't proceed.
   .fr-signoff-ready   → emerald-filled "Click to Approve for Safal
                          Review" CTA, big and confident. */
.fr-signoff-locked {
  display: block;
  width: 100%;
  background: linear-gradient(135deg, #fda4af 0%, #be123c 100%);
  color: #ffffff;
  border-radius: 0.75rem;
  padding: 1rem 1.25rem;
  border: 1px solid #9f1239;
  box-shadow: 0 4px 12px rgba(190, 18, 60, 0.25);
  cursor: not-allowed;
}
.fr-signoff-locked .fr-signoff-headline {
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.fr-signoff-locked .fr-signoff-detail {
  margin-top: 0.5rem;
  font-size: 11px;
  font-weight: 500;
  line-height: 1.4;
  color: #ffe4e6;
}
.fr-signoff-ready-btn {
  display: block;
  width: 100%;
  background: linear-gradient(135deg, #34d399 0%, #059669 100%);
  color: #ffffff;
  border-radius: 0.75rem;
  padding: 0.875rem 1.25rem;
  border: 1px solid #047857;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  box-shadow: 0 4px 14px rgba(5, 150, 105, 0.35);
  transition: transform 120ms ease, box-shadow 120ms ease, filter 120ms ease;
  cursor: pointer;
}
.fr-signoff-ready-btn:hover {
  filter: brightness(1.05);
  box-shadow: 0 6px 18px rgba(5, 150, 105, 0.45);
  transform: translateY(-1px);
}
.fr-signoff-ready-btn:active {
  transform: translateY(0);
}
.fr-signoff-blocked-btn {
  display: block;
  width: 100%;
  margin-top: 0.5rem;
  background: #ffffff;
  color: #be123c;
  border-radius: 0.5rem;
  padding: 0.625rem 1rem;
  border: 1.5px solid #fda4af;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.fr-signoff-blocked-btn:hover {
  background: #fff1f2;
}
.fr-signoff-blocked-btn[data-active="true"] {
  background: #be123c;
  color: #ffffff;
  border-color: #9f1239;
}

/* Phase 66 — "ready for coach approval" neon tiles in the RST
   Dashboard drilldown. Hot-magenta gradient + soft glow so they
   pop against the gray-on-white program row and read as live work
   the coach hasn't claimed yet. Subtle pulse keeps the queue
   feeling alive without being annoying. */
.fr-neon-tile {
  background: linear-gradient(135deg, #f0abfc 0%, #c026d3 50%, #a21caf 100%);
  color: #ffffff;
  border: 1px solid #c026d3;
  box-shadow:
    0 0 0 1px rgba(192, 38, 211, 0.35),
    0 0 8px rgba(217, 70, 239, 0.55),
    0 0 14px rgba(217, 70, 239, 0.35);
  text-shadow: 0 0 4px rgba(255, 255, 255, 0.4);
  letter-spacing: 0.02em;
  transition: transform 120ms ease, box-shadow 120ms ease;
  animation: fr-neon-pulse 2.6s ease-in-out infinite;
}
.fr-neon-tile:hover {
  transform: translateY(-1px);
  box-shadow:
    0 0 0 1px rgba(192, 38, 211, 0.55),
    0 0 12px rgba(217, 70, 239, 0.75),
    0 0 22px rgba(217, 70, 239, 0.45);
}
/* Phase 85 — amber variant for the "NEED RAW SCORE / NEED DES N"
   missing-cell pills in the report body. Same neon energy as the
   coach-ready tiles but in a warning palette so a coach skimming
   a near-complete indicator can spot the holdouts at a glance. */
.fr-neon-tile.fr-neon-amber {
  background: linear-gradient(135deg, #fde68a 0%, #f59e0b 50%, #b45309 100%);
  border-color: #d97706;
  color: #1f2937;
  box-shadow:
    0 0 0 1px rgba(217, 119, 6, 0.35),
    0 0 8px rgba(245, 158, 11, 0.55),
    0 0 14px rgba(245, 158, 11, 0.35);
  text-shadow: 0 0 4px rgba(255, 255, 255, 0.55);
}
.fr-neon-tile.fr-neon-amber:hover {
  box-shadow:
    0 0 0 1px rgba(217, 119, 6, 0.55),
    0 0 12px rgba(245, 158, 11, 0.75),
    0 0 22px rgba(245, 158, 11, 0.45);
}
@keyframes fr-neon-pulse {
  0%, 100% {
    box-shadow:
      0 0 0 1px rgba(192, 38, 211, 0.35),
      0 0 8px rgba(217, 70, 239, 0.55),
      0 0 14px rgba(217, 70, 239, 0.35);
  }
  50% {
    box-shadow:
      0 0 0 1px rgba(192, 38, 211, 0.55),
      0 0 12px rgba(217, 70, 239, 0.85),
      0 0 22px rgba(217, 70, 239, 0.55);
  }
}
/* Easter-egg spin animation — fires when the operator hits the
   7-click threshold. JS toggles .brand-logo-btn.spinning to
   trigger the swap. */
.brand-logo-btn.spinning .brand-logo-img {
  animation: brand-logo-spin 600ms ease-in-out;
}
@keyframes brand-logo-spin {
  0%   { transform: rotate(0deg) scale(1); }
  50%  { transform: rotate(540deg) scale(0.7); opacity: 0.4; }
  100% { transform: rotate(1080deg) scale(1); opacity: 1; }
}

/* Phase 284.21 — Copyediting row color-wash: a left-to-right reveal
   of the row's background color, played once when the toggle switch
   flips (either direction). Shared between app.js's admin tile board
   (.cv-wash-sweep) and copyedit_entrance.html's standalone tile board
   (.ce-wash-sweep, loaded via the styles.css link added in Phase
   284.20) — same keyframes, two class names so each surface's JS can
   apply its own without touching the other's markup. */
@keyframes copyedit-wash-sweep {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}
.cv-wash-sweep, .ce-wash-sweep {
  animation: copyedit-wash-sweep 700ms ease-out;
}

/* Phase 95 — login-screen logo card. Two imgs stacked absolutely
   inside a relative-positioned card so the same brand-logo-spin
   keyframes can fire on successful login (IMRA seal -> IMRAface
   character). overflow: hidden clips the 1080° spin to the rounded
   card edges so it doesn't bleed onto the navy backdrop. */
.login-logo-card {
  position: relative;
  overflow: hidden;
}
.login-logo-card .login-logo-img {
  position: absolute;
  top: 0.5rem;
  left: 0.5rem;
  right: 0.5rem;
  bottom: 0.5rem;
  width: calc(100% - 1rem);
  height: calc(100% - 1rem);
  object-fit: contain;
  transition: transform 600ms ease;
}
.login-logo-card.spinning .login-logo-img {
  animation: brand-logo-spin 600ms ease-in-out;
}

/* ---- Brand-color overrides on Tailwind utilities ---- */
/* Indigo primary buttons → navy */
.bg-indigo-600              { background-color: var(--navy) !important; }
.hover\:bg-indigo-500:hover { background-color: var(--navy-light) !important; }
.bg-indigo-500              { background-color: var(--navy-light) !important; }
.bg-indigo-700              { background-color: var(--navy-dark) !important; }
.text-indigo-600,
.text-indigo-700            { color: var(--navy) !important; }
.text-indigo-800,
.text-indigo-900            { color: var(--navy-dark) !important; }
.text-indigo-500            { color: var(--navy-light) !important; }
.border-indigo-200          { border-color: #d4dbec !important; }
.border-indigo-300          { border-color: #a8b5d6 !important; }
.border-indigo-400,
.border-indigo-500          { border-color: var(--navy-light) !important; }
.bg-indigo-50               { background-color: #eaeef7 !important; }
.bg-indigo-100              { background-color: #d4dbec !important; }

/* Emerald success buttons → Safal green */
.bg-emerald-500             { background-color: var(--green) !important; }
.bg-emerald-600             { background-color: var(--green) !important; }
.bg-emerald-700             { background-color: var(--green-dark) !important; }
.hover\:bg-emerald-500:hover{ background-color: var(--green) !important; }

/* Light-theme watermark — fixed, centered, barely visible. */
body {
  position: relative;
  background-color: var(--bg);
  color: var(--text);
}
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  background-image: url("/static/logo-safal-2024.svg");
  background-repeat: no-repeat;
  background-position: center center;
  background-size: min(55vmin, 480px);
  opacity: 0.04;
  z-index: 0;
}

/* Keep app content above the watermark. Phase 24 — #confirm-modal is
   intentionally NOT in this list; it's a true overlay with its own
   fixed position + very high z-index (see #confirm-modal rule below).
   Including it here forced position:relative; z-index:1 (ID specificity
   beat Tailwind's .fixed utility) which trapped it inside the <main>
   stacking context. */
header, main, #lock-modal, #sim-terminal { position: relative; z-index: 1; }

/* Phase 24 — True-overlay modal. Lives as a direct <body> child so it
   escapes every ancestor stacking context. Uses !important ONLY where
   we need to beat the utility-class loading order; z-index 9999 ensures
   it sits above Tailwind's sticky header, the viewport-locked
   Replacements section, and any sibling modal. */
#confirm-modal,
#diag-modal {
  position: fixed !important;
  inset: 0 !important;
  z-index: 9999;
}
#confirm-modal.hidden,
#diag-modal.hidden { display: none !important; }
#confirm-modal:not(.hidden),
#diag-modal:not(.hidden) {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Phase 30 — Active tab uses the Navy palette. */
.tab-btn[data-active="true"] {
  color: #ffffff;
  background: var(--navy);
  border-color: var(--navy);
}
.tab-btn { font-weight: 500; }

/* Candidate card (Phase 23) + chain glyph decorations. */
.candidate-row { transition: transform 120ms ease, background 120ms ease, box-shadow 120ms ease; }
.candidate-row:hover { box-shadow: 0 6px 18px rgba(15, 23, 42, 0.08); }

/* Phase 23 — hover-reveal the Select Candidate button on the card. The
   wrap reserves space so the card doesn't collapse when the button is
   hidden; only opacity + pointer-events flip on hover. */
.candidate-card .pick-btn-wrap { opacity: 0; transition: opacity 140ms ease; pointer-events: none; }
.candidate-card:hover .pick-btn-wrap,
.candidate-card:focus-within .pick-btn-wrap { opacity: 1; pointer-events: auto; }
/* Keyboard focus on the button itself reveals it too (accessibility). */
.candidate-card .pick-btn-wrap:focus-within { opacity: 1; pointer-events: auto; }
.chain-arrow::after { content: "→"; margin: 0 0.5rem; color: rgb(156, 163, 175); }
.chain-arrow:last-child::after { content: ""; }

/* Phase 10 — Qualitative feedback hover tooltip.
   Pure CSS: the `.notes-tip` anchor reveals its child `.notes-tip-body`
   on hover/focus. Positioned absolutely so it floats above the candidate
   table without being clipped by the table wrapper (which switched to
   overflow-visible for this). */
.notes-tip { position: relative; display: inline-block; }
.notes-tip-body {
  display: none;
  position: absolute;
  left: 0;
  top: 100%;
  margin-top: 0.375rem;
  z-index: 60;
  width: 22rem;
  max-width: 92vw;
  padding: 0.75rem;
  background: #ffffff;
  border: 1px solid rgb(229, 231, 235);   /* gray-200 */
  border-radius: 0.5rem;
  box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18), 0 2px 4px rgba(15, 23, 42, 0.08);
  font-size: 0.75rem;
  line-height: 1.25rem;
  color: rgb(55, 65, 81);                 /* gray-700 */
  white-space: pre-wrap;
  text-align: left;
  pointer-events: auto;
}
.notes-tip:hover > .notes-tip-body,
.notes-tip:focus-within > .notes-tip-body { display: block; }
.notes-tip-body::before {
  content: "";
  position: absolute;
  top: -6px;
  left: 14px;
  width: 10px;
  height: 10px;
  background: #ffffff;
  border-left: 1px solid rgb(229, 231, 235);
  border-top: 1px solid rgb(229, 231, 235);
  transform: rotate(45deg);
}
.notes-tip-icon {
  cursor: help;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 9999px;
  background: rgb(253, 242, 248);         /* fuchsia-50 */
  color: rgb(162, 28, 175);               /* fuchsia-800 */
  border: 1px solid rgb(249, 168, 212);   /* fuchsia-300 */
  font-size: 0.75rem;
  line-height: 1;
  transition: transform 120ms ease;
}
.notes-tip:hover > .notes-tip-icon { transform: scale(1.1); }

/* Phase 17 / 19 — Visual DNA Blocks. Each block is a small rounded pill
   with a single letter/value. Lit state = solid color tied to the trait
   kind; dim state = hollow gray ring so the mismatch reads at a glance.
   The unified DNA grid (Phase 19) lays these out in a predictable flex
   row so chiclet columns align across the outgoing card and every
   candidate row. */
.dna-strip {
  /* Phase 22 — wrap instead of scroll so a 15-chiclet fingerprint breaks
     to a second or third line instead of widening the card. */
  display: flex; flex-wrap: wrap; gap: 4px; align-items: center;
  /* Room for the hover scale + box-shadow so chiclets don't clip their
     tops/bottoms when they "light up". */
  padding: 3px 2px;
}
.dna-block {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 3rem; height: 1.55rem; padding: 0 0.5rem;
  border-radius: 0.45rem; border: 1px solid transparent;
  font-size: 0.68rem; font-weight: 600; letter-spacing: 0.02em;
  line-height: 1; white-space: nowrap; position: relative;
  transition: opacity 140ms ease, transform 140ms ease, box-shadow 140ms ease, filter 140ms ease;
  opacity: 0.9;  /* Phase 19 default resting state */
  flex: 0 0 auto;
}
.dna-block[title] { cursor: help; }

/* Default / static states */
.dna-block.dim { opacity: 0.35; background: #ffffff; color: rgb(107, 114, 128); border-color: rgb(209, 213, 219); }
.dna-block.dim::after {
  /* diagonal strike to reinforce the missing state for colorblind users */
  content: ""; position: absolute; inset: 45% 6% auto 6%; height: 1px;
  background: rgb(156, 163, 175);
}
.dna-block.lit { opacity: 0.9; }

/* Phase 23 — 4-state Traffic Light on hover. Applied to (a) the hovered
   candidate card/row and (b) the outgoing card's DNA strip. Non-hovered
   cards dim to stay out of the way of the focused comparison.
   Precedence: tier-green / tier-yellow / tier-red override the earlier
   per-slot color when the user is actively hovering a candidate. */
body.dna-hover .candidate-row.is-hovered .dna-block.tier-green,
body.dna-hover [data-outgoing-strip] .dna-block.tier-green {
  background: rgb(187, 247, 208) !important;   /* emerald-200 */
  color: rgb(6, 95, 70) !important;
  border-color: rgb(16, 185, 129) !important;
  opacity: 1;
  box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.28);
  transform: translateY(-1px);
}
body.dna-hover .candidate-row.is-hovered .dna-block.tier-yellow,
body.dna-hover [data-outgoing-strip] .dna-block.tier-yellow {
  background: rgb(254, 240, 138) !important;   /* yellow-200 */
  color: rgb(113, 63, 18) !important;
  border-color: rgb(234, 179, 8) !important;
  opacity: 1;
  box-shadow: 0 0 0 2px rgba(234, 179, 8, 0.25);
  transform: translateY(-1px);
}
body.dna-hover .candidate-row.is-hovered .dna-block.tier-red,
body.dna-hover [data-outgoing-strip] .dna-block.tier-red {
  background: rgb(254, 202, 202) !important;   /* rose-200 */
  color: rgb(127, 29, 29) !important;
  border-color: rgb(244, 63, 94) !important;
  opacity: 1;
  box-shadow: 0 0 0 2px rgba(244, 63, 94, 0.25);
}
body.dna-hover .candidate-row.is-hovered .dna-block.tier-dim,
body.dna-hover [data-outgoing-strip] .dna-block.tier-dim {
  opacity: 0.2;
  filter: grayscale(0.7);
}
/* Non-hovered cards keep their per-slot rest state; dim the rest of the
   list so the focus stays on the hovered comparison. */
body.dna-hover .candidate-row:not(.is-hovered) .dna-block { opacity: 0.45; }

/* Placeholder chiclet — renders when an attribute is blank so the grid
   alignment never breaks. */
.dna-block.placeholder {
  opacity: 0.45; background: rgb(249, 250, 251); color: rgb(156, 163, 175);
  border: 1px dashed rgb(209, 213, 219); font-weight: 500;
}
.dna-block.lit-track-q       { background: rgb(224, 231, 255); color: rgb(55, 48, 163); border-color: rgb(165, 180, 252); }
.dna-block.lit-track-s       { background: rgb(254, 226, 226); color: rgb(153, 27, 27); border-color: rgb(252, 165, 165); }
.dna-block.lit-content       { background: rgb(219, 234, 254); color: rgb(29, 78, 216); border-color: rgb(147, 197, 253); }
.dna-block.lit-secondary     { background: rgb(207, 250, 254); color: rgb(14, 116, 144); border-color: rgb(103, 232, 249); }
.dna-block.lit-spanish       { background: rgb(254, 243, 199); color: rgb(146, 64, 14); border-color: rgb(253, 224, 71); }
.dna-block.lit-demo          { background: rgb(220, 252, 231); color: rgb(6, 95, 70); border-color: rgb(134, 239, 172); }

/* Phase 59 — Pool Depth slider (3-stop snap). Replaces the legacy Blend
   Slider. Track gradient runs amber → gray so step 1 (left) reads as the
   "hot" Preferred Waitlist anchor and step 3 (right) recedes visually. */
.pool-depth-slider {
  -webkit-appearance: none; appearance: none; width: 100%; height: 6px;
  border-radius: 9999px;
  background: linear-gradient(to right,
    rgb(245, 158, 11) 0%,
    rgb(245, 158, 11) 33%,
    rgb(203, 213, 225) 33%,
    rgb(203, 213, 225) 66%,
    rgb(203, 213, 225) 66%,
    rgb(203, 213, 225) 100%);
  outline: none; cursor: pointer;
}
.pool-depth-slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 18px; height: 18px; border-radius: 9999px;
  background: #ffffff; border: 2px solid rgb(217, 119, 6);
  box-shadow: 0 1px 4px rgba(15, 23, 42, 0.25); cursor: pointer;
}
.pool-depth-slider::-moz-range-thumb {
  width: 18px; height: 18px; border-radius: 9999px;
  background: #ffffff; border: 2px solid rgb(217, 119, 6);
  box-shadow: 0 1px 4px rgba(15, 23, 42, 0.25); cursor: pointer;
}

/* Phase 22 — strict single-screen Replacements layout.
   The section fills the remaining viewport height so the page itself
   never scrolls during the replacement flow. Each column scrolls
   independently (sidebar on the left, candidate feed on the right).
   Mobile falls back to normal vertical stacking — tablet+ gets the lock. */
@media (min-width: 768px) {
  #tab-replacements.replacements-viewport {
    height: calc(100vh - 110px);  /* header + sticky passcode bar */
    overflow: hidden;
  }
  #tab-replacements.replacements-viewport .replacements-grid {
    height: 100%;
  }
  #tab-replacements.replacements-viewport .replacements-sidebar,
  #tab-replacements.replacements-viewport .replacements-feed {
    height: 100%;
    overflow-y: auto;
    overflow-x: visible;  /* absolute-positioned dropdowns can still escape */
    padding-right: 0.25rem;
    /* Phase 22 — padding on top so hover-scaled chiclets aren't clipped. */
    padding-top: 0.5rem;
  }
}

/* Phase 19 nit — make sure the personnel-search dropdown floats above
   everything in the Replacements tab (sidebar, candidate feed,
   mutation panels). */
#personnel-search-results { z-index: 50; }

/* Phase 19 — Multi-select Mutation dropdowns. Custom tag-pill control.
   Summary button shows N selected tags; click to reveal a checkbox panel. */
.multi-select { position: relative; width: 100%; }
.multi-select-btn {
  width: 100%; background: #ffffff;
  border: 1px solid rgb(199, 210, 254);
  color: rgb(55, 48, 163);
  border-radius: 0.5rem;
  padding: 0.35rem 1.6rem 0.35rem 0.55rem;
  font-size: 0.72rem; font-weight: 600; line-height: 1.2;
  text-align: left; cursor: pointer; min-height: 2rem;
  display: flex; align-items: center; flex-wrap: wrap; gap: 4px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%236366F1'><path fill-rule='evenodd' d='M5.23 7.21a.75.75 0 011.06.02L10 10.94l3.71-3.71a.75.75 0 011.08 1.04l-4.24 4.24a.75.75 0 01-1.06 0L5.21 8.27a.75.75 0 01.02-1.06z' clip-rule='evenodd'/></svg>");
  background-repeat: no-repeat;
  background-position: right 0.4rem center;
  background-size: 1rem;
  transition: border-color 120ms ease, background-color 120ms ease;
}
.multi-select-btn:focus,
.multi-select.open .multi-select-btn {
  outline: none;
  border-color: rgb(99, 102, 241);
  box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
}
.multi-select.mutated .multi-select-btn {
  border-color: rgb(251, 146, 60);
  color: rgb(154, 52, 18);
  background-color: rgb(255, 247, 237);
}
.multi-select-tag {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 1px 5px;
  background: rgb(238, 242, 255); color: rgb(55, 48, 163);
  border: 1px solid rgb(199, 210, 254);
  border-radius: 0.3rem; font-size: 0.65rem; font-weight: 600;
}
.multi-select.mutated .multi-select-tag {
  background: rgb(255, 237, 213); color: rgb(154, 52, 18); border-color: rgb(253, 186, 116);
}
.multi-select-tag button {
  border: 0; background: transparent; color: inherit; padding: 0; line-height: 1;
  font-size: 0.8rem; cursor: pointer;
}
.multi-select-placeholder {
  color: rgb(156, 163, 175); font-weight: 500;
}
.multi-select-panel {
  position: absolute; left: 0; right: 0; top: calc(100% + 4px);
  z-index: 40;
  background: #ffffff;
  border: 1px solid rgb(199, 210, 254);
  border-radius: 0.5rem;
  box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18), 0 2px 4px rgba(15, 23, 42, 0.08);
  max-height: 260px; overflow-y: auto;
  padding: 0.25rem;
}
.multi-select-option {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.3rem 0.5rem; border-radius: 0.35rem;
  font-size: 0.72rem; color: rgb(55, 65, 81); cursor: pointer;
}
.multi-select-option:hover { background: rgb(238, 242, 255); }
.multi-select-option input[type="checkbox"] {
  accent-color: rgb(99, 102, 241);
  width: 14px; height: 14px;
}
.multi-select-option.checked { background: rgb(238, 242, 255); color: rgb(55, 48, 163); font-weight: 600; }

/* Tablet / touch tweaks — make tap targets breathable without touching
   desktop density. */
@media (hover: none) and (pointer: coarse) {
  button, .tab-btn, .constraint-toggle, .candidate-row, .dna-block { min-height: 36px; }
  input[type="search"], textarea, select { font-size: 16px; }   /* prevents iOS zoom */
}

/* Phase 7 — highlight ring for the person the Global Find Personnel search
   landed on. Pulses three times then fades; class is removed by app.js after
   the animation so the highlight doesn't persist forever. */
@keyframes imrafaceHighlightPulse {
  0%   { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.55); }
  70%  { box-shadow: 0 0 0 10px rgba(99, 102, 241, 0); }
  100% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0); }
}
.search-highlight {
  background-color: rgb(238, 242, 255) !important;        /* indigo-50 */
  outline: 2px solid rgb(99, 102, 241);                   /* indigo-500 */
  outline-offset: 1px;
  border-radius: 0.5rem;
  animation: imrafaceHighlightPulse 1.6s ease-out 0s 3;
}

/* Phase 32 — Command Center metrics. The progress ring wrapper positions
   an absolute percentage label on top of the SVG arc. `.issue-badge`
   reacts to hover so the CTA to expand the drill-down is discoverable,
   and the spilldown body drops its own box-shadow so it floats cleanly
   inside the rose-tinted card it sits on. */
.progress-ring svg { display: block; }
.progress-ring { line-height: 1; }

.issue-badge { cursor: pointer; }
.issue-badge:hover { transform: translateY(-1px); transition: transform 120ms ease; }
.issue-badge-chev { display: inline-block; font-size: 0.7rem; }

.issue-drill { animation: issueDrillIn 180ms ease-out; }
@keyframes issueDrillIn {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Phase 33 — scrollable checklist rectangles on coach/publisher cards.
   The `.check-item` rows live inside a `max-h-32 overflow-y-auto` panel;
   this block tightens the scrollbar so 10+ items read as a compact list
   rather than a full-screen drop. */
.check-item { cursor: pointer; }
.check-item + span { pointer-events: none; }
label:has(.check-item:checked) {
  background-color: rgba(1, 33, 105, 0.06);  /* navy-10 */
}

/* Phase 43 — compact vertical sync cluster. The legacy pill container
   styling is retired; the cluster is now a transparent stacked column
   nestled in the upper-right corner of the nav row.
   .sync-alarm gives the countdown a 90s digital alarm-clock aesthetic:
   monospaced, tabular numerals, and a subtle green-phosphor glow on a
   navy body so it reads as "display, not text". */
#sync-countdown-value {
  min-width: 3.25rem; display: inline-block; text-align: right;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
}
.sync-alarm {
  color: var(--navy-dark, #011245);
  text-shadow:
    0 0 1px rgba(66, 135, 63, 0.7),
    0 0 6px rgba(66, 135, 63, 0.25);
  font-variant-numeric: tabular-nums;
}
.sync-alarm-icon {
  font-size: 0.95rem;
  line-height: 1;
  filter: drop-shadow(0 0 2px rgba(66, 135, 63, 0.35));
}
/* Active-sync shimmer — swap the phosphor green for a pulsing navy so
   operators get a non-verbal cue that the cron is burning cycles. */
.sync-alarm.sync-alarm-running {
  text-shadow:
    0 0 1px rgba(1, 33, 105, 0.75),
    0 0 6px rgba(1, 33, 105, 0.35);
  animation: syncAlarmPulse 900ms ease-in-out infinite;
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
@keyframes syncAlarmPulse {
  0%, 100% { opacity: 1.0; }
  50%      { opacity: 0.55; }
}

/* Phase 37 — airport schedule board clocks. Tabular numerals so the
   4-character time never jitters width between "1:05 AM" and "11:30 AM";
   a subtle flicker on update hints that the value is live. */
#sync-last-at,
#sync-next-at {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.03em;
  min-width: 5.25rem;
  transition: opacity 160ms ease;
}
.sync-flash { animation: syncFlash 420ms ease; }
@keyframes syncFlash {
  0%   { background: rgba(1, 33, 105, 0.12); }
  100% { background: transparent; }
}

/* The Sync Now button — subtle navy tint at rest, solid navy-light on
   hover, indigo accents while running. Uses CSS vars so the light-theme
   palette override in the Navy/Green reskin applies automatically. */
.sync-now-btn {
  color: var(--navy, #012169);
  background-color: #eaeef7;       /* navy-50 */
  border-color: #d4dbec;           /* navy-100 */
}
.sync-now-btn:hover:not(:disabled) {
  color: #ffffff;
  background-color: var(--navy-light, #1a3a7a);
  border-color: var(--navy-light, #1a3a7a);
  transform: translateY(-0.5px);
}
.sync-now-btn:active:not(:disabled) {
  transform: translateY(0);
}
.sync-now-btn:disabled {
  cursor: wait;
  color: #ffffff;
  background-color: var(--navy, #012169);
  border-color: var(--navy, #012169);
  opacity: 0.85;
}
#sync-now-btn:disabled #sync-now-spinner { display: inline-block; }
#sync-now-btn:disabled #sync-now-icon    { display: none; }

/* Keep the refresh glyph stable + visually weighted on both idle and
   hover states (otherwise it shifts slightly when the background fills). */
#sync-now-icon { line-height: 1; transform: translateY(-0.5px); }

/* Phase 33 — sync modal + score modal both use flex when unhidden.
   Phase 40 — same treatment for the Workbook help modal. */
#sync-modal:not(.hidden),
#score-modal:not(.hidden),
#workbook-help-modal:not(.hidden),
#universal-fun-modal:not(.hidden),
#report-modal:not(.hidden),
#post-swap-comms-modal:not(.hidden),
#dz-confirm-modal:not(.hidden),
#dz-add-modal:not(.hidden),
#dz-swap-modal:not(.hidden) { display: flex; }

/* Phase 46 — Workbooks full-bleed. The portal's <main> caps every
   tab at max-w-screen-2xl, but the embedded Google Sheet looks
   shrunken on ultrawide monitors when constrained that way. The
   margin trick below pushes the Workbooks panel out to the viewport
   edges (matching `body { overflow-x: hidden }`) without touching
   any other tab's layout. The inline padding mirrors <main>'s px-6
   so the dropdowns + iframe still breathe. */
#tab-workbooks:not(.hidden) {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  padding-left: 1.5rem;
  padding-right: 1.5rem;
}

/* Phase 46 — Fullscreen workbook viewer. When the toggle is active,
   the iframe's wrapper escapes its place in the document and covers
   the entire viewport. The inner iframe is forced to fill 100% so
   the Google Sheet renders at the largest possible canvas. Z-index
   beats the login gate (10000) and the help modals (9999) but stays
   under the universal fun modal (99999) so error toasts can still
   surface above it. */
#wb-viewer-wrap.workbook-fullscreen {
  position: fixed;
  inset: 0;
  z-index: 99990;
  width: 100vw;
  height: 100vh;
  background: #ffffff;
  border-radius: 0;
  border: 0;
  display: flex;
  flex-direction: column;
}
#wb-viewer-wrap.workbook-fullscreen #wb-iframe {
  flex: 1 1 auto;
  height: auto !important;
  min-height: 0 !important;
  width: 100%;
}
/* While in fullscreen, prevent the underlying body from scrolling so
   trackpad fly-bys don't pull the cached page into view behind the
   embedded sheet. */
body.has-workbook-fullscreen {
  overflow: hidden;
}

/* Phase 280 — same fullscreen mechanism, reused for the Copyediting
   tab's real-Doc/native viewer wrapper (#cv-viewer-wrap). Kept as its
   own ID-scoped rule rather than a generic selector so it can't
   accidentally affect the Workbooks/Cross-Alignment viewers. */
#cv-viewer-wrap.workbook-fullscreen {
  position: fixed;
  inset: 0;
  z-index: 99990;
  width: 100vw;
  height: 100vh;
  background: #ffffff;
  border-radius: 0;
  border: 0;
  display: flex;
  flex-direction: column;
  padding: 1rem;
  overflow-y: auto;
}
#cv-viewer-wrap.workbook-fullscreen #cv-gdoc-iframe {
  flex: 1 1 auto;
  height: auto !important;
  min-height: 0 !important;
  width: 100%;
}

/* Phase 33 — score chip hover state. */
.score-chip { cursor: pointer; background: transparent; border: 0; padding: 0; }
.score-chip:hover { text-decoration: underline; }

/* ============================================================================
 * Phase 4 — Active Tickets stack (lives inside #modal-tickets).
 *
 * The Phase-3 right-hand sidebar was reverted in Phase 4 to give the
 * workbook iframe its full width back. The ticket queue now lives
 * inside the #modal-tickets overlay; the .cc-active-issues container
 * is the scrolling region INSIDE that modal. The .cc-ticket* rules
 * style each ticket card identically to the original sidebar render.
 * ========================================================================== */

.cc-active-issues {
  /* Scroll target inside #modal-tickets. The modal card itself caps
     at max-h-[85vh] (Tailwind utility on the parent) and uses
     flex-direction: column; this child takes flex: 1 1 0 so it
     grows from a zero basis up to whatever space the header leaves
     it, then defers to overflow-y: auto for anything past that. */
  flex: 1 1 0;
  max-height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  /* `overscroll-behavior: contain` stops wheel-scroll from bubbling
     up to the document underneath the modal once the list reaches
     its top/bottom. */
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 0.5rem 0.5rem 0.75rem;
  min-height: 0;
}

/* Individual ticket card. Phase 235 — cards are info-only now (no
   magic jump), so cursor/hover affordances are gone. Padding kept
   generous so the expanded submission details read cleanly. */
.cc-ticket {
  display: block;
  width: 100%;
  text-align: left;
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 10px;
  padding: 0.625rem 0.75rem;
  margin: 0 0 0.5rem;
}
.cc-ticket-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}
.cc-ticket-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 0.04em;
  color: #4338ca;                     /* indigo-700 */
  background: #eef2ff;                /* indigo-50 */
  border: 1px solid #e0e7ff;          /* indigo-100 */
  border-radius: 9999px;
  padding: 1px 8px;
}
.cc-ticket-time {
  font-size: 10px;
  color: #9ca3af;                     /* gray-400 */
  white-space: nowrap;
}
.cc-ticket-name {
  font-size: 12px;
  font-weight: 600;
  color: #111827;                     /* gray-900 */
  margin-top: 0.25rem;
}
.cc-ticket-meta {
  font-size: 10px;
  color: #6b7280;                     /* gray-500 */
}
.cc-ticket-desc {
  font-size: 11px;
  color: #374151;                     /* gray-700 */
  margin-top: 0.375rem;
  line-height: 1.35;
  white-space: pre-wrap;
  word-break: break-word;
}
/* Phase 235 — expanded submission-detail rows (email, full timestamp,
   screenshot link) sit at the bottom of every ticket card so the
   admin doesn't have to open Smartsheet to see them. */
.cc-ticket-details {
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px dashed #e5e7eb;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  font-size: 10.5px;
  color: #4b5563;                     /* gray-600 */
}
.cc-ticket-detail-label {
  display: inline-block;
  min-width: 4.5rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 9.5px;
  color: #9ca3af;                     /* gray-400 */
  margin-right: 0.35rem;
}
.cc-ticket-detail-value {
  color: #374151;                     /* gray-700 */
  word-break: break-word;
}

/* ============================================================================
 * Phase 5 — Workbook Health badge (lives in the header next to the Sync
 * Cluster) and the matching health-modal row affordances.
 *
 * `.health-dot` is the 10px circle inside the badge button. When the
 * /api/health/workbooks endpoint returns status === "ok" it gets
 * `--ok` (steady emerald). When status === "issues" it gets
 * `--issues` (rose) plus a continuous pulse animation so the alarm
 * registers in peripheral vision.
 *
 * The pulse uses a layered box-shadow trick so we don't need to
 * animate the parent button's size — keeps the header layout stable
 * while the alarm rings.
 * ========================================================================== */

.health-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 9999px;
  flex-shrink: 0;
}

.health-dot--ok {
  background: #10b981;            /* emerald-500 */
  box-shadow: 0 0 0 0 rgb(16 185 129 / 0);
}

.health-dot--issues {
  background: #ef4444;            /* red-500 */
  animation: health-dot-pulse 1.6s ease-in-out infinite;
}

@keyframes health-dot-pulse {
  0%, 100% {
    box-shadow: 0 0 0 0 rgb(239 68 68 / 0.55);
  }
  50% {
    box-shadow: 0 0 0 6px rgb(239 68 68 / 0);
  }
}

/* Tints for the host button so the badge container also signals
   state, not just the dot. The button itself does not animate to
   keep the nav-bar layout from jittering. */
#health-badge {
  border-color: #d1d5db;          /* gray-300 default */
  color: #374151;                 /* gray-700 default */
}
#health-badge.is-ok {
  border-color: #a7f3d0;          /* emerald-200 */
  color: #047857;                 /* emerald-700 */
  background: #ecfdf5;            /* emerald-50 */
}
#health-badge.is-issues {
  border-color: #fecaca;          /* red-200 */
  color: #b91c1c;                 /* red-700 */
  background: #fef2f2;            /* red-50 */
}
#health-badge:hover {
  filter: brightness(0.97);
}

/* Each row inside #modal-health-list — clickable card identical in
   feel to the ticket cards, but tinted rose so the affordance
   carries the issue connotation. */
.health-row {
  display: block;
  width: 100%;
  text-align: left;
  background: #fff;
  border: 1px solid #fecaca;      /* red-200 */
  border-radius: 10px;
  padding: 0.625rem 0.75rem;
  cursor: pointer;
  transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease;
}
.health-row:hover {
  border-color: #f87171;          /* red-400 */
  box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.06);
  background: #fef2f2;            /* red-50 */
}
.health-row-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 0.04em;
  color: #b91c1c;                 /* red-700 */
  background: #fef2f2;
  border: 1px solid #fecaca;
  border-radius: 9999px;
  padding: 1px 8px;
}
.health-row-error {
  font-size: 12px;
  color: #1f2937;                 /* gray-800 */
  margin-top: 0.25rem;
  line-height: 1.35;
}

/* ============================================================================
 * Phase 7 — Roster Manager Master-Detail layout.
 *
 * `.rm-shell` is the grid container that caps total height; the inner
 * `.rm-pane` flex columns own the scrolling regions. The min-h-0 +
 * overflow-hidden pairing on each pane is the load-bearing flex
 * fix — without it, the inner overflow-y-auto would expand the pane
 * past the cap and the outer page would scroll instead.
 * ========================================================================== */

.rm-pane {
  /* See Phase-4 sidebar comment in `.cc-sidebar`: min-height: 0 is
     required so the inner `overflow-y-auto` actually scrolls instead
     of expanding the pane. The Tailwind `min-h-0` utility on the
     element itself does the real work; this rule is here for
     emphasis + browsers that ignore the utility. */
  min-height: 0;
}

/* Filter chips on the left pane's master list. Toggle the
   `rm-chip-active` class on the picked one to give it the
   "selected" treatment. */
.rm-chip {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border: 1px solid #e5e7eb;            /* gray-200 */
  border-radius: 9999px;
  background: #fff;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #6b7280;                        /* gray-500 */
  cursor: pointer;
  transition: border-color 120ms ease, color 120ms ease, background 120ms ease;
}
.rm-chip:hover {
  border-color: #c7d2fe;                 /* indigo-200 */
  color: #4338ca;                        /* indigo-700 */
  background: #eef2ff;                   /* indigo-50 */
}
.rm-chip-active {
  border-color: #6366f1;                 /* indigo-500 */
  background: #4338ca;                   /* indigo-700 */
  color: #ffffff;
}
.rm-chip-active:hover {
  background: #4338ca;
  color: #ffffff;
}

/* Each entity card in the master list. Keep the affordance compact
   so a few hundred reviewers fit without exotic virtualization. */
.rm-card {
  display: block;
  width: 100%;
  text-align: left;
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 10px;
  padding: 0.625rem 0.75rem;
  margin: 0 0 0.375rem;
  cursor: pointer;
  transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease;
}
.rm-card:hover {
  border-color: #c7d2fe;
  background: #f8fafc;                  /* slate-50 */
  box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.06);
}
.rm-card.is-active {
  border-color: #6366f1;
  background: #eef2ff;                  /* indigo-50 */
  box-shadow: 0 0 0 3px rgb(99 102 241 / 0.18);
}
.rm-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}
.rm-card-name {
  font-size: 13px;
  font-weight: 600;
  color: #111827;                       /* gray-900 */
  line-height: 1.25;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rm-card-meta {
  font-size: 11px;
  color: #6b7280;                       /* gray-500 */
  margin-top: 0.125rem;
  line-height: 1.25;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Type badge — color-coded so the picker reads at a glance. */
.rm-type-badge {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
  font-weight: 700;
  font-size: 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-radius: 9999px;
  padding: 1px 6px;
  border: 1px solid;
  white-space: nowrap;
}
.rm-type-coach    { color: #4338ca; background: #eef2ff; border-color: #e0e7ff; }
.rm-type-reviewer { color: #047857; background: #ecfdf5; border-color: #a7f3d0; }
.rm-type-team     { color: #b45309; background: #fffbeb; border-color: #fde68a; }

/* Detail-pane sections (rendered into #rm-detail by the JS). */
.rm-detail-header {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  padding-bottom: 0.75rem;
  margin-bottom: 1rem;
  border-bottom: 1px solid #e5e7eb;
}
.rm-detail-team-card {
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 14px;
  padding: 1rem;
  margin-bottom: 0.875rem;
}
.rm-detail-team-card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}
.rm-roster-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: start;
  gap: 0.5rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid #f3f4f6;     /* gray-100 */
}
.rm-roster-row:last-child { border-bottom: 0; }
.rm-roster-name {
  font-size: 13px;
  font-weight: 600;
  color: #111827;
}
.rm-roster-role {
  display: inline-block;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 1px 6px;
  border-radius: 9999px;
  margin-left: 0.375rem;
  vertical-align: middle;
  background: #f3f4f6;
  color: #4b5563;
  border: 1px solid #e5e7eb;
}
.rm-roster-contact {
  font-size: 11px;
  color: #6b7280;
  line-height: 1.45;
  margin-top: 0.125rem;
}
.rm-roster-contact a {
  color: #4338ca;
  text-decoration: none;
}
.rm-roster-contact a:hover {
  text-decoration: underline;
}
.rm-roster-rev-list { padding-left: 0; margin-top: 0.5rem; }

/* ============================================================================
 * Phase 9.1 — Blast Success Overlay.
 *
 * The card pops in via a subtle scale+fade so the celebratory cue
 * registers without feeling jarring. The animation only runs when
 * the modal flips out of `.hidden`, so dismiss-and-reopen always
 * replays the entrance.
 * ========================================================================== */

@keyframes blast-success-pop {
  0%   { opacity: 0; transform: scale(0.92); }
  60%  { opacity: 1; transform: scale(1.02); }
  100% { opacity: 1; transform: scale(1.00); }
}

#modal-blast-success:not(.hidden) .blast-success-card {
  animation: blast-success-pop 320ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

/* Larger, slightly looser leading on the summary text — it's the
   only thing the user reads before the auto-dismiss. */
#blast-success-summary {
  font-size: 14px;
  line-height: 1.45;
}
#blast-success-summary .blast-success-line { display: block; }
#blast-success-summary .blast-success-headline {
  display: block;
  font-size: 15px;
  font-weight: 600;
  color: #047857;                 /* emerald-700 */
  margin-bottom: 0.25rem;
}
#blast-success-summary code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
  font-size: 12px;
  background: #ecfdf5;            /* emerald-50 */
  border: 1px solid #a7f3d0;      /* emerald-200 */
  border-radius: 6px;
  padding: 1px 6px;
  color: #065f46;                 /* emerald-800 */
}

/* ============================================================================
 * Phase 11 — Contextual Help modal (.help-body).
 *
 * Caller-injected HTML inside #modal-contextual-help-body uses these
 * defaults so help content stays readable without every caller
 * re-defining heading / list / code styles.
 * ========================================================================== */

.help-body h4 {
  font-size: 13px;
  font-weight: 700;
  color: #1f2937;                 /* gray-800 */
  letter-spacing: 0.01em;
  margin: 1.1em 0 0.35em;
}
.help-body h4:first-child { margin-top: 0; }
.help-body p              { margin: 0 0 0.6em; }
.help-body ul, .help-body ol {
  padding-left: 1.4em;
  margin: 0 0 0.7em;
}
.help-body ul { list-style: disc; }
.help-body ol { list-style: decimal; }
.help-body li             { margin: 0.2em 0; }
.help-body strong         { font-weight: 600; color: #111827; }
.help-body code, .help-body kbd {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
  font-size: 12px;
  background: #eef2ff;            /* indigo-50 */
  border: 1px solid #e0e7ff;      /* indigo-100 */
  border-radius: 4px;
  padding: 1px 5px;
  color: #4338ca;                 /* indigo-700 */
}
/* Tip / warning callouts. Add `<div class="help-tip">…</div>` or
   `help-warn` to surface boxed advice. */
.help-body .help-tip,
.help-body .help-warn {
  border-radius: 8px;
  padding: 0.55em 0.7em;
  margin: 0.6em 0;
  font-size: 12.5px;
  line-height: 1.45;
}
.help-body .help-tip {
  background: #ecfdf5;            /* emerald-50 */
  border: 1px solid #a7f3d0;      /* emerald-200 */
  color: #065f46;                 /* emerald-800 */
}
.help-body .help-warn {
  background: #fef2f2;            /* red-50 */
  border: 1px solid #fecaca;      /* red-200 */
  color: #991b1b;                 /* red-800 */
}

/* ===========================================================================
   Final Reports — digital recreation of the IMRA Cycle final-report PDFs.
   Color-banded table headers match the static PDF deliverables (navy
   header bars, red Suitability Noncompliance, green Suitability
   Excellence, blue Public Feedback) so the live view reads identically.
   =========================================================================== */

.fr-report {
  /* Constrain reading width similar to a US-Letter page so the live
     view feels print-shaped on screen. The actual print render uses
     the @media print rules below, not this class. */
  max-width: 8.5in;
  margin: 0 auto;
}

.fr-page {
  /* Each .fr-page is a logical "page" — page breaks fire between
     them in print so the page-1 summary and the report body land on
     separate sheets when saved as PDF. */
  background: #ffffff;
}

.fr-table-flat {
  border-collapse: collapse;
  table-layout: auto;
}
/* Phase 81 — three first-page summary tables (Quality Rubric Section,
   Suitability Noncompliance, Suitability Excellence) share this fixed
   4-column grid so their column boundaries + right edges line up
   beneath one another. Label col fills the left half; the three data
   slots on the right are equal-width so headers like "IMRA REVIEWERS"
   stop overflowing and the right edges of all three tables align. */
.fr-summary-grid {
  table-layout: fixed;
  width: 100%;
}
.fr-summary-grid col.fr-col-label { width: 52%; }
.fr-summary-grid col.fr-col-data  { width: 16%; }
.fr-th {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
  text-align: center;
  padding: 6px 8px;
  border: 1px solid #d1d5db;
  color: #111827;
  background: #f9fafb;
}
.fr-th-white {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
  text-align: center;
  padding: 8px;
  border: 1px solid rgba(255, 255, 255, 0.2);
  color: #ffffff;
}
.fr-td {
  border: 1px solid #d1d5db;
  font-size: 12px;
  color: #111827;
  padding: 6px 10px;
  vertical-align: top;
}
.fr-td-center {
  border: 1px solid #d1d5db;
  font-size: 12px;
  color: #111827;
  padding: 6px 10px;
  text-align: center;
  vertical-align: middle;
}

/* Section bars + color bands. Navy mirrors the PDF's quality rubric
   header, red mirrors the suitability noncompliance band, green
   mirrors excellence, blue mirrors public feedback. */
.fr-section-bar {
  font-size: 13px;
  font-weight: 700;
  color: #111827;
  background: #f3f4f6;
  border: 1px solid #d1d5db;
  border-bottom: none;
  padding: 6px 12px;
}
.fr-bar-gray       { background: #f3f4f6 !important; }
.fr-bar-gray-soft  { background: #f3f4f6 !important; color: #111827; }
.fr-bar-navy       { background: #012169 !important; }
.fr-bar-red        { background: #991b1b !important; }
.fr-bar-green      { background: #15803d !important; }
.fr-bar-blue       { background: #1e40af !important; }
.fr-bar-navy .fr-th-white,
.fr-bar-red  .fr-th-white,
.fr-bar-green .fr-th-white,
.fr-bar-blue .fr-th-white {
  background: inherit;
}

/* Per-guidance writeup card. Reads like the static PDF — bold title
   with the full guidance statement, score line, then a multi-
   paragraph evidence block. */
.fr-guidance {
  page-break-inside: avoid;
}

/* Preliminary Report workspace — clamp the workspace to the
   viewport so the page itself never scrolls. Both panes (TOC +
   report) scroll independently inside their own boxes. Uses 100dvh
   so browser-chrome changes (mobile address bar, etc.) recompute
   correctly. ``overflow: hidden`` on the workspace + the grid
   ensures the report content can never escape its scroll-box and
   force the body to scroll. ``min-height`` removed: Jacob saw the
   page scroll on a shorter window where 520px > available height
   when the min-height was active. On print we revert to natural
   flow (see the @media print block below). */
.fr-workspace-viewport {
  height: calc(100dvh - 220px);
  max-height: calc(100dvh - 220px);
  min-height: 360px;
  overflow: hidden;
}
.fr-workspace-viewport > div {
  overflow: hidden;
}

/* -----------------------------------------------------------------
   Print rules — fire when the operator hits "Print / Save as PDF".
   We hide the app shell chrome (nav tabs, login gate, modals) so
   the printable bytes carry only the report itself.

   Phase 106 — added print-color-adjust:exact so colored pills /
   verdict badges / status chips survive the PDF render instead of
   washing out to white-on-white. Also tightened page margins +
   page-break behavior so the on-screen layout maps 1:1 to the
   exported PDF.
   ----------------------------------------------------------------- */
@page {
  /* Half-inch margin matches the on-screen card padding closely so
     the printed report doesn't feel cramped against the page edge. */
  margin: 0.5in;
}
@media print {
  /* Preserve every Tailwind-applied background color, border color,
     and shadow. Without this Chrome / Edge silently drop colored
     fills, which is why the old PDF "looked lacking" — verdict pills,
     status chips, and the colored phase headers all came out white. */
  *, *::before, *::after {
    print-color-adjust: exact !important;
    -webkit-print-color-adjust: exact !important;
  }
  /* Drop the viewport bound so the full report flows onto the page
     instead of being clipped to one viewport-worth of content.
     Phase 106.1 — also drop max-height + overflow:hidden (the
     screen-mode rules use them to keep the report inside a scroll
     box; under print those are exactly what causes Chrome to think
     the document is only ~2 pages long, because the print engine
     can't see content scrolled out of the box). The inner grid
     wrapper (.fr-workspace-viewport > div) ALSO has overflow:hidden
     and has to be released, otherwise the right-pane content is
     still clipped one level up. */
  html, body {
    height: auto !important;
    max-height: none !important;
    overflow: visible !important;
  }
  .fr-workspace-viewport,
  .fr-workspace-viewport > div {
    height: auto !important;
    max-height: none !important;
    min-height: 0 !important;
    overflow: visible !important;
  }
  #fr-stage {
    height: auto !important;
    max-height: none !important;
    overflow: visible !important;
    background: #ffffff !important;
    border: none !important;
    padding: 0 !important;
  }
  /* Pull the report to full width and drop the screen-only chrome. */
  body { background: #ffffff !important; }
  .no-print,
  #app-nav, header nav,
  #login-gate,
  #modal-tickets,
  #modal-tickets-report,
  #modal-resolve-notify,
  #report-modal,
  #fr-download-menu,
  .tab-btn { display: none !important; }
  /* Hide every tab panel except Final Reports so accidental
     printing from another tab doesn't bleed onto the page. */
  .tab-panel { display: none !important; }
  #tab-final-reports { display: block !important; }
  /* Drop the picker, left rail, workflow modal, and any in-app
     chrome — only the rendered report on the right should print. */
  #fr-picker, #fr-print-btn, #fr-back-btn,
  #fr-left-rail, #fr-workflow-modal,
  #fr-issues-modal, #fr-xa-modal { display: none !important; }
  /* Collapse the workspace grid into a single column for print. */
  #fr-workspace > div { grid-template-columns: 1fr !important; }
  /* Force page breaks between the logical "pages" of the digital
     report so the page-1 summary lands on its own sheet. */
  .fr-page { page-break-after: always; break-after: page; }
  .fr-page:last-child { page-break-after: auto; break-after: auto; }
  /* Use a print-friendly font size and remove shadows. */
  .fr-report {
    max-width: 100% !important;
    box-shadow: none !important;
    border: none !important;
  }
  /* Keep logical units together so a section / guidance block doesn't
     split awkwardly across pages. break-inside is the modern alias for
     page-break-inside; we list both so Chrome + Firefox both honor it. */
  .fr-section,
  .fr-guidance,
  .fr-section-header,
  .fr-sub-section,
  .fr-verdict-row,
  .fr-score-summary {
    page-break-inside: avoid;
    break-inside: avoid;
  }
  /* Heading rules — give them a touch of head-room above the next
     pagebreak so an h2 doesn't strand at the page bottom. */
  h1, h2, h3 {
    page-break-after: avoid;
    break-after: avoid-page;
  }
  /* Tailwind's "shadow-sm" / "shadow" doesn't print useful — strip it.
     Borders we keep (they communicate section structure). */
  .shadow-sm, .shadow, .shadow-md, .shadow-lg {
    box-shadow: none !important;
  }
  /* Phase 107 — the inline "Score: X/X" helper sits below each
     guidance heading in the on-screen view (a quick eyeballable
     ratio for the coach). The score is already encoded in the
     guidance table above, so duplicating it on the PDF just adds
     visual noise. Hide in print. */
  .fr-score-helper { display: none !important; }
}

/* Phase 108 — Notes from Vince "unread" pulse.
   Replaces the on-login modal popup with a header-button pulse so
   the alert stays peripheral instead of demanding immediate
   attention. Bluish-purple ring expands + fades on a 1.4s loop;
   the button text shifts to indigo so the chrome reads as
   "something new here." Reduced-motion users get a steady ring
   instead of the animation. */
@keyframes notesFromVincePulse {
  0%, 100% {
    box-shadow:
      0 0 0 0 rgba(124, 58, 237, 0.85),
      0 0 8px 1px rgba(124, 58, 237, 0.45);
  }
  50% {
    box-shadow:
      0 0 0 6px rgba(124, 58, 237, 0.0),
      0 0 14px 4px rgba(124, 58, 237, 0.9);
  }
}
.notes-from-vince-pulse {
  animation: notesFromVincePulse 1.4s ease-in-out infinite;
  color: #6d28d9 !important;           /* indigo-700 for the button text */
  background: rgba(167, 139, 250, 0.12); /* faint violet tint */
  border-color: rgba(124, 58, 237, 0.55) !important;
  position: relative;
  z-index: 4;
}
@media (prefers-reduced-motion: reduce) {
  .notes-from-vince-pulse {
    animation: none;
    box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.9);
  }
}

/* Phase 108 — Cross-alignment matrix sticky-column rules.
   The matrix has two pinned-left columns (Grade, Program) followed
   by N indicator score columns. Before this rule, long program
   titles bled under the score cells when scrolled horizontally
   because the sticky <td>s had no z-index — they were defaulting
   to auto and getting painted behind any positioned descendants
   in the scrolled cells. Explicit widths + z-index + an opaque
   background per row pins them cleanly above the scroll. */
.fr-xa-table {
  border-collapse: separate;
  border-spacing: 0;
}
.fr-xa-sticky-grade {
  position: sticky;
  left: 0;
  width: 5.5rem;
  min-width: 5.5rem;
  max-width: 5.5rem;
  z-index: 3;
  border-right: 1px solid rgba(0, 0, 0, 0.04);
}
.fr-xa-sticky-program {
  position: sticky;
  left: 5.5rem;
  width: 16rem;
  min-width: 16rem;
  max-width: 16rem;
  z-index: 3;
  border-right: 1px solid rgba(0, 0, 0, 0.06);
}
/* Lift the sticky header cells slightly above their body siblings
   so a header doesn't slide under the first row's bg when both
   are sticky. */
.fr-xa-table thead .fr-xa-sticky-grade,
.fr-xa-table thead .fr-xa-sticky-program {
  z-index: 5;
}

/* Phase 102 — Quality Reports tile "needs attention" pulse. Fires on
   an unaddressed Safal block so the indicator visibly nags for action
   on the coach's homepage. A pulsing amber ring reads as "look here"
   without changing the tile's stage color. z-index lift keeps the
   glow above neighboring tiles. */
@keyframes qrTilePulse {
  0%, 100% {
    box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.85), 0 0 6px 1px rgba(245, 158, 11, 0.55);
  }
  50% {
    box-shadow: 0 0 0 4px rgba(245, 158, 11, 0.0), 0 0 12px 3px rgba(245, 158, 11, 0.85);
  }
}
.qr-tile-pulse {
  animation: qrTilePulse 1.25s ease-in-out infinite;
  position: relative;
  z-index: 4;
}
@media (prefers-reduced-motion: reduce) {
  /* Respect reduced-motion: hold a steady amber ring instead of pulsing. */
  .qr-tile-pulse {
    animation: none;
    box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.9);
  }
}

/* Phase 199 — funny-GIF master switch. When the V1281 admin flips the
   toggle in Diagnostics OFF, every inline easter-egg GIF tagged with
   .js-gif-easter-egg collapses entirely (display:none, no broken
   image space). showFunModal handles the dynamic-GIF popups via the
   same _gifsEnabled() check in JS. */
body.gifs-muted .js-gif-easter-egg { display: none !important; }

/* ============================================================
   Phase 196 — PIE (Personally Interesting Enhancement).
   Tetris-flavored brick stack. Each .pie-brick gets a 3D-ish
   color tile and animates in from above on first render. The
   :nth-child delay creates the staggered "falling stack" effect.
   ============================================================ */

@keyframes pieTetrisDrop {
  0%   { transform: translateY(-300px) rotate(-3deg); opacity: 0; }
  70%  { transform: translateY(8px) rotate(0deg);    opacity: 1; }
  85%  { transform: translateY(-3px); }
  100% { transform: translateY(0);    opacity: 1; }
}

.pie-stack-animating .pie-brick {
  animation: pieTetrisDrop 0.6s cubic-bezier(0.3, 0.7, 0.4, 1.05) backwards;
}
.pie-stack-animating .pie-brick:nth-child(1)  { animation-delay: 0.00s; }
.pie-stack-animating .pie-brick:nth-child(2)  { animation-delay: 0.12s; }
.pie-stack-animating .pie-brick:nth-child(3)  { animation-delay: 0.24s; }
.pie-stack-animating .pie-brick:nth-child(4)  { animation-delay: 0.36s; }
.pie-stack-animating .pie-brick:nth-child(5)  { animation-delay: 0.48s; }
.pie-stack-animating .pie-brick:nth-child(6)  { animation-delay: 0.60s; }
.pie-stack-animating .pie-brick:nth-child(7)  { animation-delay: 0.72s; }
.pie-stack-animating .pie-brick:nth-child(8)  { animation-delay: 0.84s; }
.pie-stack-animating .pie-brick:nth-child(9)  { animation-delay: 0.96s; }
.pie-stack-animating .pie-brick:nth-child(n+10) { animation-delay: 1.08s; }

/* Tetris-piece color palette assignment by sort_order (cycled). */
.pie-brick[data-tetris="0"] { --pie-fill: #ef4444; --pie-shade: #b91c1c; } /* Z */
.pie-brick[data-tetris="1"] { --pie-fill: #f59e0b; --pie-shade: #b45309; } /* L */
.pie-brick[data-tetris="2"] { --pie-fill: #eab308; --pie-shade: #a16207; } /* O */
.pie-brick[data-tetris="3"] { --pie-fill: #22c55e; --pie-shade: #15803d; } /* S */
.pie-brick[data-tetris="4"] { --pie-fill: #06b6d4; --pie-shade: #0e7490; } /* I */
.pie-brick[data-tetris="5"] { --pie-fill: #6366f1; --pie-shade: #4338ca; } /* J */
.pie-brick[data-tetris="6"] { --pie-fill: #a855f7; --pie-shade: #7e22ce; } /* T */

.pie-brick {
  background: linear-gradient(180deg, var(--pie-fill, #94a3b8) 0%, var(--pie-shade, #475569) 100%);
  border: 2px solid rgba(0, 0, 0, 0.18);
  border-radius: 6px;
  box-shadow:
    inset 2px 2px 0 rgba(255, 255, 255, 0.35),
    inset -3px -3px 0 rgba(0, 0, 0, 0.22),
    0 2px 6px rgba(0, 0, 0, 0.15);
  color: white;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
}

.pie-brick[data-status="done"]   { opacity: 0.55; filter: saturate(0.7); }
.pie-brick[data-status="parked"] { opacity: 0.65; filter: grayscale(0.4); }

.pie-subtask {
  background: rgba(255, 255, 255, 0.18);
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 4px;
}

/* Phase 269.2 — login passcode masking. Deliberately NOT type="password":
   Phase 79 found that flips Chrome's leaked-password breach-check on for
   these short numeric codes, which confused coaches mid-login. Masking via
   -webkit-text-security instead keeps the input type="text" (so that fix
   stays intact) while still hiding the characters during screen shares.
   Chrome/Edge/Safari support this; Firefox has no equivalent and will show
   the passcode in plain text there — acceptable given this is a polish
   item, not a real credential boundary. */
#login-input.pw-masked { -webkit-text-security: disc; }
