/* ─────────────────────────────────────────────────────────────────
   Guestnet Stats — Oberfläche

   Design-Sprache übernommen aus dem Wechselangebot: freundliches Blau,
   grosszügige Radien, weiche Schatten, Systemschrift. Ein Werkzeug darf
   ruhig einladend aussehen — das ist der Unterschied zwischen einem
   Dashboard, das nach zwei Wochen noch benutzt wird, und einem, das nicht.
   ───────────────────────────────────────────────────────────────── */

:root {
  --accent:   #1954d3;
  --accent-d: #123fa6;
  --accent-l: #eef2fb;
  /* The second axis. Teal rather than a second blue, because a line on its own
     axis measures a different quantity from the bars underneath and should look
     like it — the same teal as the "Check-out" group. */
  --accent-2: #17808c;

  --green:  #1e8e3e;  --green-l: #e8f4ec;
  --amber:  #8a5a08;  --amber-l: #fdf4e3;
  --red:    #c0392b;  --red-l:   #fbeceb;

  --ink:    #1d1d1f;
  --ink-2:  #5c5c66;
  --ink-3:  #8a8a94;
  --line:   #e4e5ec;
  --line-2: #f0f1f5;
  --bg:     #f4f6fb;
  --card:   #fff;

  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 22px; --s6: 30px;
  --r1: 9px; --r2: 13px; --r3: 18px; --r4: 24px;

  --sh:    0 1px 2px rgba(20,20,50,.04), 0 12px 28px -14px rgba(20,20,50,.18);
  --sh-up: 0 16px 36px -12px rgba(20,20,50,.30);

  --fs: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --fm: ui-monospace, SFMono-Regular, Menlo, monospace;
}

* { box-sizing: border-box; }

/* Muss nach allen display-Regeln greifen: Klassen wie .palette setzen
   display:grid und schlagen sonst die UA-Regel [hidden]{display:none}. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 15px/1.55 var(--fs);
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3 { margin: 0; font-weight: 700; letter-spacing: -.015em; }
button { font-family: inherit; }

.sr, .skip:not(:focus) {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}
.skip:focus {
  position: fixed; top: 10px; left: 10px; z-index: 200; width: auto; height: auto;
  clip: auto; background: var(--card); border: 1px solid var(--accent);
  border-radius: var(--r1); padding: 9px 14px; font-weight: 700;
}

/* ── Kopf ────────────────────────────────────────────────────────── */

.top {
  position: sticky; top: 0; z-index: 50;
  background: rgba(244, 246, 251, .9);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
  backdrop-filter: blur(16px) saturate(180%);
  border-bottom: 1px solid var(--line);
}
.top-row {
  max-width: 1400px; margin: 0 auto;
  padding: var(--s3) var(--s4);
  display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap;
}
.brand { display: flex; align-items: baseline; gap: 9px; margin-right: auto; min-width: 0; }
.brand-mark {
  width: 9px; height: 9px; border-radius: 50%; background: var(--accent);
  align-self: center; flex: none;
}
.brand-name { font-size: 16px; font-weight: 700; letter-spacing: -.015em; white-space: nowrap; }
.brand-sub  { font-size: 12.5px; color: var(--ink-3); white-space: nowrap; }

.top-meta { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }
.tag {
  font: 700 10.5px var(--fs); letter-spacing: .05em; text-transform: uppercase;
  border-radius: 100px; padding: 4px 10px;
}
.tag-demo { background: var(--amber-l); color: var(--amber); }
.stamp { font-size: 12.5px; color: var(--ink-3); white-space: nowrap; }
/* Stale data is a warning, not a footnote — the replica is behind. */
.stamp.stale {
  color: var(--amber); font-weight: 700;
  background: var(--amber-l); border-radius: 100px; padding: 4px 10px;
}
.kbd-hint {
  display: flex; align-items: center; gap: 5px;
  font: 700 12.5px var(--fs); color: var(--ink-2);
  background: var(--card); border: 1px solid var(--line);
  border-radius: 100px; padding: 6px 13px; cursor: pointer;
}
.kbd-hint:hover { border-color: var(--accent); color: var(--accent-d); }
.kbd-hint kbd {
  font: 600 11px var(--fm); background: rgba(120,120,140,.12);
  border-radius: 4px; padding: 1px 5px; color: var(--ink-2);
}

/* ── Reiter ──────────────────────────────────────────────────────── */

.tabsrow { max-width: 1400px; margin: 0 auto; padding: 0 var(--s4) var(--s3); }
.tabs {
  display: flex; gap: var(--s1); background: rgba(120,120,140,.1);
  padding: 3px; border-radius: 100px; overflow-x: auto; scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  position: relative;
  display: inline-flex; align-items: center; gap: 7px;
  font: 700 13px var(--fs); padding: 7px 15px; border: 0; border-radius: 100px;
  background: none; color: var(--ink-3); cursor: pointer; white-space: nowrap;
  transition: color .15s ease, background .15s ease;
}
.tab .mod-icon {
  width: 15px; height: 15px; flex: none; stroke: currentColor; stroke-width: 1.8;
  opacity: .75; transition: opacity .15s ease, transform .15s ease;
}
/* Hover fills the pill rather than only tinting the text: on a strip of seven
   the old version gave no sense of which one the pointer was actually over. */
.tab:hover {
  color: var(--ink);
  background: var(--card);
  box-shadow: 0 1px 2px rgba(20,20,50,.07);
}
/* The selected tab keeps its own weight; hovering it must not look like a
   second selection. */
.tab[aria-selected="true"]:hover { box-shadow: 0 1px 3px rgba(20,20,50,.12); }
.tab:hover .mod-icon { opacity: 1; transform: translateY(-1px); }
.tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tab[aria-selected="true"] {
  background: var(--card); color: var(--ink); box-shadow: 0 1px 3px rgba(20,20,50,.12);
}
.tab[aria-selected="true"] .mod-icon { stroke: var(--accent); opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .tab, .tab .mod-icon { transition: none; }
  .tab:hover .mod-icon { transform: none; }
}
/* Below this the labels stop fitting; the marks carry the meaning. */
@media (max-width: 720px) {
  .tab { padding: 8px 11px; gap: 0; }
  .tab .mod-icon { width: 17px; height: 17px; }
  .tab-text { display: none; }
  .tab[aria-selected="true"] .tab-text { display: inline; margin-left: 7px; }
}

/* ── Filterleiste ────────────────────────────────────────────────── */

.qbar {
  position: relative;
  max-width: 1400px; margin: 0 auto;
  padding: 0 var(--s4) var(--s3);
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
}
.qbar-range select {
  font: 700 12.5px var(--fs); color: var(--accent-d);
  background: var(--accent-l); border: 1px solid rgba(25,84,211,.16);
  border-radius: 100px; padding: 6px 12px; cursor: pointer;
}
.qbar-range select:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.qbar-chips { display: flex; gap: 6px; flex-wrap: wrap; }
/* The page-wide property picker. Its own control, not two clicks deep under
   "+ Filter" — narrowing the page to one house is the common case. */
.qbar-pick {
  font: 700 12.5px var(--fs); color: var(--ink-2);
  background: var(--card); border: 1px solid var(--line);
  border-radius: 100px; padding: 6px 14px; cursor: pointer;
  max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.qbar-pick::before { content: '⌂'; margin-right: 7px; color: var(--ink-3); }
.qbar-pick:hover { border-color: var(--accent); color: var(--accent-d); }
.qbar-pick.on {
  background: var(--accent-l); border-color: rgba(25,84,211,.2); color: var(--accent-d);
}
.qbar-pick.on::before { color: var(--accent); }
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  font: 600 12px var(--fs); color: var(--ink-2);
  background: var(--card); border: 1px solid var(--line);
  border-radius: 100px; padding: 4px 5px 4px 11px; white-space: nowrap;
}
.chip-key { color: var(--ink-3); font-weight: 500; }
.chip b { color: var(--ink); font-weight: 700; }
.chip-x {
  border: 0; background: rgba(120,120,140,.12); color: var(--ink-2);
  width: 18px; height: 18px; border-radius: 50%; cursor: pointer;
  font-size: 12px; line-height: 1; padding: 0;
}
.chip-x:hover { background: var(--red-l); color: var(--red); }

.qbar-actions { display: flex; gap: 6px; margin-left: auto; }
.qbar-add, .qbar-reset, .qbar-copy {
  font: 700 12.5px var(--fs); padding: 6px 13px; border-radius: 100px;
  border: 1px solid var(--line); background: var(--card); color: var(--ink-2); cursor: pointer;
}
.qbar-add:hover, .qbar-reset:hover, .qbar-copy:hover { border-color: var(--accent); color: var(--accent-d); }
.qbar-add { border-style: dashed; }

/* ── Popover für die Filterauswahl ───────────────────────────────── */

.popover {
  position: absolute; top: 100%; right: var(--s4); z-index: 60;
  width: 300px; max-height: 62vh; overflow: auto;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r2); box-shadow: var(--sh-up); padding: var(--s1);
}
.pop-label {
  font: 700 10.5px var(--fs); letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3); padding: 8px 12px 4px;
}
.pop-item {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: 100%; text-align: left; background: none; border: 0;
  padding: 9px 12px; border-radius: var(--r1); cursor: pointer;
  font: 400 14px var(--fs); color: var(--ink);
}
.pop-item:hover { background: var(--bg); }
.pop-search { padding: 4px 6px 8px; position: sticky; top: 0; background: var(--card); z-index: 1; }
.pop-search .tsearch-input { width: 100%; }
.pop-list { max-height: 46vh; overflow: auto; }
.pop-item[aria-pressed="true"] { background: var(--accent-l); color: var(--accent-d); font-weight: 700; }
/* The empty state inherited .pop-item, so "No matches" hovered and read as a
   choice you could pick. */
.pop-empty {
  color: var(--ink-3); font-style: italic; cursor: default;
}
.pop-empty:hover { background: transparent; }

.pop-back {
  display: block; width: 100%; text-align: left; background: none; border: 0;
  font: 700 12.5px var(--fs); color: var(--ink-3);
  padding: 8px 12px; cursor: pointer; border-radius: var(--r1);
}
.pop-back:hover { background: var(--bg); color: var(--accent-d); }
.pk {
  font: 700 10.5px var(--fs); letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3);
}

/* ── Inhalt ──────────────────────────────────────────────────────── */

#main { max-width: 1400px; margin: 0 auto; padding: var(--s5) var(--s4) 64px; }
.lead { font-size: 13px; color: var(--ink-3); margin: 0 0 var(--s4); max-width: 90ch; }

/* ── KPI-Kacheln ─────────────────────────────────────────────────── */

/* Room to breathe.
   The row was sized for six tiles at 168px; a seventh arrived and each one
   lost width until "Upsell revenue per room-night" broke across two lines and
   the numbers sat shoulder to shoulder. A wider minimum lets the row wrap to
   two lines instead of squeezing seven onto one — the tiles get their measure
   back and the labels stop breaking. */
.kpis {
  display: grid; gap: var(--s3); margin-bottom: var(--s5);
  /* A fixed count, not auto-fit.
     Seven tiles on one row left each about 190px wide and the labels broke
     mid-word; auto-fit at a comfortable minimum gave six and one, which reads
     as an accident. Four and three is deliberate, and every tile has room for
     its name on one or two lines without the row changing height. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
@media (max-width: 1180px) { .kpis { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 860px)  { .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px)  { .kpis { grid-template-columns: 1fr; } }
.kpi {
  display: flex; flex-direction: column;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r2); padding: 16px 18px 14px;
}
.kpi .spark { margin-top: auto; }
.kpi-label {
  display: block;
  font: 700 10.5px/1.35 var(--fs); letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3);
  /* Two lines' worth of space whether the label needs it or not, so a tile
     with a long name sits at the same height as its neighbours. */
  min-height: 2.7em;
}
/* Value and change on one baseline: the eye reads the number, then the
   direction, without a second line of vertical travel. */
.kpi-row { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-top: 4px; }
.kpi-value {
  /* 24px rather than 25: at the narrowest tile width a value like "968.1k"
     plus its delta chip was one or two pixels too wide and wrapped, so half
     the row sat on a second line and half did not. */
  font-size: 24px; font-weight: 800; letter-spacing: -.03em;
  font-variant-numeric: tabular-nums; line-height: 1.15;
}
.kpi-delta {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 11px; font-weight: 700; white-space: nowrap;
  border-radius: 100px; padding: 2px 7px;
  background: rgba(120,120,140,.1); color: var(--ink-3);
}
.kpi-delta .kpi-arrow { font-size: 8px; line-height: 1; }
.kpi-delta.up   { background: var(--green-l); color: var(--green); }
.kpi-delta.down { background: var(--red-l);   color: var(--red); }
/* The within-range comparison: outlined rather than filled.
   It is a real movement in measured data, but a different comparison from the
   period-over-period one — same shape so the row stays even, less ink so the
   two are never mistaken for each other. The caption beside it says which. */
.kpi-delta.is-half {
  background: transparent;
  box-shadow: inset 0 0 0 1px currentColor;
  opacity: .8;
}
/* A hint is now a tooltip. The dotted rule is the only thing that has to say
   "there is more here" — a paragraph on every tile made the row unreadable. */
