/* FM Labs Sales Command Center — dark theme.
 *
 * ── Where these values come from ─────────────────────────────────────────────
 *
 * Palette: the "Financial Dashboard" dark set — dark ground, green for positive
 * movement. It suits a screen whose job is revenue, kilograms and pipeline, and
 * its accent is #22C55E, which is the FM Labs green the application already
 * used. The brand did not have to move to make the theme work.
 *
 * Type: IBM Plex Sans throughout, with tabular figures wherever a number is
 * shown. Plex reads as corporate without reading as cold, and tabular figures
 * mean a column of money lines up on the decimal without a second typeface.
 * Plex Mono is loaded for one thing only — the timezone clock badge — where a
 * monospaced face is the point rather than a side effect.
 *
 * ── Every text pair here was measured, not eyeballed ─────────────────────────
 *
 *   body #F8FAFC on ground #020617    19.28:1
 *   body #F8FAFC on card   #0E1223    17.77:1
 *   muted #94A3B8 on card  #0E1223     7.25:1
 *   green #22C55E on card  #0E1223     8.16:1
 *   every status chip                  6.7:1 and up
 *
 * Two borders, not one, and the difference matters. A card edge is decoration
 * and may be quiet (#334155). The edge of an input is a user interface
 * component, which WCAG 1.4.11 requires to reach 3:1 — so inputs get #64748B
 * (3.47:1). Using the quiet border on both would look tidier and would make
 * every form control on the site fail.
 */

/* ── Tokens ───────────────────────────────────────────────────────────────── */

:root {
  /* Surfaces, darkest first. Depth is carried by surface lightness rather than
   * by shadow: a drop shadow on a near-black ground is invisible, so raising an
   * element means lightening it. */
  --fm-ground: #020617;
  --fm-surface: #0e1223;
  --fm-surface-2: #1a1e2f;
  --fm-surface-3: #232840;
  --fm-navy: #0f172a;

  --fm-text: #f8fafc;
  --fm-text-muted: #94a3b8;
  --fm-text-dim: #64748b;

  /* Quiet: card edges, table rules. Loud: anything a person operates. */
  --fm-border: #334155;
  --fm-border-strong: #64748b;

  --fm-green: #22c55e;
  --fm-green-bright: #4ade80;
  --fm-green-dark: #16a34a;
  --fm-amber: #fbbf24;
  --fm-blue: #60a5fa;
  --fm-purple: #c084fc;
  --fm-orange: #fb923c;
  --fm-red: #f87171;

  --fm-sidebar-width: 232px;
  --fm-radius: 0.6rem;

  /* Density 8/10 — a dashboard, not a brochure. */
  --fm-space-1: 0.25rem;
  --fm-space-2: 0.5rem;
  --fm-space-3: 0.75rem;
  --fm-space-4: 1rem;
  --fm-space-5: 1.5rem;
  --fm-space-6: 2rem;

  /* One easing, three durations. Motion that means different things should
   * differ in what moves, not in how it accelerates. */
  --fm-ease: cubic-bezier(0.22, 1, 0.36, 1);
  --fm-fast: 140ms;
  --fm-base: 240ms;
  --fm-slow: 480ms;
}

/* Bootstrap 5.3 reads these, so components we never touch — dropdowns, alerts,
 * modals, pagination — come out in the same palette instead of needing to be
 * overridden one at a time. */
[data-bs-theme='dark'] {
  --bs-body-bg: var(--fm-ground);
  --bs-body-color: var(--fm-text);
  --bs-body-font-family: 'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --bs-body-font-size: 0.875rem;
  --bs-body-line-height: 1.55;
  --bs-emphasis-color: #ffffff;
  --bs-secondary-color: var(--fm-text-muted);
  --bs-tertiary-color: var(--fm-text-dim);
  --bs-border-color: var(--fm-border);
  --bs-border-color-translucent: rgba(255, 255, 255, 0.08);

  --bs-primary: var(--fm-green);
  --bs-primary-rgb: 34, 197, 94;
  --bs-link-color: var(--fm-green-bright);
  --bs-link-color-rgb: 74, 222, 128;
  --bs-link-hover-color: #86efac;

  --bs-success: var(--fm-green);
  --bs-danger: var(--fm-red);
  --bs-warning: var(--fm-amber);
  --bs-info: var(--fm-blue);

  --bs-card-bg: var(--fm-surface);
  --bs-card-border-color: var(--fm-border);
  --bs-secondary-bg: var(--fm-surface-2);
  --bs-tertiary-bg: var(--fm-surface-3);
}

/* ── Base ─────────────────────────────────────────────────────────────────── */

body {
  background: var(--fm-ground);
  color: var(--fm-text);
  font-family: 'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 0.875rem;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, h5, h6 {
  font-weight: 600;
  letter-spacing: -0.015em;
  color: #fff;
}

