/* Page sheet for honor.php. Linked after style.css by includes/css.php,
   so these rules win ties against the shared sheet. */

/* "Online now" indicator, identical to rank.php's. */
.online-dot {
    display: inline-block;
    width: 8px; height: 8px;
    margin-left: 7px;
    border-radius: 50%;
    background: var(--sage);
    vertical-align: middle;
    box-shadow: 0 0 6px rgba(111, 143, 95, 0.55);
    animation: onlinePulse 2.4s ease-out infinite;
}
@keyframes onlinePulse {
    0%   { box-shadow: 0 0 6px rgba(111,143,95,0.55), 0 0 0 0   rgba(111,143,95,0.5); }
    70%  { box-shadow: 0 0 6px rgba(111,143,95,0.55), 0 0 0 5px rgba(111,143,95,0);   }
    100% { box-shadow: 0 0 6px rgba(111,143,95,0.55), 0 0 0 0   rgba(111,143,95,0);   }
}

/* The wrapping insurance this page used to declare (the first label
   carries the whole season string, which is long in some translations,
   and .glass-card's overflow:hidden clips instead of scrolling) now
   lives on .mode-toggle in style.css, for rank.php's three tabs too. */

/* ── Hero band ─────────────────────────────────────────────────────
   Season switch on the left, the numbers that describe this board on
   the right. Gives the page a masthead instead of opening straight
   into a control, and gives a returning player their own standing
   without hunting the table for it. */
.hero-band {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
    padding-bottom: 20px;
    margin-bottom: 22px;
    border-bottom: 1px solid var(--border);
}
.hero-band .mode-toggle { margin-bottom: 0; }

/* One tab per season, newest first. A green pip marks the season that
   is still running, so it stays findable once you have scrolled back
   through a year of them and the leftmost tab is off your radar. */