.kpi-label.has-hint {
  text-decoration: underline dotted var(--line-strong, #c9d0cd);
  text-underline-offset: 3px; cursor: help;
}
/* Date under the cursor. Takes the delta chip's place so nothing shifts. */
.kpi-readout {
  font: 700 11.5px var(--fs); color: var(--ink-3);
  border-radius: 100px; padding: 2px 8px; background: rgba(120,120,140,.1);
}
.kpi-readout:empty { display: none; }
.kpi.hovering .kpi-value { color: var(--accent-d); }

/* ── Sparkline ───────────────────────────────────────────────────── */
/* Sized in CSS, drawn in a unitless viewBox — the stroke has to be scaled
   back by vector-effect or it would stretch with the tile. */
/* Sits flush in the bottom of the card: negative margins cancel the card's own
   padding so the curve reaches both edges, and the bottom corners follow the
   card radius. No padding here — it would shrink the content box the viewBox is
   measured against. */
.spark {
  display: block; width: auto; height: 52px; overflow: visible;
  margin: 16px -18px -14px; padding: 0;
  border-bottom-left-radius: var(--r2); border-bottom-right-radius: var(--r2);
}
.spark .spark-line {
  fill: none; stroke: var(--accent); stroke-width: 1.6;
  stroke-linejoin: round; stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.spark .spark-dot  { fill: var(--accent); }
/* Reference rule at the period average. A rate line without it says "it moved";
   with it, it says "above or below normal". */
.spark .spark-avg {
  stroke: var(--ink-3); stroke-width: 1; stroke-dasharray: 2 3; opacity: .38;
  vector-effect: non-scaling-stroke;
}
.spark .spark-cursor {
  stroke: var(--ink-3); stroke-width: 1; opacity: .5;
  vector-effect: non-scaling-stroke;
}
.spark .spark-hit { fill: var(--ink); }
.spark { cursor: crosshair; }
.spark .s0 { stop-color: var(--accent); stop-opacity: .20; }
.spark .s1 { stop-color: var(--accent); stop-opacity: .02; }
.spark.up   .spark-line, .spark.up   .spark-dot { stroke: var(--green); fill: var(--green); }
.spark.up   .spark-line { fill: none; }
.spark.up   .s0 { stop-color: var(--green); }
.spark.up   .s1 { stop-color: var(--green); }
.spark.down .spark-line, .spark.down .spark-dot { stroke: var(--red); fill: var(--red); }
.spark.down .spark-line { fill: none; }
.spark.down .s0 { stop-color: var(--red); }
.spark.down .s1 { stop-color: var(--red); }
.kpi.fail { color: var(--red); background: var(--red-l); border-color: rgba(192,57,43,.25); font-size: 13px; }

/* ── Panel groups ────────────────────────────────────────────────── */
/* A block heading inside the grid. Sits on the page background rather than on
   a card, so it reads as a divider between blocks and not as another panel. */
.group-head {
  grid-column: 1 / -1;
  display: flex; align-items: baseline; gap: var(--s3); flex-wrap: wrap;
  margin: var(--s4) 0 calc(var(--s1) * -1); padding: 0 var(--s1);
}
.group-head:first-child { margin-top: 0; }
.group-title {
  font-size: 12px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--accent-d);
}
.group-title::before {
  content: ''; display: inline-block; width: 16px; height: 2px; border-radius: 2px;
  background: var(--accent); vertical-align: 4px; margin-right: 9px;
}
.group-note { font-size: 12.5px; color: var(--ink-3); }

/* Coloured bands, one tone per topic — carried over from the Looker version,
   where the colour was how you knew which block you were reading. Tone lives on
   the band only; charts stay brand blue so values remain comparable across
   sections. */
.group-head {
  border-left: 3px solid var(--tone, var(--accent));
  padding-left: 11px;
}
.group-head .group-title { color: var(--tone, var(--accent-d)); }
.group-head .group-title::before { background: var(--tone, var(--accent)); }
.tone-accent  { --tone: #1954d3; }
.tone-purple  { --tone: #6b3fa0; }
.tone-teal    { --tone: #17808c; }
.tone-maroon  { --tone: #8f1d4e; }
.tone-blue    { --tone: #2b4fa8; }
.tone-green   { --tone: #1e8e3e; }
.tone-amber   { --tone: #8a5a08; }

/* ── Panels ──────────────────────────────────────────────────────── */

.grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--s3); }
/* A container query context: panels of the same span sit at very different
   widths depending on the viewport, and a card has to size itself to the box it
   is in — not to the window. The clamp that hung off vw picked 22px inside a
   500px card and truncated everything in it. */
.panel {
  container-type: inline-size;
  --panel-pad: var(--s5);
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r4); box-shadow: var(--sh); padding: var(--panel-pad);
  min-width: 0;
}
.panel.span-3  { grid-column: span 3; }
.panel.span-4  { grid-column: span 4; }
.panel.span-8  { grid-column: span 8; }
.panel.span-6  { grid-column: span 6; }
.panel.span-12 { grid-column: span 12; }
/* Thirds carry a funnel, a trend and a leaderboard side by side — the chart
   area has to shrink with them or the row grows taller than it needs to be. */
.panel.span-3 .panel-canvas,
.panel.span-4 .panel-canvas { height: 232px; }
.panel.span-8 .panel-canvas { height: 300px; }
/* A paged list sizes to its rows. Scrolling inside a panel that also has a
   pager means two ways to move through the same data — pick one. */
.panel .panel-canvas:has(.ptable-wrap),
.panel.span-12 .panel-canvas:has(.ptable-wrap) { height: auto; min-height: 0; }
.panel-head { display: flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap; margin-bottom: var(--s3); }
/* The card supplies its own heading; the panel keeps only the warning slot. */
.panel.bare .panel-head:empty,
.panel.bare .panel-head:has(> .note:empty) { margin-bottom: 0; min-height: 0; }
.panel-title { font-size: 17px; }
.panel-sub { font-size: 12.5px; color: var(--ink-3); }
.note { font: 700 11px var(--fs); color: var(--amber); margin-left: auto; }
/* Marks a chart that shows one property rather than the portfolio. */
.focus-chip {
  display: inline-flex; align-items: center; gap: 6px; margin-left: auto;
  font-size: 12px; color: var(--accent-d); background: var(--accent-l);
  border: 1px solid rgba(25,84,211,.18); border-radius: 100px; padding: 3px 5px 3px 11px;
}
.focus-chip b { font-weight: 700; }
.focus-x {
  border: 0; background: rgba(25,84,211,.14); color: var(--accent-d);
  width: 17px; height: 17px; border-radius: 50%; cursor: pointer;
  font-size: 12px; line-height: 1; padding: 0;
}
.focus-x:hover { background: var(--accent); color: #fff; }
.panel-canvas { height: 268px; }
.panel.span-12 .panel-canvas { height: 300px; }

.empty, .fail {
  display: flex; align-items: center; justify-content: center; height: 100%;
  font-size: 13px; color: var(--ink-3); text-align: center; padding: 0 var(--s4);
}
.fail { color: var(--red); }

/* Ladezustand — ruhiger Puls, kein Springen */
.skel { position: relative; overflow: hidden; border-radius: var(--r2); background: var(--line-2); }
.skel::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.75), transparent);
  animation: sweep 1.5s cubic-bezier(.4,0,.2,1) infinite;
}
@keyframes sweep { 0% { transform: translateX(-100%); } 100% { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { .skel::after { animation: none; } }
.kpi-label.skel, .kpi-value.skel { color: transparent; }

/* ── Module card ─────────────────────────────────────────────────── */
/* Icon, coloured title, three headline numbers, one proportion bar, one quiet
   line for the rest. Earlier this was a stack of labelled rows — every figure
   the same weight, so nothing stood out and the eye had to read all of it. */
.mod { display: flex; flex-direction: column; height: 100%; }
.mod-head { display: flex; align-items: center; gap: 9px; margin-bottom: var(--s4); }
.mod-icon {
  width: 22px; height: 22px; flex: none;
  fill: none; stroke: var(--tone, var(--accent)); stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
.mod-title { font-size: 17px; font-weight: 700; color: var(--tone, var(--accent-d)); }

/* The path, as one object.
   Three numbers floating side by side read as three unrelated facts. Inside a
   single framed strip, split by rules with the conversion sitting on them, they
   read as what they are: traffic becoming requests becoming money. */
.mod-flow {
  display: flex; align-items: stretch;
  border: 1px solid var(--line); border-radius: var(--r2);
  background: var(--card);
}
/* Content-sized, not equal thirds. Split evenly, a short number like "5,688"
   sat in a 380px cell and the tinted outcome block became a wide empty field —
   which is what made the strip look unbalanced. */
.mod-cell { flex: 1 1 auto; min-width: 0; padding: 13px clamp(8px, 2.4%, 14px); }
.mod-cell.outcome { flex: 0 1 auto; min-width: 132px; }
.mod-cell-label {
  display: block; font: 700 9px var(--fs); letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mod-cell-value {
  display: block; font-weight: 800; letter-spacing: -.03em;
  font-size: 22px;
  font-variant-numeric: tabular-nums; line-height: 1.15; margin-top: 4px;
  white-space: nowrap;
}
}
.mod-cell .cell-delta { display: inline-block; margin: 5px 0 0; font-size: 10px; }

/* The outcome cell. Tinted in the module's own colour, with the number in that
   colour too — the eye should land on the money without being told to. */
.mod-cell.outcome {
  background: color-mix(in srgb, var(--tone, var(--accent)) 7%, transparent);
  border-radius: 0 var(--r2) var(--r2) 0;
}
.mod-cell.outcome .mod-cell-value { color: var(--tone, var(--accent-d)); }
.mod-cell.outcome .mod-cell-label { color: color-mix(in srgb, var(--tone, var(--accent)) 70%, var(--ink-3)); }

/* A rule with the conversion on it.
   The badge gets a column of its own rather than floating on a 1px divider —
   absolutely positioned it overhung both neighbours and ran into the numbers. */
/* The divider column gives way first. Check-out has four cells where the other
   modules have three, and three fixed 58px gutters left 82px per cell — enough
   to truncate both the label and the number. */
.mod-link {
  position: relative; flex: 0 1 58px; min-width: 30px;
  display: flex; align-items: center; justify-content: center;
}
.mod-link::before {
  content: ''; position: absolute; top: 10px; bottom: 10px; left: 50%;
  width: 1px; background: var(--line);
}
.mod-link-pill, .mod-link-arrow { position: relative; z-index: 1; white-space: nowrap; }
.mod-link-pill {
  font: 800 10px var(--fs); font-variant-numeric: tabular-nums;
  color: var(--tone, var(--accent-d));
  background: var(--card); border: 1px solid var(--line);
  border-radius: 100px; padding: 3px 7px;
}
.mod-link-pill.weak { color: var(--amber); border-color: rgba(138,90,8,.28); background: var(--amber-l); }
.mod-link-arrow {
  color: var(--ink-3); font-size: 13px; line-height: 1;
  background: var(--card); padding: 2px 4px;
}

@media (max-width: 560px) {
  .mod-flow { flex-direction: column; }
  .mod-link { width: auto; height: 30px; }
  .mod-link::before { top: 0; bottom: 0; left: 50%; }
  .mod-cell.outcome { border-radius: 0 0 var(--r2) var(--r2); }
}

/* Four cells in a half-width card. Placed after every base rule it overrides:
   an @container block still loses to a later declaration of equal specificity,
   which is why the gutters stayed at 52px on the first attempt. */
@container (max-width: 620px) {
  .mod-cell { padding: 11px 9px; }
  .mod-cell-value { font-size: 17px; }
  .mod-cell-label { font-size: 8.5px; letter-spacing: .03em; }
  /* The outcome cell reserved 132px, which is most of what four cells have to
     share at this width. */
  .mod-cell.outcome { min-width: 104px; }
  .mod-link { flex: 0 1 34px; min-width: 24px; }
  .mod-link-pill { font-size: 9px; padding: 2px 5px; }
  .mod-link-arrow { font-size: 11px; padding: 2px; }
}

/* What is on offer, above the path. Reads as the module's stock: one number,
   and where a catalogue has free and chargeable items, how it splits. */
.mod-supply { margin-bottom: var(--s3); }
.mod-supply-line { display: flex; align-items: baseline; gap: 7px; }
.mod-supply-value {
  font-size: 17px; font-weight: 800; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.mod-supply-value .cell-delta { font-size: 10px; margin-left: 5px; }
.mod-supply-label {
  font: 700 9.5px var(--fs); letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3);
}
.mod-supply-split { margin-top: 7px; }
.mod-supply-track {
  display: block; height: 8px; border-radius: 100px;
  background: rgba(120,120,140,.15); overflow: hidden;
}
.mod-supply-track i {
  display: block; height: 100%; border-radius: 100px; background: var(--tone, var(--accent));
}
.mod-supply-note {
  display: block; font-size: 11px; color: var(--ink-3); margin-top: 5px;
  font-variant-numeric: tabular-nums;
}

/* Secondary figures on one line. They matter, but not at headline weight. */
.mod-rest {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  margin-top: var(--s3); font-size: 12px; color: var(--ink-3);
}
.mod-rest-item b {
  color: var(--ink); font-weight: 800; font-variant-numeric: tabular-nums;
}
.mod-rest .cell-delta { font-size: 9.5px; margin-left: 4px; }
.mod-dot { color: var(--line-strong, #c9d0cd); }

.mod-note.warn {
  background: var(--amber-l); color: var(--amber);
}
.mod-note {
  margin-top: var(--s3); border-radius: var(--r1); padding: 9px 12px;
  font-size: 12px; line-height: 1.5;
  background: color-mix(in srgb, var(--tone, var(--accent)) 8%, transparent);
  color: color-mix(in srgb, var(--tone, var(--accent)) 78%, var(--ink));
}

/* The card fills the panel so its bottom edge is the panel's bottom edge. */
.panel.bare { display: flex; flex-direction: column; }
/* Daily line under the numbers. The figures say how much; this says whether it
   is a trend or a spike, which no single number can. Pushed to the bottom with
   margin-top:auto — grid rows stretch to the tallest panel, so without it the
   line floats with dead space beneath. */
.mod-trend {
  margin-top: auto; padding-top: var(--s3);
  border-top: 1px solid var(--line-2);
}
.mod-trend-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s2);
  font: 700 9.5px var(--fs); letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3);
  min-height: 14px;   /* the readout must not resize the row on hover */
}
.mod-trend-label { color: inherit; }   /* explicit, so no class ships unstyled */
.mod-trend-read {
  font: 700 10.5px var(--fs); line-height: 14px; letter-spacing: 0; text-transform: none;
  color: var(--tone, var(--accent-d)); font-variant-numeric: tabular-nums;
}
/* An SVG is a replaced element: width:auto resolves to its intrinsic size, not
   to the parent's width, so it has to be stated. */
.mod-trend .spark {
  height: 64px; overflow: visible;
  width: calc(100% + var(--panel-pad) * 2);
  margin: 8px calc(var(--panel-pad) * -1) calc(var(--panel-pad) * -1);
  border-bottom-left-radius: var(--r4); border-bottom-right-radius: var(--r4);
}
/* The card's own tone rather than the red/green of a delta — this line is the
   shape of the series, not a verdict on it. */
.spark.tone .spark-line { stroke: var(--tone, var(--accent)); }
.spark.tone .spark-dot  { fill: var(--tone, var(--accent)); }
.spark.tone .s0 { stop-color: var(--tone, var(--accent)); stop-opacity: .18; }
.spark.tone .s1 { stop-color: var(--tone, var(--accent)); stop-opacity: .02; }

.panel .panel-canvas:has(.mod),
.panel.span-12 .panel-canvas:has(.mod) { height: auto; min-height: 0; flex: 1 1 auto; }

/* ── Summary header ──────────────────────────────────────────────── */
.sum-wrap { display: flex; flex-direction: column; }
.sum { display: flex; align-items: stretch; gap: var(--s5); }
.sum-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }

.sum-figs { display: flex; gap: var(--s6); flex-wrap: wrap; }
.sum-fig { min-width: 0; }
.sum-fig-label {
  display: block; font: 700 9.5px var(--fs); letter-spacing: .05em;
  text-transform: uppercase; color: var(--ink-3);
}
/* ── Ancillary RevPOR as its own object ──────────────────────────────────
   Teal, like the tile on the Overview, so the same number wears the same badge
   wherever it turns up — a vocabulary only takes hold if it is consistent.
   Its own block rather than a cell in the figure row, because a rate among
   three counts reads as a fourth count. */
.sum-hero {
  display: flex; flex-direction: column;
  padding: 12px 14px; border-radius: var(--r2);
  border: 1px solid color-mix(in oklab, var(--accent-2) 24%, var(--line));
  background: linear-gradient(168deg,
    color-mix(in oklab, var(--accent-2) 7%, var(--card)), var(--card) 60%);
}
@supports not (background: color-mix(in oklab, red 50%, blue)) {
  .sum-hero { background: var(--card); border-color: var(--line); }
}
.sum-hero-head {
  display: flex; align-items: flex-start; gap: 6px;
  font: 700 9.5px/1.35 var(--fs); letter-spacing: .05em; text-transform: uppercase;
  color: var(--accent-2);
}
.sum-hero-head .mod-icon {
  flex: none; width: 15px; height: 15px; margin-top: -2px;
  fill: none; stroke: currentColor; stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
}
.sum-hero-value {
  margin-top: 6px; font-size: 27px; font-weight: 800; letter-spacing: -.03em;
  color: var(--ink); font-variant-numeric: tabular-nums;
}
.sum-hero-term { font: 600 10px var(--fs); color: var(--accent-2); opacity: .85; }
.sum-hero-delta { margin-top: 6px; }
.sum-hero-name { flex: 1; min-width: 0; }

/* ── "i", and what it opens ──────────────────────────────────────────────
   A definition that has to be reachable and must not cost height. Unlike a
   tooltip it stays open, which matters for a figure somebody reads out on a
   call — and unlike a tooltip it exists for touch. */
.info-dot {
  flex: none; width: 15px; height: 15px; padding: 0; margin-top: -1px;
  border: 1px solid currentColor; border-radius: 50%;
  background: transparent; color: inherit; cursor: pointer;
  font: 700 9.5px/13px var(--fs); font-style: italic;
  text-transform: none; letter-spacing: 0; opacity: .6;
}
.info-dot:hover, .info-dot[aria-expanded="true"] { opacity: 1; }
.info-dot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.info-bubble {
  position: absolute; z-index: 30; top: 34px; left: 12px; right: 12px;
  padding: 11px 13px; border-radius: 10px;
  background: var(--ink); color: #fff;
  box-shadow: 0 16px 34px -16px rgba(0,0,0,.55);
  font: 400 11px/1.5 var(--fs); text-transform: none; letter-spacing: 0;
}
.info-bubble p { margin: 0 0 7px; }
.info-bubble p:last-child { margin-bottom: 0; }
/* The first line is the trade term, the last is the arithmetic. Both want to
   stand apart from the prose between them. */
.info-bubble p:first-child { font-weight: 700; }
.info-bubble p:last-child:not(:first-child) {
  padding-top: 7px; border-top: 1px solid rgba(255,255,255,.18);
  font-variant-numeric: tabular-nums; opacity: .9;
}
/* Nothing to show, and why. A dash here reads as a broken panel; a missing PMS
   connection is a sales conversation. */
.sum-hero.is-empty { border-style: dashed; background: var(--card); }
.sum-hero.is-empty .sum-hero-head { color: var(--ink-3); }
.sum-hero-none {
  margin-top: 8px; font: 700 14px var(--fs); color: var(--ink-2);
}
.sum-hero-why {
  margin: 4px 0 0; font: 400 10.5px/1.45 var(--fs); color: var(--ink-3);
}
.sum-fig-value {
  display: block; font-size: 27px; font-weight: 800; letter-spacing: -.03em;
  font-variant-numeric: tabular-nums; line-height: 1.1; margin-top: 3px;
}
.sum-fig .cell-delta { display: inline-block; margin-top: 4px; font-size: 10px; }
/* Reserves its filled height while empty. box-sizing is border-box, so a
   min-height of 14px left only 2px of content box once the padding was counted
   — the card grew by 10px the moment the cursor touched the line. */
.sum-read {
  margin-top: auto; padding-top: var(--s3);
  font: 700 10.5px var(--fs); line-height: 14px; color: var(--accent-d);
  font-variant-numeric: tabular-nums;
  min-height: calc(var(--s3) + 14px);
}

/* The split, at a size that says "context", not "headline". */
.sum-side {
  flex: 0 0 250px; padding-left: var(--s5); border-left: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 5px;
}
.sum-side-head {
  font: 700 9.5px var(--fs); letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3); margin-bottom: 2px;
}
.sum-bar { display: grid; grid-template-columns: 58px 1fr 44px; align-items: center; gap: 8px; }
.sum-bar-name { font-size: 12px; font-weight: 600; }
.sum-bar-track {
  height: 12px; border-radius: 100px; background: rgba(120,120,140,.15); overflow: hidden;
}
.sum-bar-track i { display: block; height: 100%; border-radius: 100px; background: var(--accent); }
.sum-bar-pct {
  font: 800 11.5px var(--fs); text-align: right;
  font-variant-numeric: tabular-nums; color: var(--accent-d);
}
.sum-side-key { font-size: 10.5px; color: var(--ink-3); margin-top: 2px; }

/* Top three, on the same row rhythm as the split beside it: name, bar, value.
   Anything more elaborate and the header stops reading as one object. */
.sum-top {
  flex: 0 0 260px; padding-left: var(--s5); border-left: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 5px;
}
.sum-top-row {
  display: grid; grid-template-columns: 1fr 56px 54px; align-items: center; gap: 8px;
  cursor: pointer;
}
.sum-top-name {
  font-size: 12px; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sum-top-row:hover .sum-top-name { color: var(--accent-d); }
.sum-top-row:hover .sum-top-track i { background: var(--accent-d); }
.sum-top-track { height: 12px; border-radius: 100px; background: rgba(120,120,140,.15); overflow: hidden; }
.sum-top-track i { display: block; height: 100%; border-radius: 100px; background: var(--accent); }
.sum-top-val {
  font: 800 11.5px var(--fs); text-align: right;
  font-variant-numeric: tabular-nums; color: var(--accent-d); white-space: nowrap;
}

/* Same edge-to-edge treatment as a KPI tile. */
.sum-wrap .spark {
  height: 62px; overflow: visible;
  width: calc(100% + var(--panel-pad) * 2);
  margin: var(--s3) calc(var(--panel-pad) * -1) calc(var(--panel-pad) * -1);
  border-bottom-left-radius: var(--r4); border-bottom-right-radius: var(--r4);
}
.sum-wrap .spark.tone .spark-line { stroke: var(--accent); }
.sum-wrap .spark.tone .spark-dot  { fill: var(--accent); }
.sum-wrap .spark.tone .s0 { stop-color: var(--accent); stop-opacity: .16; }
.sum-wrap .spark.tone .s1 { stop-color: var(--accent); stop-opacity: .02; }

.panel .panel-canvas:has(.sum-wrap),
.panel.span-12 .panel-canvas:has(.sum-wrap) { height: auto; min-height: 0; }

@media (max-width: 1180px) {
  .sum { flex-wrap: wrap; }
  .sum-main { flex: 1 1 100%; }
  .sum-side, .sum-top {
    flex: 1 1 240px; padding-left: 0; border-left: 0;
    padding-top: var(--s3); border-top: 1px solid var(--line);
  }
}
@media (max-width: 620px) {
  .sum { flex-direction: column; gap: var(--s3); }
  .sum-figs { gap: var(--s5); }
}

/* ── Stacked comparison ──────────────────────────────────────────── */
/* Deliberately slim. The insight is a constant — once you know a login is
   worth 7.5x you do not re-read it every morning — so it keeps its place at the
   top but not a card's worth of height. */
.stack { display: flex; flex-direction: column; gap: 5px; }
.stack-row {
  display: grid; grid-template-columns: 116px 1fr 168px;
  align-items: center; gap: var(--s3);
}
.stack-name { font-size: 12.5px; font-weight: 700; }
.stack-track {
  display: flex; height: 18px; border-radius: 100px; overflow: hidden;
  background: rgba(120,120,140,.12);
}
.stack-part { background: var(--accent); }
.stack-rest { background: rgba(120,120,140,.22); flex: 1; }
.stack-part, .stack-rest {
  display: flex; align-items: center; justify-content: center;
  min-width: 0; transition: width .4s cubic-bezier(.22,.7,.3,1);
}
.stack-in {
  font: 700 10.5px var(--fs); font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden;
}
.stack-part .stack-in { color: #fff; }
.stack-rest .stack-in { color: var(--ink-2); }
.stack-val {
  font-size: 11.5px; color: var(--ink-3); text-align: right;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.stack-key {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  font-size: 11px; color: var(--ink-3); padding-left: 128px; margin-top: 2px;
}
.stack-key i { width: 9px; height: 9px; border-radius: 2px; flex: none; }
.stack-key i + span { margin-right: var(--s3); }
.key-part { background: var(--accent); }
.key-rest { background: rgba(120,120,140,.22); }

.panel .panel-canvas:has(.stack),
.panel.span-12 .panel-canvas:has(.stack) { height: auto; min-height: 0; }

@media (max-width: 700px) {
  .stack-row { grid-template-columns: 96px 1fr; }
  .stack-val { display: none; }
  .stack-key { padding-left: 108px; }
}

/* ── Funnel ──────────────────────────────────────────────────────── */
.fn { display: flex; flex-direction: column; padding: 2px 0; }
.fn-stage { padding: 2px 0; }

.fn-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3);
  margin-bottom: 5px;
}
.fn-name { font-size: 13.5px; font-weight: 700; }
.fn-val {
  font-size: 17px; font-weight: 800; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}

/* The track is the stage above. What is missing from the fill is the loss —
   that is the whole funnel, without needing a tapering outline. */
.fn-track {
  height: 14px; border-radius: 100px; background: rgba(120,120,140,.13); overflow: hidden;
}
.fn-fill {
  display: block; height: 100%; border-radius: 100px; position: relative;
  background: var(--accent);
  transition: width .4s cubic-bezier(.22,.7,.3,1);
}
.fn-fill.last { background: var(--green); }
/* Subset of this stage: same bar, deeper tone, anchored left. */
.fn-seg {
  position: absolute; inset: 0 auto 0 0; display: block;
  border-radius: 100px; background: rgba(0, 0, 0, .3);
}

.fn-meta {
  display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
  margin-top: 5px; font-size: 11px; color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.fn-segnote { display: inline-flex; align-items: center; gap: 5px; }
.fn-swatch {
  width: 9px; height: 9px; border-radius: 2px; flex: none;
  background: var(--accent); box-shadow: inset 0 0 0 9px rgba(0,0,0,.3);
}

/* Between two stages: the step conversion and what it cost. */
.fn-drop {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 0 9px 2px; font-size: 11.5px;
}
.fn-arrow { color: var(--ink-3); font-size: 12px; line-height: 1; }
.fn-conv {
  font-weight: 700; color: var(--accent-d);
  background: var(--accent-l); border-radius: 100px; padding: 2px 9px;
}
/* Under 5 % carrying through is not a step, it is a wall. */
.fn-conv.weak { color: var(--amber); background: var(--amber-l); }
.fn-lost { color: var(--ink-3); }

.panel .panel-canvas:has(.fn),
.panel.span-12 .panel-canvas:has(.fn) { height: auto; min-height: 0; }

/* ── Adoption ladder ─────────────────────────────────────────────── */
/* Four columns on a fixed grid so the bars start on one line and the numbers
   stay right-aligned — a ranked list only reads as ranked if it lines up. */
.ladder { display: flex; flex-direction: column; gap: 2px; }
.ladder-row {
  display: grid; grid-template-columns: 132px 1fr 58px 84px;
  align-items: center; gap: var(--s3);
  padding: 7px var(--s2); border-radius: var(--r1);
}
.ladder-row:hover { background: var(--accent-l); }
.ladder-name { font-size: 13.5px; font-weight: 600; }
.ladder-track {
  height: 9px; border-radius: 100px; background: rgba(120,120,140,.14); overflow: hidden;
}
.ladder-track i {
  display: block; height: 100%; border-radius: 100px; background: var(--accent);
  transition: width .32s ease;
}
/* Under a third switched on, the gap is the story — amber, not brand blue. */
.ladder-track i.low { background: var(--amber); }
.ladder-pct {
  font-size: 14px; font-weight: 800; text-align: right;
  font-variant-numeric: tabular-nums; letter-spacing: -.02em;
}
.ladder-gap {
  font-size: 11.5px; color: var(--ink-3); text-align: right;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
@media (max-width: 640px) {
  .ladder-row { grid-template-columns: 100px 1fr 52px; }
  .ladder-gap { display: none; }
}

/* A ladder panel is a list, not a canvas. Needs to out-specify the
   `.panel.span-12 .panel-canvas` height rule above, hence the .panel prefix. */
.panel .panel-canvas:has(.ladder),
.panel.span-12 .panel-canvas:has(.ladder) { height: auto; min-height: 0; }

/* ── Tabelle ─────────────────────────────────────────────────────── */

.tablewrap {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r4); box-shadow: var(--sh);
  padding: var(--s5); margin-top: var(--s3);
}
.tablescroll { overflow-x: auto; }
table.dt { width: 100%; border-collapse: collapse; font-size: 13.5px; }
table.dt th {
  text-align: right; white-space: nowrap;
  font: 700 10.5px var(--fs); letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3); padding: 9px 8px; border-bottom: 1px solid var(--line);
  cursor: pointer; user-select: none;
}
table.dt th:first-child { text-align: left; }
table.dt th:hover { color: var(--accent-d); }
table.dt th::after { content: '↕'; opacity: .3; margin-left: 4px; font-size: 9px; }
table.dt th:first-child::after { content: ''; }
table.dt th[aria-sort] { color: var(--accent-d); }
table.dt th[aria-sort="ascending"]::after  { content: '↑'; opacity: 1; }
table.dt th[aria-sort="descending"]::after { content: '↓'; opacity: 1; }
table.dt td {
  padding: 9px 8px; border-bottom: 1px solid var(--line-2);
  text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums;
}
table.dt td:first-child { text-align: left; font-weight: 600; white-space: normal; }
table.dt tbody tr:hover { background: var(--accent-l); }

/* Inline-Balken: liegt unter der Zahl, nicht daneben — sonst wandert die
   Spalte und die Zahlen fluchten nicht mehr. */
.cellbar { display: inline-block; position: relative; padding-bottom: 6px; min-width: 42px; }
.cellbar i {
  display: block; position: absolute; left: 0; right: auto; bottom: 0;
  height: 4px; border-radius: 100px; background: var(--accent); opacity: .55;
}
.cellbar.risk { color: var(--red); font-weight: 700; }

/* ── Schnellsuche ────────────────────────────────────────────────── */

.palette {
  position: fixed; inset: 0; z-index: 80;
  background: rgba(20,20,50,.32);
  display: grid; place-items: start center; padding-top: 12vh;
}
.palette-box {
  width: min(560px, 92vw); background: var(--card);
  border: 1px solid var(--line); border-radius: var(--r3);
  box-shadow: 0 24px 60px -12px rgba(20,20,50,.4); overflow: hidden;
}
.palette-box input {
  width: 100%; border: 0; border-bottom: 1px solid var(--line);
  padding: 16px 18px; font: 400 15px var(--fs); color: var(--ink); outline: none;
}
.palette-box ul { list-style: none; margin: 0; padding: var(--s1); max-height: 46vh; overflow: auto; }
.palette-box li {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 13px; border-radius: var(--r1); cursor: pointer; font-size: 14px;
}
.palette-box li[aria-selected="true"] { background: var(--accent-l); color: var(--accent-d); font-weight: 600; }

/* ── Search in a property list ───────────────────────────────────── */
.tsearch { display: flex; align-items: center; gap: var(--s2); margin-bottom: var(--s2); }
.tsearch-input {
  flex: 1; min-width: 0;
  font: 400 13px var(--fs); color: var(--ink);
  border: 1px solid var(--line); border-radius: 100px;
  padding: 6px 13px; background: var(--bg);
}
.tsearch-input:focus {
  outline: none; border-color: var(--accent); background: var(--card);
  box-shadow: 0 0 0 3px rgba(25,84,211,.12);
}
.tsearch-input::placeholder { color: var(--ink-3); }
.tsearch-hits {
  font: 700 10.5px var(--fs); letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-3); white-space: nowrap;
}

/* ── Pager ───────────────────────────────────────────────────────── */
.pager {
  display: flex; align-items: center; gap: 6px; padding-top: 6px;
  font-variant-numeric: tabular-nums;
}
.pager-count {
  font: 700 10.5px var(--fs); letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-3); margin-right: auto;
}
.pager-page { font-size: 11.5px; color: var(--ink-3); min-width: 46px; text-align: center; }
.pager-btn {
  width: 22px; height: 22px; border-radius: 50%;
  border: 1px solid var(--line); background: var(--card);
  color: var(--ink-2); cursor: pointer; font-size: 15px; line-height: 1; padding: 0;
}
.pager-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent-d); }
.pager-btn { font-size: 13px; }
.pager-btn:disabled { opacity: .35; cursor: default; }

