/* By PM Dashboard — ONLY what is new.
 *
 * The table, the chips, the week strip and the metric tabs are `bpreport.css` classes, unchanged
 * and un-overridden: Miguel asked for the By Program template with a money column, and reusing
 * the stylesheet is what keeps it the same template instead of a lookalike that drifts a
 * half-pixel every time either page is touched. So this file styles three things and stops:
 * the money column group, the money strip above the table, and the `workbook` tag.
 *
 * Tokens only (`--navy`, `--border`, `--text-muted`, the `--bb-*` set). No imported palettes.
 */

/* --------------------------------------------------------------- the money column group */

/* The money is a DIFFERENT SOURCE from the four columns to its left (Marketing Dashboard vs By
 * Program), so it gets the same visual firewall the By Program page already uses to separate the
 * workbook's columns from BridgeBI's arithmetic. A reader must never have to remember which file
 * a column came from. */
/* A FROZEN header cell must be fully OPAQUE. These four were the money group's, and they were the
 * only translucent cells in the thead — so once the header started floating (above), the rows
 * scrolling underneath showed straight through "INVERSIÓN · MARKETING DASHBOARD" and its three
 * column labels. A 7% tint reads as a tint until something moves behind it.
 *
 * The tint is kept AS a tint and laid over an opaque surface token instead of being flattened to a
 * hex: a baked-in `#EEF0F4` would be a light-mode colour hardcoded into a themed page. A gradient
 * of one colour is the standard way to stack a translucent layer on an opaque base in one
 * `background`. The BODY money cells stay translucent on purpose — they are not sticky, and the
 * shared highlight layer paints row colours underneath them. */
.pm-hg-money {
  background:
    linear-gradient(rgba(13, 42, 94, 0.07), rgba(13, 42, 94, 0.07)),
    var(--off-white, #f5f3ee) !important;
  color: var(--navy) !important;
  border-left: 2px solid rgba(13, 42, 94, 0.22) !important;
}
.pm-t th.pm-th {
  text-align: right;
  color: var(--navy);
  background:
    linear-gradient(rgba(13, 42, 94, 0.035), rgba(13, 42, 94, 0.035)),
    var(--warm-white, #faf9f6);
}
.pm-t th.pm-th:first-of-type { border-left: 2px solid rgba(13, 42, 94, 0.22); }

.pm-t td.pm-m {
  text-align: right;
  font-family: 'JetBrains Mono', monospace;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  background: rgba(13, 42, 94, 0.028);
}
.pm-t td.pm-m-inv {
  border-left: 2px solid rgba(13, 42, 94, 0.22);
  /* The share bar is absolutely positioned against this cell. */
  position: relative;
  min-width: 132px;
}
.pm-t tr:hover td.pm-m { background: rgba(13, 42, 94, 0.055); }

.pm-inv-v { font-weight: 600; }

/* A rate under the 25-lead floor is SHOWN, never emphasised — the app's standing rule
 * (`docs/rules/lte.md`): 1-of-2 must not read as loudly as 355-of-715. Grey, and the cell's
 * title says which threshold it fell under. */
.pm-t td.pm-thin { color: var(--text-muted); font-weight: 400; }

/* Inversión vs PY, inline. A small tag rather than a column: eleven numeric columns is a wall,
 * and the direction is what he reads — the exact pair is in the cell's title.
 *
 * `.pm-dev-flat` is the MONEY's direction and it is deliberately colourless. Red for "spent more"
 * asserts a verdict the figure cannot support: more spend beside more leads is the plan working.
 * The arrow gives the direction; the leads column two cells to the left gives the meaning. */
.pm-dev {
  display: inline-block;
  margin-left: 7px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
}

/* The share bar. A FILL, not a verdict — a big number here is not "bad", it is where the money
 * went, which is the question the column exists to answer. Sits on the cell's bottom edge so it
 * never competes with the figure for space, and is `pointer-events:none` so it cannot swallow the
 * hover that reveals the title (the `.rc-thumb-open` lesson: an absolutely positioned child in a
 * cell intercepts the pointer unless it is told not to). */
.pm-bar {
  position: absolute;
  left: 10px;
  right: 10px;
  bottom: 2px;
  height: 2px;
  border-radius: 2px;
  background: rgba(13, 42, 94, 0.10);
  pointer-events: none;
}
.pm-bar::after {
  content: '';
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--pm-w, 0%);
  border-radius: 2px;
  background: var(--navy);
  opacity: 0.55;
}

/* The `workbook` tag on a channel row whose money the file STATES rather than this page
 * allocating it. Two words is the whole label; the reasoning is in its title. */
.pm-tag {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 5px;
  border: 1px solid rgba(13, 42, 94, 0.28);
  border-radius: 6px;
  font-size: 9.5px;
  font-style: normal;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--navy);
  background: rgba(13, 42, 94, 0.05);
  cursor: help;
  vertical-align: 1px;
}
.pm-t tr.pm-stated td.pm-m-inv { background: rgba(13, 42, 94, 0.055); }