/* Figures use Plex SANS with tabular figures, not the Mono sibling.
 *
 * Mono was the first attempt and it was wrong for money. In a monospaced face
 * every glyph occupies one cell including the comma, so $14,230 sets as
 * "$14 , 230" with a visible gap either side of the separator — legible, but it
 * reads as a typo at 1.75rem.
 *
 * font-variant-numeric: tabular-nums gets the property that actually matters:
 * every digit the same width, so a column lines up and a figure that changes
 * does not shuffle the row. Plex Sans has proper tabular figures, and its comma
 * stays comma-width.
 *
 * Mono is kept for the timezone badge, where the text is a clock and the
 * monospaced look is the point. */
.fm-num,
.fm-kpi-value,
th.text-end,
td.text-end,
.fm-chart-value,
.fm-chart-axis {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

/* One focus style, everywhere, and never removed. 2px plus an offset clears
 * the AAA minimum for focus appearance; the green reaches 7.25:1 against every
 * surface it can land on. */
:focus-visible {
  outline: 2px solid var(--fm-green);
  outline-offset: 2px;
  border-radius: 3px;
}

::selection {
  background: rgba(34, 197, 94, 0.28);
  color: #fff;
}

/* Anything clickable says so. */
a, button, [role='button'], summary, .fm-nav-link, label[for] {
  cursor: pointer;
}

/* ── Sidebar ──────────────────────────────────────────────────────────────── */

.fm-sidebar {
  background: linear-gradient(180deg, var(--fm-navy) 0%, #0b1020 100%);
  color: var(--fm-text-muted);
  width: var(--fm-sidebar-width);
  border-right: 1px solid var(--fm-border);
}

@media (min-width: 992px) {
  .fm-sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    overflow-y: auto;
  }

  /* The offset belongs to pages that HAVE a sidebar, not to .fm-main. */
  .fm-main-with-sidebar {
    margin-left: var(--fm-sidebar-width);
  }
}

.fm-brand {
  color: #fff;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
}

.fm-brand small {
  color: var(--fm-green);
  font-weight: 500;
  font-size: 0.68rem;
  letter-spacing: 0.02em;
}

.fm-nav-section {
  color: rgba(255, 255, 255, 0.34);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.11em;
  text-transform: uppercase;
}

.fm-nav-link {
  color: #cbd5e1;
  border-radius: 0.4rem;
  padding: 0.44rem 0.6rem;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  text-decoration: none;
  font-size: 0.85rem;
  position: relative;
  transition: background-color var(--fm-fast) var(--fm-ease),
              color var(--fm-fast) var(--fm-ease);
}

.fm-nav-link:hover {
  background: rgba(255, 255, 255, 0.06);
  color: #fff;
}

.fm-nav-link.active {
  background: rgba(34, 197, 94, 0.14);
  color: #fff;
  font-weight: 600;
}

/* The current page is marked by a bar as well as by color, so the state does
 * not depend on distinguishing two greens. */
.fm-nav-link.active::before {
  content: '';
  position: absolute;
  left: -0.6rem;
  top: 0.35rem;
  bottom: 0.35rem;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--fm-green);
}

.fm-nav-link .fm-icon {
  flex-shrink: 0;
  opacity: 0.85;
}

.fm-nav-link.active .fm-icon {
  opacity: 1;
  color: var(--fm-green-bright);
}

.fm-nav-link .badge {
  margin-left: auto;
  font-size: 0.68rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.fm-goal {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: var(--fm-radius);
}

.fm-goal .progress {
  height: 6px;
  background: rgba(255, 255, 255, 0.1);
}

/* ── Sticky column ────────────────────────────────────────────────────────── */

/* Keeps a short summary column in view while a long one scrolls beside it.
   Only above lg, where the two columns actually sit side by side; stacked on
   phones, a sticky column would pin itself over the content below it. */
@media screen and (min-width: 992px) {
  .fm-sticky-col {
    position: sticky;
    top: 1rem;

    /* A flex item stretches to the row's height by default, so the column
       would never scroll past its own top and sticky would do nothing at all.
       Shrink it to its content. */
    align-self: flex-start;

    /* Taller than the viewport, sticky pins the top and puts the bottom
       permanently out of reach. Cap the height and let the column scroll
       itself so the last card stays reachable. */
    max-height: calc(100vh - 2rem);
    overflow-y: auto;

    /* The scrollbar is hidden (see below), so the column keeps the plain
       Bootstrap gutter rather than reserving a channel for it. */
    overscroll-behavior: contain;
  }
}

/* ── Social profile icons ─────────────────────────────────────────────────── */

.fm-social-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;

  /* 44px is the smallest comfortable touch target, and these sit next to each
     other — an icon-sized hit area would make them a lottery on a phone. */
  width: 2.5rem;
  height: 2.5rem;
  border: 1px solid var(--fm-border);
  border-radius: 0.5rem;
  color: var(--fm-text-muted);
  transition: color 120ms ease, border-color 120ms ease, background-color 120ms ease;
}

.fm-social-link .fm-icon {
  width: 1.25rem;
  height: 1.25rem;
}