.season-tabs .mode-btn.is-live::before {
    content: '';
    display: inline-block;
    width: 6px; height: 6px;
    margin-right: 7px;
    border-radius: 50%;
    background: var(--sage);
    vertical-align: middle;
    box-shadow: 0 0 5px rgba(111, 143, 95, 0.6);
}
.season-tabs .mode-btn.active.is-live::before { background: #fff; box-shadow: none; }

/* ── Your standing ─────────────────────────────────────────────────
   One panel for the signed-in account, with a character selector when
   the account owns more than one. Switching is client-side: every
   character's numbers are already on the page, so there is nothing to
   fetch and no reload. */
.me-panel {
    min-width: 300px;
    background: linear-gradient(180deg, rgba(154, 35, 35, 0.10), rgba(255,255,255,0.018));
    border: 1px solid var(--border);
    border-top: 2px solid var(--accent);
    border-radius: 3px;
}
.me-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--border);
}
.me-icon { color: var(--accent); flex-shrink: 0; }
.me-label {
    font-size: var(--fs-2xs);
    font-weight: 700;
    letter-spacing: var(--ls-chip);
    text-transform: uppercase;
    color: var(--text-dim);
}
.me-select, .me-only {
    margin-left: auto;
    max-width: 168px;
    font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    font-size: var(--fs-sm);
    font-weight: 700;
    color: var(--text-main);
}
.me-select {
    background: #0a0a0b;
    border: 1px solid var(--border-light);
    border-radius: 2px;
    padding: 3px 6px;
    cursor: pointer;
}
.me-select:focus { outline: none; border-color: var(--accent); }
.me-only { text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.me-only:hover { color: var(--accent-hover); }

.me-stats { display: grid; grid-template-columns: repeat(4, 1fr); }
.me-stat {
    padding: 8px 12px 10px;
    border-left: 1px solid var(--border);
    min-width: 0;
}
.me-stat:first-child { border-left: none; }
.me-k {
    display: block;
    font-size: var(--fs-2xs);
    letter-spacing: var(--ls-chip);
    text-transform: uppercase;
    color: var(--text-dim);
    margin-bottom: 2px;
    line-height: 1.25;
}
.me-v {
    display: block;
    font-family: 'Consolas', 'Menlo', monospace;
    font-size: 1.06rem;
    font-weight: 700;
    color: var(--text-main);
    line-height: 1.1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* The tier name is a word, not a number, so it takes the body face and
   its own tier colour. */
.me-v.me-tier {
    font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    font-size: var(--fs-2xs);
    font-weight: 700;
    letter-spacing: var(--ls-chip);
    text-transform: uppercase;
    color: var(--tier, var(--text-mid));
    padding-top: 3px;
}
@media (max-width: 620px) {
    .me-panel { min-width: 0; width: 100%; }
    .me-stats { grid-template-columns: repeat(2, 1fr); }
    .me-stat:nth-child(3) { border-left: none; }
    .me-stat:nth-child(n+3) { border-top: 1px solid var(--border); }
}

/* ── Live row: recent fights, who is Marked, what the season pays ──
   Three sheets across, on a wider wrap than the rest of the site uses
   (page-wrap--xwide). The feed is the widest of the three because its
   rows are sentences; the poster and the prize list are both short.

   It reflows in TWO steps rather than one. Dropping straight from
   three columns to one throws away a laptop's width, so the middle
   step keeps the feed on a full-width line of its own (it is the panel
   whose rows read worst when squeezed) and pairs the two short ones
   underneath it. */
.live-row {
    display: grid;
    /* Measured, not guessed. On the live board at a 1280px window the
       feed's rows needed 369px and had exactly 369, and a Wanted name
       was already clipped by 4px, while the rewards sheet had slack:
       it is a short list whatever it is given. So the two panels that
       were at their limit take the width the third does not need. */
    grid-template-columns: 1.6fr 1.05fr 1fr;
    gap: 18px;
    margin-bottom: 26px;
}
@media (max-width: 1180px) {
    .live-row { grid-template-columns: 1fr 1fr; }
    .live-row > .feed-sheet { grid-column: 1 / -1; }
}
@media (max-width: 900px) {
    .live-row { grid-template-columns: 1fr; }
    .live-row > .feed-sheet { grid-column: auto; }
}
/* The prize sheet standing on its own, on a board with no fights yet.
   A single sheet stretched across 1380px is a banner, not a notice. */
/* min(), not minmax(): a minmax track keeps its 470px maximum and
   overhangs a narrow card, which showed up as the sheet being cut off
   on a 320px phone. min() clamps to the card instead. */
.live-row.is-solo { grid-template-columns: min(470px, 100%); justify-content: center; }

/* ── Season Rewards ────────────────────────────────────────────────
   The sheet, its head, and the medals are all shared primitives
   (style.css: THE PARCHMENT SHEET, A ROSTER ON PARCHMENT). Only the
   prize rows are below.

   The medal IS the place: a "1st / 2nd / 3rd" label beside it would
   say the same thing twice, and the hexagon is already the ladder's
   own mark for the top three. */
/* This panel's head may wrap where the shared one may not. .sheet-head h2
   is nowrap, which is right for "Latest Fights", and wrong for a title
   that runs to four words in some languages ("Recompensas de la
   temporada") inside a 313px column on a phone. */
/* This panel's head may wrap where the shared one may not: .sheet-head h2
   is nowrap, which is right for "Latest Fights" and wrong for a title
   in a column this narrow. It should not have to in practice: the head
   carries the title alone, and all six fit one line with 28px to spare
   at the width this panel now gets. There is no season chip because no
   other panel on the page repeats the season either: the tabs directly
   above say which one, and a chip here cost 22px of head height for
   something already on screen. */
.prize-sheet .sheet-head { flex-wrap: wrap; }
/* A notch under the shared 1.18rem. This is the narrowest of the three
   columns, and at 1200px the longest title ("Recompensas") missed one
   line by a single pixel; 1.1rem leaves it 11px of room instead. */
.prize-sheet .sheet-head h2 { white-space: normal; line-height: 1.16; font-size: 1.1rem; }

/* On a phone the sheet is ~250px wide inside its padding, which the
   heading and the season chip cannot share at full size. Tightening the
   title keeps them on one line instead of stacking into a 90px head. */
@media (max-width: 620px) {
    .prize-sheet .sheet-head h2 { font-size: 1rem; letter-spacing: 0.04em; }
}

.prize-list { list-style: none; margin: 0; padding: 0; }
.prize-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 9px;
    align-items: start;
    padding: 9px 0;
    border-bottom: 1px solid var(--ink-rule);
}
.prize-row:last-child { border-bottom: none; padding-bottom: 3px; }
/* Smaller than the ladder's. There the medal is a table cell in a
   52px column; here it is a marker on a narrow notice, and the full
   30x33 crowded the prize beside it. */
.prize-medal {
    width: 26px;
    height: 29px;
    margin-top: 1px;
    font-size: var(--fs-2xs);
}
.prize-main  { min-width: 0; }
.prize-loot  { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }

/* Gold takes the numeral face money has everywhere else on the site,
   in ink rather than the dark board's struck metal: a gradient clipped
   to the glyphs is invisible on cream. */
.prize-gold {
    display: inline-flex;
    align-items: baseline;
    /* Wraps, so "5,000,000" and "monedas de oro" stack instead of pushing
       the row wider than the sheet on a phone. */
    flex-wrap: wrap;
    gap: 0 5px;
    color: var(--ink-gold);
}
.prize-gold b {
    font-family: 'Consolas', 'Menlo', monospace;
    font-size: 1.02rem;
    font-variant-numeric: tabular-nums;
}
.prize-gold small { font-size: var(--fs-2xs); color: var(--ink-dim); }
.prize-coin { align-self: center; flex-shrink: 0; color: var(--ink-gold); }

/* An item as it would be pinned to the notice: its sprite pressed into
   the paper, the name beside it, the stack count last. */
.prize-item {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    max-width: 100%;
    /* Tall enough for the sprite slot, so a chip with no picture (free
       text, or a name items.json does not know) still lines up with
       the ones that have one. */
    min-height: 32px;
    padding: 2px 7px 2px 2px;
    border: 1px solid var(--ink-rule);
    border-radius: 3px;
    background: rgba(255,255,255,0.16);
    color: var(--ink);
    font-size: var(--fs-xs);
    font-weight: 600;
    text-decoration: none;
}
a.prize-item:hover {
    border-color: var(--ink-bad);
    background: rgba(255,255,255,0.32);
    color: #000;
}
/* A name db/items.json does not know, and free text that was never an
   item: both keep the chip but lose the picture and the link. */
.prize-item.is-unknown,
.prize-item.is-extra { padding-left: 7px; color: var(--ink-mid); font-style: italic; }
/* Wraps rather than truncates. The prize IS the content, so
   "Adamantite Bastard Sw..." is the one thing a chip must never say;
   a two-line name in a narrow column is the better trade. */
.prize-item-n { min-width: 0; overflow-wrap: anywhere; }
.prize-qty {
    font-family: 'Consolas', 'Menlo', monospace;
    font-size: var(--fs-2xs);
    color: var(--ink-dim);
}
/* A slot, not a bare sprite. Item art is drawn for the game's dark
   ground, so a pale one (silver, bone, ice) would sit on cream with
   nothing behind it. The well gives every sprite the same edge
   whatever colour it happens to be. */
.prize-slot {
    display: grid;
    place-items: center;
    /* Portrait, not square: item art is drawn taller than it is wide
       (a 54x81 sword is typical), so a square cell wastes its width. */
    width: 22px; height: 26px;
    flex-shrink: 0;
    border-radius: 2px;
    background: rgba(44,32,21,0.14);
    box-shadow: inset 0 1px 2px rgba(44,32,21,0.32);
}
.prize-slot img {
    max-width: 18px; max-height: 22px;
    image-rendering: pixelated;
    filter: drop-shadow(0 1px 0 rgba(0,0,0,0.45));
}
.prize-nil { font-size: var(--fs-xs); font-style: italic; color: var(--ink-dim); }

/* Who is in line for it, demoted under the prize on purpose: the panel
   answers "what do I win" first and "who has it" second. */
.prize-holder {
    margin-top: 5px;
    font-size: var(--fs-2xs);
    line-height: 1.4;
    color: var(--ink-dim);
}
.prize-holder .ph-k { text-transform: uppercase; letter-spacing: var(--ls-chip); }
.prize-holder .ph-n {
    color: var(--ink);
    font-weight: 700;
    text-decoration: none;
    border-bottom: 1px solid rgba(74,54,30,0.35);
}
.prize-holder .ph-n:hover { color: var(--ink-bad); border-bottom-color: currentColor; }
.prize-holder .ph-open { font-style: italic; }

/* With no prizes the list is only a podium, so it closes up: one line
   per row instead of three. */
.prize-list.is-bare .prize-row { padding: 7px 0; align-items: center; }
.prize-list.is-bare .prize-holder { margin-top: 0; font-size: var(--fs-xs); }

/* A note, not an empty state. It used to borrow .sheet-empty, whose
   26px of padding and centred italic say "there is nothing here" and
   took 88px of a 356px sheet on the live board. Left-aligned and
   tight, it reads as what it is: a line of small print above the
   list. Same style whether or not there are prizes under it. */
.prize-note {
    margin: 0 0 4px;
    padding: 2px 0 12px;
    font-size: var(--fs-xs);
    font-style: italic;
    line-height: 1.45;
    text-align: left;
    color: var(--ink-mid);
}
.prize-foot {
    margin: 13px 0 0;
    padding-top: 9px;
    border-top: 1px solid var(--ink-rule);
    font-size: var(--fs-2xs);
    font-style: italic;
    color: var(--ink-dim);
}

/* A season that has not opened yet. The pip is HOLLOW where the live
   one is filled: "not yet" must not read as "running". */
.season-tabs .mode-btn.is-upcoming::before {
    content: '';
    display: inline-block;
    width: 6px; height: 6px;
    margin-right: 7px;
    border: 1px solid currentColor;
    border-radius: 50%;
    vertical-align: middle;
    opacity: 0.75;
}
/* No dimming of the label itself. --text-dim is already the tab colour
   and 70% of it on the dark card falls under the contrast floor; the pip
   carries the distinction, exactly as the filled green one carries
   "live". Filled = running, hollow = not yet, none = finished. */

/* The two sheets themselves are the shared parchment primitive:
   .parchment > .sheet > .sheet-head, plus .seal, all of which now
   live in style.css under THE PARCHMENT SHEET, along with the ink
   tokens and the poster head. Only what is specific to this page's
   two lists is below. */
/* Briefly dim the whole list while a refresh is in flight. */
.feed.is-loading { opacity: 0.55; transition: opacity .15s; }

/* A row that was not in the previous poll. Fades its wash away, so
   a new fight is noticed without the panel jumping.

   Two washes, because this page has both grounds on it: feedArrive
   is the translucent gold the dark ladder below still uses, and
   inkArrive (style.css) is the paper equivalent, gold being
   invisible on cream. Retuning feedArrive instead of adding the
   second one would have silently taken the highlight off the
   ladder. */
@keyframes feedArrive {
    from { background-color: rgba(201, 168, 76, 0.16); }
    to   { background-color: transparent; }
}
.feed-row.is-new { animation: inkArrive 2.4s ease-out 1; }

/* ── The ladder, refreshed in place ─────────────────────────────
   The roster's staggered entrance (rosterRowIn in style.css, delayed by
   --i) is a first-paint effect. Replaying it on every poll would make
   the whole table slide and fade again every five minutes, which reads
   as a page glitch rather than as news, so a refreshed tbody cancels it
   for its own rows.

   Instead only the rows that MOVED are marked, with the same treatment
   the feed uses for an arriving fight. A row that merely shifted because
   somebody else overtook it is not itself news and stays quiet. */
#rank-body[data-refreshed] tr.rank-row { animation: none; }
#rank-body[data-refreshed] tr.rank-row.is-new {
    animation: feedArrive 2.4s ease-out 1;
}

