/* ============================================================
   apexisle — shared prototype stylesheet

   PALETTE (decided 2026-07-17; the brand accent went from red to blue 2026-08-16):
     brand  = brand, primary action, active state  -- the NURSERY BLUE, see --brand below
              ...and, since the currency rebrand, BOTH WALLET CHIPS (.wal) -- the top-right pair reads as
              one accent cluster rather than as two more colours; see the note at .wal
     danger = failure, loss, destructive action    -- the old brand red, see --danger below
     green  = live / earned / a Fossil Fragments PRICE (.cur.cents)
     blue   = information / purchased / a Paleo Points PRICE (.cur.dollars) / Elder
     white  = all type and every rule
   The vitals readout is deliberately MONOCHROME: brand/blue/green now carry brand
   meaning, so they can't also encode health/thirst without lying.

   LAYOUT is deliberately not Dino Den's: vertical rail, landscape specimen
   records, stamped readouts, no medal podium.
   ============================================================ */
:root{
  /* base — neutral, not warm; warm browns fight RGB accents */
  /* --panel is the home page's logo panel colour (.hero in index.html), applied to every in-page panel:
     a TRANSLUCENT near-black, so the site background image reads through every surface the way it does
     behind the logo. Was an opaque #121216.
     --panel-solid keeps that old opaque value for the surfaces that must NOT be see-through: anything
     floating OVER the page (modals, the arcade's game-over card) where you would otherwise read the page
     through the dialog, and native <select> option lists, which are unreadable translucent. */
  --ink:#08080a; --panel:rgba(7,7,10,.4); --panel-solid:#121216; --panel2:#1b1b21;

  /* the three.
     --brand IS --diet-omni, the blue the rail's nav items and the Nursery's hatch picker already wear.
     It was #e0393e red until 2026-08-16; the rail turning blue left the red accent on every page
     arguing with it, so the accent followed the rail rather than the other way round. The literal
     value is repeated here instead of written var(--diet-omni) only because --diet-omni is declared
     further down this same block -- keep the two in step by hand, they are ONE colour.
     The tokens are named --brand/--danger, NOT --red, precisely so nothing has to read "red" and
     render blue; the ~290 call sites were renamed with them. */
  --brand:#00afcb;   --brand-lo:#00788c;   --brand-hi:#3fd3ec;
  --blue:#3d86d6;  --blue-lo:#2a5f9c;  --blue-hi:#6aa9ec;
  --green:#34b877; --green-lo:#23805a; --green-hi:#5ada97;

  /* DANGER is the old brand red, kept whole. It is no longer decorative: a page reaches for it only to
     say something failed, was lost, or is about to be destroyed -- errors, a busted blackjack hand, a
     lost coinflip, Delete/Release/Cancel/Refund, the staff-abuse warning. Anything that merely wants
     "the accent colour" now takes --brand. Do not use --danger for emphasis. */
  --danger:#e0393e; --danger-lo:#a02b2f; --danger-hi:#ff5c60;

  /* THE TEXT LADDER — three steps that must be TELLABLE APART, because the whole site encodes rank with them:
     --white is what you read, --mute is supporting copy, --mute-dim is a label naming the thing beside it.

     History, because this has now moved twice. Originally #9b9ba1 / #5f5f68: a genuine legibility problem at
     the 7-9px label sizes, which is what the 2026-07-30 readability pass was answering. That pass took them
     to #e6e6ea / #c2c2ca -- but it fixed legibility by DELETING the hierarchy. In perceptual lightness the
     ladder became L*96 / L*91 / L*79: the top two steps were 5 points apart, near enough identical, so a
     caption shouted as loudly as the sentence it captioned and nothing on a page looked more important than
     anything else.

     Now L*96 / L*74 / L*56 - gaps of 23 and 18, three steps you can actually rank.

     This is a PARTIAL walk-back, not a revert, and the measurements are why. The original --mute-dim was
     3.13:1 on the page and 2.74:1 on a light panel: it genuinely FAILED AA, so the 2026-07-30 complaint was
     real and must not be re-created. The new value lands at 5.42:1 / 4.74:1 - still clear of the 4.5 floor on
     the LIGHTEST surface a page can present (#1a1a20, a panel over a bright patch of the background image) -
     while --mute sits at 9.61:1 / 8.42:1.
     If these are pushed darker again, re-check --mute-dim first: it is the one with margin to lose, and it
     carries the 8px labels.

     The names stay accurate again: --mute-dim IS dimmer than --mute now. */
  --white:#f5f5f3; --mute:#b4b4bd; --mute-dim:#85858f;
  /* OUTLINES ARE OFF SITE-WIDE. --rule and --edge are the two tokens every panel, card, tray, modal and
     section divider draws its 1px outline with, so making them transparent removes ~240 outlines in one
     place. Surfaces are separated by their fill and their spacing instead.
     Anything you TYPE INTO or CLICK was repointed at --control-line first, so controls stay defined; that
     was done at the ~40 source declarations rather than with an !important override here, because page
     <style> blocks load after this file and would win every specificity tie.
     To put the outlines back, give these two their old values: rule .09, edge .16. */
  --rule:transparent;
  --edge:transparent;
  --control-line:rgba(245,245,243,.22);
  /* the old --rule/--edge values, for the handful of places that used them as a FILL, not an outline */
  --rule-fill:rgba(245,245,243,.09);
  --edge-fill:rgba(245,245,243,.16);
  --wash:rgba(245,245,243,.04);

  /* THE diet colours — orange carnivore, blue omnivore, green herbivore. Read off the roster reference art
     in ref/, which is drawn colour-coded by diet (hues 31 / 188 / 97). This is the SINGLE source: the rail's
     nav items cycle through them, the Dino Nursery's hatch picker tints its buttons with them, and data.js
     reads these very properties back out rather than keeping its own copy. Change them here only. */
  --diet-carni:#ff9b32; --diet-omni:#00afcb; --diet-herbi:#7cdf3d;
  /* the same three as "r,g,b", so a caller can build rgba() at any alpha. A flat hex cannot be given an
     opacity in CSS, and the rail's nav items need exactly that — a translucent wash of their own colour. */
  --diet-carni-rgb:255,155,50; --diet-omni-rgb:0,175,203; --diet-herbi-rgb:124,223,61;

  /* tinted washes (used instead of hardcoded rgba so a repalette stays one edit).
     --brand-* is 0,175,203 = #00afcb = --diet-omni-rgb; --danger-* is 224,57,62 = the old red. */
  --brand-wash:rgba(0,175,203,.11);  --brand-line:rgba(0,175,203,.42);
  --danger-wash:rgba(224,57,62,.09); --danger-line:rgba(224,57,62,.42);
  /* ...and the same two as "r,g,b", for the handful of places that need a THIRD alpha (page glows,
     glass fills, box-shadows) and so had been hardcoding rgba(224,57,62,...) past the tokens above.
     Same trick, same reason, as --diet-*-rgb below. */
  --brand-rgb:0,175,203; --danger-rgb:224,57,62;
  /* Text and glyphs sitting ON a SOLID --brand fill -- primary buttons, unread badges, the selection
     highlight, a sent chat bubble. This token exists because the swap changed the contrast maths: the old
     red carried white at about 4:1, but #00afcb is a far lighter colour and white on it is about 2.4:1,
     which fails at every size. Dark on it is about 7.6:1. So every surface that FILLS with --brand takes
     --on-brand, not #fff. The rail's selected nav item had already made this call for the diet colours
     (see .rail .it.on) -- this is the same decision, named.
     Only for solid fills: --brand as TEXT on the dark page, or over --brand-wash, stays as it is. */
  --on-brand:var(--ink);
  --blue-wash:rgba(61,134,214,.09); --blue-line:rgba(61,134,214,.42);
  --green-wash:rgba(52,184,119,.09);--green-line:rgba(52,184,119,.42);

  /* WARN — the fourth signal the palette had been using without naming. Between --green ("this is fine") and
     --danger ("this failed"), a lot of surfaces needed "careful, but nothing is broken": a prime window about
     to shut, an out-of-gamut channel, an NSFW flag, a mute. Three near-identical golds had grown to carry it -
     #c9a24a, #e0a24a and #e0b64a, twenty uses between them, indistinguishable on screen and impossible to
     re-palette in one place. One token now; the -lo/-hi/-wash/-line set matches every other colour here.
     It is NOT --danger. Reserving red for genuine failure only works if there is something else to reach for,
     and its absence is exactly why --danger kept getting borrowed for emphasis. */
  --warn:#e0a24a; --warn-lo:#a87631; --warn-hi:#f0bd72;
  --warn-wash:rgba(224,162,74,.11); --warn-line:rgba(224,162,74,.42); --warn-rgb:224,162,74;

  /* ---- legacy aliases: page-level <style> blocks still reference these ---- */
  --obsidian:var(--ink); --char:var(--panel); --char2:var(--panel2);
  --ember:var(--brand); --ember-lo:var(--brand-lo);
  --amber:var(--green);          /* "amber" role (figures/highlights) is now green */
  --blood:var(--brand); --live:var(--green); --hydr:var(--blue);
  --elder:var(--blue); --dollar:var(--blue);
  --bone:var(--white); --ash:var(--mute); --ash-dim:var(--mute-dim);

  --font-d:"Anton",Impact,"Arial Narrow Bold",sans-serif;
  --font-e:"Cinzel",Georgia,serif;
  --font-b:"Rajdhani","Segoe UI",system-ui,sans-serif;
  --mono:ui-monospace,"Cascadia Mono","Consolas",monospace;
  --rail:210px;
  --radius:6px;   /* global corner softening — every hard rectangle gets a slight round */
}
*{margin:0;padding:0;box-sizing:border-box}
a{color:inherit;text-decoration:none}
button,input,select,textarea{font-family:inherit;color:inherit}
html,body{height:100%}
body{background:var(--ink);color:var(--white);font-family:var(--font-b);line-height:1.4;
  -webkit-font-smoothing:antialiased;height:100vh;overflow:hidden;display:flex}
