/* Arena console — «Досье» (views/profile.js) and «Аналитика»
   (views/analytics.js). Two screens in one stylesheet because index.html is
   frozen this round and cannot link a second one; the analytics rules are
   fenced at the foot of the file under their own `anl-` prefix.

   EVERY NUMBER IN THE «ДОСЬЕ» HALF IS THE CLAUDE DESIGN CONSOLE MOCK'S,
   copied rather than eyeballed — the mock writes its styles inline with exact
   pixels, so a port that guesses is a port that drifts. Each block carries the
   mock's own declaration in the comment above it.

   The tone triplets in tokens.css are the mock's palette exactly
   (--t-gold 241,193,82 · --t-violet 167,139,250 · --t-ok 53,199,131 ·
   --t-mute 236,231,250), so `rgba(var(--t-mute), .44)` below IS the mock's
   `rgba(236,231,250,.44)` and not an approximation of it.

   WRITTEN NARROW-FIRST. The phone reading is the base and the console arrives
   in the @container block, because the console layout is the one that needs
   `display: contents` to fold two wrappers away.

   EVERY multi-column arrangement here is an @container query against `scene`,
   not a window media query. The rail and the context column are subtracted
   from the window before the scene sees any of it and they change
   independently of it — at a 1280px window the scene is ~858px, so a
   `min-width: 1280px` media query would lay a wide grid into a narrow hole.
   shell.css's budget: three columns need >= 900 scene pixels, two need >= 620.
   620 is the threshold below, and `TWO_COLUMN_SCENE` in profile.js is its
   twin — change them together. */

/* mock: `padding:16px 18px` on a console, `0 15px 12px` on a phone. The
   selector carries #scene because shell.css sets `padding-top: 18px` on
   `#scene.shell-scene .container`, which outranks a plain #view rule. */
#view.pf-host,
#scene.shell-scene #view.pf-host {
  max-width: none;
  padding: 0 15px 12px;
  overflow-x: hidden;
}

/* mock (phone): one column, `gap:11px`. `display: contents` on the identity
   wrapper below flattens its children into this flow, which is what lets the
   marks row, the speed list and the fire card sit where the phone layout puts
   them without a second copy of any of them. */
.pf-console {
  display: flex;
  flex-direction: column;
  gap: 11px;
}

/* ==========================================================================
   THE IDENTITY STRIP
   ========================================================================== */

/* On a phone there is no card here at all: the mock draws a bare avatar-and-
   name row and lets the blocks under it carry the borders. */
.pf-id { display: contents; }
.pf-id-glow { display: none; }