/* Change against the previous period, beside the value rather than under it.
   Stacked, each row needed two lines and a list of eight properties filled the
   panel — a ranking you have to scroll is not a ranking you scan. */
.cell-main { display: inline; }
.cell-delta {
  display: inline; font-size: 10px; font-weight: 700; margin-left: 5px;
  color: var(--ink-3); white-space: nowrap;
}
.cell-delta.up   { color: var(--green); }
.cell-delta.down { color: var(--red); }

/* ── Table panel ─────────────────────────────────────────────────── */
.ptable-wrap { overflow-x: auto; }
.ptable {
  width: 100%; border-collapse: collapse; font-size: 12.5px;
  /* Fixed, because `width` under auto layout is only a hint: the browser widens
     any column whose content asks for it, so "€28.9k ▲ 38.4%" and "3,817 ▲ 11.4%"
     produced different column positions in every list on the page. Fixed layout
     takes the widths as given; the name column is left unspecified and absorbs
     whatever is left. */
  table-layout: fixed;
}
.ptable th {
  position: sticky; top: 0; z-index: 1; background: var(--card);
  text-align: right; white-space: nowrap; cursor: pointer; user-select: none;
  font: 700 9.5px var(--fs); letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3); padding: 6px 7px; border-bottom: 1px solid var(--line);
}
.ptable th:first-child { cursor: default; }
.ptable th:not(:first-child)::after { content: '↕'; opacity: .3; margin-left: 3px; font-size: 8px; }
.ptable th[aria-sort] { color: var(--accent-d); }
.ptable th[aria-sort="ascending"]::after  { content: '↑'; opacity: 1; }
.ptable th[aria-sort="descending"]::after { content: '↓'; opacity: 1; }
.ptable th:first-child, .ptable td:first-child { text-align: left; }
.ptable td {
  padding: 4px 7px; border-bottom: 1px solid var(--line-2);
  text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums;
  line-height: 1.5;
}
.ptable th:first-child, .ptable td:first-child {
  /* Room for the 3px focus bar. At padding-left: 0 it sat underneath the first
     letter of the property name.
     A fixed width, not a maximum: sized to its content the name column came out
     differently in every list on the page, so the numbers beside it sat at a
     different x in each one and could not be read down the page. */
  font-weight: 600; padding-left: 10px;
  overflow: hidden; text-overflow: ellipsis;
}
.ptable th { font-size: 9px; }
.ptable tbody tr.drill, .ptable tbody tr.focusable { cursor: pointer; }
.ptable tbody tr.focused { background: var(--accent-l); }
.ptable tbody tr.focused td:first-child {
  font-weight: 800; color: var(--accent-d);
  box-shadow: inset 3px 0 0 var(--accent);
}
.ptable tbody tr:hover { background: var(--accent-l); }
.ptable-foot {
  font: 700 9.5px var(--fs); letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3); padding-top: 6px;
}

/* The change sits in its own column, sized by the table to its widest entry,
   so every value in a column ends on the same edge. `width: 1px` on a nowrap
   cell is the standard way to say "as narrow as your content". */
.ptable td.cell-v { padding-right: 3px; }
.ptable th:not(.ptable-pad):not(:first-child) { padding-right: 3px; }
/* Fixed, not shrink-to-fit. A value column sized to its own content put the
   column after it at a different x in every list on the page — 400px in one,
   420px in the next — and columns that do not line up cannot be read down a
   page of stacked tables. The name column takes the slack instead, which is
   what a text column is for. */
/* Wide enough for the longest header rather than for the longest number:
   "Transactions" needs 96px, and a header that does not fit is painted over by
   the next cell's background — it read as "TRANSACTIOI". Wrapping was no help,
   a single word cannot break without being mangled. */
.ptable td.cell-v, .ptable th:not(.ptable-pad):not(:first-child) { width: 100px; }
.ptable td.cell-d, .ptable th.ptable-pad { width: 72px; }
/* Under fixed layout a long value would spill into the next column rather than
   widen its own, so it has to be clipped like the name is. */
.ptable td.cell-v, .ptable td.cell-d { overflow: hidden; text-overflow: ellipsis; }
.ptable td.cell-d {
  text-align: left; padding-left: 3px; padding-right: 13px;
}
.ptable th.ptable-pad {
  padding-left: 3px; padding-right: 13px; cursor: default;
}
.ptable th.ptable-pad::after { content: none; }
.ptable td.cell-d .cell-delta { margin-left: 0; }
.ptable td:last-child, .ptable th:last-child { padding-right: 7px; }

/* ── Property drawer ─────────────────────────────────────────────── */

/* The detail docks to the LEFT, and deliberately does not cover the page.
   Every route into it is a list of properties sitting on the right — so
   opening one over the top of that list hid the very thing you navigate by,
   and reading two properties in a row meant open, read, close, hunt, open.
   Docked left, the list stays put and stays clickable: click the next name
   and the panel simply swaps. That is why there is no scrim and why the body
   is not scroll-locked; the × and Escape still close it. */
.drawer-wrap { position: fixed; inset: 0; z-index: 90; pointer-events: none; }
.drawer-scrim { display: none; }
.drawer {
  position: absolute; top: 0; left: 0; bottom: 0;
  width: min(620px, 52vw); background: var(--card);
  border-right: 1px solid var(--line);
  box-shadow: 24px 0 60px -20px rgba(20,20,50,.35);
  display: flex; flex-direction: column;
  pointer-events: auto;
  animation: slide-in .26s cubic-bezier(.22,.7,.3,1);
}
/* Below that the page has no room for two columns, so it behaves as a sheet. */
@media (max-width: 900px) {
  .drawer-wrap { pointer-events: auto; }
  .drawer-scrim { display: block; position: absolute; inset: 0;
    background: rgba(20,20,50,.34); animation: fade .18s ease; }
  .drawer { width: 94vw; }
}
@keyframes fade { from { opacity: 0; } }
@keyframes slide-in { from { transform: translateX(-28px); opacity: .4; } }