::selection{background:var(--brand);color:var(--on-brand)}

/* ============ emphasis ============
   <b> appears ~130 times across 19 pages and had NO site-wide treatment - every page that wanted a bold word
   to stand out styled its own `.something b`, eleven different times. What the tag is actually used for is
   consistent though: the one word in the sentence that carries it. A name, a date, an amount, a tier, a state
   ("Asleep.", "It's sick!"). So it is worth defining once.

   Bold gets BOTH signals: the weight, and a step up the text ladder to --white. Weight alone was doing very
   little, because Rajdhani's 700 is not a heavy bold and until today the body colour was already near-white,
   so there was nothing to step up FROM. With --mute pulled back down to L*74 the lift is now the larger half
   of the effect.

   SCOPED TO RUNNING COPY (p / li), not a bare `b,strong`, and that restriction is load-bearing. A global rule
   would force --white onto bold inside COLOUR-CODED surfaces, where the bold word is precisely the one that
   must keep the colour: tamagotchi's .sickbanner is --danger-hi and reads "<b>It's sick!</b>", .sleepbanner is
   the purple #b9a8ee and reads "<b>Asleep.</b>". Both are divs, so p/li misses them by construction. Audited:
   no <p> or <li> on the site sets a non-white colour and contains a <b>, and the only coloured p/li rules are
   memberships' ::before arrow bullets, which a descendant rule cannot reach.
   Element specificity (0,0,2) also means every existing `.x b` rule still wins, so the eleven local
   treatments - dino's deliberately DIMMER meta bold included - are untouched. */
p b,p strong,li b,li strong{color:var(--white)}

/* An accent-coloured key term, for the word a sentence is actually about, where bold is not enough. Uses
   --brand-hi rather than --brand: --brand as text on the dark page is a touch low-contrast at body size.
   Deliberately NOT --danger; per the palette note, danger means something failed, not something matters. */
.key{color:var(--brand-hi);font-weight:700}
/* site-wide background image — fixed, covers the viewport, BEHIND all content.
   Kept faint (15%) so writing stays readable against it everywhere. */
/* BLURRED at 8px — settled on by eye against a temporary slider, which has since been removed.
   inset is NEGATIVE on purpose: a blur samples past the element's edge, and at inset:0 that edge is the
   viewport edge, so the filter reads transparency there and draws a visible pale band around the whole
   page. Bleeding the layer ~2x the blur radius beyond the viewport pushes those soft edges off-screen.
   One number to tune: the blur radius. The inset is NOT independent — keep it at roughly twice the radius,
   or the band comes back. */
body::before{content:"";position:fixed;inset:-20px;z-index:-1;pointer-events:none;
  background:url(img/background.jpg) center/cover no-repeat;opacity:.15;filter:blur(8px)}

/* ============ rail ============ */
/* The rail carries the SAME translucent fill as every other panel (the home page's .hero shade), so the
   background image reads through it exactly as it does everywhere else. The ITEMS are what stay opaque —
   see .rail .it. Making the whole rail solid was tried and reverted: it fixed the empty stretch at the
   cost of the rail no longer matching any other surface on the site. */
.rail{flex:0 0 var(--rail);display:flex;flex-direction:column;border-right:1px solid var(--control-line);
  background:var(--panel);z-index:20}
/* The wordmark is artwork (img/logoRail.png). FULL BLEED: no padding, so the image spans the rail edge
   to edge and the row is exactly as tall as the art at that width — which is why the file is trimmed to
   its own content rather than sitting on a square canvas: transparent margin would become rail padding. */
.rail .mark{display:block;padding:0;border-bottom:1px solid var(--edge);flex:0 0 auto}
.rail .mark img{display:block;width:100%;height:auto}
.rail .items{flex:1 1 auto;min-height:0;overflow-y:auto;padding:6px 0;display:flex;flex-direction:column;gap:1px}
/* A THIN BAR, not width:0. The list was silently scrollable with no affordance whatsoever, so on a short
   window the tabs past the fold simply did not exist as far as the player was concerned -- Games (10th of
   13) is the first to go, and it needs ~712px of viewport to be fully visible. The short-viewport block
   below is what actually buys the room; this is the honest fallback for when even that is not enough. */
.rail .items::-webkit-scrollbar{width:4px;height:4px}
.rail .items::-webkit-scrollbar-thumb{background:var(--control-line);border-radius:2px}
.rail .items::-webkit-scrollbar-thumb:hover{background:var(--mute-dim)}
/* Nav items are the Dino Nursery's species buttons: a diet colour in --sp (cycled carni -> herbi -> omni by
   nav.js), MUTED — the same hue, mixed down into the panel colour so it reads as a tinted surface rather
   than a row of highlighter bars. Full-strength --sp was too loud for something the eye passes over on
   every page.
   TWO DIALS, one background declaration: --it-c is how much diet colour is in the fill, --it-a is how opaque
   the fill is. Each state sets the pair, so there is no separate background rule per state to drift.
   The alpha is EXPLICIT rather than inherited from mixing into a translucent --panel. That was tried and was
   invisible: it landed at 58% alpha, and against the near-black rgb(8,8,10) behind the rail a 58% fill of an
   already-dark colour composites within 7/255 per channel of the opaque version. Transparency here reads
   almost entirely as "darker" — the page behind is ink plus a 15%-opacity blurred image, so there is little
   for it to reveal — which is why the alpha has to drop to around half before the change is visible at all.
   The plain declaration above the mix is the fallback for engines without color-mix; they get an untinted
   button rather than no background at all.
   Muting darkens the fill, so the label at REST and on HOVER is LIGHT — ink on a muted mix drops to roughly
   2:1. (The SELECTED item goes the other way: it takes the colour at full strength and an ink label, which
   is only readable because the fill is bright. See .rail .it.on below — the two are opposite ends of the
   same dial, not an inconsistency.)
   --spr (the same colour as an "r,g,b" triplet) is no longer used for the item fill, but nav.js still sets
   it alongside --sp and other rules read it, so it stays. */
.rail .it{position:relative;display:flex;flex-direction:row;align-items:center;gap:11px;padding:15px 18px;
  color:var(--mute);transition:.16s;--sp:var(--diet-carni);--spr:var(--diet-carni-rgb);
  --it-c:45%;--it-a:65%;
  background:var(--panel);
  background:color-mix(in srgb, color-mix(in srgb, var(--sp) var(--it-c), var(--panel-solid)) var(--it-a), transparent)}
