/* deepset Vantage — shared styles.
   Tokens carried over from the validated design mockup (deepset-vantage.html,
   kept in design-reference/ in this repo) so the real app matches it exactly. */

:root{
  --navy:#0A1B3D; --navy-2:#122A5E; --navy-3:#1B3875;
  --electric:#2F5FFF; --electric-2:#5C84FF;
  --bg:#F2F6FF; --surface:#FFFFFF; --surface-2:#F8FAFF;
  --ink:#0A1B3D; --muted:#5B6B8C; --faint:#8C99B8;
  --border:#DCE5F7; --border-2:#C9D7F2;
  --band-1:#E0534B; --band-1-bg:#FDEBEA;
  --band-2:#E59A3A; --band-2-bg:#FDF1E1;
  --band-3:#1E9E78; --band-3-bg:#E4F6EF;
  --band-4:#2E86D6; --band-4-bg:#E7F1FC;
  --band-5:#6B55DE; --band-5-bg:#EDE9FC;
  --good:#1E9E78; --warn:#E59A3A; --crit:#E0534B;
  --shadow:0 1px 2px rgba(10,27,61,.06), 0 8px 24px -12px rgba(10,27,61,.18);
  --font-display:'Manrope',system-ui,sans-serif;
  --font-body:'IBM Plex Sans',system-ui,sans-serif;
  --font-mono:'IBM Plex Mono',ui-monospace,monospace;
  color-scheme:light;
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --navy:#060E22; --navy-2:#0D1B3B; --navy-3:#15295A;
    --electric:#5C84FF; --electric-2:#7FA0FF;
    --bg:#0A1120; --surface:#111A30; --surface-2:#0D1526;
    --ink:#E7ECFA; --muted:#9FACCB; --faint:#6D7A9C;
    --border:#223259; --border-2:#2C3E6B;
    --band-1:#FF8178; --band-1-bg:#2B1620;
    --band-2:#F3B463; --band-2-bg:#2A2015;
    --band-3:#4FD3A6; --band-3-bg:#122A26;
    --band-4:#6FB0F2; --band-4-bg:#102232;
    --band-5:#A796F5; --band-5-bg:#1E1A38;
    --good:#4FD3A6; --warn:#F3B463; --crit:#FF8178;
    color-scheme:dark;
  }
}

*{box-sizing:border-box}
/* min-height, not height: 100%, 2026-11-02 — a straight height:100% caps
   html/body at exactly the viewport's height. Nothing else here actually
   needs that exact cap (board.html's own fixed-height pieces all size off
   vh units or position:fixed, which measure the viewport directly, not
   body's box), but it silently broke the whole page's ability to scroll
   on any page whose content runs taller than one screen — invisible until
   analytics.html grew past a single viewport with the new competency
   radar/heatmap/technical sections, at which point everything past "By
   level" became completely unreachable, not just scrolled-off. min-height
   keeps the original intent (a short page's background still fills the
   screen) while letting a tall one grow and scroll normally. */
html,body{min-height:100%}
/* Admin tables (Cycles, Invites especially) can get wider than the viewport
   on a narrower screen — without this, an un-contained wide table pushes the
   WHOLE page into horizontal scroll, dragging the descriptive text above it
   off-screen too (only its right-hand edge peeking in). Each table's own
   .table-scroll wrapper is still what should scroll in the normal case; this
   is a backstop so a future table that forgets that wrapper can't do it again. */
html,body{overflow-x:hidden}
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--font-body);
  -webkit-font-smoothing:antialiased}
.wrap{max-width:1400px;margin:0 auto;padding-left:28px;padding-right:28px}
@media (max-width:640px){ .wrap{padding-left:18px;padding-right:18px} }

/* Full screen (board.html only — is-fullscreen is toggled by board.js'
   toggleFullscreen()/updateFullscreenBtn()): beyond just hiding the
   browser's own chrome, let the board's toolbar and canvas use the
   reclaimed width instead of staying capped at 1400px, so full screen
   actually shows more cards rather than just adding side margins
   (Mert, 2026-11-02). Scoped to .board-wrap, not every .wrap on every
   page. */
body.is-fullscreen .board-wrap{max-width:none}
h1,h2,h3,.display{font-family:var(--font-display);letter-spacing:-.01em}
.mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums}
a{color:inherit;text-decoration:none}
button{font-family:var(--font-body)}