/* A bounty that just appeared, or whose price went up. Same treatment as
   an arriving fight, since it is the same kind of news. */
.marked-row.is-new { animation: inkArrive 2.4s ease-out 1; }

/* ── The feed ──────────────────────────────────────────────────── */
.feed { list-style: none; margin: 0; padding: 2px 0; }
.feed-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 6px;
    border-top: 1px solid var(--ink-rule);
    font-size: var(--fs-sm);
}
.feed-row:first-child { border-top: none; }
/* A gank is the one thing on this page worth calling out in the feed. */
.feed-row.is-gank { background: linear-gradient(90deg, rgba(140,43,32,0.16), transparent 62%); }

.feed-pts {
    flex-shrink: 0;
    min-width: 46px;
    text-align: right;
    font-family: 'Consolas', 'Menlo', monospace;
    font-size: 1.06rem;
    font-weight: 700;
}
/* .pos / .neg / .zero are global (style.css) and tuned for the dark
   card, so the sheet overrides them on the points cell only. Do not
   retune the globals: the hover score-card uses them too. */
.feed-pts.pos  { color: var(--ink-good); }
.feed-pts.neg  { color: var(--ink-bad);  }
.feed-pts.zero { color: var(--ink-dim);  }
.feed-fight {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 5px;
    flex-wrap: wrap;
}
.feed-name {
    color: var(--ink);
    text-decoration: none;
    font-weight: 600;
    max-width: 148px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.feed-name:hover { color: var(--ink-bad); text-decoration: underline; }
/* Winner reads first and darkest. Without this the two names look
   identical and the row only says who won through word order. On
   paper the contrast runs the other way round from the dark card:
   the winner is the heavier ink, not the brighter text. */
.feed-name.is-victim { color: var(--ink-mid); font-weight: 500; }
/* Levels ride tight against their own name so the pairing is
   unambiguous, and they are the one thing on the row the sheet's ink
   floor was never applied to: the two olives here measured 3.49:1
   (killer) and 2.53:1 (victim) on --paper, against the 4.5 every other
   ink on this page clears. Steel is 4.56:1, and it is the ladder's own
   level ink (.sheet .r-lvl), so the same datum now reads the same on
   both halves of the page.

   The victim's level no longer takes a colour of its own. A level is a
   magnitude, not a verdict: the names beside it already carry who won
   (--ink at 600 against --ink-mid at 500), and making the level say it
   a second time is exactly what pushed the victim's ink down to 2.53:1.

   --fs-2xs is the chip size, and these are bare digits with no chip
   around them to give them presence, so the level sits one step up the
   scale and takes the weight the size alone does not give it. */
.feed-lvl {
    font-family: 'Consolas', 'Menlo', monospace;
    font-size: var(--fs-xs);
    font-weight: 600;
    color: var(--ink-steel);
    margin-left: 4px;
}
.feed-vs { display: inline-flex; opacity: 0.6; margin: 0 2px; }
.feed-swords { color: var(--ink-mid); }

.feed-meta {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 8px;
}
.feed-tag {
    font-family: 'Consolas', 'Menlo', monospace;
    font-size: var(--fs-2xs);
    font-weight: 700;
    padding: 2px 6px;
    border-radius: 2px;
    white-space: nowrap;
}
.feed-tag.is-bounty { color: var(--ink-gold); border: 1px solid rgba(138,100,23,0.50); }
.feed-tag.is-loss   { color: var(--ink-dim);  border: 1px solid rgba(74,54,30,0.32); }
/* Below the level floor. Deliberately the quietest tag on the row: it
   marks a fight as not counting, which is an absence, not an accusation.
   Dashed so it reads apart from is-loss at a glance without needing a
   colour of its own, since red and gold are both already spoken for. */
.feed-tag.is-low    {
    color: var(--ink-dim);
    border: 1px dashed rgba(74,54,30,0.38);
    font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    text-transform: uppercase;
    letter-spacing: var(--ls-chip);
}
.feed-tag.is-gank   {
    color: var(--ink-bad);
    border: 1px solid rgba(140,43,32,0.50);
    font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    text-transform: uppercase;
    letter-spacing: var(--ls-chip);
}
.feed-when {
    font-size: var(--fs-2xs);
    font-style: italic;
    color: var(--ink-dim);
    white-space: nowrap;
    min-width: 86px;
    text-align: right;
}
/* On a phone the row becomes two deliberate lines rather than letting
   the flex children wrap wherever they land: forcing the meta onto its
   own full-width line keeps every row the same height, which an
   arbitrary wrap did not (75px next to 92px reads as broken). The
   indent lines the second row up under the names, not the points. */
@media (max-width: 560px) {
    .feed-row { flex-wrap: wrap; gap: 4px 10px; }
    .feed-meta {
        flex: 0 0 100%;
        justify-content: flex-start;
        padding-left: 58px;
    }
    .feed-when { min-width: 0; margin-left: auto; }
}

/* The bounty sheet's head is the shared .poster-head notice
   (style.css). Only the list under it is this page's. */
.marked-list { list-style: none; margin: 0; padding: 4px 0 2px; }
.marked-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 4px;
    border-top: 1px solid rgba(74,54,30,0.22);
    text-decoration: none;
    font-size: var(--fs-sm);
    transition: background 0.15s;
}
.marked-list li:first-child .marked-row { border-top: none; }
.marked-row:hover { background: rgba(140, 43, 32, 0.11); }
/* The face, size and tracking are all set explicitly, and that is
   deliberate: this is the default a name falls back to when the
   `is-scribed` guard below withholds the blackletter. Leaving any of
   them unstated leaves the body face to whatever else on the page
   happens to match `.marked-name` (a stale card-era rule did exactly
   that once, and a bare class beats nothing at all), so a name that is
   NOT scribed would silently pick up blackletter anyway, which is
   exactly the case the guard exists to prevent. */