/* --------------------------------------------------------------------------- the strip */

/* The Marketing Dashboard's own money summary, on top of the By Program table. This IS the merge,
 * made visible before the table is read — and it is why the page needs no explanatory sentence:
 * the total, the CPL and the four channels are on screen as numbers.
 *
 * Grid rather than flex-wrap so the tiles keep one rhythm at every width; `auto-fit` + `minmax`
 * means the count reflows instead of the tiles squashing. */
.pm-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
  gap: 1px;
  margin: 0 0 14px;
  border: 1px solid var(--border, #e3e0d8);
  border-radius: 10px;
  background: var(--border, #e3e0d8);
  overflow: hidden;
}
.pm-tile {
  padding: 11px 13px 10px;
  background: var(--bb-surface, #fff);
  border-top: 3px solid transparent;
}
.pm-tile[title] { cursor: help; }
/* Only the total wears the accent stripe. Striping every tile makes none of them the headline. */
.pm-tile-big { border-top-color: var(--navy); }

.pm-tk {
  font-size: 9.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-muted);
}
.pm-tv {
  margin-top: 4px;
  font-family: 'JetBrains Mono', monospace;
  font-variant-numeric: tabular-nums;
  font-size: 19px;
  font-weight: 650;
  line-height: 1.15;
  color: var(--navy);
}
.pm-tile-big .pm-tv { font-size: 23px; }
.pm-ts {
  margin-top: 3px;
  min-height: 15px;
  font-size: 11px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.pm-ts .pm-dev { margin-left: 4px; }

/* A narrower control row for the recinto picker: the campus list is long (TEC ships twelve) and
 * at the division tabs' size it wraps to three lines before the table starts. */
.bp-tabs-sm .bp-tab { font-size: 11px; padding: 4px 9px; }

/* ------------------------------------------------------- riding the template's navy bands
 *
 * `bpreport.css` paints the section total and every SCHOOL row as a solid navy band
 * (`.bp-total td, .bp-school td { background: var(--navy); color: #fff }`). That is the By
 * Program look and it is not ours to change — but the money columns were added after it, so
 * without these rules a school row ran navy for five columns and then went pale for three,
 * which reads as the table having broken rather than as a band.
 *
 * So the money cells JOIN the band, and everything inside them that carried a navy tint
 * (the share bar, the workbook tag, the thin-row grey) is re-tinted for light-on-navy. A
 * `--navy`-on-`--navy` bar is invisible; grey text on navy is unreadable. */
.pm-t tr.bp-total td.pm-m,
.pm-t tr.bp-school td.pm-m {
  background: var(--navy) !important;
  color: #fff;
  border-left-color: rgba(255, 255, 255, 0.28);
}
.pm-t tr.bp-total:hover td.pm-m,
.pm-t tr.bp-school:hover td.pm-m { background: var(--navy) !important; }
/* The band already carries the emphasis, so a thin row inside it dims by opacity rather than by
   going grey — grey on navy is unreadable, and dropping the flag entirely would let a 2-lead CPL
   read as loudly as a 700-lead one. */
.pm-t tr.bp-total td.pm-m.pm-thin,
.pm-t tr.bp-school td.pm-m.pm-thin { color: rgba(255, 255, 255, 0.62); }
.pm-t tr.bp-total .pm-bar,
.pm-t tr.bp-school .pm-bar { background: rgba(255, 255, 255, 0.20); }
.pm-t tr.bp-total .pm-bar::after,
.pm-t tr.bp-school .pm-bar::after { background: #fff; opacity: 0.8; }
.pm-t tr.bp-total .pm-tag,
.pm-t tr.bp-school .pm-tag {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.45);
  background: rgba(255, 255, 255, 0.12);
}
/* `.bp-pos` / `.bp-neg` are already forced to #fff inside a band by bpreport.css for the report
   columns; the inline deviation tag needs the same treatment or a red arrow disappears into navy. */
.pm-dev-flat { color: var(--text-muted); font-weight: 500; }
.pm-t tr.bp-total .pm-dev,
.pm-t tr.bp-school .pm-dev { color: rgba(255, 255, 255, 0.92) !important; }

/* ----------------------------------------------------------------- FREEZE PANES (Excel-style)
 *
 * His ask: *"can you make the header row follow me as i scroll so i dont forget what metric is it
 * called u know what i mean like how in excel i can freeze a row or column so i can scroll and it
 * follows me?"*
 *
 * ------------------------------------------------------------------ what the first cut got wrong
 *
 * It made this pane an inner vertical scroller (`max-height: calc(100vh - …)`) and stuck the header
 * to the PANE. His verdict: *"it doesnt even follow and there's a scroll bar that wasnt there
 * before and it doesnt work"*. Both halves were real and both were measured afterwards:
 *
 *     page scroll   header row     header on screen?
 *     pageY   0     hg @  626      YES
 *     pageY 400     hg @  226      YES
 *     pageY 600     hg @   26      NO   ← slid under the 56px app bar
 *     pageY 639     hg @  -13      NO   ← 29 data rows on screen, no header at all
 *
 * A header stuck to the PANE dies the moment the PANE's top scrolls off, and the page scrolls 639px
 * here. And the inner scrollbar was pure cost: the table is 1,190px wide in a 1,190px pane, so
 * there was nothing to scroll sideways for in the first place.
 *
 * ------------------------------------------------------------------------------- what it does now
 *
 * MODE PAGE (the default, and the only one on a normal screen): this pane does NOT scroll. The
 * PAGE is the single scroller — no second scrollbar anywhere — and the header sticks to the
 * VIEWPORT, parking just under the app's own 56px bar. The chrome above (strip, chips, tabs) scrolls
 * away and the column headers stay.
 *
 * `--pm-top` is MEASURED from the real app header in `pmFreezeHeader()`, not hardcoded, because that
 * bar is shared chrome and a magic 56 would drift the day it changes.
 *
 * MODE HSCROLL (`.pm-hscroll`, set from JS only when the table genuinely does not fit): the standing
 * rule is that wide content scrolls inside its own box and the page body never scrolls sideways, so
 * here the pane must own `overflow-x`. That re-creates the trap above, so in this mode the PANE
 * ITSELF is sticky under the app bar and capped to the viewport — it parks instead of scrolling away,
 * and the header pins to its top. Two scrollbars, but only where horizontal reach is unavoidable.
 *
 * THE TRAP THAT MAKES ALL OF THIS SUBTLE: any ancestor whose overflow is not `visible` becomes the
 * sticky containing block. `.bp-scroll` declares `overflow-x: auto`, which computes `overflow-y` to
 * `auto` too (the two axes cannot be auto and visible) — that is why `bpreport.css`'s
 * `.bp-hm th { position: sticky; top: 0 }` has never once worked on By Program either. `.bp-card`
 * carries `overflow: hidden` and would capture it just the same, which is why it is released below,
 * scoped to this page so By Program is untouched. */
.page-bypm .bp-card {
  /* An overflow ancestor would capture the sticky header and pin it to a box that never scrolls.
     Released here only; the card's own rounded corners are re-applied to the pane instead. */
  overflow: visible;
}
.pm-scroll {
  overflow: visible;
  max-height: none;
  border-bottom-left-radius: 12px;
  border-bottom-right-radius: 12px;
}

/* THE STICKY GOES ON `<thead>`, NEVER ON THE `<th>`. Measured, all four variants, in this page:
 *
 *     th sticky                              ✗  travels with content
 *     th sticky + border-collapse: separate  ✗  so the collapsed-border bug is NOT the cause
 *     thead sticky                           ✓
 *     tr sticky                              ✓
 *
 * For a table cell the sticky CONTAINING BLOCK is its ROW GROUP, and a sticky element is clamped
 * inside its containing block — `<thead>` is ~52px tall, so a `<th>` has no vertical travel at all.
 * The same clamp is why sticky-LEFT on a cell works perfectly: a row group is full-table-WIDTH, so
 * there is room on that axis. One property, two axes, two outcomes.
 *
 * Sticking the `<thead>` also pins both header rows as ONE block, so the second row needs no
 * measured offset to keep in step with the font size. */
.pm-scroll .pm-t thead {
  position: sticky;
  top: var(--pm-top, 56px);
  z-index: 20;
}

/* AND EVERY HEADER CELL MUST BE POSITIONED, or the body paints straight over it.
 *
 * Sticking the thead is not enough, and the way this failed is invisible to review — the header
 * looked pinned, and the rows sliding through it read as a font-rendering artifact. Measured with
 * every `tbody td` forced to `#ff0000`, counting red pixels inside the header band:
 *
 *     before:  16,256 red px — 75 of the GROUP row's 77 scanlines, full width
 *              …and only 3 of the MEASURE row's 76
 *
 * The asymmetry is the diagnosis. In a `border-collapse: collapse` table a STATIC cell's background
 * is painted at the TABLE's background level — not as the cell's own — so document order decides,
 * and `<tbody>` comes after `<thead>`. The measure row escaped purely by accident: `bpreport.css`
 * already declares `position: sticky` on `.bp-hm th`, which promotes those cells into the positioned
 * layer. The group row had nothing and lost to every body row.
 *
 * `relative` is enough — the thead owns the sticking; these only need to be in the positioned layer,
 * above the body's own positioned cells. Locked by `npm run bypm:freeze`. */
.pm-scroll .pm-t thead th {
  position: relative;
  z-index: 5;
}

/* `border-collapse: collapse` hands a cell's borders to the TABLE, and the table does not travel
 * with the sticky thead — so the header's bottom rule vanishes the moment it lifts off and the rows
 * scroll under it with nothing separating them. An inset shadow belongs to the cell and survives. */
.pm-t thead tr.bp-hm th {
  box-shadow: inset 0 -1px 0 var(--border, #e3e0d8);
}
/* Once it is floating over content it should read as a layer, not a row that stopped moving. */
.pm-scroll.pm-lifted .pm-t thead tr.bp-hm th {
  box-shadow: inset 0 -1px 0 var(--border, #e3e0d8), 0 6px 12px -8px rgba(13, 42, 94, 0.35);
}

/* ---- MODE HSCROLL: only when the table really is wider than the pane (set from JS) ---- */
.pm-scroll.pm-hscroll {
  overflow: auto;
  position: sticky;
  top: var(--pm-top, 56px);
  max-height: calc(100vh - var(--pm-top, 56px) - 10px);
}
/* Here the pane is the scrollport, so the header pins to the pane's own top, not the viewport's. */
.pm-scroll.pm-hscroll .pm-t thead { top: 0; }

/* The FROZEN COLUMN — the other half of the same request, and it only means anything in HSCROLL
 * mode: in MODE PAGE nothing scrolls sideways, so there is nothing to freeze against. Scoped to
 * `.pm-hscroll` so a sticky cell is never declared where it cannot act (a dead `position: sticky`
 * is exactly the thing that hid the header bug on By Program for months).
 *
 * A sticky cell needs its OWN background — it slides over the cells behind it — and that background
 * has to match the row it belongs to. A blanket white one printed the navy band's white label text
 * onto near-white and made every school row's name invisible. */
.pm-hscroll .pm-t td.bp-lbl,
.pm-hscroll .pm-t th.bp-lbl {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--bb-surface, #fff);
}
.pm-hscroll .pm-t tr.bp-total td.bp-lbl,
.pm-hscroll .pm-t tr.bp-school td.bp-lbl { background: var(--navy); color: #fff; }
.pm-hscroll .pm-t tr.bp-grp td.bp-lbl { background: var(--off-white, #f5f3ee); }
/* The corner is sticky on BOTH axes, so it outranks the row it is in AND the column it is in. */
.pm-hscroll .pm-t thead th.bp-lbl { position: sticky; left: 0; z-index: 6; }

@media (max-width: 720px) {
  /* A phone is where the label column earns its keep — scrolling right there strands you among
     numbers with no idea which program they belong to — so it stays pinned (HSCROLL mode is on at
     this width by definition) and the width is paid for by narrowing the label, not unpinning it. */
  .pm-t td.bp-lbl, .pm-t th.bp-lbl { max-width: 42vw; }
  .pm-t td.bp-lbl { overflow: hidden; text-overflow: ellipsis; }
  .pm-tile-big .pm-tv { font-size: 20px; }
}

/* A short table must NOT get a pane taller than itself: an inner scrollbar on content that fits,
   and a header frozen against nothing, both read as broken. `fit-content` caps the pane at the
   table's own height and the viewport cap only bites once the table is the taller of the two. */
@supports (height: fit-content) {
  .pm-scroll { height: fit-content; }
}

/* ---------- Semana a semana (the By PM trend chart) ----------
   The component classes (.rv-tr*, .rv-tr-tip, .rv-tip) are review.css's — the Weekly Review
   chart's own, already loaded globally in <head>. Only what that component needs to sit
   INSIDE the By PM card is restated here, and nothing about it is re-styled. */
.bp-card .rv-tr {
  border-top: 1px solid var(--bb-line, rgba(13,42,94,.14));
  margin-top: 14px;
  padding-top: 14px;
}
/* The tip is positioned wrap-relative like review.js's own (left/top from the cell rect);
   only the anti-edge flip differs slightly: on a chart this wide the cell is always near the
   vertical middle, so below-cell placement alone would cover the row being read. */
.bp-card .rv-tr-tip { pointer-events: none; z-index: 6; }