.drawer-head {
  display: flex; align-items: flex-start; gap: var(--s3);
  padding: var(--s5) var(--s5) var(--s4);
  border-bottom: 1px solid var(--line);
}
.drawer-eyebrow {
  display: block;
  font: 700 10.5px var(--fs); letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3);
}
.drawer-name { font-size: 21px; margin-top: 3px; }
.drawer-span { display: block; font-size: 12.5px; color: var(--ink-3); margin-top: 4px; }
.drawer-x {
  margin-left: auto; flex: none;
  width: 32px; height: 32px; border-radius: 50%;
  border: 1px solid var(--line); background: var(--card);
  font-size: 18px; line-height: 1; color: var(--ink-2); cursor: pointer;
}
.drawer-x:hover { border-color: var(--red); color: var(--red); background: var(--red-l); }

.drawer-body { flex: 1; overflow-y: auto; padding: var(--s4) var(--s5) var(--s6); }
.drawer-loading { color: var(--ink-3); font-size: 13.5px; padding: var(--s5) 0; }

.drawer-section-label {
  font: 700 10.5px var(--fs); letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3); margin: var(--s5) 0 var(--s2);
}
.drawer-section-label:first-child { margin-top: 0; }

.drawer-facts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  gap: 2px var(--s4); margin: 0;
}
.drawer-facts dt {
  font: 700 9.5px var(--fs); letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3); padding-top: 7px;
}
.drawer-facts dd {
  margin: 0; font-size: 14px; font-weight: 700;
  font-variant-numeric: tabular-nums; padding-bottom: 6px;
  border-bottom: 1px solid var(--line-2);
}

.drawer-kpis {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: var(--s2);
}
.drawer-kpi {
  border: 1px solid var(--line); border-radius: var(--r1); padding: 10px 12px;
  background: var(--bg);
}
.drawer-kpi-label {
  display: block; font: 700 9.5px var(--fs); letter-spacing: .05em;
  text-transform: uppercase; color: var(--ink-3);
}
.drawer-kpi-value {
  display: block; font-size: 19px; font-weight: 800; letter-spacing: -.02em;
  margin-top: 2px; font-variant-numeric: tabular-nums;
}

.drawer-chart {
  border: 1px solid var(--line); border-radius: var(--r2);
  padding: var(--s3) var(--s3) var(--s2); margin-bottom: var(--s2);
}
.drawer-chart-title { font-size: 12.5px; font-weight: 700; color: var(--ink-2); }
.drawer-canvas { height: 148px; margin-top: 6px; }

.drawer-split { display: flex; flex-direction: column; gap: 4px; }
.drawer-split-row {
  display: grid; grid-template-columns: 104px 1fr 84px; align-items: center; gap: var(--s3);
  font-size: 13px;
}
.drawer-split-name { font-weight: 600; }
.drawer-split-track { height: 8px; border-radius: 100px; background: rgba(120,120,140,.14); overflow: hidden; }
.drawer-split-track i { display: block; height: 100%; border-radius: 100px; background: var(--accent); }
.drawer-split-value { text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; }

.drawer-mods { display: flex; flex-wrap: wrap; gap: 6px; }
.drawer-mod {
  font: 600 12px var(--fs); border-radius: 100px; padding: 4px 11px;
  background: rgba(120,120,140,.1); color: var(--ink-3);
  text-decoration: line-through; text-decoration-thickness: 1px;
}
.drawer-mod.on { background: var(--green-l); color: var(--green); text-decoration: none; }

/* Anything that opens the drawer says so. */
.panel-canvas.clickable { cursor: pointer; }
table.dt tbody tr.drill { cursor: pointer; }
table.dt tbody tr.drill td:first-child::after {
  content: '›'; color: var(--ink-3); margin-left: 7px; font-weight: 700;
}
table.dt tbody tr.drill:hover td:first-child::after { color: var(--accent-d); }

@media (max-width: 640px) {
  .drawer { width: 100vw; }
  .drawer-split-row { grid-template-columns: 88px 1fr 70px; }
}

/* ── Toast ───────────────────────────────────────────────────────── */

.toast {
  position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%);
  z-index: 85; background: var(--ink); color: #fff;
  padding: 10px 18px; border-radius: 100px; font-size: 13.5px; font-weight: 600;
  box-shadow: var(--sh-up);
}

/* ── Klein ───────────────────────────────────────────────────────── */

@media (max-width: 1200px) {
  .panel.span-3 { grid-column: span 6; }
  .panel.span-4 { grid-column: span 6; }
  .panel.span-8 { grid-column: span 12; }
}