.fm-social-link:hover,
.fm-social-link:focus-visible {
  color: var(--fm-text);
  border-color: var(--fm-border-strong);
  background: var(--fm-surface-3);
}

@media (prefers-reduced-motion: reduce) {
  .fm-social-link { transition: none; }
}

/* ── Prospect summary band ────────────────────────────────────────────────── */

.fm-stat-tile {
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--fm-border);
  border-radius: 0.5rem;
  background: var(--fm-surface-2);
  height: 100%;
}

.fm-stat-value {
  font-size: 1.45rem;
  line-height: 1.1;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.fm-stat-label {
  font-size: 0.72rem;
  color: var(--fm-text-muted);
  margin-top: 0.15rem;
}

/*
 * Six segment colors for a stacked proportion bar.
 *
 * Distinguishable by more than hue: every segment carries its label and count
 * in the legend beneath, and the same figures again in the <details> table, so
 * the colors order the bar rather than being the only thing carrying meaning.
 */
.fm-chart-seg { fill: var(--fm-accent); }

.fm-seg-1 { fill: #22c55e; }
.fm-seg-2 { fill: #38bdf8; }
.fm-seg-3 { fill: #a78bfa; }
.fm-seg-4 { fill: #fbbf24; }
.fm-seg-5 { fill: #fb7185; }
.fm-seg-6 { fill: #64748b; }

.fm-legend-dot {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 2px;
  display: inline-block;
  flex: 0 0 auto;
}

/* The legend dots are <span>s, so they take a background rather than a fill. */
.fm-legend-dot.fm-seg-1 { background: #22c55e; }
.fm-legend-dot.fm-seg-2 { background: #38bdf8; }
.fm-legend-dot.fm-seg-3 { background: #a78bfa; }
.fm-legend-dot.fm-seg-4 { background: #fbbf24; }
.fm-legend-dot.fm-seg-5 { background: #fb7185; }
.fm-legend-dot.fm-seg-6 { background: #64748b; }

/* A KPI tile that is a link. The arrow and the lift say so before the click. */
.fm-kpi-link { color: inherit; transition: border-color 120ms ease, transform 120ms ease; }

.fm-kpi-link:hover,
.fm-kpi-link:focus-visible {
  border-color: var(--fm-green);
  transform: translateY(-2px);
}

.fm-kpi-go { opacity: 0; transition: opacity 120ms ease; }
.fm-kpi-link:hover .fm-kpi-go,
.fm-kpi-link:focus-visible .fm-kpi-go { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .fm-kpi-link, .fm-kpi-go { transition: none; }
  .fm-kpi-link:hover, .fm-kpi-link:focus-visible { transform: none; }
}

/* ── A note under a call ──────────────────────────────────────────────────── */

/*
 * These rows carried Bootstrap's .table-light, which paints a LIGHT background —
 * on a dark theme that produced a bright bar with the table's pale text on it,
 * and the note itself was the least readable thing on the page. Which is the
 * wrong way round: the note is what somebody actually said on the call.
 *
 * A tint of the surface instead, with a left rule tying it to the row above, and
 * text at full strength rather than muted. Contrast measured against the tinted
 * ground, not the card behind it.
 */
.fm-table > tbody > tr.fm-note-row,
.fm-table > tbody > tr.fm-note-row:hover {
  --bs-table-bg: transparent;
  --bs-table-hover-bg: transparent;
  background: transparent;
}

.fm-note-row > td {
  background: rgba(148, 163, 184, 0.07);
  border-top: 0;
  border-left: 2px solid var(--fm-border-strong);
  color: var(--fm-text) !important;
  padding-top: 0.35rem;
  padding-bottom: 0.55rem;
}

/* Who owns an account, in the width of two letters. */
.fm-owner {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.35rem;
  height: 1.35rem;
  padding: 0 0.25rem;
  border-radius: 0.3rem;
  border: 1px solid var(--fm-border);
  background: var(--fm-surface-3);
  color: var(--fm-text-muted);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  vertical-align: 0.05em;
}

/* ── Prospect quick-view panel ────────────────────────────────────────────── */

.fm-prospect-panel {
  width: min(30rem, 92vw);
  background: var(--fm-surface-2);
  border-left: 1px solid var(--fm-border);
}

.fm-prospect-panel .offcanvas-header { border-bottom: 1px solid var(--fm-border); }

/* Section headings inside the panel: small, quiet, and doing the work of the
   card borders that a full page would have. */
.fm-panel-heading {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--fm-text-muted);
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0 0 0.5rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--fm-border);
}

.fm-poc {
  border: 1px solid var(--fm-border);
  border-radius: 0.45rem;
  padding: 0.5rem 0.6rem;
  margin-bottom: 0.5rem;
  background: var(--fm-surface-3);
}

/*
 * The call history as a timeline. The rule and the dots carry the sequence, so
 * the eye follows it downwards without every entry needing its own box.
 */
.fm-touchpoints {
  list-style: none;
  margin: 0;
  padding: 0 0 0 1rem;
  border-left: 1px solid var(--fm-border);
}

.fm-touchpoint { position: relative; padding: 0 0 0.85rem 0.6rem; }

.fm-touchpoint::before {
  content: '';
  position: absolute;
  left: -1.35rem;
  top: 0.35rem;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--fm-green);
  box-shadow: 0 0 0 3px var(--fm-surface-2);
}

/* What was said, at full strength — it is the reason the entry exists. */
.fm-touchpoint-note {
  background: rgba(148, 163, 184, 0.09);
  border-left: 2px solid var(--fm-border-strong);
  border-radius: 0 0.3rem 0.3rem 0;
  padding: 0.35rem 0.55rem;
  margin-top: 0.35rem;
  font-size: 0.78rem;
  white-space: pre-wrap;
}

/* ── Favorite star ───────────────────────────────────────────────────────── */

/*
 * A button that looks like an icon. It stays a <button> — it submits a form, so
 * it is keyboard-operable and announced as a control, and aria-pressed says
 * whether the star is on without relying on the color alone.
 */
.fm-star {
  background: none;
  border: 0;
  padding: 0 0.15rem 0 0;
  line-height: 1;
  color: var(--fm-border-strong);
  vertical-align: -0.12em;
  transition: color 120ms ease, transform 120ms ease;
}

.fm-star:hover, .fm-star:focus-visible { color: var(--fm-amber); transform: scale(1.15); }
.fm-star.is-on { color: var(--fm-amber); }
.fm-star.is-on svg { fill: var(--fm-amber); }

@media (prefers-reduced-motion: reduce) {
  .fm-star { transition: none; }
  .fm-star:hover, .fm-star:focus-visible { transform: none; }
}

/* ── Hidden scrollbars ────────────────────────────────────────────────────── */

/*
 * The panels that scroll inside the page do it without painting a bar.
 *
 * Wheel, trackpad, touch, keyboard and find-in-page all still scroll them —
 * only the bar is gone. What is lost is the standing hint that there is more
 * below, so this is deliberately limited to panels whose overflow is vertical
 * and obvious from the content running to the edge.
 *
 * .table-responsive is NOT in this list. Its bar is horizontal and it is the
 * only sign that a table continues past the right edge; hiding it would hide
 * data on a narrow screen rather than tidy a decoration.
 */
.fm-sticky-col,
.fm-sidebar,
.modal-body {
  scrollbar-width: none;      /* Firefox, and Chrome 121+ */
  -ms-overflow-style: none;   /* the old Edge and IE */
}

.fm-sticky-col::-webkit-scrollbar,
.fm-sidebar::-webkit-scrollbar,
.modal-body::-webkit-scrollbar {
  width: 0;
  height: 0;
}

/* ── Icons ────────────────────────────────────────────────────────────────── */

/* Line icons, sized in ems so they scale with their label rather than needing a
 * size for every context. Emoji were used here before: they render as a
 * different picture on every platform, cannot be recolored, and are read aloud
 * by a screen reader as their CLDR name — "bar chart increasing" in the middle
 * of a navigation list. */
.fm-icon {
  width: 1.15em;
  height: 1.15em;
  display: inline-block;
  vertical-align: -0.18em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.fm-icon-lg {
  width: 1.4em;
  height: 1.4em;
}

/* ── Cards and figures ────────────────────────────────────────────────────── */

.card {
  background: var(--fm-surface);
  border: 1px solid var(--fm-border);
  border-radius: var(--fm-radius);
}

.card-body {
  padding: var(--fm-space-4);
}

.fm-kpi {
  border: 1px solid var(--fm-border);
  border-radius: var(--fm-radius);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.035) 0%, rgba(255, 255, 255, 0) 60%),
    var(--fm-surface);
  position: relative;
  overflow: hidden;
  transition: border-color var(--fm-base) var(--fm-ease),
              transform var(--fm-base) var(--fm-ease);
}

.fm-kpi:hover {
  border-color: var(--fm-border-strong);
  transform: translateY(-1px);
}

.fm-kpi-value {
  font-size: 1.75rem;
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: #fff;
}

.fm-kpi-label {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--fm-text-muted);
}

.fm-kpi-sub {
  font-size: 0.76rem;
  color: var(--fm-text-muted);
}

/* A hairline of accent along the top of a KPI, so a screenful of cards has a
 * reading order without needing more words. */
.fm-kpi-accent::after {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: linear-gradient(90deg, var(--fm-green) 0%, rgba(34, 197, 94, 0) 100%);
}

/* ── Status and urgency ───────────────────────────────────────────────────── */

.fm-chip {
  display: inline-block;
  padding: 0.12rem 0.5rem;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 500;
  white-space: nowrap;
  border: 1px solid transparent;
}

.fm-status-won,
.fm-status-connected      { background: rgba(34, 197, 94, 0.14);  color: var(--fm-green-bright); border-color: rgba(34, 197, 94, 0.28); }
.fm-status-callback       { background: rgba(251, 191, 36, 0.13); color: var(--fm-amber);        border-color: rgba(251, 191, 36, 0.26); }
.fm-status-quote-sent,
.fm-status-pricing-sent   { background: rgba(192, 132, 252, 0.13);color: var(--fm-purple);       border-color: rgba(192, 132, 252, 0.26); }
.fm-status-catalog-sent,
.fm-status-emailed        { background: rgba(96, 165, 250, 0.13); color: var(--fm-blue);         border-color: rgba(96, 165, 250, 0.26); }
.fm-status-voicemail,
.fm-status-no-answer      { background: rgba(251, 146, 60, 0.13); color: var(--fm-orange);       border-color: rgba(251, 146, 60, 0.26); }
.fm-status-not-qualified  { background: rgba(248, 113, 113, 0.13);color: var(--fm-red);          border-color: rgba(248, 113, 113, 0.26); }
.fm-status-not-started,
.fm-status-unknown        { background: rgba(148, 163, 184, 0.11);color: var(--fm-text-muted);   border-color: rgba(148, 163, 184, 0.22); }

/* A lead nobody has touched yet. Deliberately NOT the muted gray of Not Started:
   the whole point of the stage is that these are worth picking up, and a chip
   that looks like the two hundred dormant accounts defeats it. */
.fm-status-new-lead       { background: rgba(45, 212, 191, 0.14); color: var(--fm-teal, #2dd4bf); border-color: rgba(45, 212, 191, 0.30); }

.fm-urgency-overdue  { background: rgba(248, 113, 113, 0.14); color: var(--fm-red);   border-color: rgba(248, 113, 113, 0.3); }
.fm-urgency-today    { background: rgba(251, 191, 36, 0.14);  color: var(--fm-amber); border-color: rgba(251, 191, 36, 0.3); }
.fm-urgency-upcoming { background: rgba(96, 165, 250, 0.13);  color: var(--fm-blue);  border-color: rgba(96, 165, 250, 0.26); }

.fm-row-overdue { box-shadow: inset 3px 0 0 var(--fm-red); }
.fm-row-today   { box-shadow: inset 3px 0 0 var(--fm-amber); }

/* ── Timezone badge ───────────────────────────────────────────────────────── */

.fm-tz {
  font-size: 0.68rem;
  font-weight: 500;
  padding: 0.06rem 0.38rem;
  border-radius: 0.3rem;
  white-space: nowrap;
  font-family: 'IBM Plex Mono', monospace;
}

.fm-tz-in  { background: rgba(34, 197, 94, 0.14);  color: var(--fm-green-bright); }
.fm-tz-out { background: rgba(148, 163, 184, 0.1); color: var(--fm-text-dim); }

/* Filled when they are reachable, hollow when they are not. Shape first,
 * color second — the state must survive being read by somebody who cannot
 * separate the two hues. */
.fm-tz-dot {
  display: inline-block;
  width: 0.44em;
  height: 0.44em;
  border-radius: 50%;
  margin-right: 0.3em;
  vertical-align: 0.04em;
  border: 1px solid currentColor;
}

.fm-tz-in .fm-tz-dot  { background: currentColor; }
.fm-tz-out .fm-tz-dot { background: transparent; }

/* ── Tables ───────────────────────────────────────────────────────────────── */

.fm-table {
  font-size: 0.83rem;
  --bs-table-bg: transparent;
  --bs-table-color: var(--fm-text);
  --bs-table-border-color: var(--fm-border);
  --bs-table-hover-bg: rgba(255, 255, 255, 0.035);
  --bs-table-hover-color: var(--fm-text);
}

.fm-table > thead th {
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--fm-text-muted);
  font-weight: 600;
  white-space: nowrap;
  border-bottom: 1px solid var(--fm-border);
  background: transparent;
}

.fm-table > tbody td {
  vertical-align: middle;
  border-color: rgba(255, 255, 255, 0.05);
}

.fm-sticky thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--fm-surface);
}

/* ── Forms ────────────────────────────────────────────────────────────────── */

.form-control,
.form-select {
  background-color: var(--fm-surface-2);
  border-color: var(--fm-border-strong);
  color: var(--fm-text);
  transition: border-color var(--fm-fast) var(--fm-ease),
              box-shadow var(--fm-fast) var(--fm-ease);
}

.form-control:focus,
.form-select:focus {
  background-color: var(--fm-surface-2);
  border-color: var(--fm-green);
  color: var(--fm-text);
  box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.2);
}

.form-control::placeholder { color: var(--fm-text-dim); }
.form-label { font-size: 0.8rem; font-weight: 500; color: var(--fm-text-muted); margin-bottom: 0.3rem; }
.form-text  { font-size: 0.76rem; color: var(--fm-text-dim); }

.form-check-input {
  background-color: var(--fm-surface-2);
  border-color: var(--fm-border-strong);
}

.form-check-input:checked {
  background-color: var(--fm-green);
  border-color: var(--fm-green);
}

.btn { transition: all var(--fm-fast) var(--fm-ease); font-weight: 500; }

.btn-success {
  --bs-btn-bg: var(--fm-green-dark);
  --bs-btn-border-color: var(--fm-green-dark);
  --bs-btn-hover-bg: var(--fm-green);
  --bs-btn-hover-border-color: var(--fm-green);
  --bs-btn-color: #04140a;
  --bs-btn-hover-color: #04140a;
  font-weight: 600;
}

.btn-outline-secondary {
  --bs-btn-color: var(--fm-text-muted);
  --bs-btn-border-color: var(--fm-border-strong);
  --bs-btn-hover-bg: var(--fm-surface-3);
  --bs-btn-hover-border-color: var(--fm-text-dim);
  --bs-btn-hover-color: var(--fm-text);
}

/* Every interactive control clears the 44x44 touch minimum on a phone, where
 * a mis-tap costs a page load. On a mouse-driven desktop the rows can be
 * tighter without the same cost. */
@media (max-width: 991.98px) {
  .btn, .form-control, .form-select { min-height: 44px; }
  .fm-nav-link { min-height: 44px; }
}

/* ── Typeahead ────────────────────────────────────────────────────────────── */

.fm-typeahead { position: relative; }

.fm-typeahead-results {
  position: absolute;
  z-index: 20;
  left: 0;
  right: 0;
  max-height: 17rem;
  overflow-y: auto;
  background: var(--fm-surface-3);
  border: 1px solid var(--fm-border-strong);
  border-radius: 0.4rem;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55);
}

.fm-typeahead-results button {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  background: none;
  color: var(--fm-text);
  padding: 0.45rem 0.6rem;
  font-size: 0.85rem;
}

.fm-typeahead-results button:hover,
.fm-typeahead-results button.is-active {
  background: rgba(34, 197, 94, 0.16);
  color: #fff;
}

/* ── Split-screen sign-in ─────────────────────────────────────────────────── */

.fm-split {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  grid-template-columns: 1fr;
}

@media (min-width: 992px) {
  .fm-split { grid-template-columns: 55fr 45fr; }
}

.fm-split-media {
  display: none;
  position: relative;
  background-color: var(--fm-navy);
}

/* A CSS background, not an <img>, and inside a min-width query — so a phone
 * never downloads 94 KB of decoration to sign somebody in. */
@media (min-width: 992px) {
  .fm-split-media {
    display: block;
    background-image: url('/assets/login-bg.jpg');
    background-image: -webkit-image-set(
      url('/assets/login-bg.webp') type('image/webp'),
      url('/assets/login-bg.jpg') type('image/jpeg'));
    background-image: image-set(
      url('/assets/login-bg.webp') type('image/webp'),
      url('/assets/login-bg.jpg') type('image/jpeg'));
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
  }
}

/* The photograph measures 194–238 of 255 in luminance. White text on it raw is
 * illegible, so the scrim is heavy at the bottom where the wordmark sits and
 * clears at the top where nothing is written. Darker here than it was on the
 * light theme, so the panel does not glare beside a near-black form. */
.fm-split-media::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgba(2, 6, 23, 0.42) 0%,
    rgba(2, 6, 23, 0.55) 42%,
    rgba(2, 6, 23, 0.92) 100%);
}

.fm-split-caption {
  position: relative;
  z-index: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 2.5rem;
  color: #fff;
}

.fm-split-caption h2 {
  font-size: 1.55rem;
  font-weight: 600;
  letter-spacing: -0.025em;
  max-width: 22ch;
}

.fm-split-caption p {
  color: rgba(255, 255, 255, 0.74);
  font-size: 0.88rem;
  max-width: 40ch;
}

.fm-split-form {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 2rem 1.25rem;
  background: var(--fm-surface);
}

@media (min-width: 992px) {
  .fm-split-form { padding: 3rem; }
}

.fm-split-inner { width: 100%; max-width: 24rem; margin: 0 auto; }

/* ── Wordmark ─────────────────────────────────────────────────────────────── */

/* The logo is one flat gray on transparency, legible on neither a pale
 * photograph nor a dark panel. Used as a MASK, the shape comes from the alpha
 * channel and the color from background-color, so one 2 KB file paints
 * whatever the surface needs. */
.fm-logo {
  display: block;
  width: 3rem;
  height: 3rem;
  background-color: var(--fm-text);
  -webkit-mask-image: url('/assets/fm-labs-logo.png');
  mask-image: url('/assets/fm-labs-logo.png');
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
}

.fm-logo-light { background-color: #fff; }
.fm-logo-lg { width: 4.25rem; height: 4.25rem; }

.fm-logo img { width: 100%; height: 100%; object-fit: contain; }

@supports (mask-image: url('')) or (-webkit-mask-image: url('')) {
  .fm-logo img { visibility: hidden; }
}

/* ── Charts ───────────────────────────────────────────────────────────────── */

.fm-chart { display: block; width: 100%; height: auto; overflow: visible; }

.fm-chart-grid line {
  stroke: rgba(255, 255, 255, 0.07);
  stroke-width: 1;
}

.fm-chart-axis {
  fill: var(--fm-text-dim);
  font-size: 10px;
}

.fm-chart-line {
  fill: none;
  stroke: var(--fm-green);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.fm-chart-area { fill: url(#fmAreaFill); }

/* Focusable points: the chart is operable by keyboard, not only by hover.
 * Hover-only data is data a keyboard user cannot read. */
.fm-chart-point {
  fill: var(--fm-ground);
  stroke: var(--fm-green);
  stroke-width: 2;
  r: 3.5;
  transition: r var(--fm-fast) var(--fm-ease);
  cursor: pointer;
}

.fm-chart-point:hover,
.fm-chart-point:focus-visible { r: 6; }

.fm-chart-bar {
  fill: var(--fm-green);
  transition: fill var(--fm-fast) var(--fm-ease);
}

.fm-chart-bar:hover { fill: var(--fm-green-bright); }

.fm-chart-track { fill: rgba(255, 255, 255, 0.06); }

.fm-chart-label {
  fill: var(--fm-text-muted);
  font-size: 11px;
}

.fm-chart-value {
  fill: #fff;
  font-size: 11px;
  font-weight: 500;
}

/* Funnel stages are separated by a gap and labeled in text, never by hue
 * alone — the guidance for funnels is explicit that a gradient is not a label. */
.fm-funnel-stage {
  transition: opacity var(--fm-fast) var(--fm-ease);
  cursor: pointer;
}

.fm-funnel-stage:hover { opacity: 0.82; }

.fm-ring-track { fill: none; stroke: rgba(255, 255, 255, 0.08); }
.fm-ring-value { fill: none; stroke: var(--fm-green); stroke-linecap: round; }

/* Tooltip, positioned by JS. */
.fm-chart-tip {
  position: absolute;
  pointer-events: none;
  z-index: 30;
  background: var(--fm-surface-3);
  border: 1px solid var(--fm-border-strong);
  border-radius: 0.35rem;
  padding: 0.35rem 0.55rem;
  font-size: 0.75rem;
  color: var(--fm-text);
  white-space: nowrap;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
  opacity: 0;
  transform: translate(-50%, -120%);
  transition: opacity var(--fm-fast) var(--fm-ease);
}

.fm-chart-tip.is-visible { opacity: 1; }

.fm-chart-tip b {
  font-variant-numeric: tabular-nums;
  color: var(--fm-green-bright);
}

/* Sparkline inside a KPI card. */
.fm-spark { display: block; width: 100%; height: 30px; overflow: visible; }
.fm-spark path { fill: none; stroke: var(--fm-green); stroke-width: 1.75; stroke-linecap: round; }
.fm-spark .fm-spark-fill { fill: rgba(34, 197, 94, 0.14); stroke: none; }

/* ── Motion ───────────────────────────────────────────────────────────────── */

/* Restraint is the rule the guidance is most emphatic about: animate one or two
 * things per view, and only where the movement carries meaning. So the motion
 * here is on the DATA — a line drawing itself left to right is the passage of
 * time, a bar growing from zero is a quantity accumulating, a counter running
 * up is a total being reached. The chrome does not bounce.
 *
 * Everything below is one-shot on load. Nothing loops: a looping animation in
 * the corner of a screen somebody stares at for seven hours is an irritation,
 * not a delight. */

@keyframes fm-rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

.fm-rise {
  animation: fm-rise var(--fm-base) var(--fm-ease) both;
}

/* Stagger, capped at eight. Past that the last item lags far enough behind the
 * first that the page feels slow rather than alive. */
.fm-stagger > *:nth-child(1) { animation-delay: 0ms; }
.fm-stagger > *:nth-child(2) { animation-delay: 45ms; }
.fm-stagger > *:nth-child(3) { animation-delay: 90ms; }
.fm-stagger > *:nth-child(4) { animation-delay: 135ms; }
.fm-stagger > *:nth-child(5) { animation-delay: 180ms; }
.fm-stagger > *:nth-child(6) { animation-delay: 225ms; }
.fm-stagger > *:nth-child(7) { animation-delay: 270ms; }
.fm-stagger > *:nth-child(8) { animation-delay: 315ms; }

/* The line draws itself. stroke-dashoffset animates on the compositor and does
 * not touch layout — unlike animating width, which reflows on every frame. */
.fm-chart-line.fm-draw {
  stroke-dasharray: var(--fm-draw-length, 1200);
  stroke-dashoffset: var(--fm-draw-length, 1200);
  animation: fm-draw var(--fm-slow) var(--fm-ease) forwards;
}

@keyframes fm-draw { to { stroke-dashoffset: 0; } }

.fm-chart-area.fm-fade { animation: fm-fade-in var(--fm-slow) var(--fm-ease) 200ms both; }

@keyframes fm-fade-in { from { opacity: 0; } to { opacity: 1; } }

/* Bars and funnel stages scale from their own baseline, so they grow out of the
 * axis rather than appearing to slide in from somewhere else. */
.fm-grow {
  transform-origin: var(--fm-grow-origin, bottom);
  animation: fm-grow var(--fm-slow) var(--fm-ease) both;
}

@keyframes fm-grow {
  from { transform: scaleY(0); opacity: 0; }
  to   { transform: scaleY(1); opacity: 1; }
}

.fm-grow-x {
  transform-origin: left center;
  animation: fm-grow-x var(--fm-slow) var(--fm-ease) both;
}

@keyframes fm-grow-x {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

.fm-ring-value.fm-draw-ring {
  stroke-dasharray: var(--fm-ring-circumference, 360);
  stroke-dashoffset: var(--fm-ring-circumference, 360);
  animation: fm-draw-ring var(--fm-slow) var(--fm-ease) 120ms forwards;
}

@keyframes fm-draw-ring { to { stroke-dashoffset: var(--fm-ring-offset, 0); } }

/* ── Reduced motion ───────────────────────────────────────────────────────── *
 *
 * Not "slower" and not "less" — off, with the final state rendered
 * immediately. Somebody who has asked their operating system for reduced motion
 * has often asked because motion makes them ill.
 *
 * The dasharray properties are reset explicitly: leaving them set with the
 * animation cancelled would leave every chart line invisible. */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .fm-chart-line.fm-draw,
  .fm-ring-value.fm-draw-ring {
    stroke-dasharray: none;
    stroke-dashoffset: 0;
  }

  .fm-grow, .fm-grow-x, .fm-rise { transform: none; opacity: 1; }
}

/* ── Print ────────────────────────────────────────────────────────────────── *
 *
 * A dark theme prints as a page of ink. Anything printed comes back to light. */

@media print {
  .fm-sidebar,
  .fm-noprint { display: none !important; }

  .fm-main { margin-left: 0; }

  body, .card, .fm-kpi {
    background: #fff !important;
    color: #000 !important;
    border-color: #ccc !important;
  }

  .fm-kpi-value, h1, h2, h3, .fm-chart-value { color: #000 !important; }
  .fm-table > thead th, .fm-kpi-label, .fm-kpi-sub { color: #333 !important; }
}

/* ── Company research ─────────────────────────────────────────────────────── */

.fm-research-score {
  font-size: 2.4rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.03em;
  color: #fff;
}

/* Key points, marked for and against.
 *
 * The mark is a shape as well as a color — a filled bar pointing up for a
 * reason to call, a hollow one for a reason to be careful. Color alone would
 * lose the distinction for a reader who cannot separate green from amber, and
 * this list is the part of the card a rep actually decides on. Each item also
 * carries a visually-hidden "In favor:" / "Against:" for a screen reader. */
.fm-research-points {
  list-style: none;
  padding: 0;
  margin: 0;
}

.fm-research-points li {
  position: relative;
  padding-left: 1.4rem;
  margin-bottom: 0.35rem;
  font-size: 0.84rem;
  line-height: 1.45;
}

.fm-point-mark {
  position: absolute;
  left: 0;
  top: 0.42em;
  width: 0.62rem;
  height: 0.62rem;
  border-radius: 2px;
}

.fm-point-for .fm-point-mark {
  background: var(--fm-green);
  /* A notch out of the top-right: reads as a rising mark next to a flat one. */
  clip-path: polygon(0 100%, 0 35%, 50% 0, 100% 35%, 100% 100%);
}

.fm-point-against .fm-point-mark {
  background: transparent;
  border: 1.5px solid var(--fm-amber);
}

/* ── Modal ────────────────────────────────────────────────────────────────── */

.modal-content {
  background: var(--fm-surface);
  border: 1px solid var(--fm-border-strong);
  border-radius: var(--fm-radius);
}

.modal-header,
.modal-footer {
  border-color: var(--fm-border);
}

.modal-footer {
  background: rgba(0, 0, 0, 0.22);
}

/* Bootstrap's default backdrop is only 50% black, which over a near-black page
 * leaves the modal and the page at almost the same lightness and the dialog
 * looking like part of the document. */
.modal-backdrop.show {
  opacity: 0.72;
}

/* ── Handbook ─────────────────────────────────────────────────────────────
   The documentation page. Long-form prose, so it gets a comfortable measure
   and a little more line height than a table would want. */
.fm-handbook-body {
  font-size: .88rem;
  line-height: 1.6;
  max-width: 62ch;
}

.fm-handbook-body li { max-width: 60ch; }

/* An aside: the thing somebody will otherwise learn by getting it wrong. */
.fm-handbook-note {
  border-left: 3px solid var(--fm-green-bright, #22c55e);
  background: rgba(34, 197, 94, .07);
  padding: .5rem .75rem;
  border-radius: .25rem;
}

/* Which readership a run of sections belongs to, in the contents list. */
.fm-handbook-band {
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  opacity: .6;
}

/* The contents follow you down the page, but only where there is room for a
   column beside the text. */
@media (min-width: 992px) {
  .fm-handbook-toc {
    position: sticky;
    top: 1rem;
  }
}