/* ---------- Topbar ---------- */
.topbar{position:sticky;top:0;z-index:40;background:var(--navy);color:#EAF0FF}
.topbar-inner{display:flex;align-items:center;gap:18px;padding-block:14px}
.brand{display:flex;align-items:center;gap:10px;cursor:pointer;flex:0 0 auto}
.brand-logo{height:20px;width:auto;display:block;flex:0 0 auto}
.brand-div{width:1px;height:22px;background:rgba(255,255,255,.22);flex:0 0 auto}
.brand-text{font-family:var(--font-display);font-weight:800;font-size:17px;letter-spacing:-.01em}
.brand-text small{display:block;font-family:var(--font-body);font-weight:400;font-size:10.5px;color:#9FB0DE;letter-spacing:.06em;text-transform:uppercase;margin-top:1px;white-space:nowrap}
.topbar-spacer{flex:1}
.pill-btn{display:inline-flex;align-items:center;gap:6px;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.14);color:#EAF0FF;border-radius:999px;padding:7px 14px;font-size:13px;font-weight:600;cursor:pointer;transition:background .15s ease}
.pill-btn:hover{background:rgba(255,255,255,.16)}
.avatar-chip{width:30px;height:30px;border-radius:50%;background:var(--electric);display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;color:#fff;flex:0 0 auto}
@media (max-width:480px){
  .topbar-inner{gap:10px}
  .brand-text small{display:none}
  .pill-btn .lbl{display:none}
  .pill-btn{padding:7px}
}

/* ---------- Generic page bits ---------- */
.hero{padding:44px 0 8px}
.hero h1{font-size:clamp(24px,3.2vw,34px);font-weight:800;margin:0 0 8px}
.hero p{color:var(--muted);font-size:14.5px;max-width:58ch;line-height:1.55;margin:0}
.section-head{display:flex;align-items:baseline;justify-content:space-between;margin:30px 0 14px;gap:12px;flex-wrap:wrap}
.card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px;margin-bottom:40px}
.cycle-card{background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:18px;cursor:pointer;transition:border-color .12s ease,transform .12s ease}
.cycle-card:hover{border-color:var(--border-2);transform:translateY(-1px)}
.cycle-card h3{margin:0 0 6px;font-size:16px}
.status{display:inline-block;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;padding:3px 9px;border-radius:999px;margin-bottom:10px}
.status.draft{background:var(--surface-2);color:var(--muted)}
.status.open{background:var(--band-4-bg);color:var(--band-4)}
.status.locked{background:var(--band-3-bg);color:var(--band-3)}
.status.archived{background:var(--border);color:var(--faint)}
.cycle-meta{font-size:12.5px;color:var(--muted);line-height:1.7}
.empty-note{color:var(--muted);font-size:13.5px;border:1px dashed var(--border-2);border-radius:12px;padding:18px;text-align:center}

.btn{display:inline-flex;align-items:center;gap:7px;border-radius:10px;padding:9px 16px;font-size:13px;font-weight:700;cursor:pointer;border:1px solid var(--border)}
.btn.primary{background:var(--navy);color:#fff;border-color:var(--navy)}
.btn.primary:hover{background:var(--navy-2)}
.btn.ghost{background:var(--surface);color:var(--ink)}
.btn.ghost:hover{border-color:var(--border-2)}
.btn.danger{background:var(--surface);color:var(--crit);border-color:var(--band-1-bg)}
.btn:disabled{opacity:.5;cursor:not-allowed}

/* ---------- Board ---------- */
.board-toolbar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin:22px 0 16px}
.track-tabs{display:flex;gap:6px;background:var(--surface-2);border:1px solid var(--border);border-radius:999px;padding:3px}
.track-tab{border:none;background:transparent;padding:7px 15px;border-radius:999px;font-size:12.5px;font-weight:700;cursor:pointer;color:var(--muted)}
.track-tab.active{background:var(--navy);color:#fff}
/* Binary LOCKED / Not Locked badge, next to the cycle name — see
   renderLockBadge() in board.js. */
.cycle-lock-badge{display:inline-flex;align-items:center;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;padding:5px 12px;border-radius:999px}
.cycle-lock-badge.locked{background:var(--band-1-bg);color:var(--band-1)}
.cycle-lock-badge.unlocked{background:var(--band-3-bg);color:var(--band-3)}
/* How much of this track's cycle has a Manager's final rating set yet —
   the same room-wide "how many rough calibrations changed things" view
   Mert asked for, next to the per-person Pending/Calibrated pill in the
   panel (2026-10-30). */
.calib-progress{display:inline-flex;align-items:center;font-size:11.5px;font-weight:600;color:var(--muted);background:var(--surface-2);border:1px solid var(--border);padding:5px 12px;border-radius:999px;white-space:nowrap}

/* Fixed-size "viewport" onto the board canvas — overflow:hidden because
   movement now comes from panning/zooming #boardCanvas's CSS transform,
   not native scrolling (see wireZoomPanOnce() in board.js). Needs an
   explicit height, not max-height: #boardCanvas is position:absolute
   (out of flow), so without one this box would collapse to zero height. */
.board-scroll{position:relative;overflow:hidden;height:74vh;border:1px solid var(--border);border-radius:16px;background:var(--surface);margin-bottom:24px;cursor:grab;touch-action:none}
.board-scroll.panning{cursor:grabbing}
.board-canvas{position:absolute;top:0;left:0;transform-origin:0 0;will-change:transform}

/* Free-form weighted-lane board layout (2026-10-30, replacing the old
   CSS-Grid "5 equal columns" board) — Mert wanted the crowded middle
   bands wider than the sparse extremes, and cards placed completely
   freely (no snapping into a cell), so the board can no longer be a
   grid of fixed cells. Instead: one fixed-width flex column (.board),
   a header row (.board-head-row) and one .level-row per level, each
   holding a .score-lane whose only children are an absolutely
   positioned background (.lane-bg, painted once in board.js as
   LANE_BG_HTML) and freely positioned .person-card elements — nothing
   in a lane is laid out by the browser, every pixel comes from
   board.js's scoreToFraction()/boardY math. 1600px of lane width +
   96px of level-label width gives .board a known, fixed intrinsic size
   (not min-width — fitToView()/applyZoom() need a *stable* number to
   scale from, and leaving it as min-width would let flex stretch it to
   whatever's wider). Because .board-head-row's band-head items use the
   exact same weighted flex-basis percentages as .lane-bg's band-zone
   items, but share their row with the 96px label spacer, flexbox's own
   shrink math (equal flex-shrink on all 5 band-heads, non-shrinking
   spacer) works out to the *same* pixel widths as the zones below —
   don't add flex-grow/shrink overrides to .band-head, that cancels it. */
.board{display:flex;flex-direction:column;width:1696px}
.board-head-row{display:flex}
.level-row-label-spacer{flex:0 0 96px;border-bottom:1px solid var(--border);background:var(--surface)}
.band-head{padding:12px 10px;text-align:center;border-bottom:1px solid var(--border);border-left:1px solid var(--border);background:var(--surface)}
.band-head:first-child{border-left:none}
.band-head b{font-size:12.5px}
/* Numeric score-cutoff strip between the band names and the first lane —
   e.g. "2.50", "3.25" — so a glance tells you exactly which number is
   covered by which section, not just which band (Mert, 2026-10-30,
   working from his old Figma reference). Same flex-basis-per-band
   structure as .board-head-row, on purpose — see that rule's comment. */
.tick-row{display:flex;background:var(--surface)}
.tick-zone{position:relative;height:26px;border-left:1px solid var(--border)}
.tick-zone:first-child{border-left:none}
.tick-mark{position:absolute;bottom:0;transform:translateX(-50%);font-family:var(--font-display);font-size:10.5px;font-weight:800;white-space:nowrap;padding-bottom:8px}
.tick-mark::after{content:'';position:absolute;left:50%;bottom:0;width:1px;height:8px;background:currentColor;opacity:.55}
.level-row{display:flex}
/* Big, bold, horizontal (not the old small rotated text) so the level
   you're looking at still reads at a glance even zoomed way out for a
   "bird's eye" view of the whole board — Mert's ask, 2026-10-30, after
   comparing against the old Figma board's large level labels. */
.level-row-label{flex:0 0 96px;display:flex;align-items:center;justify-content:center;font-family:var(--font-display);font-weight:800;font-size:26px;letter-spacing:-.02em;border-top:1px solid var(--border);background:var(--surface-2);color:var(--ink)}
/* ---------- Frozen header/column overlays (2026-10-30) ----------
   Mert: "we should aim at making level and ratings always on the screen
   available, whenever we zoom, think of them as the frozen cells on google
   sheet, so it never goes away no matter where you go on the table/zoom."
   #frozenHeader/#frozenCol (siblings of #boardCanvas, see board.html's
   markup via board.js's renderBoard()) sit OUTSIDE the panned/zoomed
   canvas and are repositioned every applyZoom() tick by board.js's
   syncFrozenOverlays() to track the real header/tick/label elements below.
   Those real elements are kept — board.js still measures its geometry off
   them every render — just made invisible here, since the frozen copies
   are what's actually shown. #frozenCol's z-index is higher than
   #frozenHeader's so it naturally paints over the header's top-left
   corner, forming the "corner" without a 3rd element. Both sit above even
   a dragging card (z-index:60 below) so a card can never be dragged over
   the frozen panes. */
.board-head-row,.tick-row,.level-row-label{visibility:hidden}
.frozen-header{position:absolute;left:0;right:0;top:0;z-index:70;background:var(--surface);border-bottom:1px solid var(--border);overflow:hidden;pointer-events:none}
.frozen-col{position:absolute;left:0;top:0;bottom:0;width:96px;z-index:71;background:var(--surface-2);border-right:1px solid var(--border);overflow:hidden;pointer-events:none}
.frozen-band-head{position:absolute;top:0;display:flex;align-items:center;justify-content:center;text-align:center;border-left:1px solid var(--border);box-sizing:border-box}
.frozen-band-head:first-child{border-left:none}
.frozen-band-head b{font-size:12.5px}
.frozen-tick-mark{position:absolute;bottom:4px;transform:translateX(-50%);font-family:var(--font-display);font-size:10.5px;font-weight:800;white-space:nowrap}
.frozen-level-label{position:absolute;left:0;right:0;border-top:1px solid var(--border);box-sizing:border-box}
/* The sticky text itself — positioned (via JS, see syncFrozenOverlays()) at
   a `top` relative to .frozen-level-label's own top, clamped to stay within
   the visible viewport instead of always sitting at the lane's literal
   (possibly off-screen) center. Fixed height so the clamp math has a stable
   box size to work with regardless of zoom. */
.frozen-level-label-text{position:absolute;left:0;right:0;height:40px;display:flex;align-items:center;justify-content:center;font-family:var(--font-display);font-weight:800;font-size:26px;letter-spacing:-.02em;color:var(--ink)}
/* The lane itself — a level's whole horizontal strip. flex:1 1 auto
   soaks up everything but the 96px label (its own in-flow content is
   empty, since the background + cards are both position:absolute). Height
   is set per-row, inline, by board.js's layoutLevelCards() — a crowded
   level gets a taller lane than a quiet one, exactly as tall as its own
   collision-free card packing needs (2026-10-30, replacing one blanket
   fixed height that real data very quickly outgrew). The value here is
   only a fallback for the instant before JS sets that inline height. */
.score-lane{position:relative;flex:1 1 auto;height:340px;border-top:1px solid var(--border);overflow:hidden}
/* .lane-bg is the 5 colored band zones (painted once per band width,
   see LANE_BG_HTML in board.js) plus their soft dotted "in-between
   scoring" reference lines — Mert's ask for a visual way to place a
   card between two scores without an actual grid cell there.
   pointer-events:none so it never steals a click/drag meant for the
   lane or the canvas-pan behind it. */
.lane-bg{position:absolute;inset:0;display:flex;pointer-events:none}
.band-zone{position:relative;height:100%;border-left:1px solid var(--border)}
.band-zone:first-child{border-left:none}
.zone-divider{position:absolute;top:0;bottom:0;border-left:1px dashed var(--border-2);opacity:.6}

/* Floating zoom readout + "fit all" reset button, bottom-right of the
   viewport. */
.zoom-hud{position:absolute;bottom:12px;right:12px;display:flex;align-items:center;gap:8px;background:var(--surface);border:1px solid var(--border);border-radius:999px;padding:6px 10px;box-shadow:var(--shadow);font-size:11.5px;color:var(--muted);z-index:20}
.zoom-hud button{border:none;background:var(--surface-2);border-radius:999px;padding:4px 10px;font-size:11px;font-weight:700;cursor:pointer;color:var(--ink)}
.zoom-hud button:hover{background:var(--border)}

/* ---------- Full-detail person card (board) ---------- */
/* Absolutely positioned within its .score-lane at the left/top % board.js
   computes (left = scoreToFraction(mgr score), top = boardY) — translate
   centers the card ON that exact point rather than anchoring a corner to
   it, so the point itself is what's meaningful, Figma-sticker style. */
/* A per-card "resist shrinking" counter-scale briefly lived here
   (2026-10-30) but was reverted the same day: it inflated cards beyond the
   exact size layoutLevelCards()'s collision packing (board.js) assumed,
   so zoomed-out cards visually piled back on top of each other — the very
   bug this packing system exists to prevent. Cards scale 1:1 with the
   canvas, same as always. */
.person-card{position:absolute;width:210px;transform:translate(-50%,-50%);background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--card-accent,var(--electric));border-radius:12px;padding:12px;cursor:grab;box-shadow:var(--shadow);font-size:12px;display:flex;flex-direction:column;gap:10px;user-select:none;z-index:1}
.person-card.dragging{cursor:grabbing;z-index:60;box-shadow:0 2px 10px rgba(10,27,61,.1),0 18px 34px -14px rgba(10,27,61,.35)}
.person-card.view-only{cursor:pointer}
.pc-top{display:flex;gap:9px;align-items:flex-start}
.pc-photo{width:42px;height:42px;border-radius:9px;background:var(--navy-3);color:#fff;font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center;flex:0 0 auto;background-size:cover;background-position:center}
.pc-info{flex:1;min-width:0}
.pc-name{font-weight:700;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pc-meta{font-size:10.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pc-meta.pc-dept{text-transform:uppercase;letter-spacing:.03em;color:var(--faint);font-size:9.5px}
/* A horizontal row under the name, not a side column (2026-10-30: with 4
   possible badges — calibrated, flag, promo, level — stacking them beside
   a 1-2 line name block made the column run taller than the name itself
   and look lopsided). Wraps if a narrow card ever needs it. */
.pc-badges{display:flex;flex-direction:row;flex-wrap:wrap;gap:4px;align-items:center;margin:3px 0}
.pc-badge{display:flex;align-items:center;justify-content:center;min-width:18px;height:18px;border-radius:50%;font-size:9.5px;font-weight:800;color:#fff;padding:0 2px}
.pc-badge.pc-level{background:var(--navy);font-size:7.5px}
.pc-badge.pc-promo-yes{background:var(--good)}
.pc-badge.pc-promo-considering{background:var(--warn)}
.pc-badge.pc-promo-no{background:var(--crit)}
.pc-badge.pc-promo-none{background:var(--border);color:var(--muted)}
/* Calibration-progress + outlier-flag badges on the card itself — visible
   while scanning the whole board, not just inside a person's panel
   (Mert, 2026-10-30: "so from view we can also see how many rough
   calibrations changed things"; and the outlier-flags backlog item: "so
   managers spot them before the meeting instead of during it"). Only shown
   when true/present — an un-calibrated, unflagged card shows neither, so
   the normal state of the board during an active calibration (mostly
   pending, mostly unflagged) doesn't look cluttered. */
.pc-badge.pc-calibrated{background:var(--good);font-size:10px}
.pc-badge.pc-flag{background:var(--warn);font-size:10px}
/* Explains, inside the panel, exactly why a card earned its ⚠ badge on the
   board (divergent self/peer/mgr scores, or a big swing since last cycle)
   — the badge alone only says "something's unusual", this says what. */
.outlier-banner{display:flex;flex-direction:column;gap:5px;margin-bottom:16px;padding:9px 11px;border-radius:12px;background:var(--band-2-bg);border:1px solid var(--warn);color:var(--warn);font-size:12px;line-height:1.4}
.pc-scores-label{font-size:9.5px;text-transform:uppercase;letter-spacing:.04em;color:var(--faint);margin-bottom:4px}
.pc-score-row{display:flex;gap:6px}
.pc-score-box{flex:1;background:var(--surface-2);border:1px solid var(--border);border-radius:8px;padding:5px 4px;text-align:center}
.pc-score-box b{display:block;font-family:var(--font-display);font-size:14px}
.pc-score-box span{font-size:8.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.03em}
.pc-overall{display:flex;align-items:center;gap:8px;background:var(--overall-bg,var(--surface-2));border:1px solid var(--border);border-radius:8px;padding:6px 8px}
.pc-overall b{font-family:var(--font-display);font-size:16px;color:var(--overall-color,var(--ink))}
.pc-overall span{font-size:9.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.03em}
.pc-comp-row{display:flex;flex-wrap:wrap;gap:5px}
.pc-comp-chip{background:var(--surface-2);border:1px solid var(--border);border-radius:6px;padding:3px 6px;font-size:9.5px;color:var(--muted);white-space:nowrap}
.pc-comp-chip b{color:var(--ink);font-weight:700}

/* ---------- Score distribution (below the board) ---------- */
/* Headcount-per-band bar chart, Mert's ask (2026-10-30) alongside the
   free-form board — a quick "shape of the room" read that doesn't need
   zooming/panning the board itself to get a feel for. Sits in the
   normal page flow below .board-scroll, so it's always full page width
   regardless of the board's own zoom/pan state. */
.distribution-section{background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:20px 22px;margin:0 0 40px}
.distribution-head{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:16px}
.distribution-head h2{font-size:15px;margin:0}
.distribution-total{font-size:12px;color:var(--muted)}
.dist-bars{display:flex;align-items:flex-end;gap:18px;height:160px}
.dist-bar-col{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;height:100%;gap:4px}
.dist-bar-track{flex:1;width:100%;max-width:64px;display:flex;align-items:flex-end;background:var(--surface-2);border:1px solid var(--border);border-radius:8px 8px 3px 3px;overflow:hidden}
.dist-bar-fill{width:100%;min-height:2px;border-radius:6px 6px 0 0;transition:height .25s ease}
.dist-bar-count{font-family:var(--font-display);font-weight:800;font-size:15px;color:var(--ink)}
.dist-bar-pct{font-size:10.5px;color:var(--muted);margin-top:-4px}
.dist-bar-label{font-size:10.5px;font-weight:700;text-align:center;line-height:1.3;max-width:120px}

/* ---------- Person panel ---------- */
/* Mert, 2026-10-30: the panel used to sit behind a full-page dimming
   overlay that captured every click — "there is a pop up... which blocks
   movement on the page, as it is layered. instead, can the block be
   opened up on the side?" #panelOverlay is kept (board.js still holds a
   reference and a close-on-click listener on it) but neutralized — it no
   longer paints or intercepts anything, so the board stays fully
   interactive (pan, zoom, open a different person's card) while a panel
   is open. Closing now happens only via the explicit X button. */
.panel-overlay{display:none}
/* Widened 400px -> 560px (2026-10-30, Mert: the panel "feels shrink") —
   400px forced a full name + title + department onto cramped wrapped
   lines and squeezed the promotion badge against them. 560px fits a long
   name on one line and gives the 4 score tiles and the trajectory chart
   real breathing room; still caps at 95vw for small screens.
   z-index raised above the frozen header/column (70/71, see their rules
   below) so the panel always paints on top of them, rather than a wide
   viewport letting the frozen header bleed over the panel's top edge. */
.panel{position:fixed;top:0;right:0;height:100%;width:min(560px,95vw);background:var(--surface);z-index:80;box-shadow:var(--shadow);overflow-y:auto;transform:translateX(100%);transition:transform .2s ease}
.panel.open{transform:translateX(0)}
.panel-inner{padding:26px}
.panel-close{float:right;border:none;background:var(--surface-2);border-radius:999px;width:28px;height:28px;cursor:pointer;color:var(--ink)}

.panel-head{display:flex;align-items:center;gap:12px;margin:10px 0 18px;flex-wrap:wrap}
.panel-av{width:46px;height:46px;border-radius:50%;background:var(--navy-3);color:#fff;font-weight:700;display:flex;align-items:center;justify-content:center;font-size:16px;background-size:cover;background-position:center;flex:0 0 auto}
/* Admin → Profile's larger avatar, next to the upload/remove controls
   (2026-10-30: one-time photo upload per person, see person.js's
   resizePhotoToDataUrl). Same cover/center pattern as .panel-av, just
   bigger since this is the one place a person's photo is actually set. */
.profile-photo{width:64px;height:64px;border-radius:50%;background:var(--navy-3);color:#fff;font-weight:700;display:flex;align-items:center;justify-content:center;font-size:20px;background-size:cover;background-position:center;flex:0 0 auto}
.panel-head h3{margin:0;font-size:17px;line-height:1.3}
.panel-head small{color:var(--muted);font-size:12px}
.score-row{display:flex;gap:10px;margin-bottom:16px}
.score-tile{flex:1;background:var(--surface-2);border:1px solid var(--border);border-radius:10px;padding:10px;text-align:center}
.score-tile b{display:block;font-family:var(--font-display);font-size:20px}
.score-tile span{font-size:10.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.04em}
.final-rating-row{display:flex;align-items:center;gap:10px;margin-bottom:16px;padding:9px 10px;border:1px solid var(--border);border-radius:12px;background:var(--surface-2)}
.final-rating-row select{margin-left:auto;font-size:13px;padding:5px 8px;border-radius:8px;border:1px solid var(--border)}
/* Marks a field that's read-only because it comes straight from Leapsome
   — self/peer/manager/overall scores, overall performance score and
   promotion readiness can never be edited in Vantage (2026-10-30, Mert:
   Leapsome is the only source of truth). Sits where an editable
   .final-rating-row's <select> would otherwise be. */
.locked-field{margin-left:auto;font-size:11px;font-weight:700;color:var(--faint);white-space:nowrap}
.comp-toggles{display:flex;flex-direction:column;gap:6px;margin-bottom:10px}
.comp-toggle{display:flex;align-items:center;gap:6px;font-size:11.5px;color:var(--muted);margin-bottom:0;cursor:pointer}

/* Calibration notes (Mert, 2026-10-30: "a free-text space on each person's
   card to capture the rationale behind a rating change in the moment,
   visible to the other calibrators in that session"). Sits just below the
   final-rating row, styled as its own card like .final-rating-row/.comp-
   toggles rather than a bare list, since it's the one part of the panel
   calibrators actively write into mid-meeting. */
.cal-notes-section{margin-bottom:16px;padding:10px;border:1px solid var(--border);border-radius:12px;background:var(--surface-2)}
.cal-notes-list{display:flex;flex-direction:column;gap:8px;margin:8px 0}
.cal-note{padding:8px 9px;border:1px solid var(--border);border-radius:9px;background:var(--surface)}
.cal-note-meta{font-size:10.5px;color:var(--muted);margin-bottom:3px}
.cal-note-text{font-size:12.5px;line-height:1.4;color:var(--ink);white-space:pre-wrap;word-break:break-word}
.cal-notes-empty{font-size:12px;color:var(--faint);margin:8px 0}
.cal-notes-add{display:flex;flex-direction:column;gap:6px}
.cal-notes-add textarea{width:100%;box-sizing:border-box;font-family:inherit;font-size:12.5px;padding:7px 9px;border-radius:9px;border:1px solid var(--border);background:var(--surface);color:var(--ink);resize:vertical}
.cal-notes-add .btn{align-self:flex-end}
.comp-row{margin-bottom:10px}
.comp-row .lab{display:flex;justify-content:space-between;font-size:12px;margin-bottom:4px}
.comp-track{position:relative;height:7px;border-radius:4px;background:var(--surface-2);border:1px solid var(--border)}
.comp-fill{position:absolute;inset:0;border-radius:4px;background:var(--electric)}
.comp-avg-mark{position:absolute;top:-3px;bottom:-3px;width:2px;background:var(--ink);opacity:.5}
.comp-company-mark{position:absolute;top:-3px;bottom:-3px;width:2px;background:var(--band-5);opacity:.65}

/* Discussion queue panel (2026-11-02, from Mert's 1:1 with Chris) — three
   stacked sections of .queue-row buttons, styled like a plainer version of
   .cal-note above since these are click targets, not read-only cards. */
.queue-section{margin-bottom:18px}
.queue-section-head{display:flex;justify-content:space-between;align-items:baseline;font-size:13.5px;margin-bottom:2px}
.queue-row{display:flex;align-items:center;gap:8px;width:100%;text-align:left;padding:8px 9px;margin-bottom:6px;border:1px solid var(--border);border-radius:9px;background:var(--surface);cursor:pointer;font:inherit;color:inherit}
.queue-row:hover{border-color:var(--border-2);background:var(--surface-2)}
.queue-row-name{font-weight:700;font-size:12.5px}
.queue-row-meta{color:var(--muted);font-size:11.5px;flex:1}
.queue-empty{font-size:12px;color:var(--faint);margin:4px 0}

/* Hover/focus tooltip for the (i) info icons next to the peer-comparison
   checkboxes (Mert, 2026-10-30: "add an (i) and when people see when
   hovering on it what we mean by peer"). Pure CSS — no JS needed since
   it's just a hover/focus reveal. */
.info-icon{
  position:relative;display:inline-flex;align-items:center;justify-content:center;
  width:14px;height:14px;border-radius:50%;background:var(--border-2);color:var(--muted);
  font-size:10px;font-style:italic;font-family:Georgia,serif;cursor:help;flex:none;line-height:1;
}
.info-icon .info-tooltip{
  display:none;position:absolute;bottom:calc(100% + 7px);left:50%;transform:translateX(-50%);
  width:230px;padding:8px 10px;border-radius:8px;background:var(--navy);color:#fff;
  font-size:11px;line-height:1.4;font-weight:400;font-style:normal;font-family:var(--font-body);
  box-shadow:var(--shadow);z-index:95;text-align:left;cursor:auto;
  /* Reset regardless of ancestor — a parent that sets white-space:nowrap
     (e.g. analytics.js's .breakdown-count, 2026-11-02) would otherwise be
     inherited here too, stopping this text from wrapping inside its own
     230px box and spilling it out unstyled instead of clipping cleanly. */
  white-space:normal;
}
.info-icon .info-tooltip::after{
  content:'';position:absolute;top:100%;left:50%;transform:translateX(-50%);
  border:5px solid transparent;border-top-color:var(--navy);
}
.info-icon:hover .info-tooltip,.info-icon:focus .info-tooltip,.info-icon:focus-visible .info-tooltip{display:block}

/* ---------- Trajectory + position (person panel) ---------- */
/* Mert, 2026-10-30: "I want historical data on someone... how this person
   has been developing over time... what is the trajectory... and then
   how about their position within their department, within their team,
   etc.?" trajectoryChartSvg()/positionBarHtml() in board.js build the
   actual markup; this just styles it. Pure hand-rolled inline SVG — no
   charting library — stays dependency-free on Firebase's static hosting. */
.trajectory-section{margin-top:20px;padding-top:18px;border-top:1px solid var(--border)}
.trajectory-head{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:8px;font-size:13.5px}
.traj-legend{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
.traj-legend-item{display:flex;align-items:center;gap:5px;border:1px solid var(--border);background:var(--surface-2);border-radius:999px;padding:4px 10px 4px 8px;font-size:10.5px;font-weight:700;color:var(--ink);cursor:pointer}
.traj-legend-item.off{opacity:.4}
.traj-legend-item .traj-dot{width:8px;height:8px;border-radius:50%;background:var(--traj-color)}

.position-bar-wrap{margin-top:6px}
.position-bar{position:relative;height:10px;border-radius:999px;background:linear-gradient(90deg,var(--band-1-bg),var(--band-2-bg),var(--band-3-bg),var(--band-4-bg),var(--band-5-bg));margin:14px 2px 10px}
.position-mk{position:absolute;top:-3px;width:2px;height:16px;background:var(--muted);transform:translateX(-50%)}
.position-mk.pos-mk-team{background:var(--electric)}
.position-mk.pos-mk-dept{background:var(--band-2)}
.position-mk.pos-mk-company{background:var(--muted)}
.position-mk-own{position:absolute;top:-6px;width:12px;height:12px;border-radius:50%;background:var(--navy);border:2px solid var(--surface);transform:translateX(-50%);box-shadow:var(--shadow)}
.position-legend{display:flex;flex-wrap:wrap;gap:10px 14px;font-size:10.5px;color:var(--muted)}
.pos-dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:4px;vertical-align:middle}
.pos-dot-own{background:var(--navy)}
.pos-dot.pos-mk-team{background:var(--electric)}
.pos-dot.pos-mk-dept{background:var(--band-2)}
.pos-dot.pos-mk-company{background:var(--muted)}

/* ---------- Admin ---------- */
.admin-section{margin:34px 0}
.admin-section h2{font-size:17px;margin:0 0 6px}
.admin-section p.note{color:var(--muted);font-size:13px;line-height:1.6;max-width:72ch;margin:0 0 16px}
.invite-form{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:18px}
.invite-form input[type=email]{flex:1;min-width:220px;padding:9px 12px;border:1px solid var(--border);border-radius:10px;font-size:13px}
.chip-group{display:flex;gap:6px;flex-wrap:wrap}
.chip-check{display:inline-flex;align-items:center;gap:5px;font-size:12px;border:1px solid var(--border);border-radius:999px;padding:5px 10px;cursor:pointer;user-select:none}
.chip-check input{accent-color:var(--electric)}
table.admin-table{width:100%;border-collapse:collapse;background:var(--surface);border:1px solid var(--border);border-radius:12px;overflow:hidden;font-size:13px}
table.admin-table th{text-align:left;font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);padding:10px 12px;border-bottom:1px solid var(--border);background:var(--surface-2)}
table.admin-table td{padding:10px 12px;border-bottom:1px solid var(--border);vertical-align:top}
table.admin-table tr:last-child td{border-bottom:none}
.link-action{color:var(--electric);cursor:pointer;font-weight:600;font-size:12.5px}
.link-action.danger{color:var(--crit)}

/* ---------- Analytics breakdown tables (analytics.js, 2026-11-02) ----------
   A compact grid rather than repeating the board's full bar chart per
   group — department/manager/level breakdowns can run to dozens of rows,
   where a tiny stacked bar + avg score scans far faster than a chart
   each. */
/* No overflow:hidden on the table itself — the header row's (i) tooltips
   pop up above their icons and would get clipped by it. Each end row
   rounds its own corners instead, to still match the table's own
   border-radius. */
.breakdown-table{background:var(--surface);border:1px solid var(--border);border-radius:12px}
.breakdown-row{display:grid;grid-template-columns:minmax(140px,1.4fr) 76px 92px minmax(120px,1fr);gap:14px;align-items:center;padding:9px 14px;border-bottom:1px solid var(--border);font-size:12.5px}
.breakdown-row:last-child{border-bottom:none;border-radius:0 0 12px 12px}
.breakdown-row.breakdown-head{background:var(--surface-2);font-size:11px;text-transform:uppercase;letter-spacing:.03em;color:var(--muted);font-weight:700;border-radius:12px 12px 0 0}
.breakdown-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600}
.breakdown-row.breakdown-head .breakdown-name{font-weight:700}
.breakdown-count{color:var(--muted);white-space:nowrap;display:flex;align-items:center;gap:4px}
.breakdown-avg{font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-weight:700;display:flex;align-items:center;gap:4px}
.breakdown-row.breakdown-head > div{display:flex;align-items:center;gap:4px}
.breakdown-row.breakdown-head .info-icon{font-family:Georgia,serif;text-transform:none;font-weight:400}
.mini-dist{display:flex;width:100%;height:14px;border-radius:5px;overflow:hidden;background:var(--surface-2);border:1px solid var(--border)}
.mini-dist-seg{height:100%}
.mini-dist-empty{font-size:11px;color:var(--muted);display:flex;align-items:center}
@media (max-width:640px){
  .breakdown-row{grid-template-columns:minmax(100px,1fr) 60px 70px minmax(80px,0.8fr);gap:8px;font-size:11.5px}
}

/* ---------- Competency radar chart (analytics.js, 2026-11-02) ----------
   Hand-rolled SVG, no charting library — org-wide baseline (muted/faint)
   vs. one selected department/manager/level (electric blue), on 4
   universal competencies + a pooled technical axis. */
.radar-controls{margin:2px 0 16px;font-size:13px;color:var(--muted);display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.radar-controls label{display:flex;align-items:center;gap:8px}
.radar-wrap{display:flex;gap:28px;align-items:center;flex-wrap:wrap}
.radar-svg{width:min(360px,100%);height:auto;flex:0 0 auto;overflow:visible}
.radar-grid-ring{fill:none;stroke:var(--border);stroke-width:1}
.radar-spoke{stroke:var(--border);stroke-width:1}
.radar-axis-label{font-size:10px;fill:var(--muted);font-weight:700;text-transform:uppercase;letter-spacing:.02em}
.radar-poly{fill-opacity:.16;stroke-width:2}
.radar-poly-org{fill:var(--faint);stroke:var(--faint)}
.radar-poly-sel{fill:var(--electric);stroke:var(--electric)}
.radar-dot{stroke:var(--surface);stroke-width:1.5}
.radar-dot-org{fill:var(--faint)}
.radar-dot-sel{fill:var(--electric)}
.radar-legend{flex:1 1 260px;min-width:240px;background:var(--surface);border:1px solid var(--border);border-radius:12px;overflow:hidden}
.radar-legend-row{display:grid;grid-template-columns:1fr 86px 86px;gap:8px;padding:8px 12px;font-size:12px;border-bottom:1px solid var(--border);align-items:center}
.radar-legend-row:last-child{border-bottom:none}
.radar-legend-head{background:var(--surface-2);font-size:10.5px;text-transform:uppercase;letter-spacing:.03em;color:var(--muted);font-weight:700;padding-top:9px;padding-bottom:9px}
.radar-legend-name{font-weight:600}
.radar-legend-head .radar-legend-name{font-weight:700}
.radar-legend-val{text-align:right;font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.radar-legend-org{color:var(--faint)}
.radar-legend-sel{color:var(--electric)}
.radar-legend-head .radar-legend-val{font-weight:700;font-family:var(--font-body);text-transform:none;letter-spacing:0;font-size:10.5px;white-space:normal;line-height:1.3}
@media (max-width:640px){
  .radar-wrap{gap:16px}
  .radar-legend{min-width:0;width:100%}
}

/* ---------- Competency heatmap (analytics.js, 2026-11-02) ----------
   Department/Manager/Level toggle (reuses .track-tabs/.track-tab) +
   sortable, color-graded cells. Cell background is a live red→blue
   gradient resolved from the board's own --band-N variables at render
   time (see resolvedBandRgb() in analytics.js), so it follows light/dark
   mode automatically; text is always white with a dark shadow so it stays
   legible across every hue in that gradient rather than needing per-band
   text-color rules. */
.heatmap-table{background:var(--surface);border:1px solid var(--border);border-radius:12px;overflow:hidden}
.heatmap-row{display:grid;grid-template-columns:minmax(120px,1.3fr) 60px repeat(5,minmax(64px,1fr));align-items:stretch;border-bottom:1px solid var(--border)}
.heatmap-row:last-child{border-bottom:none}
.heatmap-row.heatmap-head{background:var(--surface-2);font-size:10.5px;text-transform:uppercase;letter-spacing:.03em;color:var(--muted);font-weight:700}
.heatmap-name{padding:9px 14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600;display:flex;align-items:center}
.heatmap-row.heatmap-head .heatmap-name{font-weight:700}
.heatmap-total{padding:9px 6px;display:flex;align-items:center;justify-content:center;color:var(--muted);font-family:var(--font-mono);font-variant-numeric:tabular-nums;border-left:1px solid var(--border)}
.heatmap-col-head{padding:9px 6px;display:flex;align-items:center;justify-content:center;text-align:center;cursor:pointer;user-select:none;border-left:1px solid var(--border)}
.heatmap-col-head:hover{color:var(--ink)}
.heatmap-col-head.active{color:var(--electric);background:var(--surface)}
.heatmap-cell{display:flex;align-items:center;justify-content:center;font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-weight:800;font-size:12.5px;border-left:1px solid var(--border);color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.45);padding:9px 6px}
@media (max-width:640px){
  .heatmap-row{grid-template-columns:minmax(90px,1fr) 44px repeat(5,minmax(48px,1fr));font-size:11px}
  .heatmap-name{padding:7px 8px}
  .heatmap-cell,.heatmap-total,.heatmap-col-head{padding:7px 3px;font-size:11px}
}

/* ---------- Historical trend (analytics.js, 2026-11-02) ----------
   Two pieces sharing one section: a line chart of org-wide vs. a selected
   group's average manager score across every cycle Vantage has ratings
   for, and a row of per-cycle stacked bars (bottom band 1 → top band 5,
   via column-reverse) showing how the whole org's band distribution has
   shifted over that same span — the average alone can look flat while the
   shape underneath it is actually moving. */
.hist-controls{margin:2px 0 16px;font-size:13px;color:var(--muted);display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.hist-controls label{display:flex;align-items:center;gap:8px}
.hist-wrap{display:flex;gap:24px;align-items:center;flex-wrap:wrap;margin-bottom:8px}
.hist-line-svg{width:min(620px,100%);height:auto;flex:1 1 420px;overflow:visible}
.hist-legend{display:flex;flex-direction:column;gap:8px;font-size:12.5px;color:var(--ink)}
.hist-legend-item{display:flex;align-items:center;gap:7px}
.hist-dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto}
.hist-dot-org{background:var(--faint)}
.hist-dot-sel{background:var(--electric)}
.hist-stack-row{display:flex;gap:18px;align-items:flex-end;padding:8px 2px 0;overflow-x:auto}
.hist-stack-col{display:flex;flex-direction:column;align-items:center;gap:6px;flex:0 0 auto;min-width:46px}
.hist-stack-total{font-size:11px;color:var(--muted);font-family:var(--font-mono);font-variant-numeric:tabular-nums}
.hist-stack-bar{width:34px;height:160px;border-radius:7px;overflow:hidden;display:flex;flex-direction:column-reverse;background:var(--surface-2);border:1px solid var(--border)}
.hist-stack-seg{width:100%}
.hist-stack-label{font-size:11px;color:var(--muted);text-align:center;white-space:nowrap}
@media (max-width:640px){
  .hist-wrap{gap:14px}
  .hist-stack-row{gap:10px}
  .hist-stack-bar{width:28px;height:130px}
}

/* ---------- My Team (my-team.js, 2026-11-02) ----------
   A roster of a manager's own direct reports, each a card with a compact
   score/band/promotion header and the full trajectory chart (reused as-is
   from trajectory.js — board.js's panel and person.js's profile use the
   exact same chart) underneath. */
.team-roster{display:flex;flex-direction:column;gap:14px}
.team-card{background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:16px 18px}
.team-card-head{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.team-card-id{flex:1;min-width:0}
.team-card-name{font-weight:700;font-size:14.5px}
.team-card-meta{font-size:12px;color:var(--muted);margin-top:1px}
.team-card-score{display:flex;align-items:center;gap:8px;flex:0 0 auto}
.team-score-value{font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-weight:800;font-size:16px}
.team-band-label{font-size:10.5px;font-weight:700;padding:3px 9px;border-radius:999px;white-space:nowrap}
.team-score-empty{font-size:12px;color:var(--muted);font-style:italic}
@media (max-width:640px){
  .team-card-head{gap:8px}
  .team-card{padding:13px 14px}
}

/* "View As" bar (my-team.js, 2026-11-02) — Super-Admin-only impersonation
   picker on the My Team page, so Mert (who has no direct reports himself)
   can see the page exactly as any other manager would. Deliberately styled
   as a flagged, distinct strip (navy-tinted, not just another form row) so
   it reads as an admin override rather than a normal page control — the
   same instinct as .badge.super below. */
.view-as-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;background:var(--navy-3,var(--surface-2));border:1px solid var(--border-2);border-radius:12px;padding:10px 14px;margin:0 0 18px}
.view-as-label{font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;color:var(--electric,var(--navy))}
.view-as-indicator{font-size:13px;color:var(--muted);margin:0 0 14px}
.view-as-indicator strong{color:var(--ink)}
@media (max-width:640px){
  .view-as-bar{padding:9px 12px}
}

.badge{display:inline-block;font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;padding:2px 8px;border-radius:999px}
.badge.pending{background:var(--band-2-bg);color:var(--band-2)}
.badge.active{background:var(--band-3-bg);color:var(--band-3)}
.badge.super{background:var(--navy);color:#fff}
.badge.promo-yes{background:var(--band-3-bg);color:var(--good)}
.badge.promo-considering{background:var(--band-2-bg);color:var(--warn)}
.badge.promo-no{background:var(--band-1-bg);color:var(--crit)}
/* Per-person "has this one been decided yet" indicator on the Manager's
   final rating row — distinct wording from the cycle-wide LOCKED/Not Locked
   badge (cycle-lock-badge) so the two are never confused: this is scoped to
   one person's rating and never blocks editing, it's purely a glance-able
   status (Mert, 2026-10-30). Reuses .badge.pending's existing amber styling
   for the "not decided yet" state. */
.badge.calibrated{background:var(--band-3-bg);color:var(--good)}
select.inline-select{font-size:12.5px;padding:4px 6px;border-radius:7px;border:1px solid var(--border);max-width:140px}
/* Text inputs (e.g. Title) shouldn't share <select>'s max-width cap — let
   them size to their column instead of clipping at 140px/110px. The column
   itself is still free to grow since table-scroll lets the whole table
   widen/scroll rather than forcing columns to compress. */
input.inline-select{min-width:140px;width:100%;max-width:none;box-sizing:border-box}
.table-scroll{overflow-x:auto}
.table-scroll table{width:max-content;min-width:100%}

.loading{color:var(--muted);font-size:13.5px;padding:30px;text-align:center}
.toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);background:var(--navy);color:#fff;padding:10px 18px;border-radius:10px;font-size:13px;box-shadow:var(--shadow);z-index:90;opacity:0;pointer-events:none;transition:opacity .2s ease}
.toast.show{opacity:1}