@media (max-width: 900px) {
  .panel.span-3, .panel.span-4, .panel.span-6,
  .panel.span-8, .panel.span-12 { grid-column: span 12; }
  .brand-sub { display: none; }
  .qbar-actions { margin-left: 0; }
  .popover { left: var(--s4); right: var(--s4); width: auto; }
  .panel, .tablewrap { padding: var(--s4); border-radius: var(--r3); }
  .panel { --panel-pad: var(--s4); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* ══════════════════════════════════════════════════════════════════════
   Customer report
   ══════════════════════════════════════════════════════════════════════ */

/* ── Start page ──────────────────────────────────────────────────────
   Deliberately almost empty. The eye should land on the field and nowhere
   else, so there is no card, no border and no second column above the fold.
   What is below it earns its place by being a way into a report. */
/* The page is as wide as the four lists need; the field keeps its own, much
   narrower measure. A 1,040px search box would look like a mistake. */
/* The hero holds the middle of the screen and the lists sit under it. Before
   this the whole page was one column with a fixed top padding: the search box
   floated near the top, the lists clung to it, and the rest of a tall screen
   was empty. Now the hero centres itself in whatever height is left and the
   lists are a deliberate second step, not leftovers. */
.rep-start {
  max-width: 1080px; margin: 0 auto; padding: 0 var(--s4) var(--s6);
  display: flex; flex-direction: column;
  min-height: calc(100vh - 170px);
}
.rep-hero {
  flex: 1 0 auto;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 7vh 0 6vh;
}
.rep-start-box, .rep-hero > .rep-hits { width: 100%; max-width: 640px; }
/* The product, as an app icon beside its name rather than stacked above it.
   Stacked, the mark and the two lines of heading cost about 140px of the fold
   on a page whose only job is to get you to the search field. */
.rep-start-lockup {
  display: flex; align-items: center; gap: 14px;
  margin-bottom: var(--s3);
}
.rep-start-tile {
  width: 48px; height: 48px; flex: none;
  border-radius: 13px; display: grid; place-items: center;
  background: linear-gradient(150deg, var(--accent), color-mix(in oklab, var(--accent) 72%, #000));
  box-shadow: 0 9px 22px -11px color-mix(in oklab, var(--accent) 70%, transparent),
              inset 0 1px 0 rgba(255,255,255,.3);
}
.rep-start-tile svg { width: 25px; height: 25px; fill: #fff; color: #fff; }
/* color-mix is recent; a flat accent is a perfectly good fallback. */
@supports not (background: color-mix(in oklab, red 50%, blue)) {
  .rep-start-tile { background: var(--accent); box-shadow: 0 9px 22px -11px rgba(25,84,211,.6); }
}
.rep-start-words { display: flex; flex-direction: column; gap: 1px; text-align: left; }
.rep-start-product {
  font: 700 10.5px var(--fs); letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent);
}
.rep-start-word {
  font-size: 29px; font-weight: 800; letter-spacing: -.035em; line-height: 1.1;
  color: var(--ink); margin: 0;
}
.rep-start-sub {
  margin: 9px 0 var(--s5); font-size: 13.5px; color: var(--ink-3); text-align: center;
}

/* The field is the page. Everything else defers to it. */
/* The field is the page. It gets a soft halo that lifts on focus rather than
   a hard ring, and the glass turns into the accent colour so the change is
   legible without any layout moving. The hint on the right says what happens
   on Enter, which is the one thing a bare search box never tells you. */
.rep-start-box { position: relative; }
.rep-start-box::before {
  content: ''; position: absolute; inset: -14px; z-index: -1; border-radius: 100px;
  background: radial-gradient(60% 120% at 50% 50%, var(--accent-l), transparent 72%);
  opacity: 0; transition: opacity .28s ease;
}
.rep-start-box:focus-within::before { opacity: .85; }
.rep-start-icon {
  position: absolute; left: 22px; top: 50%; transform: translateY(-50%);
  width: 19px; height: 19px; pointer-events: none;
  fill: none; stroke: var(--ink-3); stroke-width: 1.8;
  stroke-linecap: round; transition: stroke .18s ease, transform .18s ease;
}
.rep-start-box:focus-within .rep-start-icon {
  stroke: var(--accent); transform: translateY(-50%) scale(1.06);
}
.rep-start-input {
  width: 100%; box-sizing: border-box;
  font: 400 17px var(--fs); color: var(--ink); letter-spacing: -.01em;
  padding: 18px 78px 18px 56px; border-radius: 100px;
  border: 1px solid var(--line); background: var(--card);
  box-shadow: 0 1px 2px rgba(20, 30, 60, .04), 0 8px 24px -18px rgba(20, 30, 60, .5);
  transition: box-shadow .2s ease, border-color .2s ease;
  -webkit-appearance: none; appearance: none;
}
.rep-start-input::placeholder { color: var(--ink-3); }
.rep-start-input:hover {
  border-color: var(--line-2);
  box-shadow: 0 2px 6px rgba(20, 30, 60, .06), 0 14px 30px -20px rgba(20, 30, 60, .55);
}
.rep-start-input:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-l), 0 14px 34px -20px rgba(20, 30, 60, .55);
}
.rep-start-input::-webkit-search-cancel-button { cursor: pointer; }

/* Fades out once there is something to search for — by then it has been read. */
.rep-start-hint {
  position: absolute; right: 20px; top: 50%; transform: translateY(-50%);
  display: flex; align-items: center; gap: 5px; pointer-events: none;
  font: 600 10.5px var(--fs); color: var(--ink-3);
  transition: opacity .18s ease;
}
.rep-start-box.has-text .rep-start-hint { opacity: 0; }
.rep-start-hint kbd {
  font: 700 10px var(--fs); border: 1px solid var(--line); border-bottom-width: 2px;
  border-radius: 5px; padding: 2px 6px; background: var(--bg); color: var(--ink-2);
}
.rep-start-note { margin-top: var(--s5); font-size: 13px; color: var(--red); text-align: center; }

/* What you get out of this. Not a slogan — three things the document does that
   a hotel cannot do for itself. Quiet enough to sit above a search field. */
.rep-pitch {
  display: flex; justify-content: center; flex-wrap: wrap;
  gap: var(--s3) var(--s5); margin: var(--s3) 0 var(--s6);
}
.rep-pitch-item { display: inline-flex; gap: 7px; align-items: center; }
.rep-pitch-item .mod-icon { width: 16px; height: 16px; stroke: var(--accent); }
.rep-pitch-text { font-size: 12px; font-weight: 600; color: var(--ink-2); white-space: nowrap; }

/* ── Top performers ──────────────────────────────────────────────────
   The only content on the page, and it is deliberately quiet: small grey
   headings, four columns, no card around them. */
.rep-leaders { width: 100%; flex: none; }
/* Four across on a desktop, and they wrap on their own below that. Not
   auto-fit: with four short lists auto-fit produces a 2x2 block whose second
   row is half empty, which reads as a layout accident. */
.rep-leader-grid {
  display: grid; grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr));
  gap: var(--s5) var(--s5);
}
@media (max-width: 900px) { .rep-leader-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* A third-width panel has less to give; the numbers stay narrower there so the
   name is still readable. */
.panel.span-4 .ptable td.cell-v,
.panel.span-4 .ptable th:not(.ptable-pad):not(:first-child) { width: 66px; }
.panel.span-4 .ptable td.cell-d,
.panel.span-4 .ptable th.ptable-pad { width: 58px; }
@media (max-width: 520px) { .rep-leader-grid { grid-template-columns: 1fr; } }
/* ── Who is doing well ────────────────────────────────────────────────────
   Three lists that have to be readable at a glance and must not turn into a
   dashboard. Each row is one object: rank, the property's own app icon, the
   name, the number. The share of the leader is a wash behind all of it rather
   than a fourth thing competing for the width — a stub of a bar between a
   truncated name and a figure was the version that did not work.

   Numbers are tabular and share a fixed column, so the three lists line up
   with each other and not only within themselves. */
.lead { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rep-leader-grid { align-items: stretch; }
.lead-head {
  display: flex; align-items: center; gap: 6px;
  font: 700 9.5px var(--fs); letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-3); margin: 0 0 7px; padding-left: 9px;
  /* Fixed, because "Welcome mails opened" wraps where "Most visitors" does
     not, and a heading one line taller pushed its whole column out of step
     with the three beside it. */
  height: 30px;
}
.lead-head .mod-icon { width: 14px; height: 14px; stroke: var(--accent); flex: none; }
.lead-empty { font-size: 11.5px; color: var(--ink-3); font-style: italic; padding-left: 9px; }

.lead-row {
  position: relative; isolation: isolate;
  display: grid; grid-template-columns: 14px 22px 1fr auto;
  align-items: center; gap: 9px;
  width: 100%; padding: 9px 11px 9px 9px;
  border: 0; background: transparent; border-radius: 9px;
  font: inherit; text-align: left; cursor: pointer;
  /* Every row the height of a two-line name, whether it needs two lines or
     not. Without it the four columns drift out of step the moment one hotel
     has a long name, and rank 4 in one column sits beside rank 3 in the next —
     which is exactly the misalignment that makes a set of lists hard to read
     across rather than only down. */
  min-height: 50px;
}
.lead-fill {
  position: absolute; left: 0; top: 0; bottom: 0; z-index: -1;
  background: var(--accent-l); border-radius: 9px; opacity: .7;
  transition: opacity .15s ease;
}
/* A row is a link to a report, so it has to say so under the pointer. The
   share-of-leader wash alone was too quiet to read as "clickable". */
.lead-row { transition: background .15s ease, transform .15s ease; }
.lead-row:hover { background: var(--card); transform: translateX(2px); }
.lead-row:hover .lead-fill { opacity: 1; }
.lead-row:hover .lead-name { color: var(--accent-d); }
.lead-row:active { transform: translateX(2px) scale(.995); }
.lead-row:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.lead-rank {
  font: 800 11px var(--fs); color: var(--accent-d); opacity: .5;
  font-variant-numeric: tabular-nums; text-align: center;
}
.lead-row .rep-appicon {
  width: 22px; height: 22px; border-radius: 6px; border-color: transparent;
}
.lead-row .rep-appicon.is-initials { font: 800 8.5px var(--fs); background: var(--card); }
.lead-name {
  font: 600 12px/1.32 var(--fs); color: var(--ink); min-width: 0;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.lead-who { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.lead-reach {
  font: 500 10.5px var(--fs); color: var(--ink-3); font-variant-numeric: tabular-nums;
}
.lead-num { display: flex; flex-direction: column; align-items: flex-end; gap: 1px; }
.lead-val {
  font: 800 12.5px var(--fs); color: var(--ink); font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.lead-delta {
  font: 700 10.5px var(--fs); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.lead-delta.is-up   { color: var(--green, #17915c); }
.lead-delta.is-down { color: var(--red, #b3261e); }

/* Hits sit under the field like a suggestion list, not in a panel. */
.rep-hits { width: 100%; margin-top: var(--s3); }
.rep-hits-note { padding: var(--s4) 22px; font-size: 13px; color: var(--ink-3); }
.rep-hit-list { display: flex; flex-direction: column; }
.rep-hit {
  display: flex; align-items: baseline; gap: var(--s4);
  width: 100%; text-align: left; cursor: pointer;
  padding: 11px 22px; border: 0; border-radius: var(--r1);
  background: transparent; font: inherit; color: var(--ink);
}
.rep-hit:hover, .rep-hit:focus-visible { background: var(--accent-l); outline: none; }
.rep-hit-name { font-weight: 600; font-size: 15px; }
.rep-hit-meta {
  margin-left: auto; display: flex; gap: var(--s4);
  font-size: 11.5px; color: var(--ink-3); font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ── The document ────────────────────────────────────────────────────
   Narrower than the dashboard on purpose: this is prose with figures in it,
   and a 1400px line of text cannot be read. */
.report { max-width: 940px; margin: 0 auto; }
.rep-loading { padding: 12vh 0; text-align: center; color: var(--ink-3); font-size: 14px; }

.rep-head {
  display: flex; align-items: flex-start; gap: var(--s5);
  flex-wrap: wrap; padding-bottom: var(--s4);
}
.rep-head-main { flex: 1 1 320px; min-width: 0; }

/* The property's own app icon beside its name. Guessed from the name, so it
   has to degrade to initials without ever showing a broken image. */
.rep-lockup { display: flex; align-items: center; gap: var(--s3); }
.rep-appicon {
  width: 52px; height: 52px; flex: none;
  border-radius: 13px; overflow: hidden;
  border: 1px solid var(--line); background: var(--card);
  display: grid; place-items: center;
}
.rep-appicon img { width: 100%; height: 100%; object-fit: cover; display: block; }
.rep-appicon.is-initials {
  background: var(--accent-l); border-color: transparent;
  color: var(--accent-d); font: 800 17px var(--fs); letter-spacing: -.02em;
}
.rep-eyebrow {
  font: 700 10px var(--fs); letter-spacing: .09em; text-transform: uppercase;
  color: var(--accent); margin-bottom: 6px;
}
.rep-title { font-size: 32px; font-weight: 800; letter-spacing: -.03em; margin: 0; }
.rep-facts {
  display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4);
  margin-top: var(--s3); font-size: 12px; color: var(--ink-3);
}
.rep-fact { display: inline-flex; gap: 5px; align-items: baseline; }
.rep-fact b { color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; }

.rep-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.rep-btn {
  font: 700 12.5px var(--fs); padding: 8px 15px; border-radius: 100px;
  border: 1px solid var(--line); background: var(--card); color: var(--ink-2); cursor: pointer;
}
.rep-btn:hover { border-color: var(--accent); color: var(--accent-d); }
.rep-btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.rep-btn-primary:hover { background: var(--accent-d); border-color: var(--accent-d); color: #fff; }

/* A chart whose ranking list has stepped aside takes the whole row. */
.panel.span-solo { grid-column: span 12; }
/* Sits with the title it qualifies, on the left. Pushed to the right it read
   as a separate control floating in the middle of the header, unrelated to the
   heading that had just changed meaning. */
.mod-head .focus-clear { margin: 0 auto 0 var(--s3); align-self: center; }
.focus-clear {
  margin: 0 auto 0 var(--s3); font: 700 11px var(--fs); cursor: pointer;
  border: 1px solid var(--line); background: var(--card); color: var(--accent-d);
  border-radius: 100px; padding: 4px 11px;
}
.focus-clear:hover { border-color: var(--accent); background: var(--accent-l); }

/* ── Billing ─────────────────────────────────────────────────────────
   Between the name and the numbers, because it reframes everything below it. */
.rep-billing {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r2);
  padding: var(--s4) var(--s5); margin-bottom: var(--s4);
}
.rep-billing-figs { display: flex; flex-wrap: wrap; gap: var(--s5) var(--s6); align-items: baseline; }
.rep-billing-fig { display: flex; flex-direction: column; gap: 3px; }
.rep-billing-label {
  font: 700 9px var(--fs); letter-spacing: .07em; text-transform: uppercase; color: var(--ink-3);
}
.rep-billing-value {
  font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ink);
}
.rep-billing-fig.is-strong .rep-billing-value {
  font-size: 24px; font-weight: 800; letter-spacing: -.02em; color: var(--accent-d);
}
.rep-billing-addons {
  display: flex; flex-wrap: wrap; gap: 5px; align-items: center;
  margin-top: var(--s4); padding-top: var(--s3); border-top: 1px solid var(--line-2);
}
.rep-billing-addons .rep-billing-label { margin-right: 4px; }
.rep-addon {
  font: 600 10.5px var(--fs); color: var(--ink-2);
  background: var(--bg); border: 1px solid var(--line); border-radius: 100px; padding: 3px 9px;
}
.rep-billing-note {
  margin: var(--s3) 0 0; font-size: 11.5px; line-height: 1.5; color: var(--amber);
}

/* The report's own headline: what the biggest gap is worth against the bill. */
.rep-finding.kind-headline {
  border-left-color: var(--accent); background: var(--accent-l);
  border-radius: 0 var(--r1) var(--r1) 0; padding: var(--s3) var(--s4);
}
.rep-finding.kind-headline .rep-finding-kind { color: var(--accent-d); }
.rep-finding.kind-headline .rep-finding-title { font-size: 16px; }

.rep-period {
  margin: 0 0 var(--s5); font-size: 12px; color: var(--ink-3);
  padding-bottom: var(--s4); border-bottom: 1px solid var(--line);
}

.rep-block {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r3);
  padding: var(--s5); margin-bottom: var(--s5); box-shadow: var(--shadow);
}
.rep-block-head { margin-bottom: var(--s5); }
.rep-block-title { font-size: 19px; font-weight: 700; margin: 0; color: var(--tone, var(--ink)); }
.rep-block-lead { margin: 6px 0 0; font-size: 13px; line-height: 1.55; color: var(--ink-2); max-width: 72ch; }

/* ── Benchmark bars ──────────────────────────────────────────────────
   Percentile space, not value space: with a long tail of zeros a linear value
   axis pushes every real difference into the left edge. */
.rep-bars { display: flex; flex-direction: column; gap: var(--s5); }
.rep-bar { display: flex; flex-direction: column; gap: 7px; }
.rep-bar-head { display: flex; align-items: baseline; gap: var(--s3); }
.rep-bar-label { font-size: 14px; font-weight: 700; }
.rep-bar-value {
  margin-left: auto; font-size: 19px; font-weight: 800;
  font-variant-numeric: tabular-nums; letter-spacing: -.02em;
}
.rep-track {
  position: relative; height: 12px; border-radius: 100px;
  background: rgba(120, 120, 140, .1);
}
.rep-track-band {
  position: absolute; top: 0; bottom: 0;
  background: rgba(120, 120, 140, .18); border-radius: 3px;
}
.rep-track-mid { position: absolute; top: -2px; bottom: -2px; width: 2px; background: var(--ink-3); }
.rep-track-mark {
  position: absolute; top: 50%; width: 15px; height: 15px; border-radius: 50%;
  transform: translate(-50%, -50%);
  background: var(--accent); border: 2.5px solid var(--card);
  box-shadow: 0 0 0 1px var(--accent);
}
.rep-track-mark.good { background: var(--green); box-shadow: 0 0 0 1px var(--green); }
.rep-track-mark.weak { background: var(--red);   box-shadow: 0 0 0 1px var(--red); }
.rep-bar-scale {
  display: flex; justify-content: space-between;
  font: 600 10px var(--fs); color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.rep-bar-mid { color: var(--ink-2); }
.rep-bar-verdict { margin: 2px 0 0; font-size: 12.5px; line-height: 1.55; color: var(--ink-2); max-width: 76ch; }
.rep-bar-thin {
  font-size: 12.5px; color: var(--ink-3); font-style: italic;
  padding: 9px 12px; background: var(--bg); border-radius: var(--r1);
}
.rep-bar-why { margin: 0; font-size: 12.5px; line-height: 1.55; color: var(--ink-2); max-width: 76ch; }

/* ── Findings ────────────────────────────────────────────────────────
   A coloured left edge rather than a filled card: five filled cards in a row
   is a traffic accident. */
.rep-findings { display: flex; flex-direction: column; gap: var(--s3); }
.rep-finding {
  border-left: 3px solid var(--line-strong, #c9d0cd);
  padding: 2px 0 2px var(--s4);
}
.rep-finding.kind-opportunity { border-left-color: var(--accent); }
.rep-finding.kind-risk        { border-left-color: var(--amber); }
.rep-finding.kind-strength    { border-left-color: var(--green); }
.rep-finding-head { display: flex; align-items: baseline; gap: var(--s3); flex-wrap: wrap; }
.rep-finding-kind {
  font: 700 9px var(--fs); letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-3);
}
.rep-finding.kind-opportunity .rep-finding-kind { color: var(--accent); }
.rep-finding.kind-risk        .rep-finding-kind { color: var(--amber); }
.rep-finding.kind-strength    .rep-finding-kind { color: var(--green); }
.rep-finding-title { font-size: 15px; font-weight: 700; margin: 0; }
.rep-finding-value {
  margin-left: auto; font-size: 19px; font-weight: 800;
  color: var(--accent-d); font-variant-numeric: tabular-nums; letter-spacing: -.02em;
}
.rep-finding-detail { margin: 5px 0 0; font-size: 13px; line-height: 1.6; color: var(--ink-2); max-width: 78ch; }

/* ── Figures ─────────────────────────────────────────────────────────── */
.rep-values {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1px; background: var(--line); border: 1px solid var(--line);
  border-radius: var(--r2); overflow: hidden;
}
.rep-value { background: var(--card); padding: 13px 15px; }
.rep-value-label {
  font: 700 9.5px var(--fs); letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3); margin-bottom: 5px;
}
.rep-value-line { display: flex; align-items: baseline; gap: 7px; }
.rep-value-num {
  font-size: 21px; font-weight: 800; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}

.rep-modules th { background: var(--card); }
.rep-mod-off td { color: var(--ink-3); }
.rep-mod-tag {
  margin-left: 7px; font: 700 9px var(--fs); letter-spacing: .05em;
  text-transform: uppercase; color: var(--amber);
  background: var(--amber-l); padding: 2px 6px; border-radius: 100px;
}
.rep-mod-verdict { font-size: 11.5px; color: var(--ink-3); }

/* ── History ─────────────────────────────────────────────────────────── */
.rep-history { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: var(--s4); }
.rep-hist { border: 1px solid var(--line); border-radius: var(--r2); padding: var(--s4); }
.rep-hist-label {
  font: 700 9.5px var(--fs); letter-spacing: .05em; text-transform: uppercase; color: var(--ink-3);
}
.rep-hist-last {
  font-size: 21px; font-weight: 800; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums; margin: 3px 0 8px;
}
/* Same edge-to-edge treatment the KPI tiles use: an inset line looks dropped
   on the card, a full-width one looks like part of it. */
.rep-hist .spark { margin: 10px calc(var(--s4) * -1) calc(var(--s4) * -1); height: 54px; }
.rep-hist-thin { margin-top: 8px; font-size: 11.5px; color: var(--ink-3); font-style: italic; }

/* ── Notes ───────────────────────────────────────────────────────────── */
.rep-note {
  margin-top: var(--s5); padding: var(--s4);
  background: var(--accent-l); border-radius: var(--r2);
}
.rep-block > .rep-note:first-child, .report > .rep-note { margin-top: 0; }
.rep-note-head { display: flex; align-items: baseline; gap: var(--s3); margin-bottom: 6px; }
.rep-note-title {
  font: 700 9.5px var(--fs); letter-spacing: .06em; text-transform: uppercase; color: var(--accent-d);
}
.rep-note-stamp { margin-left: auto; font-size: 10.5px; color: var(--ink-3); }
.rep-note-lead { margin: 0 0 8px; font-size: 12.5px; line-height: 1.55; color: var(--ink-2); }
.rep-note-input {
  width: 100%; box-sizing: border-box; resize: vertical;
  font: 400 13.5px/1.6 var(--fs); color: var(--ink);
  padding: 11px 13px; border-radius: var(--r1);
  border: 1px solid var(--line); background: var(--card);
}
.rep-note-input:focus { outline: none; border-color: var(--accent); }
.rep-note-text { margin: 0; font-size: 13.5px; line-height: 1.6; white-space: pre-wrap; }

/* Working notes for whoever is preparing the report. Never printed, and
   stripped out of the downloaded file — the cohort sizes have to stay available
   for judging whether a comparison is sound without reaching the customer. */
.rep-internal {
  margin: 5px 0 0; display: flex; align-items: baseline; gap: 6px;
  font-size: 10.5px; color: var(--ink-3); font-variant-numeric: tabular-nums;
}
.rep-internal-tag {
  font: 700 8px var(--fs); letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-3); background: var(--bg);
  border: 1px solid var(--line); border-radius: 100px; padding: 1px 6px; flex: none;
}

.rep-foot {
  font-size: 11.5px; line-height: 1.6; color: var(--ink-3);
  border-top: 1px solid var(--line); padding-top: var(--s4); max-width: 78ch;
}

/* The report owns the page: no filter bar to leave in a state the customer
   would never see explained. */
body.is-report .qbar { display: none; }

/* ── Print, and the standalone export ────────────────────────────────
   The share path is "Save as PDF", so this is not an afterthought: it is the
   form most customers will actually receive. */
@media print {
  @page { margin: 14mm; }
  header.top, .tabsrow, .qbar, .rep-actions, .palette, .drawer-wrap, .toast,
  #kpis, #panels, .tablewrap, #lead, .rep-note-stamp,
  .rep-internal { display: none !important; }
  body { background: #fff; }
  .report { max-width: none; margin: 0; }
  .rep-block {
    box-shadow: none; border: 1px solid #dfe3ea;
    break-inside: avoid; page-break-inside: avoid;
  }
  .rep-finding, .rep-bar, .rep-hist { break-inside: avoid; page-break-inside: avoid; }
  .rep-title { font-size: 26px; }
  /* A textarea prints its box and clips its text. The export swaps these for
     paragraphs; for print the box has to go but the words must stay. */
  .rep-note-input {
    border: 0; background: transparent; padding: 0; resize: none;
    height: auto; overflow: visible;
  }
}

/* The downloaded file has no app shell around it. */
.rep-standalone {
  background: var(--bg); padding: var(--s6) var(--s5);
  max-width: 940px; margin: 0 auto;
}


/* ══════════════════════════════════════════════════════════════════════
   Experience catalogue
   ══════════════════════════════════════════════════════════════════════ */

/* Every other panel shape declares its own height exception; the catalogue was
   missing one, so it was clipped to the default 300px canvas and the block
   below it drew straight over the cards. */
.panel .panel-canvas:has(.act-rail),
.panel .panel-canvas:has(.act-summary),
.panel.span-12 .panel-canvas:has(.act-rail) { height: auto; min-height: 0; }

/* The state of the calendar in one line, before any single entry. */
.act-summary {
  display: flex; flex-wrap: wrap; gap: var(--s3) var(--s5);
  padding-bottom: var(--s4); margin-bottom: var(--s4);
  border-bottom: 1px solid var(--line);
}
.act-sum { display: flex; align-items: baseline; gap: 6px; }
.act-sum b {
  font-size: 19px; font-weight: 800; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums; color: var(--ink);
}
.act-sum span { font-size: 11.5px; color: var(--ink-3); }
.act-sum.is-good b { color: var(--green); }
.act-sum.is-warn b { color: var(--amber); }
.act-sum.is-bad  b { color: var(--red); }

/* Sort and one filter, on one line. Anything more would be a second dashboard
   inside the first. */
.act-controls {
  display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
  margin-bottom: var(--s4);
}
.act-sorts {
  display: inline-flex; background: var(--bg);
  border: 1px solid var(--line); border-radius: 100px; padding: 2px;
}
.act-sort {
  font: 700 11.5px var(--fs); padding: 5px 12px; border-radius: 100px;
  border: 0; background: transparent; color: var(--ink-3); cursor: pointer;
  white-space: nowrap;
}
.act-sort:hover { color: var(--ink); }
.act-sort.is-on { background: var(--card); color: var(--accent-d); box-shadow: var(--shadow); }
.act-only {
  font: 700 11.5px var(--fs); padding: 6px 13px; border-radius: 100px; cursor: pointer;
  border: 1px solid var(--line); background: var(--card); color: var(--ink-2); white-space: nowrap;
}
.act-only:hover { border-color: var(--amber); color: var(--amber); }
.act-only.is-on { background: var(--amber-l); border-color: transparent; color: var(--amber); }
.act-count { margin-left: auto; font-size: 11px; color: var(--ink-3); font-variant-numeric: tabular-nums; }

/* The stand-in warning is a band, not a footnote. Someone will screenshot this
   panel, and the screenshot has to carry the caveat with it. */
.act-demo {
  background: var(--amber-l); color: var(--amber);
  border-radius: var(--r2); padding: 10px 14px; margin-bottom: var(--s4);
  font-size: 12px; line-height: 1.55;
}
.act-demo b { font-weight: 700; }
/* Provenance rather than a warning: nothing here is invented any more. */
.act-demo.is-live { background: var(--bg); color: var(--ink-2); }
.act-demo.is-live b { color: var(--ink); }

/* A rail rather than a grid: on a page about five upselling modules, the
   catalogue is supporting detail and must not push everything else off screen. */
.act-rail {
  display: flex; gap: var(--s4); overflow-x: auto; padding-bottom: var(--s3);
  scroll-snap-type: x proximity; scrollbar-width: thin;
}
.act-rail::-webkit-scrollbar { height: 8px; }
.act-rail::-webkit-scrollbar-thumb { background: var(--line-strong, #c9d0cd); border-radius: 100px; }

.act {
  flex: 0 0 216px; scroll-snap-align: start;
  border: 1px solid var(--line); border-radius: var(--r2); overflow: hidden;
  background: var(--card); display: flex; flex-direction: column;
  padding: 0; text-align: left; font: inherit; color: inherit; cursor: pointer;
}
.act:hover { border-color: var(--accent); }
.act.is-open { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-l); }

/* One fixed height for every picture. The source images vary in shape, and
   aspect-ratio on the box still let a portrait one settle differently — a rail
   of cards whose images start at different heights reads as broken. */
.act-shot { position: relative; height: 124px; flex: none; background: var(--bg); overflow: hidden; }
.act-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.act-shot.is-blank {
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--accent) 22%, transparent),
    color-mix(in srgb, var(--accent) 6%, transparent));
}
.act-flag, .act-price {
  position: absolute; top: 7px; font: 700 9px var(--fs);
  letter-spacing: .04em; text-transform: uppercase;
  padding: 3px 7px; border-radius: 100px;
  background: rgba(255, 255, 255, .93); color: var(--ink-2);
}
.act-flag { left: 7px; color: var(--accent-d); }
.act-price { right: 7px; }
.act-price.is-paid { background: var(--accent); color: #fff; }
.act-flag-out { left: auto; right: 7px; top: 34px; background: var(--red); color: #fff; }

.act-body { padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 7px; flex: 1; }
.act-owner {
  font: 700 9px var(--fs); letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.act-detail-owner {
  margin-left: 9px; font: 600 11.5px var(--fs); color: var(--ink-3);
  text-transform: none; letter-spacing: 0;
}
.act-title {
  margin: 0; font-size: 13px; font-weight: 700; line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  min-height: 2.7em;   /* two lines, so the strips below line up across the rail */
}
.act-meta {
  display: flex; flex-wrap: wrap; gap: 3px 7px; align-items: center;
  font-size: 10px; color: var(--ink-3);
}
.act-tag {
  font-weight: 700; color: var(--accent-d);
  background: var(--accent-l); border-radius: 100px; padding: 2px 6px;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.act-flow {
  margin-top: auto; display: flex; align-items: stretch;
  border: 1px solid var(--line); border-radius: var(--r1); overflow: hidden;
}
.act-cell {
  flex: 1 1 0; min-width: 0; padding: 6px 4px; text-align: center;
  display: flex; flex-direction: column; gap: 1px; align-items: center;
}
.act-cell.is-outcome { background: var(--accent-l); }
.act-cell-value {
  font-size: 13px; font-weight: 800; font-variant-numeric: tabular-nums;
  letter-spacing: -.02em;
}
.act-cell.is-outcome .act-cell-value { color: var(--accent-d); }
.act-cell .cell-delta { font-size: 9px; margin: 0; }
.act-cell-label {
  font: 700 7.5px var(--fs); letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3); white-space: nowrap;
}
/* Holds the same pill the module cards use between their stages. */
.act-step {
  align-self: center; display: flex; align-items: center;
  padding: 0 2px; position: relative;
}
.act-step .mod-link-pill { font-size: 9px; padding: 2px 6px; }

.act-load { display: flex; align-items: center; gap: 7px; }
.act-load-track {
  flex: 1; height: 6px; border-radius: 100px;
  background: rgba(120,120,140,.15); overflow: hidden;
}
.act-load-track i { display: block; height: 100%; border-radius: 100px; background: var(--accent); }
.act-load-track i.is-full { background: var(--green); }
.act-load-track i.is-low  { background: var(--amber); }
.act-load-text {
  font: 700 9.5px var(--fs); color: var(--ink-3);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

.act-verdict {
  align-self: flex-start; font: 700 9px var(--fs);
  letter-spacing: .05em; text-transform: uppercase;
  border-radius: 100px; padding: 3px 8px;
  background: var(--bg); color: var(--ink-3); border: 1px solid var(--line);
}
.act-verdict.is-add     { background: var(--green-l); color: var(--green); border-color: transparent; }
.act-verdict.is-convert,
.act-verdict.is-promote { background: var(--amber-l); color: var(--amber); border-color: transparent; }
.act-verdict.is-trim    { background: var(--red-l); color: var(--red); border-color: transparent; }
.act-verdict.is-check   { background: var(--red-l); color: var(--red); border-color: transparent; }
/* Not a judgement: this entry has no booking funnel to judge. */
.act-verdict.is-info    { background: var(--bg); color: var(--ink-3); }
.act-cell.is-muted .act-cell-value { font-size: 11px; font-weight: 700; color: var(--ink-3); }

/* ── One entry, opened up ─────────────────────────────────────────── */
.act-detail:empty { display: none; }
.act-detail {
  margin-top: var(--s4); padding: var(--s4);
  border: 1px solid var(--accent); border-radius: var(--r2); background: var(--card);
}
.act-detail-head { display: flex; align-items: center; gap: var(--s3); margin-bottom: 4px; }
.act-detail-head h4 { margin: 0; font-size: 15px; font-weight: 700; }
.act-detail-x {
  margin-left: auto; border: 0; background: transparent; cursor: pointer;
  font-size: 18px; line-height: 1; color: var(--ink-3); padding: 0 4px;
}
.act-detail-x:hover { color: var(--ink); }
.act-detail-why { margin: 0 0 var(--s4); font-size: 12.5px; line-height: 1.55; color: var(--ink-2); }

/* Weekday pattern: usually the finding. */
/* Left-aligned and capped. Stretched to fill, an activity that only runs on two
   weekdays produced two bars half the panel wide, which reads as a chart about
   something else entirely. */
.act-week { display: flex; gap: 6px; align-items: flex-end; margin-bottom: var(--s4); }
.act-day { flex: 0 1 58px; display: flex; flex-direction: column; align-items: center; gap: 3px; }
.act-day-name {
  font: 700 8.5px var(--fs); letter-spacing: .05em; text-transform: uppercase; color: var(--ink-3);
}
.act-day-bar {
  width: 100%; height: 46px; display: flex; align-items: flex-end;
  background: rgba(120,120,140,.1); border-radius: var(--r1); overflow: hidden;
}
.act-day-bar i { display: block; width: 100%; background: var(--accent); border-radius: var(--r1); }
.act-day.is-best .act-day-bar i { background: var(--green); }
.act-day-val { font: 700 10px var(--fs); font-variant-numeric: tabular-nums; color: var(--ink-2); }
/* Present but empty: the day is part of the week whether or not anything runs. */
.act-day.is-idle .act-day-name,
.act-day.is-idle .act-day-val { color: var(--ink-3); opacity: .55; }
.act-day.is-idle .act-day-bar {
  background: repeating-linear-gradient(135deg,
    rgba(120,120,140,.07) 0 4px, transparent 4px 8px);
}
.act-week-note { margin: -6px 0 var(--s4); font-size: 11px; color: var(--ink-3); }

.act-dates tbody tr.is-best { background: var(--green-l); }
.act-dates tbody tr.is-best td:first-child { font-weight: 800; color: var(--green); }
/* The date column carries "2026-08-26 · 10:00" and the numbers here are small
   and carry no change column, so the widths inherited from the property lists
   are wrong in both directions. */
/* The :not(.ptable-pad):not(:first-child) selector above outweighs a plainer
   one, and under fixed layout the header row sets the column width — so the
   override has to match its specificity or the cells shrink while the headers
   keep the old width. */
.act-dates td.cell-v,
.act-dates th:not(.ptable-pad):not(:first-child) { width: 74px; }
.act-dates tr.is-far td { color: var(--ink-3); opacity: .6; }
.act-detail-note { margin: -8px 0 var(--s4); font-size: 11.5px; color: var(--ink-3); }
.act-dates td.is-over { color: var(--red); font-weight: 800; }
.act-cancelled {
  margin-left: 6px; font: 700 8.5px var(--fs); letter-spacing: .04em;
  text-transform: uppercase; color: var(--red);
}

.act-rev { font-size: 11px; font-weight: 700; color: var(--accent-d); }

/* ── Property picker ─────────────────────────────────────────────────
   A modal, not a corner popover: it is the most-used control on the page and
   there are 3,185 properties — that is a search, not a dropdown. */
.pmodal { position: fixed; inset: 0; z-index: 60; display: grid; place-items: start center; }
.pmodal-scrim { position: absolute; inset: 0; background: rgba(15, 22, 40, .34); }
.pmodal-box {
  position: relative; margin-top: 9vh; width: min(560px, calc(100vw - 32px));
  max-height: 76vh; display: flex; flex-direction: column;
  background: var(--card); border-radius: var(--r3);
  box-shadow: 0 24px 64px rgba(15, 22, 40, .28);
  overflow: hidden;
}
.pmodal-head {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s4) var(--s5) 0;
}
.pmodal-head h2 { margin: 0; font-size: 17px; font-weight: 700; }
.pmodal-x {
  margin-left: auto; border: 0; background: transparent; cursor: pointer;
  font-size: 20px; line-height: 1; color: var(--ink-3); padding: 0 4px;
}
.pmodal-x:hover { color: var(--ink); }
.pmodal-search { padding: var(--s3) var(--s5) var(--s3); }
.pmodal-search input {
  width: 100%; box-sizing: border-box;
  font: 400 15px var(--fs); color: var(--ink);
  padding: 11px 16px; border-radius: 100px;
  border: 1px solid var(--line); background: var(--bg);
}
.pmodal-search input:focus { outline: none; border-color: var(--accent); background: var(--card); }
.pmodal-list { overflow-y: auto; padding: 0 var(--s3) var(--s3); }
.pmodal-item {
  display: flex; align-items: center; gap: var(--s3); width: 100%;
  text-align: left; font: 500 13.5px var(--fs); color: var(--ink);
  border: 0; background: transparent; border-radius: var(--r1);
  padding: 9px 14px; cursor: pointer;
}
.pmodal-item:hover { background: var(--accent-l); }
.pmodal-item.is-on { background: var(--accent-l); font-weight: 700; color: var(--accent-d); }
.pmodal-item .pk {
  margin-left: auto; font: 700 9px var(--fs); letter-spacing: .05em;
  text-transform: uppercase; color: var(--accent-d);
}
.pmodal-empty { padding: var(--s5); font-size: 13px; color: var(--ink-3); text-align: center; }
.pmodal-foot {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s5); border-top: 1px solid var(--line);
  font-size: 11.5px; color: var(--ink-3);
}
.pmodal-foot .qbar-reset { margin-left: auto; }

/* ── Opening a catalogue entry ───────────────────────────────────────
   The link matters: every figure in these panels describes a page somebody
   looked at, and being able to open that page is what turns a number into an
   understanding. */
.act-link {
  margin-left: auto; font: 700 11px var(--fs); white-space: nowrap;
  color: var(--accent-d); text-decoration: none;
  border: 1px solid var(--line); border-radius: 100px; padding: 4px 11px;
}
.act-link:hover { border-color: var(--accent); background: var(--accent-l); }
.act-detail-head .act-detail-x { margin-left: var(--s3); }

.act-steps {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 1px; background: var(--line); border: 1px solid var(--line);
  border-radius: var(--r2); overflow: hidden; margin-bottom: var(--s4);
}
.act-stepbox { background: var(--card); padding: 12px 14px; display: flex; flex-direction: column; gap: 2px; }
.act-stepbox-value {
  font-size: 20px; font-weight: 800; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.act-stepbox-label {
  font: 700 9.5px var(--fs); letter-spacing: .05em; text-transform: uppercase; color: var(--ink-3);
}
.act-stepbox-sub { font-size: 10.5px; color: var(--ink-3); }

.act-detail-text {
  background: var(--bg); border-radius: var(--r2); padding: 12px 14px;
}
.act-detail-tag {
  display: block; font: 700 9px var(--fs); letter-spacing: .07em;
  text-transform: uppercase; color: var(--ink-3); margin-bottom: 5px;
}
.act-detail-text p { margin: 0; font-size: 12.5px; line-height: 1.6; color: var(--ink-2); }

/* ── Order: the one complete path in the app ─────────────────────────
   Every confirmation page is exactly one order, so unlike the other modules
   this funnel runs all the way to the end. */
.ord-flow {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: var(--s4); margin-bottom: var(--s4);
}
.ord-stage { display: flex; flex-direction: column; gap: 4px; }
.ord-stage-value {
  font-size: 22px; font-weight: 800; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.ord-stage-label {
  font: 700 9.5px var(--fs); letter-spacing: .05em; text-transform: uppercase; color: var(--ink-3);
}
.ord-stage-bar {
  display: block; height: 7px; border-radius: 100px;
  background: rgba(120,120,140,.14); overflow: hidden;
}
.ord-stage-bar i { display: block; height: 100%; border-radius: 100px; background: var(--accent); }
.ord-stage-rate { font-size: 10.5px; color: var(--ink-3); font-variant-numeric: tabular-nums; }

/* Order cards carry no drawer, so they must not offer one. Without this they
   keep the button's hover lift and pointer and read as broken when clicked. */
.act.is-flat { cursor: default; }
.act.is-flat:hover { transform: none; box-shadow: var(--shadow-1); }

/* ── The selected customer in the filter bar ──────────────────────────────
   Searching a customer filters the whole dashboard, so the selection has to
   stay visible on every section — and be recognisable at a glance, which is
   what the app icon does better than a name in a row of grey chips. */
.qbar-client {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 3px 4px 3px 3px; border-radius: 999px;
  border: 1px solid var(--accent); background: var(--accent-l);
  max-width: 260px;
}
.qbar-client .rep-appicon {
  width: 24px; height: 24px; border-radius: 7px;
}
.qbar-client .rep-appicon.is-initials { font: 800 9.5px var(--fs); }
.qbar-client-name {
  font: 700 12.5px var(--fs); color: var(--accent-d);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.qbar-client-x {
  flex: none; width: 18px; height: 18px; border: 0; border-radius: 50%;
  background: transparent; color: var(--accent-d); cursor: pointer;
  font: 600 14px/1 var(--fs);
}
.qbar-client-x:hover { background: var(--card); }
/* One selection, one place: the picker button is redundant beside the lockup. */
.qbar-client:not([hidden]) ~ #pickProperty,
#pickProperty:has(~ .qbar-client:not([hidden])) { display: none; }

/* ── Waiting states ───────────────────────────────────────────────────────
   Shaped like the content that is coming, so the panel does not jump when the
   data lands and the wait reads as work rather than as breakage. */
.panel-canvas.is-waiting { display: block; }
.skel-set { display: flex; flex-direction: column; gap: 10px; height: 100%; }

.skel-bars {
  flex: 1; display: flex; align-items: flex-end; gap: 3.5%;
  min-height: 120px; padding-top: var(--s3);
}
.skel-bars .skel-bar { flex: 1; border-radius: 4px 4px 2px 2px; }

.skel-row { display: flex; align-items: center; gap: var(--s3); }
.skel-cell { height: 11px; flex: 1; }
.skel-num  { height: 11px; width: 46px; flex: none; }
.skel-line { height: 11px; display: block; }

.skel-products, .skel-activities { flex-direction: row; gap: var(--s3); overflow: hidden; }
.skel-card {
  flex: 0 0 clamp(170px, 22%, 210px);
  display: flex; flex-direction: column; gap: 7px;
}
.skel-shot { height: 104px; border-radius: var(--r2); display: block; }

/* The report is a document, so its wait looks like one — the masthead real,
   the body sketched, and between them the three steps the server is actually
   working through. */
.rep-skel { padding: var(--s5) 0 12vh; display: flex; flex-direction: column; gap: var(--s5); }
.rep-skel-head { display: flex; align-items: center; gap: var(--s4); }
.rep-skel-icon { width: 52px; height: 52px; border-radius: 13px; flex: none; }
.rep-skel-icon .rep-appicon { width: 52px; height: 52px; }
.rep-skel-words { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.rep-skel-name {
  margin: 0; font-size: 32px; font-weight: 800; letter-spacing: -.03em;
  line-height: 1.1;
}

/* ── The three steps ── */
.rep-steps {
  list-style: none; margin: 0; padding: var(--s4) var(--s5);
  display: flex; flex-direction: column; gap: 13px;
  border: 1px solid var(--line); border-radius: var(--r2); background: var(--card);
}
.rep-step {
  display: flex; align-items: center; gap: var(--s3);
  font-size: 13.5px; color: var(--ink-3);
  transition: color .3s ease;
}
.rep-step-dot {
  width: 15px; height: 15px; flex: none; border-radius: 50%;
  border: 2px solid var(--line-2); background: var(--card);
  position: relative; transition: border-color .3s ease, background .3s ease;
}
/* Running: a ring that keeps turning, so a slow step still looks alive. */
.rep-step.is-now { color: var(--ink); font-weight: 600; }
.rep-step.is-now .rep-step-dot {
  border-color: var(--accent-l); border-top-color: var(--accent);
  animation: rep-spin .7s linear infinite;
}
/* Done: filled, with a tick drawn in. */
.rep-step.is-done { color: var(--ink-2); }
.rep-step.is-done .rep-step-dot {
  border-color: var(--accent); background: var(--accent);
  animation: rep-pop .28s cubic-bezier(.3,1.5,.5,1);
}
.rep-step.is-done .rep-step-dot::after {
  content: ''; position: absolute; left: 3px; top: .5px;
  width: 3px; height: 7px; border: solid #fff;
  border-width: 0 2px 2px 0; transform: rotate(42deg);
}
@keyframes rep-spin { to { transform: rotate(360deg); } }
@keyframes rep-pop  { from { transform: scale(.55); } }
@media (prefers-reduced-motion: reduce) {
  .rep-step.is-now .rep-step-dot { animation: none; border-top-color: var(--accent); }
  .rep-step.is-done .rep-step-dot { animation: none; }
}

.rep-skel-tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s3); }
.rep-skel-tile { height: 74px; border-radius: var(--r2); }
.rep-skel-block { display: flex; flex-direction: column; gap: 9px; }
.rep-skel-bar { height: 13px; }
.rep-skel-block .rep-skel-bar:nth-child(2) { width: 82%; }
.rep-skel-block .rep-skel-bar:nth-child(3) { width: 61%; }
@media (max-width: 720px) { .rep-skel-tiles { grid-template-columns: repeat(2, 1fr); } }

/* The page steps aside rather than hiding behind the panel.
   Docking the detail to the left only helps if the list you clicked from is
   still there to click again — otherwise the panel covers its own navigation.
   So while it is open the page becomes a genuine two-column split: detail on
   the left, the lists and charts shifted into the space beside it. */
@media (min-width: 901px) {
  body.drawer-open .top,
  body.drawer-open #main {
    margin-left: min(620px, 52vw);
    transition: margin-left .26s cubic-bezier(.22,.7,.3,1);
  }
  .top, #main { transition: margin-left .26s cubic-bezier(.22,.7,.3,1); }
}
@media (prefers-reduced-motion: reduce) {
  .top, #main, body.drawer-open .top, body.drawer-open #main { transition: none; }
}

/* ── What the property is ─────────────────────────────────────────────────
   This was a wrapping run of bold-number/grey-word pairs: every fact the same
   weight, the shape of the house and the bookkeeping dates in one list, and
   nothing to rest the eye on. Split in two — figures as tiles, record-keeping
   as one quiet line — it becomes readable at a glance and survives being sent
   to a customer. */
.rep-identity { margin: var(--s5) 0 var(--s6); }
.rep-id-tiles {
  display: grid; gap: 1px;
  grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
  background: var(--line); border: 1px solid var(--line);
  border-radius: var(--r2); overflow: hidden;
}
.rep-id-tile {
  background: var(--card); padding: var(--s4) var(--s4) 13px;
  display: flex; flex-direction: column; gap: 3px; min-width: 0;
}
.rep-id-value {
  font: 800 21px/1.1 var(--fs); letter-spacing: -.025em; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.rep-id-label {
  /* Wraps rather than clipping: "modules switch…" is not a label. */
  font: 600 11px/1.35 var(--fs); letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-3); overflow-wrap: anywhere;
}
.rep-id-meta {
  margin: var(--s3) 0 0; font-size: 12px; color: var(--ink-3); line-height: 1.7;
}
@media print {
  .rep-id-tiles { break-inside: avoid; }
}

/* The level to aim at, drawn on the same track as the property's own mark so
   the distance between them is visible rather than arithmetic. */
.rep-track-target {
  position: absolute; top: -3px; bottom: -3px; width: 2px;
  background: var(--accent); border-radius: 1px;
  box-shadow: 0 0 0 2px var(--card);
}
.rep-track-target::after {
  content: ''; position: absolute; left: -3px; top: -4px;
  border: 4px solid transparent; border-top-color: var(--accent);
}


/* ── Signing in ───────────────────────────────────────────────────────────
   Its own page rather than a modal over the dashboard: there is nothing behind
   it to see, and a blurred wall of a stranger's revenue figures would be worse
   than a blank one.

   The product lockup sits above the card, the same mark the report leads with,
   so arriving and being inside look like one product. Everything else defers
   to the two fields. */
.signin-page {
  display: grid; place-items: center; min-height: 100vh;
  background: var(--bg);
  position: relative; overflow: hidden;
}
/* A single soft light behind the card. Enough to stop the page reading as a
   blank sheet, not enough to compete with the form. */
.signin-page::before {
  content: ''; position: absolute; inset: -20% 0 auto; height: 70vh; z-index: 0;
  background: radial-gradient(46% 60% at 50% 40%, var(--accent-l), transparent 70%);
  opacity: .8; pointer-events: none;
}
.signin { position: relative; z-index: 1; width: min(392px, 92vw); }

/* ── The product lockup ── */
.appmark {
  display: flex; align-items: center; gap: 13px;
  justify-content: center; margin-bottom: var(--s5);
}
.appmark-tile {
  width: 46px; height: 46px; flex: none;
  border-radius: 13px; display: grid; place-items: center;
  background: linear-gradient(150deg, var(--accent), color-mix(in oklab, var(--accent) 72%, #000));
  box-shadow: 0 10px 24px -12px color-mix(in oklab, var(--accent) 75%, transparent),
              inset 0 1px 0 rgba(255,255,255,.3);
}
@supports not (background: color-mix(in oklab, red 50%, blue)) {
  .appmark-tile { background: var(--accent); box-shadow: 0 10px 24px -12px rgba(25,84,211,.6); }
}
.appmark-tile svg { width: 24px; height: 24px; fill: #fff; color: #fff; }
.appmark-words { display: flex; flex-direction: column; line-height: 1.1; text-align: left; }
.appmark-brand {
  font: 800 17px var(--fs); letter-spacing: -.025em; color: var(--ink);
}
.appmark-name {
  font: 700 11px var(--fs); letter-spacing: .13em; text-transform: uppercase;
  color: var(--accent);
}

/* ── The card ── */
.signin-card {
  background: var(--card); border: 1px solid var(--line); border-radius: 18px;
  padding: var(--s6) var(--s5) var(--s5);
  box-shadow: 0 1px 2px rgba(20,20,50,.04),
              0 24px 48px -32px rgba(20,20,50,.45);
}
.signin-card h1 { margin: 0 0 5px; font-size: 20px; letter-spacing: -.02em; }
.signin-lead { margin: 0 0 var(--s5); font-size: 13px; color: var(--ink-3); line-height: 1.55; }
.signin-field { display: block; margin-bottom: var(--s4); }
.signin-field span {
  display: block; margin-bottom: 6px;
  font: 700 10.5px var(--fs); letter-spacing: .07em; text-transform: uppercase; color: var(--ink-3);
}
.signin-field input {
  width: 100%; box-sizing: border-box; padding: 12px 14px;
  border: 1px solid var(--line); border-radius: 11px;
  font: 400 14.5px var(--fs); background: var(--bg); color: var(--ink);
  transition: border-color .15s ease, box-shadow .15s ease, background .15s ease;
}
.signin-field input:hover { border-color: var(--line-2); }
.signin-field input:focus {
  outline: none; background: var(--card);
  border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-l);
}
.signin-go {
  width: 100%; margin-top: var(--s3);
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 13px 16px; border: 0; border-radius: 11px;
  background: linear-gradient(150deg, var(--accent), color-mix(in oklab, var(--accent) 80%, #000));
  color: #fff; font: 700 14.5px var(--fs); cursor: pointer;
  box-shadow: 0 10px 22px -14px color-mix(in oklab, var(--accent) 85%, transparent);
  transition: transform .14s ease, box-shadow .14s ease, filter .14s ease;
}
@supports not (background: color-mix(in oklab, red 50%, blue)) {
  .signin-go { background: var(--accent); box-shadow: 0 10px 22px -14px rgba(25,84,211,.7); }
}
.signin-go svg { width: 16px; height: 16px; transition: transform .14s ease; }
.signin-go:hover { filter: brightness(1.05); box-shadow: 0 14px 26px -14px rgba(25,84,211,.65); }
.signin-go:hover svg { transform: translateX(2px); }
.signin-go:active { transform: translateY(1px); }
.signin-go:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

.signin-error, .signin-ok {
  margin: 0 0 var(--s4); padding: 10px 13px; border-radius: 10px; font-size: 12.5px;
  display: flex; align-items: center; gap: 8px;
}
.signin-error { background: var(--red-l, #fdecec); color: var(--red, #b3261e); }
.signin-ok    { background: var(--accent-l); color: var(--accent-d); }
.signin-note {
  margin: var(--s4) 0 0; padding-top: var(--s4); border-top: 1px solid var(--line);
  font-size: 11.5px; color: var(--ink-3); line-height: 1.6;
}
.signin-code {
  margin: 0 0 var(--s4); padding: 12px 13px; border-radius: 10px; overflow-x: auto;
  background: var(--bg); border: 1px solid var(--line);
  font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--ink-2);
}
@media (prefers-reduced-motion: reduce) {
  .signin-go, .signin-go svg, .signin-field input { transition: none; }
}

/* ── Who is signed in ── */
.whoami { display: flex; align-items: center; gap: var(--s3); }
.whoami-name {
  font: 600 12px var(--fs); color: var(--ink-2);
  max-width: 190px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.whoami-out { margin: 0; display: inline; }
.whoami-link {
  border: 0; background: none; padding: 0; cursor: pointer;
  font: 600 12px var(--fs); color: var(--accent); text-decoration: none;
}
.whoami-link:hover { text-decoration: underline; }

/* ── Account management ──────────────────────────────────────────────────
   A list, not a table. Five columns of wildly different weights — a person, a
   dropdown, a pill, a date, three buttons — never lined up, and the row-level
   password form tore the grid apart the moment it opened. Each account is one
   row that owns its own space, and everything destructive lives behind one
   menu instead of sitting in the open next to a select that submits on change. */
.admin { max-width: 940px; margin: 0 auto; padding: var(--s6) var(--s5) 14vh; }
.admin-card {
  background: var(--card); border: 1px solid var(--line); border-radius: 16px;
  padding: var(--s5) var(--s5) var(--s5); margin-bottom: var(--s4);
}
.admin-card h1 { margin: 0 0 5px; font-size: 22px; letter-spacing: -.025em; }
.admin-card h2 { margin: 0 0 5px; font-size: 16px; letter-spacing: -.015em; }
.admin-lead { margin: 0 0 var(--s5); font-size: 13px; color: var(--ink-3); line-height: 1.6; max-width: 62ch; }
.admin-error, .admin-ok { margin: 0 0 var(--s4); padding: 11px 14px; border-radius: 11px; font-size: 13px; }
.admin-error { background: var(--red-l, #fdecec); color: var(--red, #b3261e); }
.admin-ok    { background: var(--accent-l); color: var(--accent-d); }
.admin-inline { display: inline; margin: 0; }

.ulist { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.ulist-row {
  display: flex; align-items: center; gap: var(--s4);
  padding: 13px 0; border-bottom: 1px solid var(--line);
}
.ulist-row.is-off { opacity: .5; }

.ulist-who { display: flex; align-items: center; gap: 11px; flex: 1 1 auto; min-width: 0; }
.ulist-avatar {
  width: 34px; height: 34px; flex: none; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--accent-l); color: var(--accent-d);
  font: 800 14px var(--fs);
}
.ulist-id { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ulist-name {
  font: 700 13.5px var(--fs); color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ulist-you {
  font: 700 10px var(--fs); font-style: normal; letter-spacing: .05em;
  text-transform: uppercase; color: var(--ink-3);
  border: 1px solid var(--line); border-radius: 100px; padding: 1px 6px; margin-left: 5px;
}
.ulist-mail {
  font-size: 12px; color: var(--ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.ulist-meta { display: flex; align-items: center; gap: var(--s4); flex: none; }
.ulist-role {
  padding: 6px 10px; border: 1px solid var(--line); border-radius: 9px;
  font: 600 12.5px var(--fs); background: var(--card); color: var(--ink-2); cursor: pointer;
}
.ulist-role:hover { border-color: var(--accent); }
.ulist-seen { font-size: 12px; color: var(--ink-3); white-space: nowrap; min-width: 118px; text-align: right; }
.admin-pill {
  display: inline-block; padding: 3px 10px; border-radius: 100px;
  background: var(--line-2); color: var(--ink-3); font: 700 11px var(--fs); white-space: nowrap;
}
.admin-pill.is-on { background: var(--accent-l); color: var(--accent-d); }

/* ── The row menu ── */
.ulist-do { flex: none; }
.umenu { position: relative; }
.umenu summary { list-style: none; cursor: pointer; }
.umenu summary::-webkit-details-marker { display: none; }
.umenu[open] summary { border-color: var(--accent); color: var(--accent-d); }
.umenu-pop {
  position: absolute; right: 0; top: calc(100% + 7px); z-index: 20;
  width: 262px; padding: var(--s3);
  background: var(--card); border: 1px solid var(--line); border-radius: 13px;
  box-shadow: 0 18px 44px -22px rgba(20,20,50,.42);
}
.umenu-pop hr { border: 0; border-top: 1px solid var(--line); margin: var(--s3) 0; }
.umenu-pop form { margin: 0; }
.umenu-pw label {
  display: block; margin-bottom: 6px;
  font: 600 10.5px var(--fs); letter-spacing: .05em; text-transform: uppercase; color: var(--ink-3);
}
.umenu-pw-row { display: flex; gap: 6px; }
.umenu-pw-row input {
  flex: 1; min-width: 0; padding: 7px 10px;
  border: 1px solid var(--line); border-radius: 9px;
  font: 400 12.5px var(--fs); background: var(--card); color: var(--ink);
}
.umenu-item {
  display: block; width: 100%; text-align: left;
  border: 0; background: none; border-radius: 8px; cursor: pointer;
  padding: 8px 9px; font: 600 12.5px var(--fs); color: var(--ink-2);
}
.umenu-item:hover { background: var(--line-2); color: var(--ink); }
.umenu-item.is-bad:hover { background: var(--red-l, #fdecec); color: var(--red, #b3261e); }

.admin-btn {
  border: 1px solid var(--line); background: var(--card); color: var(--ink-2);
  border-radius: 100px; padding: 6px 13px; font: 600 12.5px var(--fs); cursor: pointer;
  line-height: 1.2;
}
.admin-btn:hover { border-color: var(--accent); color: var(--accent-d); }
.admin-btn.is-go { background: var(--accent); border-color: transparent; color: #fff; }
.admin-btn.is-go:hover { filter: brightness(1.06); }

/* ── Adding someone ── */
.admin-add {
  display: grid; gap: var(--s3);
  grid-template-columns: 1.3fr 1fr 1.2fr .9fr auto; align-items: end;
}
.admin-add label { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.admin-add label span {
  font: 600 10.5px var(--fs); letter-spacing: .05em; text-transform: uppercase; color: var(--ink-3);
}
.admin-add input, .admin-add select {
  width: 100%; box-sizing: border-box; padding: 8px 11px;
  border: 1px solid var(--line); border-radius: 9px;
  font: 400 13px var(--fs); background: var(--card); color: var(--ink);
}
.admin-add input:focus, .admin-add select:focus, .umenu-pw-row input:focus {
  outline: 2px solid var(--accent); outline-offset: 1px; border-color: transparent;
}
.admin-add .admin-btn { height: 33px; white-space: nowrap; }
.admin-note { margin: var(--s4) 0 0; font-size: 11.5px; color: var(--ink-3); line-height: 1.65; max-width: 68ch; }

@media (max-width: 820px) {
  .admin-add { grid-template-columns: 1fr 1fr; }
  .admin-add .admin-btn { grid-column: 1 / -1; }
  .ulist-row { flex-wrap: wrap; }
  .ulist-meta { width: 100%; justify-content: space-between; padding-left: 45px; }
  .ulist-seen { text-align: left; min-width: 0; }
}

/* ── The wordmark ────────────────────────────────────────────────────────
   Inherits the surrounding colour through currentColor, so the same file
   works in the header, on the sign-in card and in a dark theme. */
.gn-logo { display: block; height: 21px; width: auto; color: var(--ink); flex: none; }
.brand { gap: 12px; }
.brand-sub { padding-left: 12px; border-left: 1px solid var(--line); line-height: 1.4; }


/* The window the four lists describe, said once above them. */
.lead-period {
  margin: var(--s5) 0 var(--s4); padding-left: 9px;
  font-size: 11.5px; color: var(--ink-3);
}
.lead-period b { color: var(--ink-2); font-weight: 700; }

/* The list that is currently driving a chart beside it. A quiet marker, not a
   highlight: the row inside it already shows which property is selected, and
   two signals for one state is one too many. */
.panel.is-driving { box-shadow: inset 3px 0 0 var(--accent-l); }

/* ── From guests to revenue ───────────────────────────────────────────────
   Vertical, and the answer above the evidence.
   Six cards side by side was a table laid on its side: equal weight on every
   step, labels squeezed into two-word columns, a horizontal scrollbar on
   anything smaller than a laptop, and no statement anywhere — the reader had
   to work out which step was the weak one.
   Down the page each step gets a whole line, the reading order is the funnel's
   own, and the percentile bars stack into a profile that can be read without
   touching a single number. */
.panel .panel-canvas:has(> .flow),
.panel.span-12 .panel-canvas:has(> .flow) { height: auto; }

.flow { display: flex; flex-direction: column; gap: var(--s4); }

/* The one sentence the panel exists to say.
   A tinted block with a heavy left rule and half the line in bold was three
   devices for one sentence. A dot carries the state; the bold falls only on
   the two words that are the finding. */
.flow-lead {
  margin: 0 0 2px; padding: 0 2px;
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-size: 13px; color: var(--ink-3); line-height: 1.5;
}
.flow-lead b { color: var(--ink); font-weight: 700; }
.flow-lead-dot {
  width: 7px; height: 7px; flex: none; border-radius: 50%;
  background: var(--ink-3); align-self: center;
}
.flow-lead.is-weak   .flow-lead-dot { background: var(--red, #b3261e); }
.flow-lead.is-weak b { color: var(--red, #b3261e); }
.flow-lead.is-strong .flow-lead-dot { background: var(--green, #17915c); }

.flow-rows { display: flex; flex-direction: column; }
.flow-row {
  display: grid;
  grid-template-columns: minmax(150px, 1.4fr) 92px minmax(150px, 1.5fr) minmax(120px, 1fr);
  align-items: center; gap: var(--s4);
  padding: 11px 12px; border-radius: 10px;
  position: relative;
}
/* The thread running through the steps, drawn behind the icons.
   Positioned by arithmetic, not by eye: the row is padded 12px and the icon
   box is 19px plus 5px of padding and 1px of border either side, so its centre
   sits at 12 + 31/2 = 27.5px. It was at 21px and visibly off. */
.flow-row::before {
  content: ''; position: absolute; left: 27.5px; top: 0; bottom: 0; width: 1.5px;
  transform: translateX(-50%);
  background: var(--line); z-index: 0;
}
.flow-row:first-of-type::before { top: 50%; }
.flow-row:last-child::before    { bottom: 50%; }
/* The caption row carries no icon, so it must not carry the thread either. */
.flow-head::before { display: none; }
.flow-row:hover { background: var(--line-2); }
.flow-row.is-outcome { background: var(--accent-l); }
.flow-row.is-outcome:hover { background: var(--accent-l); }

.flow-what { display: flex; align-items: center; gap: 11px; min-width: 0; }
.flow-what .mod-icon {
  position: relative; z-index: 1; flex: none;
  width: 19px; height: 19px; padding: 5px; box-sizing: content-box;
  border-radius: 50%; background: var(--card); border: 1px solid var(--line);
  stroke: var(--accent); stroke-width: 1.7;
}
.flow-row.is-outcome .mod-icon { background: var(--accent); border-color: transparent; stroke: #fff; }
.flow-words { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.flow-label {
  font: 700 11px var(--fs); letter-spacing: .06em; text-transform: uppercase; color: var(--ink-2);
}
.flow-sub { font: 500 11px var(--fs); color: var(--ink-3); }

.flow-count {
  font: 800 19px var(--fs); letter-spacing: -.03em; color: var(--ink);
  font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap;
}
.flow-row.is-outcome .flow-count { color: var(--accent-d); }

.flow-conv { font-size: 12.5px; color: var(--ink-3); }
.flow-conv b { color: var(--ink); font-weight: 800; font-variant-numeric: tabular-nums; }
.flow-drop { color: var(--red, #b3261e); opacity: .7; }

/* One 0-100 scale for every link, whether it is a share, a rate or a price.
   That shared scale is the only reason five different measures can be read
   down a column at all. */
.flow-gauge { display: flex; align-items: center; gap: 8px; }
.flow-track {
  position: relative;
  flex: 1; height: 7px; border-radius: 100px; background: var(--line-2);
  min-width: 60px;
}
.flow-track i {
  display: block; height: 100%; border-radius: 100px; background: var(--ink-3);
}
/* The middle of the field. Without it the bar is a length with nothing to be
   long or short against, which is why nobody could read it. */
.flow-mid {
  position: absolute; left: 50%; top: -3px; bottom: -3px; width: 1.5px;
  background: var(--card); box-shadow: 0 0 0 1px var(--line-2);
}
.flow-track.is-strong i { background: var(--green, #17915c); }
.flow-track.is-weak i   { background: var(--red, #b3261e); }
.flow-pct {
  font: 800 11px var(--fs); color: var(--ink-3);
  font-variant-numeric: tabular-nums; min-width: 30px; text-align: right;
}

/* ── The caption above the gauge column ── */
.flow-head { padding-top: 0; padding-bottom: 4px; }
.flow-head:hover { background: none; }
.flow-axis-name {
  font: 700 9px var(--fs); letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-3); text-align: left;
}

.chain-foot {
  margin: var(--s4) 0 0; font-size: 12.5px; color: var(--ink-3);
  display: flex; align-items: baseline; gap: 5px; flex-wrap: wrap;
}
.chain-foot b { font-size: 17px; color: var(--ink); font-weight: 800; }
.chain-sep { color: var(--line-2); }
.chain-outside { color: var(--amber, #8a6d1f); }

@media (max-width: 860px) {
  .flow-row { grid-template-columns: 1fr auto; row-gap: 6px; }
  .flow-conv, .flow-gauge { grid-column: 1 / -1; }
  .flow-row::before { display: none; }
}
}
.chain-sub { margin-top: 1px; font: 500 10px var(--fs); color: var(--ink-3); }
.chain-sub b { font-weight: 700; color: var(--ink-2); font-variant-numeric: tabular-nums; }


/* Straight into the property's own guest app, from the filter bar. */
.qbar-client-go {
  flex: none; display: grid; place-items: center;
  width: 20px; height: 20px; border-radius: 6px;
  color: var(--accent-d); text-decoration: none;
}
.qbar-client-go svg { width: 13px; height: 13px; }
.qbar-client-go:hover { background: var(--card); }

/* ── Priorities ───────────────────────────────────────────────────────────
   The one page that decides rather than measures. Three lights for the shape
   of the portfolio, a ranked list of states for where the value is, and a work
   list underneath. Everything is sorted by worth per property, never by count:
   477 disconnected houses are EUR 333 each and 38 with a small basket are EUR
   12,400 each, so a page ordered by how many would point the wrong way. */
.tri { max-width: 1060px; margin: 0 auto; padding: var(--s5) var(--s4) 12vh; }
.tri-title { margin: 0 0 6px; font-size: 28px; font-weight: 800; letter-spacing: -.03em; }
.tri-lead { margin: 0 0 var(--s6); font-size: 13.5px; color: var(--ink-3); line-height: 1.6; max-width: 74ch; }

/* ── The lights ── */
.tri-lights {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s3);
  margin-bottom: var(--s6);
}
.tri-light {
  display: flex; align-items: flex-start; gap: 11px; text-align: left;
  padding: var(--s4); border-radius: var(--r2); cursor: pointer;
  border: 1px solid var(--line); background: var(--card); font: inherit;
  transition: border-color .15s ease, transform .15s ease;
}
.tri-light:hover { transform: translateY(-1px); }
.tri-light.on { border-color: var(--ink); }
.tri-light-dot { width: 11px; height: 11px; border-radius: 50%; flex: none; margin-top: 5px; }
.tri-light.is-red   .tri-light-dot { background: var(--red, #b3261e); }
.tri-light.is-amber .tri-light-dot { background: var(--amber, #c98a00); }
.tri-light.is-green .tri-light-dot { background: var(--green, #17915c); }
.tri-light-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.tri-light-name {
  font: 700 10px var(--fs); letter-spacing: .07em; text-transform: uppercase; color: var(--ink-3);
}
.tri-light-count {
  font: 800 26px var(--fs); letter-spacing: -.03em; color: var(--ink);
  font-variant-numeric: tabular-nums; line-height: 1.1;
}
.tri-light-note { font-size: 11.5px; color: var(--ink-3); line-height: 1.45; }

/* ── The states, ranked by worth per property ── */
.tri-states-head, .tri-state {
  display: grid; grid-template-columns: minmax(160px, 1.6fr) 88px 1fr 96px;
  align-items: center; gap: var(--s4);
}
.tri-states-head {
  padding: 0 12px 6px;
  font: 700 9px var(--fs); letter-spacing: .07em; text-transform: uppercase; color: var(--ink-3);
}
.tri-states-head span:nth-child(2) { text-align: right; }
.tri-states-head span:nth-child(3) { grid-column: 3 / span 2; }
.tri-states { display: flex; flex-direction: column; margin-bottom: var(--s6); }
.tri-state {
  width: 100%; padding: 9px 12px; border: 0; border-radius: 9px;
  background: transparent; font: inherit; text-align: left; cursor: pointer;
}
.tri-state:hover { background: var(--line-2); }
.tri-state.on { background: var(--line-2); box-shadow: inset 2px 0 0 var(--ink); }
.tri-state-name { font: 700 13px var(--fs); color: var(--ink); }
.tri-state-count {
  font: 600 12.5px var(--fs); color: var(--ink-3);
  font-variant-numeric: tabular-nums; text-align: right;
}
.tri-state-track { height: 7px; border-radius: 100px; background: var(--line-2); overflow: hidden; }
.tri-state-track i { display: block; height: 100%; border-radius: 100px; }
.tri-state.is-red   .tri-state-track i { background: var(--red, #b3261e); }
.tri-state.is-amber .tri-state-track i { background: var(--amber, #c98a00); }
.tri-state-each {
  font: 800 13px var(--fs); color: var(--ink); font-variant-numeric: tabular-nums; text-align: right;
}

/* ── The work list ── */
.tri-list-head {
  display: flex; align-items: baseline; gap: var(--s3);
  margin-bottom: 4px; padding: 0 12px;
}
.tri-list-head h2 { margin: 0; font-size: 16px; letter-spacing: -.015em; }
.tri-list-note { font-size: 12px; color: var(--ink-3); margin-right: auto; }
.tri-clear {
  border: 1px solid var(--line); background: var(--card); border-radius: 100px;
  padding: 4px 11px; font: 600 11.5px var(--fs); color: var(--ink-2); cursor: pointer;
}
.tri-clear:hover { border-color: var(--accent); color: var(--accent-d); }
.tri-action {
  margin: 0 0 var(--s3); padding: 9px 12px; border-radius: 9px;
  background: var(--line-2); font-size: 12.5px; color: var(--ink-2); line-height: 1.55;
}

.tri-list { display: flex; flex-direction: column; border-top: 1px solid var(--line); }
.tri-row {
  display: grid; grid-template-columns: 26px minmax(0, 1fr) auto 92px;
  align-items: center; gap: var(--s4);
  width: 100%; padding: 11px 12px; border: 0; border-bottom: 1px solid var(--line);
  background: transparent; font: inherit; text-align: left; cursor: pointer;
}
.tri-row:hover { background: var(--line-2); }
.tri-row .rep-appicon { width: 26px; height: 26px; border-radius: 7px; border-color: transparent; }
.tri-row .rep-appicon.is-initials { font: 800 9.5px var(--fs); background: var(--card); }
.tri-who { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.tri-name {
  font: 700 13px var(--fs); color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tri-flag {
  margin-left: 7px; padding: 1px 7px; border-radius: 100px;
  font: 700 9px var(--fs); letter-spacing: .06em; text-transform: uppercase;
  background: var(--red-l, #fdecec); color: var(--red, #b3261e);
}
.tri-why { font-size: 11.5px; color: var(--ink-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tri-state-tag {
  padding: 3px 10px; border-radius: 100px; white-space: nowrap;
  font: 700 10.5px var(--fs); background: var(--line-2); color: var(--ink-3);
}
.tri-state-tag.is-red   { background: var(--red-l, #fdecec); color: var(--red, #b3261e); }
.tri-state-tag.is-amber { background: var(--amber-l); color: var(--amber, #8a6d1f); }
.tri-state-tag.is-green { background: var(--accent-l); color: var(--accent-d); }
.tri-euros {
  font: 800 14px var(--fs); color: var(--ink);
  font-variant-numeric: tabular-nums; text-align: right;
}
.tri-more, .tri-small {
  margin: var(--s4) 0 0; padding: 0 12px;
  font-size: 11.5px; color: var(--ink-3); line-height: 1.65; max-width: 82ch;
}

/* Waiting */
.tri-light-skel { height: 84px; border-radius: var(--r2); display: block; }
.tri-row-skel { height: 46px; border-radius: 9px; display: block; margin-bottom: 6px; }

@media (max-width: 820px) {
  .tri-lights { grid-template-columns: 1fr; }
  .tri-states-head { display: none; }
  .tri-state { grid-template-columns: 1fr auto; row-gap: 5px; }
  .tri-state-track { grid-column: 1 / -1; }
  .tri-row { grid-template-columns: 26px 1fr auto; }
  .tri-state-tag { grid-column: 2 / -1; justify-self: start; }
}

/* ── Measured against the top 5% ──────────────────────────────────────────
   The scale runs from zero to what the band typically reaches, and the band's
   entry price is marked on it. That replaced a percentile against a
   like-for-like cohort: nobody's goal is to be the median of properties their
   own size, and against a top band a percentile is useless — everything
   outside it scores zero. */
.rep-track.is-band { position: relative; overflow: visible; }
.rep-track.is-band .rep-track-zone {
  position: absolute; top: 0; bottom: 0; border-radius: 0 100px 100px 0;
  background: var(--accent-l);
}
.rep-track-fill {
  position: absolute; left: 0; top: 0; bottom: 0; border-radius: 100px;
  background: var(--ink-3); z-index: 1;
}
.rep-track-fill.is-in { background: var(--green, #17915c); }
.rep-track-entry {
  position: absolute; top: -4px; bottom: -4px; width: 2px; z-index: 2;
  background: var(--accent); border-radius: 1px;
}
.rep-track-entry::after {
  content: ''; position: absolute; left: -3px; top: -4px;
  border: 4px solid transparent; border-top-color: var(--accent);
}

/* Scale labels placed at their marks rather than spread across the row. */
.rep-bar-scale.is-band { position: relative; height: 30px; display: block; }
.rep-scale-at {
  position: absolute; top: 0; white-space: nowrap;
  font: 500 10.5px/1.3 var(--fs); color: var(--ink-3);
}
.rep-scale-at.is-own    { color: var(--ink); font-weight: 700; top: 15px; }
.rep-scale-at.is-entry  { color: var(--accent-d); font-weight: 700; }
.rep-scale-at.is-target { color: var(--ink-3); }
/* Where this property sits, so its label has something to point at. */
.rep-track-own {
  position: absolute; top: -5px; bottom: -5px; width: 3px; z-index: 3;
  background: var(--ink); border-radius: 2px; box-shadow: 0 0 0 2px var(--card);
}
.rep-track-own.is-in { background: var(--green, #17915c); }

/* A section with nothing to show for the current filter. Greyed rather than
   removed: a tab that disappears makes people wonder what they broke. */
.tab.is-empty { opacity: .38; cursor: not-allowed; }
.tab.is-empty:hover { background: none; box-shadow: none; color: var(--ink-3); }
.tab.is-empty:hover .mod-icon { transform: none; opacity: .75; }

/* ── A KPI tile that drives the chart below it ────────────────────────────
   The first version underlined the selected tile with a 3px inset bar, which
   read as a browser default rather than a choice. A tab is what this actually
   is — one of seven, driving the view beneath — so it looks like one: the tile
   lifts onto the accent, and a small pointer connects it to the chart it is
   drawing. */
button.kpi {
  position: relative; text-align: left; font: inherit; cursor: pointer;
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
button.kpi:hover {
  border-color: var(--line-2);
  box-shadow: 0 6px 16px -12px rgba(20,20,50,.6);
  transform: translateY(-1px);
}
button.kpi.on {
  border-color: transparent;
  background: linear-gradient(180deg, var(--card), var(--accent-l));
  box-shadow: 0 0 0 1.5px var(--accent), 0 10px 20px -14px color-mix(in oklab, var(--accent) 70%, transparent);
}
/* No pointer down to the chart. It was meant to tie the tile to what it draws
   and instead put a paper-dart on the tidiest row of the page; the ring and
   the heading below already say which tile is live. */
@supports not (box-shadow: 0 0 0 1px color-mix(in oklab, red 50%, blue)) {
  button.kpi.on { box-shadow: 0 0 0 1.5px var(--accent); }
}
button.kpi.on .kpi-label { color: var(--accent-d); }
button.kpi.on .kpi-value { color: var(--ink); }
button.kpi:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── The tile the product is judged by ──────────────────────────────────
   Lifted rather than outlined. A hard rule along the top edge was the first
   attempt and it read as a foreign object in a row built from nothing but
   surface and spacing — the one piece of chrome on the tidiest part of the
   page.
   So: a whisper of teal in the surface, a soft shadow that raises the card off
   its neighbours, and the mark carried by the icon and the term line, which
   were doing the work anyway. Teal and not the accent blue, because blue
   already means "this tile is driving the chart" — two meanings in one colour
   and the row stops telling you anything. Nothing here touches the box-shadow
   slot the active ring uses, so a tile can be both. */
.kpi.is-feature {
  border-color: color-mix(in oklab, var(--accent-2) 26%, var(--line));
  background:
    linear-gradient(168deg,
      color-mix(in oklab, var(--accent-2) 6%, var(--card)), var(--card) 58%);
  box-shadow: 0 6px 18px -14px color-mix(in oklab, var(--accent-2) 80%, transparent);
}
@supports not (background: color-mix(in oklab, red 50%, blue)) {
  .kpi.is-feature { background: var(--card); border-color: var(--line); }
}
.kpi.is-feature .kpi-label { color: var(--accent-2); display: flex; gap: 6px; align-items: flex-start; }
/* Bigger than it was, and on its own baseline. At 15 px with a 1.9 stroke the
   mark sat below the cap height of the label beside it and read as a speck. */
.kpi.is-feature .kpi-label .mod-icon {
  flex: none; width: 17px; height: 17px; margin-top: -2px;
  fill: none; stroke: currentColor; stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
}
.kpi.is-feature .kpi-value { color: var(--ink); }
/* No denominator, so no rate. Dashed and quiet: the tile is not broken, the
   connection is missing — and the size of the type should not claim a figure
   that is not there. */
.kpi.is-feature.is-blank { border-style: dashed; background: var(--card); }
.kpi.is-feature.is-blank .kpi-value { font-size: 19px; color: var(--ink-3); }
.kpi.is-feature.is-blank .kpi-label { color: var(--ink-3); }

/* The trade term under the plain label. Small and lower-case-ish against the
   uppercase label above it, so it reads as an aside rather than a second
   heading — it is there to be recognised by people who already know it. */
.kpi-term {
  display: block; margin: -4px 0 6px;
  font: 600 10.5px var(--fs); letter-spacing: .01em;
  color: var(--accent-2); opacity: .85;
}
@media (prefers-reduced-motion: reduce) { button.kpi { transition: none; } }

/* A tile whose range has no earlier period to compare against. */
.kpi-nocmp {
  font: 500 10px var(--fs); color: var(--ink-3); opacity: .75;
  white-space: nowrap; align-self: center;
}

/* Figures beside a chart. The chart says how it moved, these say where it
   stands — a business case needs both halves of that multiplication. */
.pfacts {
  display: flex; flex-wrap: wrap; gap: var(--s5);
  padding: 0 0 var(--s4);
}
.pfact { display: flex; flex-direction: column; gap: 1px; }
.pfact-value {
  font: 800 25px var(--fs); letter-spacing: -.03em; color: var(--ink);
  font-variant-numeric: tabular-nums; line-height: 1.05;
}
.pfact-label {
  font: 600 10.5px var(--fs); letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3);
}
/* The per-month level under the total. Deliberately quiet: it is the same
   quantity in a second unit, not a second finding, and at the same weight as
   the label it would compete with the name of the thing. */
.pfact-per {
  display: block; margin-top: 2px;
  font: 500 11px var(--fs); color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