/* The edge bar is now purely the SELECTION marker, so it starts invisible instead of drawing the diet colour
   on top of itself — at rest it was the same colour as the fill behind it and read as nothing at all. */
.rail .it::after{content:"";position:absolute;left:0;top:4px;bottom:4px;width:2px;background:#fff;opacity:0;transition:.16s}
.rail .it .l{position:relative;z-index:1;flex:1 1 auto;font-family:var(--font-b);font-size:16px;letter-spacing:1px;text-transform:uppercase;font-weight:700;text-align:left;line-height:1.05}
.rail .it .i{position:relative;z-index:1;flex:0 0 auto;font-size:16px;line-height:1;opacity:.8}
.rail .it.on .i,.rail .it:hover .i{opacity:1}
/* Hover leans a little further toward the diet colour — the same dial as the rest, just a stronger mix, so
   every state sits on one scale instead of hover being a separate effect. */
.rail .it:hover{color:var(--white);--it-c:55%;--it-a:90%}
.rail .it:hover::after{opacity:.45}
/* Selected: the diet colour at FULL STRENGTH, with a DARK label on it.
   Saturation was raised across all three states 2026-08-23 (rest 30->45, hover 42->55, selected 50->100).
   The selected one could only go that far by flipping the label, and the numbers are why. With a WHITE label
   the fill is capped at 50%: herbivore green measures 4.80:1 there and 4.20:1 at 55%, under the 4.5 floor.
   That cap is what the old note here recorded. But the constraint INVERTS with a dark label -- the brighter
   the fill, the better ink reads on it -- so at 100% the worst of the three is 7.62:1 (omni), comfortably
   better than the 4.80 it replaced. More saturated and more legible are the same direction here, not a trade.
   Full strength is right for the ONE selected item even though it would be far too loud for all thirteen at
   rest (see .rail .it above): loud is the job of a "you are here" marker.
   The markers go ink for the same reason the label did -- a white hairline on bright #7cdf3d is invisible,
   and the saturated fill is now doing the marking anyway, so these only add definition. */
.rail .it.on{color:var(--ink);--it-c:100%;--it-a:100%;
  box-shadow:inset 0 0 0 1px rgba(8,8,10,.55)}
.rail .it.on .l{font-weight:800}
.rail .it.on::after{opacity:1;width:3px;background:var(--ink)}
/* The foot carries THREE things, in this order: your profile, notifications, and where you are.
   A ROW, not the old stacked column - the rail is 210px wide, which fits all three side by side, and the
   three are peers rather than a status caption with an avatar hanging off it. */
.rail .foot{flex:0 0 auto;border-top:1px solid var(--edge);padding:9px 10px;display:flex;flex-direction:row;
  align-items:center;justify-content:center;gap:8px}
/* A CIRCLE. This was the site's hexagon motif (clip-path polygon), which is still what .prow .av wears in
   the voice panel - the rail's profile is deliberately the exception, so it reads as "you" rather than as
   another piece of chrome.
   border-radius rather than clip-path, so the 1px outline actually draws: a clip cuts the border off with
   the corners, which is why this used to need its fill to carry the shape on its own. --panel2 (opaque)
   stays, because translucent it vanished against the rail and left a bare floating emoji.
   The crop itself now belongs to APEX.pfp's .pfp-c - one renderer draws every avatar on the site, photo or
   initial - so this is just the SLOT it sits in and it deliberately does not clip: a bought decoration hangs
   outside the picture, and an overflow:hidden here would shave it off. The outline and the opaque --panel2
   fill move onto .pfp-c, which is the element that is actually round; translucent, it vanished against the
   rail and left a bare floating emoji. */
.rail .av{width:32px;height:32px;flex:0 0 auto;display:grid;place-items:center;cursor:pointer}
.rail .av .pfp-c{border:1px solid var(--control-line);background:var(--panel2)}

/* notification flag. Its count badge is the SAME shape as the per-nav-item .navbadge, deliberately: this
   is the same class of signal, just totalled.
   The glyph (U+2691) is a plain text symbol, so it takes the button's colour through currentColor - which
   is what lets the :hover rule below reach it at all, and keeps it in the same monochrome vocabulary as the
   nav icons above (◆ ▤ ▣ ☰ ⬡ ◇ ✦ ▲ ⬓ ⇄ ◉ ◈ ≡). */
.rail .nbtn{position:relative;width:32px;height:32px;flex:0 0 auto;display:grid;place-items:center;cursor:pointer;
  background:var(--panel);border:1px solid var(--control-line);border-radius:var(--radius);color:var(--mute);
  font-size:15px;line-height:1;transition:.16s;padding:0}
.rail .nbtn:hover{color:var(--white);border-color:var(--white)}
.rail .nbtn .nb{position:absolute;top:-5px;right:-5px;min-width:15px;height:15px;padding:0 3px;border-radius:8px;
  background:var(--brand);color:var(--on-brand);font-size:9px;font-weight:800;display:none;place-items:center;
  line-height:1;box-shadow:0 0 0 2px var(--ink);font-family:var(--font-b)}
.rail .nbtn .nb.on{display:grid}

/* The per-nav-item unread badge. Moved here from an inline cssText in nav.js 2026-08-23, both because
   presentation belongs in the stylesheet and because it had a collision: the badge fills with --brand, which
   IS --diet-omni, and the selected nav item now fills with its diet colour at full strength — so a badge on
   the selected Omnivore item was brand-on-brand and disappeared into it.
   On the selected item it therefore inverts: ink fill, light text, light ring. Everywhere else it keeps the
   brand fill it always had. The ring exists to punch the badge off whatever is behind it, so it flips too. */
.rail .it .navbadge{position:absolute;top:5px;right:7px;min-width:15px;height:15px;padding:0 3px;border-radius:8px;
  background:var(--brand);color:var(--on-brand);font-size:9px;font-weight:800;display:grid;place-items:center;
  line-height:1;box-shadow:0 0 0 2px var(--ink);font-family:var(--font-b)}
.rail .it.on .navbadge{background:var(--ink);color:var(--white);box-shadow:0 0 0 2px rgba(245,245,243,.45)}

/* Presence as a STATUS DOT plus a place. "Online" on its own doesn't answer the question players actually
   ask, which is which server you are on - so the dot carries the region tag beside it (EU / NA) and only
   falls back to the word OFFLINE when there is no placement to name.
   The dot is a text glyph whose SHAPE already states the answer (nav.js swaps ● for ○), so everything here
   only has to reinforce it: the marker inherits --mute-dim at rest and --green in-world, and the glyph takes
   that through currentColor. Nothing sets a colour on the glyph itself, which is what lets one rule
   restyle both states. */
.rail .loc{display:flex;align-items:center;gap:5px;flex:0 0 auto;min-width:0;padding:6px 8px;border-radius:var(--radius);
  background:var(--panel);border:1px solid var(--control-line);color:var(--mute-dim);cursor:default;transition:.16s}
.rail .loc .pin{font-size:10px;line-height:1;flex:0 0 auto;opacity:.75;transition:.16s}
.rail .loc .lt{font-size:9px;letter-spacing:1px;text-transform:uppercase;font-weight:800;
  max-width:70px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rail .loc.on{color:var(--green);border-color:var(--green-line);background:var(--green-wash)}
.rail .loc.on .pin{opacity:1}

/* ---- SHORT WINDOWS: the rail is a fixed column in a 100vh body, so viewport HEIGHT is a hard budget and
   nothing here used to respond to it. At rest the chrome costs ~237px (the 420x315 wordmark alone renders
   157px tall at a 210px rail -- 3.4 nav items of pure decoration) and each item is 47px, so the 13-item
   rail needed ~859px of viewport and Games, 10th in the list, needed ~712px. A 1366x768 laptop gives about
   640px and 1080p at 150% scaling about 610px: both cut the list at ~8 items with no scrollbar to say so,
   which read as "the Games tab doesn't exist". Adding the Shop tab (2026-08-20) pushed Games down one slot
   and moved that threshold 47px, which is when players started noticing.

   So: trade decoration for tabs as the window gets shorter. The wordmark shrinks first -- it is the single
   biggest non-functional block -- then the item padding tightens. Scoped to min-width:1101px because below
   that the rail is a horizontal strip (see the max-width:1100 block) where height is not the constraint and
   these paddings would fight the ones set there.

   Budget at each step, for 13 items (staff see a 14th, `dev`): fits down to ~708px, then ~594px. */
@media (min-width:1101px) and (max-height:880px){
  /* width:auto + max-height, NOT a max-height on the full-bleed width:100% -- that one clamps the height
     while the width stays 210px and squashes the wordmark. This scales it down whole and centres it. */
  .rail .mark img{width:auto;max-width:100%;max-height:110px;margin:0 auto}
  .rail .it{padding:11px 18px}
}
@media (min-width:1101px) and (max-height:720px){
  .rail .mark img{max-height:74px}
  .rail .it{padding:8px 18px}
  /* This line did NOT take effect between 2026-07-30 and 2026-08-23. The readability pass appended
     `.rail .it .l{font-size:16px}` to the foot of the file; a media query adds no specificity, so that later
     rule beat this one at every viewport height and the label stayed 16px. The rail's whole short-window
     budget above assumes this shrink happens, so the tab-count thresholds it quotes were optimistic by one
     item. Folding the pass into the base declaration restored it. */
  .rail .it .l{font-size:14px}
}

/* ============ page shell ============ */
.page{flex:1 1 auto;min-width:0;display:flex;flex-direction:column}
.protobar{display:none;flex:0 0 auto;background:var(--brand-wash);border-bottom:1px solid var(--brand-line);
  padding:3px 16px;font-size:10px;letter-spacing:1.2px;text-transform:uppercase;color:var(--brand-hi);
  display:flex;align-items:center;gap:8px}
.protobar span{color:var(--mute-dim);text-transform:none;letter-spacing:.2px;font-size:10.5px}
/* The bottom padding is a GUTTER for the fixed Chat / Guide tabs (nav.js, vault.html), which float over the
   page at bottom-right. Without it they sit on top of whatever is at the bottom of a panel - the Apply-skin
   button in Cosmetics was the one people hit. Every scrolling panel is inside main, so reserving the band
   here covers all of them at once; the two tabs are side by side in that band rather than stacked, so it
   only costs one button height. --fab-gutter is the single source both ends read. */
:root{--fab-gutter:60px}
/* Too narrow for two tabs side by side: they stack again, so the band has to be two buttons tall. */
@media(max-width:520px){ :root{--fab-gutter:108px} }
main{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;
  padding:14px clamp(14px,1.8vw,26px) var(--fab-gutter)}

.phead{flex:0 0 auto;display:flex;align-items:flex-start;gap:16px;margin-bottom:12px}
.phead .ttl h1{font-family:var(--font-d);font-size:34px;letter-spacing:2.4px;line-height:1}
.phead .ttl .sub{color:var(--mute);font-size:16px;margin-top:3px}
.phead .wallet{margin-left:auto;display:flex;align-items:center;gap:9px;flex:0 0 auto;padding-top:2px}

/* wallet: icon + figure; the name lives in the hover tooltip

   BOTH CHIPS WEAR THE ACCENT. They used to be hardcoded green (Fossil Fragments) and blue (Paleo Points),
   which pulled two more colours into the one corner of the page that is on every single screen. They are now
   one family: --brand for the Fragments, --brand-hi for the Points, over the same wash and the same line.
   What tells them apart is the ICON SHAPE - an irregular shard against a symmetric gem - not the hue, which
   is why the two SVGs were drawn to be unmistakable in silhouette at 23px. Do not re-introduce a second hue
   here to separate them; redraw the glyph instead. */
.wal{position:relative;display:flex;align-items:center;gap:8px;padding:6px 12px;background:var(--panel);border:1px solid var(--rule);border-radius:var(--radius);cursor:default}
.wal .n{font-family:var(--font-d);font-size:27px;font-variant-numeric:tabular-nums;letter-spacing:.5px;line-height:1}
.wal .ico{width:23px;height:23px;flex:0 0 auto;display:grid;place-items:center;
  background-color:currentColor;-webkit-mask:center/contain no-repeat;mask:center/contain no-repeat}
.wal.cents{background:var(--brand-wash);border-color:var(--brand-line)}
.wal.cents .n,.wal.cents .ico{color:var(--brand)}
.wal.cents .ico{-webkit-mask-image:url(img/fossilFragment.svg);mask-image:url(img/fossilFragment.svg)}
.wal.dollars{background:var(--brand-wash);border-color:var(--brand-line)}
.wal.dollars .n,.wal.dollars .ico{color:var(--brand-hi)}
.wal.dollars .ico{-webkit-mask-image:url(img/paleoPoint.svg);mask-image:url(img/paleoPoint.svg)}
.wal::after{content:attr(data-tip);position:absolute;top:calc(100% + 7px);right:0;z-index:40;white-space:nowrap;
  font-family:var(--font-b);font-size:11px;letter-spacing:.3px;text-transform:none;color:var(--white);
  background:#16161b;border:1px solid var(--edge);border-radius:var(--radius);padding:6px 10px;box-shadow:0 8px 26px rgba(0,0,0,.7);
  opacity:0;transform:translateY(-3px);pointer-events:none;transition:.14s}
.wal::before{content:"";position:absolute;top:calc(100% + 2px);right:16px;z-index:41;border:5px solid transparent;
  border-bottom-color:var(--edge);opacity:0;transition:.14s;pointer-events:none}
.wal:hover::after,.wal:hover::before{opacity:1;transform:translateY(0)}
.wal.cents:hover{border-color:var(--brand)}
.wal.dollars:hover{border-color:var(--brand-hi)}
/* Your picture, top right, beside the name it belongs to. The name block reads right-aligned INTO it, so the
   two form one control rather than a label and a stray avatar; both carry the same title and the same click.
   No overflow/clip here on purpose - a bought decoration is drawn outside the picture (see .pfp). */
.mypfp{flex:0 0 auto;display:grid;place-items:center;cursor:pointer;margin-left:2px}
.who{text-align:right;line-height:1.15;padding-left:7px}
.who .nm{font-family:var(--font-e);font-weight:800;font-size:18px}
.who .tier{font-size:9.5px;letter-spacing:1.5px;color:var(--brand);text-transform:uppercase}

/* ============ avatars + pfp decorations (APEX.pfp) ============
   One block for every profile picture on the site. The wrapper carries the SIZE (--pfp, set inline by the
   helper), .pfp-c crops the picture, and .pfp-d is the bought decoration.
   The decoration is a SIBLING of the crop, never a child: .pfp-c has overflow:hidden so an overlay inside it
   would be clipped exactly where the art needs to escape. -18% on each side makes the decoration's square
   canvas 1.36x the avatar, which is the ~73%-of-canvas layout the art is drawn to; change one and the frames
   stop lining up with the picture. */
.pfp{position:relative;display:inline-grid;place-items:center;flex:0 0 auto;width:var(--pfp);height:var(--pfp);vertical-align:middle}
.pfp .pfp-c{width:100%;height:100%;border-radius:50%;overflow:hidden;background:var(--panel2);display:grid;place-items:center}
.pfp .pfp-c img{width:100%;height:100%;object-fit:cover;display:block}
.pfp .pfp-t{font-family:var(--font-d);font-size:calc(var(--pfp) * .44);line-height:1;color:var(--mute)}
.pfp.sq .pfp-c{border-radius:var(--radius)}
.pfp.hex .pfp-c{border-radius:0;clip-path:polygon(50% 0,100% 25%,100% 75%,50% 100%,0 75%,0 25%)}
.pfp .pfp-d{position:absolute;inset:-18%;background:center/contain no-repeat;pointer-events:none}

/* THE TWO CURRENCY GLYPHS — .coin is a Fossil Fragment, .gem is a Paleo Point. (The class names are the old
   ones on purpose: they are spelled at ~90 call sites and renaming them would be a large diff that changes
   nothing on screen. The art and the words are what moved.)
   Drawn as MASKS filled with currentColor, not as flat images, so one pair of SVGs serves every context and
   takes that context's colour for free: accent in the wallet, the price colour inside a .cur, --white in a
   sentence. That is also why swapping the accent needs no re-export. */
.coin,.gem{width:15px;height:15px;flex:0 0 auto;display:inline-block;vertical-align:-3px;
  background-color:currentColor;-webkit-mask:center/contain no-repeat;mask:center/contain no-repeat}
.coin{-webkit-mask-image:url(img/fossilFragment.svg);mask-image:url(img/fossilFragment.svg)}
.gem{-webkit-mask-image:url(img/paleoPoint.svg);mask-image:url(img/paleoPoint.svg)}
/* A PRICE, IN THE CURRENCY IT IS CHARGED IN. The site already speaks this — Fossil Fragments are drawn green
   with the shard and Paleo Points blue with the gem — so a price says which pot it comes out of by LOOKING
   like that pot instead of having to be read. One definition, because a shelf, a button and a confirmation
   dialog all showing the same price have to agree or the colour stops carrying meaning.
   NOTE the wallet chips (.wal, above) no longer follow this green/blue split — the top-right pair was moved
   onto the accent deliberately. Prices still use it. */
.cur{display:inline-flex;align-items:center;gap:4px;font-weight:700;font-variant-numeric:tabular-nums;white-space:nowrap}
.cur.cents{color:var(--green)}
.cur.dollars{color:var(--blue)}
.cur .coin,.cur .gem{vertical-align:0}

.crumbs{flex:0 0 auto;display:flex;align-items:center;gap:8px;margin-bottom:10px}
.crumbs a,.crumbs span{font-size:13px;letter-spacing:1.5px;text-transform:uppercase;color:var(--mute-dim)}
.crumbs a:hover{color:var(--brand)}
.crumbs .cur{color:var(--white)}
.crumbs .acts{margin-left:auto;display:flex;gap:6px}

/* ============ controls ============ */
.btn{background:transparent;border:1px solid var(--control-line);border-radius:var(--radius);color:var(--mute);font-size:13px;font-weight:700;
  letter-spacing:1.4px;text-transform:uppercase;padding:8px 14px;cursor:pointer;transition:.18s;display:inline-block}
.btn:hover{border-color:var(--white);color:var(--white)}
.btn.pri{background:var(--brand);color:var(--on-brand);border-color:var(--brand)}
.btn.pri:hover{background:var(--brand-hi);border-color:var(--brand-hi);color:var(--on-brand);box-shadow:0 6px 20px var(--brand-wash)}
.btn.sell{border-color:var(--green-line);color:var(--green)}
.btn.sell:hover{border-color:var(--green);color:var(--green-hi)}
/* ui: soften every hard rectangle a touch — panels, cards, buttons, inputs, tiles, badges, sheets, etc. */
button,input,select,textarea,
.btn,.wal,.field,.panel,.rec,.rec .act,.tile,.empty,.skn,.toast,.mk,.head,.gt,.zstat,.mempanel,.acard,
[class*="card"],[class*="panel"],[class*="tile"],[class*="sheet"],[class*="pop"],[class*="badge"],[class*="chip"],[class*="pill"],[class*="modal"],[class*="btn"]{border-radius:var(--radius)}
.tabs,.pager .pgs{overflow:hidden;border-radius:var(--radius)}   /* rounded groups clip their inner segments cleanly */
.tools{flex:0 0 auto;display:flex;align-items:center;gap:8px;margin-bottom:11px}
.tabs{display:flex;background:var(--panel);border:1px solid var(--rule);border-radius:var(--radius)}
.tab{background:transparent;border:none;border-right:1px solid var(--control-line);padding:7px 14px;font-size:13.5px;
  font-weight:700;letter-spacing:1.5px;text-transform:uppercase;color:var(--mute-dim);cursor:pointer;transition:.18s}
.tab:last-child{border-right:none}
.tab:hover{color:var(--white);background:var(--wash)}
.tab.on{color:var(--white);background:var(--brand-wash);box-shadow:inset 0 -2px 0 var(--brand)}
.grow{flex:1}
.field{display:flex;align-items:center;gap:6px;border:1px solid var(--rule);border-radius:var(--radius);padding:0 10px;height:30px;background:var(--panel)}
.field:focus-within{border-color:var(--blue)}
.field input,.field select{background:transparent;border:none;outline:none;font-size:15px}
.field input{width:160px}
.field select{cursor:pointer}
.field select option{background:var(--panel-solid)}   /* native option lists must be opaque */
.field .ic{color:var(--mute-dim);font-size:14px}

/* ============ panels ============ */
.panel{border:1px solid var(--rule);border-radius:var(--radius);background:var(--panel);
  display:flex;flex-direction:column;min-height:0}
.panel > h3{flex:0 0 auto;font-size:12.5px;letter-spacing:2px;text-transform:uppercase;color:var(--mute-dim);
  padding:8px 12px;border-bottom:1px solid var(--rule);display:flex;align-items:center;gap:8px}
.panel > h3 .r{margin-left:auto;color:var(--mute);letter-spacing:1.1px}
.pbody{flex:1 1 auto;min-height:0;padding:11px 12px}
.pbody.scroll{overflow-y:auto}
.pbody.scroll::-webkit-scrollbar{width:6px}
.pbody.scroll::-webkit-scrollbar-track{background:transparent}
.pbody.scroll::-webkit-scrollbar-thumb{background:var(--brand-line)}

/* ============ THE SPECIMEN RECORD ============ */
.stack{flex:1 1 auto;min-height:0;display:grid;grid-auto-rows:1fr;gap:7px}
.rec{position:relative;display:flex;min-height:0;overflow:hidden;border:1px solid var(--rule);border-radius:var(--radius);
  background:var(--panel);transition:border-color .16s,background .16s}
.rec::before{content:"";position:absolute;left:0;top:0;bottom:0;width:2px;background:transparent;transition:.16s}
.rec:hover{border-color:var(--edge);background:linear-gradient(90deg,rgba(255,255,255,.06),rgba(0,0,0,.1))}
.rec:hover::before{background:var(--brand)}

.rec .plate{flex:0 0 172px;position:relative;overflow:hidden;border-right:1px solid var(--rule);display:grid;place-items:center;
  background:repeating-linear-gradient(45deg,rgba(255,255,255,.028) 0 7px,transparent 7px 14px),
    radial-gradient(70% 70% at 50% 55%,rgba(255,255,255,.05),transparent 70%),var(--panel)}
.rec .plate::after{content:"";position:absolute;inset:0;
  background-image:linear-gradient(rgba(255,255,255,.035) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.035) 1px,transparent 1px);
  background-size:18px 18px}
.rec .plate .sil{position:relative;z-index:1;font-size:32px;opacity:.5;line-height:1}
.rec .plate .dimg{position:absolute;inset:0;z-index:0;width:100%;height:100%;object-fit:cover;display:block}
.rec .plate .tag{position:absolute;z-index:1;bottom:5px;left:0;right:0;text-align:center;
  font-size:8px;letter-spacing:1.2px;color:var(--mute-dim);text-transform:uppercase}

.rec .entry{flex:1 1 auto;min-width:0;padding:11px 14px;display:flex;flex-direction:column;justify-content:center}
.rec .no{font-family:var(--mono);font-size:12px;letter-spacing:.8px;color:var(--mute-dim);display:flex;align-items:center;gap:8px}
.rec .no .sp{letter-spacing:2px;text-transform:uppercase;font-family:var(--font-b);font-weight:600}
.rec .nm{font-family:var(--font-e);font-weight:800;font-size:24px;letter-spacing:.3px;line-height:1.2;margin-top:2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rec .marks{display:flex;gap:5px;margin-top:6px;flex-wrap:wrap}
.mk{font-size:9.5px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;padding:2px 6px;border:1px solid;border-radius:var(--radius);line-height:1.3}
.mk.prime{color:var(--brand-hi);border-color:var(--brand-line)}
.mk.elder{color:var(--blue);border-color:var(--blue-line)}
.mk.mut{color:var(--blue);border-color:var(--blue-line);opacity:.75}
.mk.timer{color:var(--mute);border-color:var(--rule);font-variant-numeric:tabular-nums}

.rec .growth{flex:0 0 96px;border-left:1px solid var(--rule);padding:11px 12px;display:flex;flex-direction:column;justify-content:center}
.rec .growth .k{font-size:9px;letter-spacing:1.5px;color:var(--mute-dim);text-transform:uppercase}
.rec .growth .v{font-family:var(--font-d);font-size:33px;line-height:1;font-variant-numeric:tabular-nums;margin-top:1px;color:var(--white)}
.rec .growth .v small{font-size:13px;color:var(--mute-dim)}
.rec .growth .st{font-size:9px;letter-spacing:1.2px;color:var(--mute-dim);text-transform:uppercase;margin-top:2px}
.rec .growth .ru{height:2px;background:rgba(255,255,255,.08);margin-top:6px;overflow:hidden}
.rec .growth .ru i{display:block;height:100%;background:var(--white);opacity:.55}

/* field readout — MONOCHROME on purpose: RGB is brand vocabulary now, not data */
.rec .read{flex:0 0 auto;border-left:1px solid var(--rule);padding:11px 13px;display:grid;
  grid-template-columns:repeat(4,46px);gap:0 12px;align-content:center}
.rd .k{font-size:8.5px;letter-spacing:1.3px;color:var(--mute-dim);text-transform:uppercase}
.rd .v{font-size:17.5px;font-weight:700;font-variant-numeric:tabular-nums;line-height:1.1;color:var(--white)}
.rd .ru{height:1px;background:rgba(255,255,255,.08);margin-top:3px;overflow:hidden}
.rd .ru i{display:block;height:100%;background:var(--white);opacity:.45}

.rec .tail{flex:0 0 auto;border-left:1px solid var(--rule);padding:11px 13px;display:flex;flex-direction:column;
  justify-content:center;align-items:flex-end;gap:6px;min-width:132px}
.rec .tail .when,.rec .tail .by{font-size:10.5px;letter-spacing:1px;color:var(--mute-dim);text-transform:uppercase;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:120px}
.rec .tail .price{display:flex;align-items:center;gap:5px}
.rec .tail .price .n{font-family:var(--font-d);font-size:25px;color:var(--green);font-variant-numeric:tabular-nums;line-height:1}
.rec .act{background:transparent;border:1px solid var(--control-line);border-radius:var(--radius);color:var(--mute-dim);font-size:10px;font-weight:700;
  letter-spacing:1.3px;text-transform:uppercase;padding:5px 11px;cursor:pointer;transition:.18s}
.rec:hover .act{border-color:var(--brand);color:var(--brand-hi);background:var(--brand-wash)}

/* ---- CARD GRID variant (My Isle: banked dinos + the transfer wallet · the Marketplace: listings) ----
   The SAME .rec markup, laid out as a rectangle tile instead of a full-width row, so dinoCard() in data.js
   stays ONE renderer for the vault, the marketplace and the transfer wallet - only the CONTAINER opts in
   (.stack.cards). A plain .stack still draws rows.
   Two deliberate choices about a part-full page:
     - grid-auto-rows is CONTENT height, never 1fr. With 1fr, three banked dinos stretched to fill the panel
       and drew as three enormous cards; align-content:start leaves dead space under them instead.
     - auto-FILL, not auto-fit: the unused columns stay empty rather than collapsing, so two cards sit at
       the same width as six do.
   Card size is set by the ART: every species png is square (640x640), so the plate is a SQUARE the full
   width of the card and the whole dino shows, uncropped.
   The track is a FIXED width, not minmax(...,1fr), and the plate's height is that same --cardw rather than
   aspect-ratio:1. Both were flexible first, and it laid out wrong: grid sizes an auto row from the item's
   intrinsic contribution, which for an aspect-ratio box whose width is not yet resolved comes out short, so
   the square plate overflowed a too-short card and painted over the name and the vitals. Two fixed numbers
   that agree cannot disagree.
   overflow-y:auto is what makes these grids SCROLL: both pages draw every row and let the panel scroll
   rather than paging. scrollbar-gutter:stable reserves the scrollbar's width whether or not it is showing,
   so a list that grows past the panel does not shunt every column sideways as the bar appears. */
.stack.cards{--cardw:288px;grid-template-columns:repeat(auto-fill,var(--cardw));grid-auto-rows:max-content;
  align-content:start;justify-content:start;gap:12px;overflow-y:auto;scrollbar-gutter:stable}
/* Below the track width there is no room for a fixed track: one full-width column instead. The plate keeps
   its 288px height (--cardw is untouched), so it is near enough square on a phone. */
@media(max-width:340px){ .stack.cards{grid-template-columns:minmax(0,1fr)} }
.stack.cards::-webkit-scrollbar{width:6px}
.stack.cards::-webkit-scrollbar-track{background:transparent}
.stack.cards::-webkit-scrollbar-thumb{background:var(--brand-line)}
.stack.cards .empty{grid-column:1/-1}      /* sign-in / no-results states are a message, not a card */
/* align-self:start so the card is its own content height even if the track is taller than it - a stretched
   card would hand its extra height to the 1fr entry row and drift out of step with the measured page size. */
.stack.cards .rec{align-self:start;display:grid;grid-template-columns:88px minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto auto;
  grid-template-areas:"plate plate" "entry entry" "growth read" "tail tail";flex-wrap:nowrap}
.stack.cards .rec .plate{grid-area:plate;flex:none;height:var(--cardw);border-right:0;border-bottom:1px solid var(--rule)}
/* contain, not the row layout's cover: the whole dino, uncropped, is the point of the taller card */
.stack.cards .rec .plate .dimg{object-fit:contain}
.stack.cards .rec .entry{grid-area:entry;flex:none;min-height:0;overflow:hidden;padding:9px 12px}
/* .entry is a COLUMN flex box in a row that is now height-capped, so its children shrink - and the name,
   having no intrinsic minimum, collapsed to zero and vanished behind overflow:hidden. Pin all three. */
.stack.cards .rec .entry>*{flex:0 0 auto}
.stack.cards .rec .nm{font-size:17px}
.stack.cards .rec .growth{grid-area:growth;flex:none;border-left:0;border-top:1px solid var(--rule);padding:8px 10px}
.stack.cards .rec .growth .v{font-size:24px}
/* four vitals still fit across a ~210px column, but only at a smaller figure - at 15.5px "100%" collides
   with its neighbour. The columns are 1fr, not the row layout's fixed 46px, so they track the card width. */
.stack.cards .rec .read{grid-area:read;flex:none;border-left:1px solid var(--rule);border-top:1px solid var(--rule);
  padding:8px 11px;grid-template-columns:repeat(4,minmax(0,1fr));gap:0 7px}
.stack.cards .rec .rd .v{font-size:13.5px}
.stack.cards .rec .tail{grid-area:tail;flex:none;flex-direction:row;align-items:center;justify-content:flex-end;
  border-left:0;border-top:1px solid var(--rule);padding:9px 12px;gap:8px;min-width:0}
/* the timestamp takes the slack so the buttons stay right-aligned on a row that is now horizontal */
.stack.cards .rec .tail .when,.stack.cards .rec .tail .by{margin-right:auto;max-width:none;min-width:0}

/* ============ pager ============ */
.pager{flex:0 0 auto;display:flex;align-items:center;gap:8px;margin-top:11px}
.pager .cnt{font-size:13.5px;letter-spacing:1.2px;color:var(--mute-dim);text-transform:uppercase}
.pager .pgs{margin-left:auto;display:flex}
.pg{min-width:27px;height:27px;border:1px solid var(--rule);border-radius:var(--radius);border-right:none;background:transparent;color:var(--mute-dim);
  font-size:12.5px;font-weight:600;cursor:pointer;transition:.18s;padding:0 7px}
.pg:last-child{border-right:1px solid var(--rule)}
.pg:hover:not(:disabled){color:var(--white);background:var(--wash)}
.pg.on{color:var(--white);background:var(--brand-wash);box-shadow:inset 0 -2px 0 var(--brand)}
.pg:disabled{opacity:.3;cursor:default}

/* ============ stat plates ============ */
.tiles{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
.tile{border:1px solid var(--rule);border-radius:var(--radius);background:var(--panel);padding:10px 12px}
.tile .t{font-size:11px;letter-spacing:1.6px;color:var(--mute-dim);text-transform:uppercase}
.tile .v{font-family:var(--font-d);font-size:28px;line-height:1.15;margin-top:2px;font-variant-numeric:tabular-nums;color:var(--white)}
.tile .v.am{color:var(--brand)}    /* headline figure */
.tile .v.lv{color:var(--green)}  /* live / yours */

/* ============ empty + skins ============ */
.empty{flex:1 1 auto;display:grid;place-items:center;border:1px dashed var(--rule);color:var(--mute-dim);text-align:center;padding:30px;font-size:15px}
.empty .k{font-family:var(--font-d);font-size:22px;letter-spacing:2px;color:rgba(245,245,243,.14)}
.empty p{font-size:16px;margin-top:7px;max-width:440px;color:var(--mute-dim)}

.skins{flex:1 1 auto;min-height:0;display:grid;grid-template-columns:repeat(4,1fr);grid-auto-rows:1fr;gap:8px;overflow-y:auto}
.skins::-webkit-scrollbar{width:6px}
.skins::-webkit-scrollbar-thumb{background:var(--brand-line)}
.skn{border:1px solid var(--rule);border-radius:var(--radius);background:var(--panel);
  display:flex;flex-direction:column;transition:.16s;min-height:0;overflow:hidden}
.skn:hover{border-color:var(--edge)}
.skn .bars{flex:1 1 auto;min-height:44px;display:flex}
.skn .bars i{flex:1}
.skn .m{flex:0 0 auto;padding:8px 10px;border-top:1px solid var(--rule)}
.skn .m .n{font-family:var(--font-e);font-weight:800;font-size:17px;display:flex;align-items:center;gap:6px}
.skn .m .n .a{font-size:7px;letter-spacing:1.2px;color:var(--blue);border:1px solid var(--blue-line);border-radius:var(--radius);padding:1px 4px;text-transform:uppercase}
.skn .m .s{font-size:10.5px;letter-spacing:1.2px;color:var(--mute-dim);text-transform:uppercase;margin-top:2px}

/* ============ shelves + decoration tiles ============
   Lives here, not in one page's <style>, because TWO pages draw the same tiles from the same catalogue:
   Shop > Customization SELLS decorations, and My Isle > Inventory > Cosmetics EQUIPS the ones you
   own. One definition, so a tile cannot drift into looking like two different things. */
.shelf{margin-bottom:22px}
.shelf > h3{font-size:10.5px;letter-spacing:2px;text-transform:uppercase;color:var(--mute-dim);
  margin:0 0 3px;display:flex;align-items:baseline;gap:8px}
.shelf > .sh-note{font-size:11.5px;color:var(--mute-dim);margin:0 0 11px}
/* YOU on the left, the shelves on the right. The big picture is the product: a frame is judged by how it
   looks at full size on YOUR photo, so one large permanent preview stays beside the grid (sticky, so it
   follows the shelf you are scrolling) and the tiles only have to be legible enough to choose from. */
.cuwrap{display:grid;grid-template-columns:230px minmax(0,1fr);gap:18px;align-items:start;padding-bottom:8px}
@media (max-width:820px){ .cuwrap{grid-template-columns:1fr} }
.cume{position:sticky;top:0;border:1px solid var(--rule);border-radius:var(--radius);background:var(--panel);
  padding:16px 14px;text-align:center;display:flex;flex-direction:column;align-items:center;gap:4px}
/* The decoration hangs ~18% outside the picture on every side, so the well is bigger than the avatar. */
.cume .cuprev{height:172px;display:grid;place-items:center;margin-bottom:2px}
.cume .cunm{font-family:var(--font-e);font-weight:800;font-size:15px;color:var(--white)}
.cume .cuwear{font-size:11px;color:var(--mute-dim);min-height:15px}
.cume .cuwear b{color:var(--green)}
.cume .cuhint{font-size:10.5px;color:var(--mute-dim);line-height:1.45;margin-top:6px}
.cume .cbtn2{margin-top:8px}
/* auto-fill, because the catalogue is server-side and this must not need editing when a decoration is added. */
.cugrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(126px,1fr));gap:10px}
.cutile{border:1px solid var(--rule);border-radius:var(--radius);background:var(--panel2);padding:12px 10px;
  text-align:center;display:flex;flex-direction:column;align-items:center;gap:6px;cursor:pointer;transition:border-color .14s,background .14s}
.cutile:hover{border-color:var(--control-line)}
.cutile.on{border-color:var(--green-line);background:var(--green-wash)}          /* currently WORN */
.cutile.sel{border-color:var(--blue);box-shadow:0 0 0 1px var(--blue) inset}     /* currently PREVIEWED */
.cutile .cuthumb{height:80px;display:grid;place-items:center}
/* A FONT or COLOUR tile has nothing to show but the name itself, so the well holds text instead of a
   picture. Same height as the picture well so the shelves line up when a page shows both. The sample is
   the player's OWN name, which can be long — it wraps and shrinks rather than pushing the tile wider. */
.cutile .cuthumb.cutext{padding:0 4px}
.cutile .cusample{font-family:var(--font-e);font-weight:800;font-size:19px;line-height:1.15;color:var(--white);
  overflow-wrap:anywhere;max-width:100%;display:block}
.cume .cuwear .cupv{font-size:9px;letter-spacing:1.4px;text-transform:uppercase;color:var(--blue);margin-top:3px}
/* THE NAME ITSELF, wherever it is drawn. Deliberately almost empty: apexName only ever sets the font and
   the colour a player bought, and everything else about how a name looks belongs to the surface drawing
   it. This exists so the span is inert rather than inheriting something unexpected. */
.uname{font:inherit;color:inherit}
.cutile .cun{font-size:12.5px;font-weight:700;color:var(--white);line-height:1.2}
.cutile .cuanim{font-size:9px;letter-spacing:.8px;text-transform:uppercase;color:var(--blue)}
.cutile .cust{font-size:11px;color:var(--mute-dim);min-height:15px}
.cutile .cbtn2{width:100%;justify-content:center;display:flex;align-items:center;gap:5px}
.cutile .cbtn2:disabled{opacity:.45;cursor:default}
.cbtn2{padding:7px 14px;border:1px solid var(--control-line);border-radius:var(--radius);font-weight:700;
  letter-spacing:.5px;text-transform:uppercase;font-size:10px;cursor:pointer;background:transparent;color:var(--white)}
.cbtn2:hover:not(:disabled){border-color:var(--green-line);color:var(--green)}

/* ============ toasts (real action feedback) ============ */
.toasts{position:fixed;right:16px;top:70px;z-index:9998;display:flex;flex-direction:column;gap:8px;align-items:flex-end}
.toast{max-width:420px;font-size:17px;letter-spacing:.2px;color:var(--white);background:#16161b;
  border:1px solid var(--edge);border-radius:var(--radius);border-left:3px solid var(--mute);padding:12px 16px;box-shadow:0 10px 30px rgba(0,0,0,.6);
  opacity:0;transform:translateY(-8px);transition:.22s}
.toast.in{opacity:1;transform:translateY(0)}
.toast.ok{border-left-color:var(--green)}
.toast.err{border-left-color:var(--danger)}   /* a failed action, not an accent */

/* ---- reclaim progress ----------------------------------------------------------------------------
   A reclaim is a multi-step server job, not a request: growth, colours, mutations, entombment, a settle
   wait, then a vitals watchdog that runs from ~3s to a 300s cap. The button alone could not carry that,
   so this sits under the toasts and narrates it. Shared by the Vault and the dino page (APEX.reclaimRun). */
.rcl{position:fixed;right:16px;top:70px;z-index:9997;width:340px;max-width:calc(100vw - 32px);
  background:#16161b;border:1px solid var(--edge);border-left:3px solid var(--brand);border-radius:var(--radius);
  padding:12px 16px 14px;box-shadow:0 10px 30px rgba(0,0,0,.6);opacity:0;transform:translateY(-8px);transition:.22s}
.rcl.in{opacity:1;transform:translateY(0)}
.rcl.ok{border-left-color:var(--green)}
.rcl.err{border-left-color:var(--danger-hi)}
.rcl .rk{font-size:10.5px;letter-spacing:2px;text-transform:uppercase;color:var(--mute-dim);margin-bottom:5px}
.rcl .rt{font-size:14px;color:var(--white);letter-spacing:.2px}
.rcl .rs{margin-top:3px;font-size:12px;color:var(--mute)}
.rcl .rbar{margin-top:10px;height:4px;border-radius:2px;background:var(--panel2);overflow:hidden}
.rcl .rbar i{display:block;height:100%;width:0;background:var(--brand);transition:width .35s ease}
.rcl.ok .rbar i{background:var(--green)}
.rcl.err .rbar i{background:var(--danger-hi)}
/* Indeterminate sweep for the vitals watchdog: its duration is genuinely unknown (2.6s or 300s), so a
   percentage there would be a fabrication. The bar stops pretending and just shows movement. */
.rcl.wait .rbar i{width:100% !important;background:linear-gradient(90deg,transparent,var(--brand),transparent);
  animation:rclsweep 1.4s linear infinite}
@keyframes rclsweep{0%{transform:translateX(-100%)}100%{transform:translateX(100%)}}
@media(prefers-reduced-motion:reduce){ .rcl.wait .rbar i{animation:none;opacity:.6} }

/* (removed dead .item.locked / .item .req — no HTML used them) */

/* ============ responsive ============ */
@media(max-width:1280px){ .rec .read{grid-template-columns:repeat(2,46px);gap:6px 12px} }
@media(max-width:1100px){
  body{height:auto;min-height:100vh;overflow:hidden auto;flex-direction:column}
  .rail{flex:0 0 auto;flex-direction:row;position:sticky;top:0;border-right:none;border-bottom:1px solid var(--edge);
    overflow-x:auto;align-items:center}
  .rail .mark{border-bottom:none;border-right:1px solid var(--control-line);width:56px;flex:0 0 auto}
  .rail .items{flex-direction:row;overflow-x:auto;overflow-y:hidden;padding:0}
  .rail .it{flex:0 0 auto;padding:8px 11px}
  .rail .foot{border-top:none;border-left:1px solid var(--edge);flex-direction:row;padding:0 10px}
  /* Narrow: the dot alone carries the state (filled/hollow = online, tooltip = which server); the region
     text is what goes, not the marker. The foot is avatar / flag / marker. */
  .rail .loc .lt{display:none}
  .rail .loc{padding:6px}
  .stack{grid-auto-rows:auto}
  .rec{flex-wrap:wrap}
  .rec .plate{flex:0 0 118px}
  .rec .entry{flex:1 1 200px}
  .rec .growth,.rec .read,.rec .tail{border-left:none;border-top:1px solid var(--rule)}
  .tiles{grid-template-columns:repeat(2,1fr)}
  .pbody.scroll{overflow:visible}
}
@media(max-width:620px){
  .phead{flex-wrap:wrap}
  .phead .wallet{margin-left:0;width:100%;justify-content:flex-start}
  .rec .read{grid-template-columns:repeat(4,1fr);width:100%}
  .rec .tail{width:100%;align-items:flex-start;flex-direction:row;justify-content:space-between}
  .tools{flex-wrap:wrap}
  .field input{width:110px}
  .skins{grid-template-columns:repeat(2,1fr)}
}

/* The 2026-07-30 readability pass (brighter greys + larger shared text) used to live HERE, as a block of 34
   font-size overrides appended to the foot of the file. It was FOLDED into the real declarations 2026-08-23:
   every selector above now states the size it actually renders at. The rendered output is unchanged - the
   overrides were plain re-declarations of the same selectors, so the cascade winner was already these values -
   but the component blocks no longer lie about themselves. Nothing between the two positions re-declared any
   of these at equal specificity; the three that looked like conflicts (.stack.cards .rec .nm / .growth .v /
   .rd .v) are MORE specific and won on specificity, not order, so they still win. */

/* Prime-eligibility tracker (shared: vault live-dino page + dino detail card — single source, see APEX.primeCard) */
.pcard{width:min(560px,100%);margin:14px auto 0;border:1px solid var(--rule);border-radius:var(--radius);background:var(--panel);padding:16px 18px}
.pcard.elig{border-color:#3a6a45}
.phd{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:10px}
.phd .k{font-size:10px;letter-spacing:1.5px;text-transform:uppercase;color:var(--mute-dim)}
.pstate{font-family:var(--font-d);font-size:13px;color:var(--mute-dim)}
.pstate.ok{color:#5bd06a}.pstate.no{color:#e0574a}.pstate.warn{color:#e0b64a}   /* window about to shut (last 5% of growth) */
.pbar{height:8px;background:rgba(255,255,255,.07);overflow:hidden}
.pbar i{display:block;height:100%;background:#c9a24a;transition:width .4s}
.pcard.elig .pbar i{background:#5bd06a}
.pnote{font-size:11px;color:var(--mute-dim);margin:8px 0 12px;line-height:1.5}
.plist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px}
.pitem{display:flex;gap:9px;align-items:flex-start;font-size:13px;color:var(--mute-dim)}
.pitem.done{color:var(--white)}
.pitem .pck{flex:0 0 16px;text-align:center;font-family:var(--font-d);color:var(--mute-dim)}
.pitem.done .pck{color:#5bd06a}
.pitem.fail{color:#e0574a}
.pitem.fail .pck{color:#e0574a}
.pitem.fail .ptx{text-decoration:line-through}
.pmuted{color:var(--mute-dim);font-size:11px}
.pitem .ptx{flex:1}
.pitem .psub{float:right;font-family:var(--font-d);font-size:11px;color:var(--mute-dim);font-variant-numeric:tabular-nums}
.pitem .pmini{display:block;height:4px;border-radius:2px;background:rgba(255,255,255,.07);overflow:hidden;margin-top:5px}
.pitem .pmini i{display:block;height:100%;background:#c9a24a;transition:width .4s}
.pitem.done .pmini i{background:#5bd06a}
/* dino-card prime conditions — same logic as the tracker, laid out as a full-width 3×3 grid (no progress bar) */
.pgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:9px 14px}
.pgi{display:flex;gap:7px;align-items:flex-start;font-size:12px;line-height:1.35;color:var(--mute-dim)}
.pgi.done{color:var(--white)}
.pgi .pck{flex:0 0 14px;text-align:center;font-family:var(--font-d);color:var(--mute-dim)}
.pgi.done .pck{color:#5bd06a}
.pgi.fail{color:#e0574a}
.pgi.fail .pck{color:#e0574a}
.pgi.fail .ptx{text-decoration:line-through}
.pgi .ptx{flex:1;min-width:0}
.pgi .psub{float:right;font-family:var(--font-d);font-size:10.5px;color:var(--mute-dim);font-variant-numeric:tabular-nums}
.pgi .pmini{display:block;height:4px;border-radius:2px;background:rgba(255,255,255,.07);overflow:hidden;margin-top:5px}
.pgi .pmini i{display:block;height:100%;background:#c9a24a;transition:width .4s}
.pgi.done .pmini i{background:#5bd06a}
.r .ok{color:#5bd06a}
.r .no{color:#e0574a}
@media(max-width:560px){.pgrid{grid-template-columns:repeat(2,1fr)}}

/* 4×4 mutation-slot grid (shared: dino detail card + vault live-dino tab — single source, see APEX.mutGrid) */
.mgroup{margin-bottom:10px}
.mgroup:last-child{margin-bottom:0}
.mgroup .gh{font-size:8px;letter-spacing:1.7px;color:var(--mute-dim);text-transform:uppercase;margin-bottom:5px;display:flex;gap:8px}
/* scoped under .mgroup — a bare `.slots` would collide with the memberships tier cards */
.mgroup .slots{display:grid;grid-template-columns:repeat(4,1fr);gap:5px}
.mgroup .slot{border:1px solid var(--rule);border-radius:var(--radius);background:var(--panel);padding:6px 7px;min-height:40px;display:flex;flex-direction:column;justify-content:center}
.mgroup .slot .sn{font-size:7.5px;letter-spacing:1.1px;color:var(--mute-dim);text-transform:uppercase}
.mgroup .slot .sv{font-size:11px;font-weight:600;margin-top:1px;line-height:1.2}
.mgroup .slot.empty{border-style:dashed;border-color:rgba(245,245,243,.1)}
.mgroup .slot.empty .sv{color:var(--mute-dim);font-weight:400;font-style:italic}
.mgroup .slot.filled .sv{color:var(--green)}
.mgroup .slot.parent.filled .sv{color:var(--brand-hi)}
.mgroup .slot.eld.filled .sv{color:var(--blue)}
@media(max-width:620px){.mgroup .slots{grid-template-columns:repeat(2,1fr)}}