/* mock: `display:flex;align-items:center;gap:12px` */
.pf-id-head {
  order: 1;
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
/* mock: 56x56, `border-radius:17px`, `font-size:26px`,
   `background:linear-gradient(135deg,#7c3aed,#241638)`,
   `border:2px solid rgba(241,193,82,.45)` */
.pf-avatar {
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  border-radius: 17px;
  display: grid;
  place-items: center;
  font-size: 26px;
  line-height: 1;
  background: linear-gradient(135deg, var(--violet-600), #241638);
  border: 2px solid rgba(var(--t-gold), 0.45);
}
.pf-id-main { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.pf-id-line { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }

/* mock: `font-family:Unbounded;font-size:17px;font-weight:800` */
.pf-name {
  margin: 0;
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 800;
  /* base.css sets 1.15 on every heading; the mock leaves the name at the
     face's own leading, which is 2px taller and is what makes the identity
     strip 116px rather than 114. */
  line-height: normal;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* mock (phone): the clan tag is bare gold inside the name; on a console it is
   a chip. The chip treatment is kept at both sizes — it is a button, and a
   tappable thing that looks like running text is a target nobody finds. */
.pf-clan {
  flex: 0 0 auto;
  padding: 3px 9px;
  border-radius: 7px;
  background: rgba(var(--t-gold), 0.16);
  border: 1px solid rgba(var(--t-gold), 0.4);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.06em;
  color: var(--gold);
  cursor: pointer;
  transition: background var(--dur-fast), border-color var(--dur-fast);
}
@media (hover: hover) {
  .pf-clan:hover { background: rgba(var(--t-gold), 0.24); border-color: rgba(var(--t-gold), 0.6); }
}
/* mock: `padding:3px 9px;border-radius:7px;font-size:10px;font-weight:700`,
   green when calibrated and violet while it still counts games. */
.pf-cal {
  flex: 0 0 auto;
  padding: 3px 9px;
  border-radius: 7px;
  font-size: 10px;
  font-weight: 700;
  white-space: nowrap;
}
.pf-cal.is-ok {
  background: rgba(var(--t-ok), 0.14);
  border: 1px solid rgba(var(--t-ok), 0.34);
  color: var(--ok);
}
.pf-cal.is-wait {
  background: rgba(var(--t-violet), 0.14);
  border: 1px solid rgba(var(--t-violet), 0.34);
  color: var(--violet-300);
}
/* mock (phone): `font-size:10px;color:rgba(236,231,250,.44)` */
.pf-since { font-size: 10px; color: rgba(var(--t-mute), 0.44); }
.pf-since:empty { display: none; }

/* mock: `display:flex;flex-wrap:wrap;gap:6px` on a phone, `gap:5px` inline
   under the name on a console. */
.pf-marks { order: 4; display: flex; flex-wrap: wrap; gap: 6px; }
.pf-marks:empty { display: none; }
/* mock: `width:28px;height:28px;border-radius:8px;background:rgba(167,139,250,.1);
   border:1px solid rgba(167,139,250,.2)`, and the rarest one rimmed in gold
   with `box-shadow:0 0 14px rgba(241,193,82,.3)`. */
.pf-mark {
  width: 28px;
  height: 28px;
  flex: 0 0 auto;
  padding: 0;
  border-radius: 8px;
  display: grid;
  place-items: center;
  background: rgba(var(--t-violet), 0.1);
  border: 1px solid rgba(var(--t-violet), 0.2);
  cursor: pointer;
  transition: transform var(--dur-fast), border-color var(--dur-fast);
}
.pf-mark.is-top {
  border-color: rgba(var(--t-gold), 0.5);
  box-shadow: 0 0 14px rgba(var(--t-gold), 0.3);
}
@media (hover: hover) {
  .pf-mark:hover { transform: translateY(-1px); border-color: rgba(var(--t-gold), 0.5); }
}
.pf-mark .ach-badge { width: 26px; height: 26px; }

/* ==========================================================================
   THE FOUR SPEED RATINGS
   ========================================================================== */

/* mock (phone): a titled column, `gap:6px`. */
.pf-speeds-sec { order: 5; display: flex; flex-direction: column; gap: 6px; }
.pf-speeds { display: flex; flex-direction: column; gap: 6px; }
.pf-speeds-head { display: flex; align-items: center; }

/* mock: `padding:9px 14px;border-radius:12px;text-align:center;
   border:1px solid rgba(167,139,250,.18);background:rgba(20,13,38,.5)`, and the
   overall card `rgba(241,193,82,.4)` over `rgba(241,193,82,.09)`.
   On a phone the mock lays the three lines out inline; they are kept as a
   centred stack so «№7 из 214» never collides with the rating beside it. */
.pf-rate-card {
  min-width: 0;
  padding: 9px 14px;
  border-radius: 12px;
  text-align: center;
  border: 1px solid rgba(var(--t-violet), 0.18);
  background: var(--panel-soft);
  transition: border-color var(--dur-fast), background var(--dur-fast);
  /* mock (phone): one centred line — «общий 1842 №7 из 214» — because four
     stacked cards at the console's 71px each would be 284px of ratings before
     anything else on the screen. */
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 6px;
}
.pf-rate-card.is-hot {
  border-color: rgba(var(--t-gold), 0.4);
  background: rgba(var(--t-gold), 0.09);
}
@media (hover: hover) {
  .pf-rate-card:hover { border-color: rgba(var(--t-gold), 0.34); }
}
/* mock: `font-size:9px;letter-spacing:.12em;text-transform:uppercase;
   color:rgba(236,231,250,.44)` */
.pf-rate-speed {
  min-width: 0;
  font-size: 10px;
  font-weight: 700;
  text-transform: lowercase;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* mock: `font-family:'JetBrains Mono';font-size:22px;font-weight:700;
   line-height:1.15`, gold on the overall card. */
.pf-rate-value {
  flex: 0 0 auto;
  font-size: 16px;
  font-weight: 700;
  line-height: 1.15;
}
.pf-rate-card.is-hot .pf-rate-value { color: var(--gold); }
.pf-rate-value:empty { display: none; }
/* «№4 из 47» is a phrase, not a quantity, and JetBrains Mono ships here with
   no Cyrillic on purpose — setting the whole string in it renders «из» in
   Manrope at mono's metrics, which reads as a bug. Quantities standing alone
   get `.mono`; phrases containing them get tabular figures and the body face. */
.pf-rate-rank {
  flex: 0 0 auto;
  font-size: 9px;
  color: rgba(var(--t-mute), 0.36);
  font-variant-numeric: tabular-nums;
}
/* Kept for the phrase-vs-quantity rule above: a bare rank set beside prose. */
.pf-spot-val { font-variant-numeric: tabular-nums; font-weight: 700; color: var(--text); }
.pf-score { font-weight: 700; color: var(--text-dim); font-variant-numeric: tabular-nums; }

/* All four speeds unreadable is one admission, and it takes the whole row —
   half a grid track would read as a fifth tile. */
.pf-speeds > .load-fail { grid-column: 1 / -1; }

/* ==========================================================================
   THE THREE STAT TILES — a phone's context column
   ========================================================================== */

/* mock (phone): `display:flex;gap:6px`, each tile `flex:1;min-height:44px;
   padding:9px;border-radius:11px;text-align:center`. Drawn only where the
   shell is not drawing its context column, which holds the same three numbers
   — profile.js toggles `is-off` off the same 800px the shell uses. */
.pf-tiles { order: 2; display: flex; gap: 6px; }
.pf-tiles.is-off { display: none; }
.pf-tile {
  flex: 1 1 0;
  min-width: 0;
  min-height: 44px;
  padding: 9px;
  border-radius: 11px;
  text-align: center;
  cursor: pointer;
  transition: transform var(--dur-fast), border-color var(--dur-fast);
}
.pf-tile-gold { background: rgba(var(--t-gold), 0.09); border: 1px solid rgba(var(--t-gold), 0.24); }
.pf-tile-plain { background: rgba(var(--t-violet), 0.08); border: 1px solid rgba(var(--t-violet), 0.18); }
.pf-tile-ok { background: rgba(var(--t-ok), 0.08); border: 1px solid rgba(var(--t-ok), 0.22); }
@media (hover: hover) {
  .pf-tile:hover { transform: translateY(-1px); }
}
/* mock: `font-family:'JetBrains Mono';font-size:16px;font-weight:700` */
.pf-tile-val { display: block; font-size: 16px; font-weight: 700; line-height: 1.2; }
.pf-tile-gold .pf-tile-val { color: var(--gold); }
.pf-tile-ok .pf-tile-val { color: var(--ok); }
/* mock: `font-size:8px;color:rgba(236,231,250,.42)` */
.pf-tile-lbl {
  display: block;
  font-size: 8px;
  color: rgba(var(--t-mute), 0.42);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ==========================================================================
   THE CARD — one frame for every block on the screen
   ========================================================================== */

/* mock: `border-radius:16px;border:1px solid rgba(167,139,250,.18);
   background:rgba(18,12,34,.56);padding:13px 15px` */
.pf-card {
  min-width: 0;
  border-radius: 16px;
  border: 1px solid rgba(var(--t-violet), 0.18);
  background: var(--panel);
  padding: 13px 15px;
}
/* mock: `display:flex;align-items:center;margin-bottom:10px` with the title in
   `Unbounded 11px/700`, `letter-spacing:.1em`, uppercase. On a phone the mock
   drops the title to 10px. */
.pf-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}
.pf-card-title {
  font-family: var(--font-display);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.pf-card-head .grow { flex: 1 1 auto; }
.pf-card-body { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
/* mock: `font-size:11px;color:rgba(236,231,250,.44)` beside the rule. */
.pf-card-count { flex: 0 0 auto; font-size: 10px; color: rgba(var(--t-mute), 0.44); }
/* mock: `font-size:9px;color:rgba(236,231,250,.38);text-wrap:pretty` */
.pf-hint {
  font-size: 9px;
  line-height: 1.4;
  color: rgba(var(--t-mute), 0.38);
  text-wrap: pretty;
}
.pf-card-head .pf-hint {
  flex: 0 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* mock: `padding:5px 10px;border-radius:9px;border:1px solid rgba(167,139,250,.28);
   background:rgba(20,13,38,.5);font-size:9px;font-weight:700;
   color:rgba(196,181,253,.92)` */
.pf-link {
  flex: 0 0 auto;
  white-space: nowrap;
  padding: 5px 10px;
  border-radius: 9px;
  border: 1px solid rgba(var(--t-violet), 0.28);
  background: var(--panel-soft);
  font-size: 9px;
  font-weight: 700;
  color: rgba(196, 181, 253, 0.92);
  cursor: pointer;
  transition: border-color var(--dur-fast), color var(--dur-fast);
}
@media (hover: hover) {
  .pf-link:hover { border-color: rgba(var(--t-gold), 0.5); color: var(--gold); }
}

/* ==========================================================================
   THE BODY — one column on a phone, the mock's 1.2 / 1 on a console
   ========================================================================== */

.pf-body { order: 7; display: grid; grid-template-columns: minmax(0, 1fr); gap: 11px; }
.pf-col { display: contents; }
.pf-extras { order: 8; display: grid; grid-template-columns: minmax(0, 1fr); gap: 11px; align-items: start; }
/* mock (phone): the radar block sits above the year and the year above the
   grid of badges; the form strip has no phone block of its own, so it takes
   the tail rather than pushing the ratings down. */
.pf-radar-card { order: 6; }
.pf-year { order: 7; }
.pf-ach { order: 8; }
.pf-form { order: 9; }

/* ==========================================================================
   «ФОРМА · 30 ДНЕЙ»
   ========================================================================== */

.pf-form .pf-card-head { margin-bottom: 9px; }
/* mock: `height:62px;display:flex;align-items:flex-end;gap:3px` */
.pf-form-bars {
  height: 62px;
  display: flex;
  align-items: flex-end;
  gap: 3px;
}
/* mock: `flex:1;border-radius:2px;background:linear-gradient(180deg,
   rgba(167,139,250,.75),rgba(124,58,237,.2))`, the last five days in gold and
   the final one carrying `box-shadow:0 0 14px rgba(241,193,82,.6)`. */
.pf-bar {
  flex: 1 1 0;
  min-width: 0;
  border-radius: 2px;
  background: linear-gradient(180deg, rgba(var(--t-violet), 0.75), rgba(124, 58, 237, 0.2));
}
.pf-bar.is-hot { background: linear-gradient(180deg, var(--gold), rgba(124, 58, 237, 0.2)); }
.pf-bar.is-last { box-shadow: 0 0 14px rgba(var(--t-gold), 0.6); }
/* A day with no game is not a rating of zero. It is the stub, and the stubs
   are what make a week off visible as a week off. */
.pf-bar.is-blank { background: rgba(var(--t-violet), 0.16); box-shadow: none; }
/* mock: `font-size:11px;font-weight:800;color:#35c783` */
.pf-delta { flex: 0 0 auto; font-size: 11px; font-weight: 800; color: rgba(var(--t-mute), 0.34); }
.pf-delta.is-up { color: var(--ok); }
.pf-delta.is-down { color: var(--danger-soft); }

/* ==========================================================================
   «ВОСХОЖДЕНИЯ» — the badge grid
   ========================================================================== */

/* mock: the panel has no padding of its own; the rule carries `padding:11px 15px`
   and `border-bottom:1px solid rgba(167,139,250,.14)`, the grid `padding:11px`. */
.pf-ach { padding: 0; overflow: hidden; }
.pf-ach .pf-card-head {
  margin-bottom: 0;
  padding: 11px 15px;
  border-bottom: 1px solid var(--hairline);
}
.pf-ach .pf-card-body { padding: 11px; }

/* mock (phone) `repeat(4,minmax(0,1fr));gap:7px`, console
   `repeat(6,minmax(0,1fr));gap:8px`.
   auto-fill against the tile's real floor rather than two hard counts: a tile
   is a 54px badge inside `padding:11px 7px 9px`, so 68px is the width below
   which the art starts clipping. At the mock's own console panel width that
   resolves to exactly six columns and at its phone width to exactly four —
   and at the in-between widths this arena actually has (a 858px scene gives
   the left column 442px) it gives five instead of six overflowing. */
.pf-ach-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(68px, 1fr));
  gap: 7px;
  align-content: start;
}
/* mock: `display:flex;flex-direction:column;align-items:center;gap:5px;
   padding:11px 7px 9px;border-radius:13px;text-align:center` over a domain
   tint — `border:1px solid rgba(hue,.3);background:rgba(hue,.07)` when held,
   `.13` and `.028` when not. */
.pf-ach-tile {
  --pf-dom: var(--t-gold);
  position: relative;
  min-width: 0;
  min-height: 44px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 11px 7px 9px;
  border-radius: 13px;
  text-align: center;
  cursor: pointer;
  border: 1px solid rgba(var(--pf-dom), 0.3);
  background: rgba(var(--pf-dom), 0.07);
  transition: transform var(--dur-fast), border-color var(--dur-fast);
}
.pf-ach-tile.is-locked {
  border-color: rgba(var(--pf-dom), 0.13);
  background: rgba(var(--pf-dom), 0.028);
}
@media (hover: hover) {
  .pf-ach-tile:hover { transform: translateY(-2px); border-color: rgba(var(--pf-dom), 0.5); }
}
/* mock: the badge is 54px inside the tile. */
.pf-ach-tile .ach-badge { width: 54px; height: 54px; }
/* mock: `font-size:9.5px;font-weight:800;line-height:1.25;
   color:rgba(236,231,250,.92)`, and `600` / `.46` when the entry is locked. */
.pf-ach-name {
  font-size: 9.5px;
  font-weight: 800;
  line-height: 1.25;
  color: rgba(var(--t-mute), 0.92);
  text-wrap: pretty;
  overflow-wrap: break-word;
  /* Three lines is what the mock's own longest tile takes at six columns;
     past that the title is a paragraph and the tooltip carries it. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.pf-ach-tile.is-locked .pf-ach-name { font-weight: 600; color: rgba(var(--t-mute), 0.46); }
/* mock: the step numeral rides the badge's own corner. Progress is not in the
   mock at all — it is here because the only locked entries this screen can
   name are the three the player is closest to, and «9/10» is the reason they
   are on the card. */
.pf-ach-prog { font-size: 8.5px; color: rgba(var(--t-mute), 0.5); }

/* THE DOMAIN TINT lives on the card under the badge, not behind the emblem:
   the emblems ship with an opaque indigo ground, so a field layer behind them
   is invisible. Eighteen catalogue domains, the mock's seven hues. The class
   is `pfd-<domain>` rather than the gallery's area name so the two screens
   cannot silently restyle each other, and an unnamed domain keeps the default
   gold above rather than losing its border. */
.pf-ach-tile.pfd-chess { --pf-dom: 124, 58, 237; }
.pf-ach-tile.pfd-battleship,
.pf-ach-tile.pfd-checkers,
.pf-ach-tile.pfd-nardy,
.pf-ach-tile.pfd-durak,
.pf-ach-tile.pfd-typing_race,
.pf-ach-tile.pfd-arcomage { --pf-dom: 45, 212, 191; }
.pf-ach-tile.pfd-platform,
.pf-ach-tile.pfd-training,
.pf-ach-tile.pfd-tournament,
.pf-ach-tile.pfd-season,
.pf-ach-tile.pfd-spectator { --pf-dom: var(--t-gold); }
.pf-ach-tile.pfd-social,
.pf-ach-tile.pfd-community,
.pf-ach-tile.pfd-clan,
.pf-ach-tile.pfd-patron { --pf-dom: 236, 72, 153; }
.pf-ach-tile.pfd-meta { --pf-dom: var(--t-violet); }
.pf-ach-tile.pfd-time { --pf-dom: 96, 165, 250; }

/* ==========================================================================
   «РАДАР НАВЫКОВ»
   ========================================================================== */

/* mock: `display:flex;flex-direction:column;gap:7px` */
.pf-radar { display: flex; flex-direction: column; gap: 6px; }
/* mock (phone): `min-height:44px;gap:8px;padding:5px 6px;border-radius:9px`,
   console `gap:9px;padding:4px 6px`. */
.pf-radar-row {
  width: 100%;
  min-height: 44px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 6px;
  border-radius: 9px;
  background: transparent;
  text-align: left;
  transition: background var(--dur-fast);
}
@media (hover: hover) {
  .pf-radar-row:hover { background: rgba(var(--t-violet), 0.08); }
}
/* mock (phone): `width:82px;font-size:9px;color:rgba(236,231,250,.55)` */
.pf-radar-name {
  flex: 0 0 auto;
  width: 82px;
  font-size: 9px;
  color: rgba(var(--t-mute), 0.55);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* mock (phone): `height:5px;border-radius:3px;background:rgba(167,139,250,.12)`,
   console `height:8px;border-radius:5px;background:rgba(167,139,250,.13)`. */
.pf-radar-row .bar {
  flex: 1 1 auto;
  min-width: 0;
  height: 5px;
  border-radius: 3px;
  background: rgba(var(--t-violet), 0.12);
  overflow: hidden;
}
.pf-radar-row .bar-fill { height: 100%; border-radius: 3px; }
/* mock: `linear-gradient(90deg, <band>, rgba(124,58,237,.6))` with the band
   green above 70%, gold above 45% and red below — a rating is a standing, and
   a standing drawn in one colour is a bar with no reading on it. The bands are
   the server's own normalisation of 1000-2500, so «red» here means «near the
   floor of the scale», not «something is wrong». */
.pf-radar-row .bar-fill {
  background: linear-gradient(90deg, var(--danger), rgba(124, 58, 237, 0.6));
  box-shadow: 0 0 10px rgba(var(--t-danger), 0.4);
}
.pf-radar-row.band-mid .bar-fill {
  background: linear-gradient(90deg, var(--gold), rgba(124, 58, 237, 0.6));
  box-shadow: 0 0 10px rgba(var(--t-gold), 0.5);
}
.pf-radar-row.band-hi .bar-fill {
  background: linear-gradient(90deg, var(--ok), rgba(124, 58, 237, 0.6));
  box-shadow: 0 0 10px rgba(var(--t-ok), 0.5);
}
/* mock: `font-family:'JetBrains Mono';font-size:9px;font-weight:700;
   color:rgba(236,231,250,.6)`, right-aligned in a 34px column on a console. */
.pf-radar-val {
  flex: 0 0 auto;
  font-size: 9px;
  font-weight: 700;
  color: rgba(var(--t-mute), 0.6);
}
.pf-radar-row.is-top .pf-radar-val { color: var(--gold); }
.pf-radar-row.is-unplayed { opacity: 0.44; }
.pf-radar-row.is-unplayed .pf-radar-val { color: var(--text-faint); }

/* ==========================================================================
   «ГОД НА АРЕНЕ»
   ========================================================================== */

/* mock (phone): `repeat(26,1fr);grid-auto-rows:9px;gap:2px`, console
   `repeat(37,1fr);gap:3px` over `aspect-ratio:37/7`. 259 cells either way —
   the last thirty-seven weeks, oldest first, filled row by row so a streak
   reads as a horizontal run rather than as a column. */
.pf-heat-grid {
  display: grid;
  grid-template-columns: repeat(26, 1fr);
  grid-auto-rows: 9px;
  gap: 2px;
}
/* mock: `border-radius:2px` and five steps from `rgba(167,139,250,.07)` to
   solid gold. Violet for a quiet day, gold for a loud one — the same two
   colours the fire uses, so the two blocks read as one year. */
.pf-heat-cell { border-radius: 2px; background: rgba(var(--t-violet), 0.07); }
.pf-heat-cell.l1 { background: rgba(var(--t-violet), 0.24); }
.pf-heat-cell.l2 { background: rgba(var(--t-violet), 0.5); }
.pf-heat-cell.l3 { background: rgba(var(--t-gold), 0.6); }
.pf-heat-cell.l4 { background: var(--gold); }
/* mock: `display:flex;align-items:center;gap:7px;margin-top:11px;font-size:9px;
   color:rgba(236,231,250,.34)` with 9x9 swatches. */
.pf-heat-legend {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 9px;
  color: rgba(var(--t-mute), 0.34);
  flex-wrap: wrap;
}
.pf-heat-legend .grow { flex: 1 1 auto; }
.pf-heat-key { width: 9px; height: 9px; flex: 0 0 auto; }

/* mock: `margin-top:12px;display:grid;grid-template-columns:repeat(3,1fr);gap:8px` */
.pf-year-tiles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}
/* mock: `padding:10px;border-radius:11px;background:rgba(167,139,250,.07)`, and
   the tile with no number `rgba(167,139,250,.04)` inside a dashed rule. */
.pf-year-tile {
  min-width: 0;
  padding: 10px;
  border-radius: 11px;
  background: rgba(var(--t-violet), 0.07);
}
.pf-year-tile.is-blank {
  background: rgba(var(--t-violet), 0.04);
  border: 1px dashed rgba(var(--t-violet), 0.16);
}
/* mock: `font-family:'JetBrains Mono';font-size:15px;font-weight:700` */
.pf-year-val { font-size: 15px; font-weight: 700; line-height: 1.2; }
.pf-year-tile.is-gold .pf-year-val { color: var(--gold); }
/* mock: `font-size:9px;color:rgba(236,231,250,.42);line-height:1.3` */
.pf-year-lbl { font-size: 9px; line-height: 1.3; color: rgba(var(--t-mute), 0.42); }
/* mock: `font-size:8px;color:rgba(236,231,250,.28);line-height:1.35` */
.pf-year-note { font-size: 8px; line-height: 1.35; color: rgba(var(--t-mute), 0.28); text-wrap: pretty; }

/* ==========================================================================
   THE FIRE
   ========================================================================== */

/* mock (phone): its own card — `display:flex;flex-direction:column;gap:9px;
   padding:13px 14px;border-radius:15px;border:1px solid rgba(hue,.34);
   background:linear-gradient(140deg,rgba(hue,.09),rgba(18,12,34,.6))`.
   The hue is gold while the fire is lit and violet-grey when it is not. There
   is no red in any state: a broken streak is a fact, not an alarm. */
.pf-fire {
  --pf-fire: var(--t-gold);
  order: 3;
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 13px 14px;
  border-radius: 15px;
  border: 1px solid rgba(var(--pf-fire), 0.34);
  background: linear-gradient(140deg, rgba(var(--pf-fire), 0.09), var(--panel));
}
.pf-fire.is-lost,
.pf-fire.is-new,
.pf-fire.is-blind {
  --pf-fire: var(--t-violet);
  border-color: rgba(var(--pf-fire), 0.22);
  background: linear-gradient(140deg, rgba(var(--pf-fire), 0.05), var(--panel));
}
.pf-fire-head { display: flex; align-items: center; gap: 9px; }
.pf-fire-head .grow { flex: 1 1 auto; }
/* mock: `font-size:21px;line-height:1;filter:drop-shadow(0 0 10px rgba(hue,.55))` */
.pf-fire-glyph {
  font-size: 21px;
  line-height: 1;
  filter: drop-shadow(0 0 10px rgba(var(--pf-fire), 0.55));
}
.pf-fire.is-lost .pf-fire-glyph,
.pf-fire.is-new .pf-fire-glyph,
.pf-fire.is-blind .pf-fire-glyph { filter: grayscale(1); opacity: 0.45; }
.pf-fire-count { display: flex; align-items: baseline; gap: 4px; min-width: 0; }
/* mock: `font-family:'JetBrains Mono';font-size:22px;font-weight:700;
   line-height:1` on a phone, 20px inside the year card. */
.pf-fire-days { font-size: 22px; font-weight: 700; line-height: 1; color: var(--gold); }
.pf-fire.is-lost .pf-fire-days,
.pf-fire.is-new .pf-fire-days,
.pf-fire.is-blind .pf-fire-days { color: var(--violet-300); }
/* mock: `font-size:9px;letter-spacing:.1em;text-transform:uppercase;
   color:rgba(236,231,250,.4)` */
.pf-fire-word {
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(var(--t-mute), 0.4);
}
/* mock: `font-family:Unbounded;font-size:10px;font-weight:700;
   letter-spacing:.1em;text-transform:uppercase;color:rgba(236,231,250,.5)` */
.pf-fire-kicker {
  flex: 0 0 auto;
  font-family: var(--font-display);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(var(--t-mute), 0.5);
}
/* mock: `font-family:Unbounded;font-size:12px;font-weight:800;line-height:1.3` */
.pf-fire-line {
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 800;
  line-height: 1.3;
  color: var(--gold);
  text-wrap: pretty;
}
.pf-fire.is-lost .pf-fire-line,
.pf-fire.is-new .pf-fire-line,
.pf-fire.is-blind .pf-fire-line { color: rgba(var(--t-mute), 0.82); }
/* mock: `font-size:10px;line-height:1.5;color:rgba(236,231,250,.5)` */
.pf-fire-sub { font-size: 10px; line-height: 1.5; color: rgba(var(--t-mute), 0.5); text-wrap: pretty; }
/* mock: `display:flex;align-items:flex-end;gap:1.5px;height:24px` on a phone,
   20px inside the year card. */
.pf-fire-spark { display: flex; align-items: flex-end; gap: 1.5px; height: 24px; }
.pf-fire-bar {
  flex: 1 1 0;
  min-width: 0;
  border-radius: 1.5px;
  align-self: flex-end;
  background: rgba(var(--t-violet), 0.16);
}
.pf-fire-bar.is-on {
  background: linear-gradient(180deg, rgba(var(--pf-fire), 0.62), rgba(var(--pf-fire), 0.16));
}
/* mock: `font-size:9px;color:rgba(236,231,250,.32)` */
.pf-fire-note { font-size: 9px; color: rgba(var(--t-mute), 0.32); }
/* mock: `min-height:38px;padding:10px 13px;border-radius:11px;
   background:rgba(hue,.14);border:1px solid rgba(hue,.45);
   font-family:Unbounded;font-size:11px;font-weight:800` */
.pf-fire-act {
  align-self: flex-start;
  white-space: nowrap;
  min-height: 38px;
  padding: 10px 13px;
  border-radius: 11px;
  background: rgba(var(--pf-fire), 0.14);
  border: 1px solid rgba(var(--pf-fire), 0.45);
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 800;
  color: var(--gold);
  cursor: pointer;
  transition: transform var(--dur-fast), background var(--dur-fast);
}
.pf-fire.is-lost .pf-fire-act,
.pf-fire.is-new .pf-fire-act,
.pf-fire.is-blind .pf-fire-act { color: var(--violet-300); }
@media (hover: hover) {
  .pf-fire-act:hover { transform: translateY(-1px); background: rgba(var(--pf-fire), 0.22); }
}

/* ==========================================================================
   THE CONSOLE — everything above, at two columns
   ========================================================================== */

@container scene (min-width: 620px) {
  /* mock: `padding:16px 18px;gap:14px` */
  #view.pf-host,
  #scene.shell-scene #view.pf-host { padding: 16px 18px; }
  .pf-console { gap: 14px; }

  /* mock: `display:flex;align-items:center;flex-wrap:wrap;gap:14px;
     padding:13px 16px;border-radius:18px;border:1px solid rgba(241,193,82,.28);
     background:linear-gradient(115deg,rgba(38,24,66,.8),rgba(18,12,34,.6))`.
     A grid rather than that flex row, because the marks sit UNDER the name and
     beside the avatar, which one flex line cannot express. Both wrappers fold
     away with `display: contents`, so the five parts are grid items here and
     ordered rows on a phone — one DOM, two layouts. */
  .pf-id {
    order: 0;
    display: grid;
    grid-template-columns: 66px minmax(0, 1fr) auto;
    grid-template-areas: "av name speeds" "av marks speeds";
    align-items: center;
    column-gap: 14px;
    row-gap: 8px;
    padding: 13px 16px;
    border-radius: 18px;
    border: 1px solid rgba(var(--t-gold), 0.28);
    background: linear-gradient(115deg, rgba(38, 24, 66, 0.8), var(--panel));
    position: relative;
    overflow: hidden;
  }
  .pf-id-head { display: contents; }
  .pf-speeds-sec { display: contents; }
  .pf-speeds-head { display: none; }
  /* mock: `right:-40px;top:-40px;width:200px;height:200px;border-radius:50%;
     background:radial-gradient(circle,rgba(241,193,82,.18),transparent 68%)` */
  .pf-id-glow {
    display: block;
    position: absolute;
    right: -40px;
    top: -40px;
    width: 200px;
    height: 200px;
    border-radius: 50%;
    pointer-events: none;
    background: radial-gradient(circle, rgba(var(--t-gold), 0.18), transparent 68%);
  }
  /* mock: 66x66, `border-radius:18px;font-size:31px;
     border:2px solid rgba(241,193,82,.5);box-shadow:0 0 30px rgba(124,58,237,.45)` */
  .pf-avatar {
    grid-area: av;
    position: relative;
    width: 66px;
    height: 66px;
    border-radius: 18px;
    font-size: 31px;
    border-color: rgba(var(--t-gold), 0.5);
    box-shadow: 0 0 30px rgba(124, 58, 237, 0.45);
  }
  .pf-id-main { grid-area: name; position: relative; gap: 5px; }
  /* mock: `font-size:22px` */
  .pf-name { font-size: 22px; }
  /* mock: `font-size:12px;color:rgba(236,231,250,.5)` */
  .pf-since { font-size: 12px; color: var(--text-soft); }
  /* mock: `display:flex;gap:5px;margin-top:3px` under the name. */
  .pf-marks { grid-area: marks; position: relative; gap: 5px; }
  /* mock: `display:flex;gap:8px` on the right of the strip. */
  .pf-speeds {
    grid-area: speeds;
    position: relative;
    flex-direction: row;
    gap: 8px;
  }

  .pf-tiles { display: none; }

  /* mock: `grid-template-columns:minmax(0,1.2fr) minmax(0,1fr);gap:12px`,
     each column `display:flex;flex-direction:column;gap:12px`. */
  .pf-body {
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
    gap: 12px;
  }
  .pf-col { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
  .pf-extras { grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); gap: 12px; }
  /* The phone's running order is a stack; the console's is the two columns'
     own DOM order, so every `order` set above is handed back here. */
  .pf-form,
  .pf-ach,
  .pf-radar-card,
  .pf-year,
  .pf-body,
  .pf-extras { order: 0; }
  /* The fire is `order: 3` in the phone stack; inside the year card it is
     simply the block between the legend and the three tiles. */
  .pf-year .pf-fire { order: 0; }

  /* mock: the title is 11px on a console. */
  .pf-card-title { font-size: 11px; }
  .pf-card-count { font-size: 11px; }

  /* mock: `gap:8px` and 6 columns. */
  .pf-ach-grid { gap: 8px; }

  /* mock: `padding:9px 14px;text-align:center` over three stacked lines — the
     9px uppercase label, the 22px figure, the 9px rank. */
  .pf-rate-card { display: block; }
  .pf-rate-speed {
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: rgba(var(--t-mute), 0.44);
  }
  .pf-rate-value { font-size: 22px; }
  .pf-radar { gap: 7px; }

  /* mock: `gap:9px;padding:4px 6px`, a 96px label, an 8px track and a 34px
     right-aligned figure. A console has a pointer, so the 44px touch floor
     does not apply and the rows close up to the mock's rhythm. */
  .pf-radar-row { min-height: 0; gap: 9px; padding: 4px 6px; }
  .pf-radar-name { width: 96px; font-size: 10px; font-weight: 700; color: rgba(var(--t-mute), 0.66); }
  .pf-radar-row .bar { height: 8px; border-radius: 5px; background: rgba(var(--t-violet), 0.13); }
  .pf-radar-row .bar-fill { border-radius: 5px; }
  .pf-radar-val { width: 34px; text-align: right; font-size: 10px; font-weight: 700; color: rgba(var(--t-mute), 0.7); }

  /* mock: the year card stacks at `margin-top:11px` (legend), `12px` (fire)
     and `12px` (the three tiles); the radar's hint sits `8px` under its rows. */
  .pf-year .pf-card-body { gap: 12px; }
  .pf-year .pf-heat-legend { margin-top: -1px; }
  .pf-radar-card .pf-card-body { gap: 8px; }

  /* mock: `repeat(37,1fr);gap:3px;width:100%;aspect-ratio:37/7` */
  .pf-heat-grid {
    grid-template-columns: repeat(37, 1fr);
    grid-auto-rows: auto;
    gap: 3px;
    aspect-ratio: 37 / 7;
  }

  /* mock: the fire inside «Год на арене» is the same card in one line —
     `padding:10px 12px;border-radius:13px;border:1px solid rgba(hue,.3);
     background:rgba(hue,.05)` with the glyph at 18px, the count at 20px and
     the strip at 20px. The card's own sub-line goes: inside the year block the
     headline is the whole message and a second sentence pushes the three year
     tiles off the bottom of the scene. */
  .pf-year .pf-fire {
    padding: 10px 12px;
    border-radius: 13px;
    border-color: rgba(var(--pf-fire), 0.3);
    background: rgba(var(--pf-fire), 0.05);
  }
  .pf-year .pf-fire-glyph { font-size: 18px; }
  .pf-year .pf-fire-days { font-size: 20px; }
  .pf-year .pf-fire-line { font-size: 11px; }
  .pf-year .pf-fire-spark { height: 20px; }
  .pf-year .pf-fire-sub { display: none; }
  /* Except when the sub-line is the whole message: «Про огонь пока ничего не
     известно» with nothing under it is a headline that explains nothing. */
  .pf-year .pf-fire.is-blind .pf-fire-sub { display: block; }
}

/* The identity strip's three-across arrangement needs 820 scene pixels and
   says so. The four speed cards want 355 of them and the avatar 66; below 820
   the name column is left with about 130, which truncates «@anton» to nothing
   before it truncates anything else. So the cards take a row of their own —
   still four across, still the mock's card — and the name gets the width back.
   Measured, not guessed: 36 padding + 2 border + 66 avatar + 14 + 355 speeds
   + 14 = 487 of chrome before the name has a pixel. */
@container scene (min-width: 620px) and (max-width: 819px) {
  .pf-id {
    grid-template-columns: 66px minmax(0, 1fr);
    grid-template-areas: "av name" "av marks" "speeds speeds";
  }
  .pf-speeds {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* ==========================================================================
   THE BLOCKS THE MOCK'S DOSSIER DOES NOT DRAW
   ==========================================================================
   «Досье» (the stamped marks), «Клады серий» and the seasons. They keep the
   card frame above so the screen reads as one design, and they sit under the
   mock's own four blocks rather than among them.

   The section chip row and the language switch used to be here too. Both were
   navigation, and the console rail plus the «Ещё» sheet carry all of it now —
   see the note in views/profile.js. The push card left with RB4: the mock's
   dossier does not draw it, and the same card still stands on /dashboard. */

/* === «Досье» — the ridge-and-above marks, stamped ==========================

   The Ridge unlock toast says «отметка легла в досье», so there has to be a
   досье. It is drawn as ink on paper rather than as more cards, and the whole
   look is CSS: no images, because a stamp that is an asset cannot carry a
   title of arbitrary length in two languages.

   The ink is a comma triple in a custom property rather than a colour, so one
   declaration feeds the text, the rules and the wash: `rgb(var(--ink))`,
   `rgba(var(--ink), 0.5)`. color-mix() would be tidier and is not safe to
   assume in the older Android Telegram webviews this app is opened in. */

.pf-stamps {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  /* A stamp rotated 3° overhangs its grid cell by roughly 4 px vertically; the
     row gap absorbs that, and the padding keeps the outermost stamps' corners
     off the card's own edge. */
  gap: 14px 10px;
  padding: 4px 2px;
}
/* The scene, not the window: at a 1280px window the rail and the context
   column leave the scene ~858px, and the stamps live inside a panel inside
   that. A media query here would give three columns to a 300px-wide panel. */
@container scene (min-width: 900px) {
  .pf-stamps { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.pf-stamp {
  --ink: 168, 190, 220;
  position: relative;
  min-width: 0;
  padding: 7px 8px 5px;
  text-align: center;
  color: rgb(var(--ink));
  background: rgba(var(--ink), 0.05);
  border: 1.25px solid rgba(var(--ink), 0.55);
  /* Four different radii: a rubber stamp is cut by hand and a perfect
     rectangle reads as a UI chip. */
  border-radius: 3px 5px 3px 4px;
  opacity: 0.92;
}

/* The frame counts the tier so the ink does not have to: one rule at Гребень,
   two at Вершина, two and a heavy outer at «Зоне смерти». The gallery gives
   Зоне смерти a black chip in both themes; that is right for a chip on a card
   and wrong here, where a solid black block on a paper metaphor would have to
   invert its own ink on the light theme to stay legible. Weight carries it. */
.pf-stamp::before {
  content: "";
  position: absolute;
  inset: 3px;
  border: 1px solid rgba(var(--ink), 0.3);
  border-radius: 2px;
  pointer-events: none;
  opacity: 0;
}
.pf-stamp.tier-summit::before,
.pf-stamp.tier-death_zone::before { opacity: 1; }
.pf-stamp.tier-death_zone { border-width: 2.5px; }

.pf-stamp.ink-red { --ink: 255, 93, 108; }
.pf-stamp.ink-gold { --ink: 241, 193, 82; }

.pf-stamp-tier {
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.78;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pf-stamp-name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 11.5px;
  line-height: 1.2;
  margin: 3px 0 4px;
  overflow-wrap: break-word;
  /* Two lines of room whatever the title is, so the dates across a row sit on
     one line; three is the ceiling, and «Первовосхождение: Картограф» — the
     longest in the catalogue at 27 characters — needs two of them at 390 px. */
  min-height: 2.4em;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-shadow: 0 0 7px rgba(var(--ink), 0.22);
}
.pf-stamp-date {
  font-size: 9.5px;
  font-variant-numeric: tabular-nums;
  opacity: 0.72;
  padding-top: 3px;
  border-top: 1px solid rgba(var(--ink), 0.28);
}

.pf-stamps-more { align-self: flex-start; }

/* The empty dossier no longer draws a card (RB4, the mock's «empty sections
   never pay for glass»): with no marks proven the section leaves the layout,
   so the empty-state rules it had are gone with it. */

/* === «Главная гордость» — the rarest mark, named at the head of the card ===

   Same ink vocabulary as the stamps, deliberately: this line IS the first
   stamp, said in words, and a second palette would make them two facts. */

.pf-pride {
  --ink: 168, 190, 220;
  padding: 6px 10px;
  background: rgba(var(--ink), 0.05);
  border-left: 2px solid rgba(var(--ink), 0.6);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
}
.pf-pride.ink-red { --ink: 255, 93, 108; }
.pf-pride.ink-gold { --ink: 241, 193, 82; }

.pf-pride-top { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.pf-pride-lbl {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(var(--ink), 0.9);
}
.pf-pride-note {
  font-size: 9.5px;
  letter-spacing: 0.06em;
  color: var(--text-faint);
}
.pf-pride-name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 12.5px;
  line-height: 1.25;
  margin-top: 1px;
  color: rgb(var(--ink));
  overflow-wrap: anywhere;
}
.pf-pride-desc {
  margin-top: 2px;
  font-size: 11.5px;
  line-height: 1.4;
  color: var(--text-dim);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

@container scene (min-width: 620px) {
  .pf-pride-name { font-size: 13.5px; }
}

/* ==========================================================================
   «АНАЛИТИКА» — views/analytics.js
   ==========================================================================
   The screen the radar and the year moved OFF. What is left is the one thing
   that is about a rating over time rather than about a player right now: the
   90-day timeline, and the strongest opponent ever beaten.

   It lives in this file because index.html is frozen for this round and no
   new stylesheet can be linked. When the freeze lifts this block is a
   `css/views/analytics.css` in one cut — nothing above it refers to `anl-`.
   ========================================================================== */

.anl-view { gap: 12px; }
.anl-view .panel-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px 13px 13px;
  overflow: visible;
}
.anl-view .panel-head .sub {
  flex: 0 0 auto;
  font-size: 10px;
  text-transform: none;
  letter-spacing: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 52%;
}
.anl-title {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-h3);
}

/* The timeline as the console's spark: a run of readings side by side, each
   toned against where the period STARTED — so «below the line» is visible as
   colour rather than as a number the player has to hold in their head. */
.anl-view .spark-tape { gap: 2px; }
.anl-view .spark-day { min-height: 3px; }

.anl-when {
  flex: 0 1 auto;
  min-width: 0;
  font-size: 11px;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.anl-num { flex: 0 0 auto; font-size: 12px; font-weight: 700; color: var(--text); }
.anl-up { color: var(--ok); }
.anl-down { color: var(--danger-soft); }

.anl-best-val {
  font-size: 30px;
  font-weight: 700;
  line-height: 1;
  color: var(--gold);
  text-shadow: 0 0 22px rgba(var(--t-gold), 0.35);
}