.marked-row .marked-name {
    flex: 1;
    min-width: 0;
    font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    font-size: var(--fs-sm);
    letter-spacing: normal;
    color: var(--ink);
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Blackletter, but only for names the face can actually set: it
   renders digits and symbols as ornaments rather than glyphs, so a
   name carrying either keeps the body face. PHP makes that call
   per name; the default here is the safe one. */
.marked-row .marked-name.is-scribed {
    font-family: 'Beaulieux', serif;
    font-size: 1.12rem;
    font-weight: normal;
    letter-spacing: 0.05em;
}
.marked-streak {
    font-size: var(--fs-2xs);
    font-style: italic;
    color: var(--ink-dim);
    white-space: nowrap;
}
/* The reward is a struck coin, not a figure in a column. */
.marked-bounty {
    display: inline-grid;
    place-items: center;
    flex-shrink: 0;
    min-width: 34px;
    height: 28px;
    padding: 0 9px;
    border-radius: 14px;
    font-family: 'Consolas', 'Menlo', monospace;
    font-weight: 700;
    font-size: 0.86rem;
    color: #4a3208;
    background: linear-gradient(180deg, #e8cd7e, #c19b3c 55%, #8f6d1e);
    box-shadow: 0 1px 2px rgba(0,0,0,0.5),
                inset 0 1px 0 rgba(255,255,255,0.5),
                inset 0 -1px 2px rgba(90,66,12,0.55);
}


/* ── Your own row ──────────────────────────────────────────────────
   Left rail in the accent instead of the tier colour, plus a chip, so
   the viewer can find themselves while scrolling without reading names.
   Kept subtle: it must not outshout the medals at the top. */
.rank-row.is-you { background: linear-gradient(90deg, rgba(154,35,35,0.12), transparent 55%); }
.rank-row.is-you:hover { background: linear-gradient(90deg, rgba(154,35,35,0.18), transparent 60%); }
.you-chip {
    margin-left: 8px;
    padding: 1px 6px;
    border: 1px solid rgba(154, 35, 35, 0.55);
    border-radius: 2px;
    font-size: var(--fs-2xs);
    font-weight: 700;
    letter-spacing: var(--ls-chip);
    text-transform: uppercase;
    color: var(--accent-hover);
    vertical-align: middle;
}

/* ── Rules panel + calculator ─────────────────────────────── */
.pvp-explain {
    display: grid;
    grid-template-columns: 1.35fr 1fr;
    gap: 22px;
    margin-bottom: 26px;
    /* Its own top rule and gap. Whatever sits above it varies (the
       rivalries block disappears when no feud is two-way), and without
       this the last table row butted straight against the panels and
       read as the list clipping into them. */
    margin-top: 30px;
    padding-top: 26px;
    border-top: 1px solid var(--border);
}
@media (max-width: 860px) { .pvp-explain { grid-template-columns: 1fr; } }

/* No padding here. .pvp-panel is the SAME element as .sheet, so any
   padding declared on it replaces the sheet's ring outright and the
   tear then eats the text. The sheet owns the ring; this only exists
   to be a grid child. */
.pvp-panel { min-width: 0; }
.pvp-panel h2 {
    font-size: var(--fs-h3);
    margin: 0 0 4px;
    letter-spacing: var(--ls-heading);
}
.pvp-panel .pvp-lead {
    font-size: var(--fs-meta);
    color: var(--text-dim);
    margin: 0 0 16px;
    line-height: 1.5;
}

.pvp-rule {
    display: flex;
    align-items: baseline;
    gap: 12px;
    padding: 7px 0;
    border-top: 1px solid var(--border);
    font-size: var(--fs-sm);
}
.pvp-rule:first-of-type { border-top: none; }
/* text-align is set explicitly on both sides: .glass-card centres its
   text, which would centre each label in its own flex track and leave
   the list looking ragged instead of like a rules table. */
.pvp-rule dt { flex: 1; color: var(--text-mid); margin: 0; text-align: left; }
.pvp-rule dd {
    margin: 0;
    flex-shrink: 0;
    max-width: 52%;
    text-align: right;
    color: var(--text-dim);
    font-size: var(--fs-meta);
}
/* Bare numbers and "+" never go in a heading: the Beaulieux face turns
   punctuation into ornaments. Monospace badge instead, as elsewhere. */
.pvp-val {
    font-family: 'Consolas', 'Menlo', monospace;
    font-weight: 700;
    color: var(--text-main);
}
.pvp-val.is-good { color: var(--sage); }
.pvp-val.is-bad  { color: var(--rust); }

/* ── Calculator ───────────────────────────────────────────── */
.pvp-calc-inputs {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
    margin-bottom: 16px;
}
.pvp-calc-inputs label {
    display: block;
    font-size: var(--fs-2xs);
    font-weight: 700;
    letter-spacing: var(--ls-chip);
    text-transform: uppercase;
    color: var(--text-dim);
    margin-bottom: 6px;
}
.pvp-calc-inputs input {
    width: 100%;
    background: #0a0a0b;
    border: 1px solid var(--border-light);
    color: var(--text-main);
    font-family: 'Consolas', 'Menlo', monospace;
    font-size: 1.05rem;
    padding: 9px 11px;
}
.pvp-calc-inputs input:focus {
    outline: none;
    border-color: var(--accent);
}
.pvp-calc-out {
    text-align: center;
    border-top: 1px solid var(--border);
    padding-top: 15px;
}
.pvp-calc-num {
    font-family: 'Consolas', 'Menlo', monospace;
    font-size: 2.5rem;
    font-weight: 700;
    line-height: 1;
    display: block;
}
.pvp-calc-num.is-good { color: var(--sage); }
.pvp-calc-num.is-bad  { color: var(--rust); }
.pvp-calc-num.is-flat { color: var(--text-dim); }
.pvp-calc-label {
    display: block;
    font-size: var(--fs-2xs);
    letter-spacing: var(--ls-chip);
    text-transform: uppercase;
    color: var(--text-dim);
    margin-bottom: 8px;
}
.pvp-calc-verdict {
    display: inline-block;
    margin-top: 10px;
    font-size: var(--fs-2xs);
    font-weight: 700;
    letter-spacing: var(--ls-chip);
    text-transform: uppercase;
    padding: 4px 10px;
    border: 1px solid currentColor;
}
.sheet .pvp-calc-verdict.is-up   { color: var(--ink-good); }
.sheet .pvp-calc-verdict.is-gank { color: var(--ink-bad); }
.sheet .pvp-calc-verdict.is-even { color: var(--ink-gold); }
/* Below the level floor: neither good nor bad, off the ladder. Dim ink
   on purpose, so it reads as "this does not count" rather than as a
   fourth kind of fight. */
.sheet .pvp-calc-verdict.is-low  { color: var(--ink-mid); }
.pvp-calc-note {
    font-size: var(--fs-2xs);
    color: var(--text-dim);
    margin: 10px 0 0;
}

/* ── Tier pill + progress ─────────────────────────────────── */
.tier-pill {
    display: inline-block;
    font-size: var(--fs-2xs);
    font-weight: 700;
    letter-spacing: var(--ls-chip);
    text-transform: uppercase;
    color: var(--tier-color, var(--text-dim));
}
/* display:block is load-bearing: .tier-bar is a <span> (it sits inside a
   table cell and beside an inline pill), and an inline box ignores both
   height and the child's percentage width, so the bar rendered as
   nothing at all. */
.tier-bar {
    display: block;
    height: 3px;
    background: var(--border-light);
    margin-top: 5px;
    overflow: hidden;
}
.tier-bar i {
    display: block;
    height: 100%;
    background: var(--tier-color, var(--accent));
    opacity: 0.9;
}

@keyframes honorSheen {
    0%,  8%  { background-position: 128% 0; }
    58%, 100% { background-position: -28% 0; }
}
/* Only the top three rows shimmer. Running it down a hundred rows
   would be noise, and restricting it makes the animation itself a
   signal for the top of the ladder. Staggered so they do not flash in
   unison. */
.rank-row:nth-child(-n+3) .pvp-score.is-good {
    animation: honorSheen 5.5s ease-in-out infinite;
}
.rank-row:nth-child(1) .pvp-score.is-good { animation-duration: 4.5s; }
.rank-row:nth-child(2) .pvp-score.is-good { animation-delay: .7s; }
.rank-row:nth-child(3) .pvp-score.is-good { animation-delay: 1.4s; }

/* The leader alone carries a brighter halo. */
.rank-row:first-child .pvp-score.is-good { text-shadow: 0 0 18px rgba(201, 168, 76, 0.5); }



/* Fight-style breakdown: the honest column. A high gank count is meant
   to be as visible as a high score. */
/* Third line of the record zone: the bar shows the split, the legend
   above shows the counts, this shows what kind of fights they were. */
.style-cell {
    display: flex;
    justify-content: space-between;
    gap: 9px;
    margin-top: 3px;
    font-family: 'Consolas', 'Menlo', monospace;
    font-size: var(--fs-2xs);
}
.style-cell span { display: inline-flex; align-items: center; gap: 2px; }
.style-up   { color: var(--sage); }
.style-even { color: var(--text-mid); }
.style-gank { color: var(--rust); }
.style-cell .is-zero { color: var(--text-dim); opacity: 0.45; }

/* ── Rivalries ────────────────────────────────────────────── */
.rival-list { display: grid; gap: 9px; }
.rival-row {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 12px;
    padding: 9px 4px;
    border-top: 1px solid var(--ink-rule);
    font-size: var(--fs-sm);
}
/* Both sides are pinned toward the scoreline. Without the explicit
   left on .b it inherits .glass-card's centring and drifts ~180px away
   from the score it belongs to. */
.rival-row .a { text-align: right; }
.rival-row .b { text-align: left; }
.rival-row a { color: var(--text-main); text-decoration: none; }
.rival-row a:hover { color: var(--accent-hover); }
.rival-score {
    font-family: 'Consolas', 'Menlo', monospace;
    font-weight: 700;
    color: var(--text-dim);
    letter-spacing: 0.06em;
    white-space: nowrap;
}
.rival-score b { color: var(--gold); font-weight: 700; }

.pvp-section-title {
    font-size: var(--fs-h3);
    margin: 30px 0 4px;
    letter-spacing: var(--ls-heading);
}
.pvp-section-note {
    font-size: var(--fs-meta);
    color: var(--text-dim);
    margin: 0 0 14px;
}

/* ── Closing notes ────────────────────────────────────────────
   The two mechanics the rules table above cannot express in one line:
   how long a season lasts, and how a bounty comes to exist in the
   first place (the table only says what ending one pays). */
.pvp-notes {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20px 28px;
    margin-top: 28px;
    padding-top: 18px;
    border-top: 1px solid var(--border);
    text-align: left;
}
@media (max-width: 720px) { .pvp-notes { grid-template-columns: 1fr; } }

.pvp-notes p {
    font-size: var(--fs-2xs);
    color: var(--text-dim);
    line-height: 1.65;
    margin: 0;
}
/* A <b>, not an <h3>: h3 gets the Beaulieux blackletter face forced on
   it site-wide, which is unreadable at this size. */
.pvp-notes b {
    display: block;
    font-family: inherit;
    font-size: var(--fs-2xs);
    font-weight: 700;
    letter-spacing: var(--ls-chip);
    text-transform: uppercase;
    color: var(--text-mid);
    margin-bottom: 4px;
}

.pvp-foot-note {
    font-size: var(--fs-2xs);
    color: var(--text-dim);
    margin-top: 18px;
    text-align: center;
}

@media (max-width: 640px) {
    .rival-row { grid-template-columns: 1fr; text-align: center; gap: 4px; }
    .rival-row .a, .rival-row .b { text-align: center; }

    /* Side by side, the label wraps to three lines against a value
       pinned to 52% of a 375px column. Stack them instead: label on
       its own line, value under it. */
    .pvp-rule { flex-direction: column; align-items: flex-start; gap: 2px; }
    .pvp-rule dd { max-width: 100%; text-align: left; }
}

/* ── The rest of the page, in ink ──────────────────────────────
   Your standing, the feuds, the rules and the calculator are all
   sheets now, so everything inside them needs the paper palette
   rather than the dark-card one. Scoped to .sheet throughout, so
   nothing here can reach the season tabs (deliberately left dark)
   or the closing notes below.

   Every colour is from the measured ink set; none of the dark
   tokens survive on cream, and the two that look closest are the
   ones that fail worst: --text-dim reads 2.4:1 on paper and
   --gold is invisible. */

/* Feuds */
.sheet .pvp-section-title { color: var(--ink); margin: 2px 0 4px; }
.sheet .pvp-section-note  { color: var(--ink-dim); font-style: italic; }
.sheet .rival-list { gap: 0; }
.sheet .rival-row:first-child { border-top: none; }
.sheet .rival-row a { color: var(--ink); }
.sheet .rival-row a:hover { color: var(--ink-bad); text-decoration: underline; }
.sheet .rival-score   { color: var(--ink-dim); }
.sheet .rival-score b { color: var(--ink-gold); }

/* Rules */
.sheet.pvp-panel h2 { color: var(--ink); text-shadow: 0 1px 0 rgba(255,255,255,0.28); }
.sheet.pvp-panel .pvp-lead { color: var(--ink-mid); font-style: italic; }
.sheet .pvp-rule    { border-top: 1px solid var(--ink-rule); }
.sheet .pvp-rule dt { color: var(--ink); }
.sheet .pvp-rule dd { color: var(--ink-mid); }
.sheet .pvp-val          { color: var(--ink); }
.sheet .pvp-val.is-good  { color: var(--ink-good); }
.sheet .pvp-val.is-bad   { color: var(--ink-bad); }

/* Calculator. The inputs become ruled lines for the same reason the
   search box did: a dark field on paper reads as a hole cut in it. */
.sheet .pvp-calc-inputs label { color: var(--ink-dim); }
.sheet .pvp-calc-inputs input {
    background: transparent;
    border: 0;
    border-bottom: 1px solid rgba(74,54,30,0.45);
    border-radius: 0;
    padding: 7px 2px;
    color: var(--ink);
}
.sheet .pvp-calc-inputs input:focus {
    border-bottom-color: var(--ink-bad);
    box-shadow: 0 1px 0 0 var(--ink-bad);
}
.sheet .pvp-calc-out   { border-top: 1px solid var(--ink-rule); }
.sheet .pvp-calc-label { color: var(--ink-dim); }
/* The verdict number is the point of the panel, so it takes the same
   emboss the honour column uses; a glow is not available on paper. */
.sheet .pvp-calc-num          { color: var(--ink); text-shadow: 0 1px 0 rgba(255,255,255,0.35); }
.sheet .pvp-calc-num.is-good  { color: #4d380c; }
.sheet .pvp-calc-num.is-bad   { color: #6b1c14; }
.sheet .pvp-calc-num.is-flat  { color: var(--ink-mid); text-shadow: none; }
.sheet .pvp-calc-note { color: var(--ink-dim); }

/* The grid gap is the gap between two sheets now, so the rule that
   used to separate this block from the table above it would draw
   across paper. */
.pvp-explain { border-top: none; padding-top: 0; }
