/* ============================================================
   INKVILLE — profile.css
   the inkling profile, the wardrobe, the rack and the album counter

   Part of the game.css split. Loaded as <link> tags from index.html in
   THIS ORDER — the cascade depends on it. Do not reorder the links.
   ============================================================ */
/* ===== STYLE — the wardrobe, Persona-5 flavour: the inkling in focus, bold angled panels, everything animates ===== */
#scr-style{overflow:hidden auto; color:var(--ink); position:absolute; inset:0; scrollbar-width:none;
  /* ⭐ v0.430 — MEASURED, NOT GUESSED. This was a flat 290px for a pad that is only 240px at --gs 1;
     on the unfolded Fold the pad is taller than the reservation and the last rows of the record sat
     underneath it. pfPadClear() measures the pad's real top edge and writes --pfpad on this element
     every render and on every resize. The constant below is only the pre-measurement fallback. */
  padding-bottom:var(--pfpad, 290px);   /* v3.43 — position:absolute (was relative, which let the screen grow past #app's overflow:hidden = NO scroll); now the screen itself scrolls, past the D-pad, on both folds */
  background:linear-gradient(#7fccff 0%, #a8e0ff 44%, #cfeeff 62%, #8fd66a 62%, #74c353 100%);
  background-repeat:no-repeat;}   /* sky + grass fill the whole window; chunky pixel clouds ride on ::before */
/* CHUNKY pixel-art clouds + a smiling pixel sun (the "character") — drift slowly across the sky */
#scr-style::before{content:''; position:absolute; left:-40px; right:-40px; top:0; height:60%; z-index:1; pointer-events:none;
  image-rendering:pixelated;
  /* ⚠ v0.456 — THE SUN IS NO LONGER ONE OF THESE. It was a 12×12 data-URI stretched to 74px;
     it is now a real 64×64 procedural sprite drawn by pfSunSvg() into .pf-sun. Only clouds here. */
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='9' shape-rendering='crispEdges'%3E%3Cg fill='%23ffffff'%3E%3Crect x='4' y='2' width='7' height='1'/%3E%3Crect x='3' y='3' width='10' height='1'/%3E%3Crect x='2' y='4' width='12' height='1'/%3E%3Crect x='1' y='5' width='14' height='1'/%3E%3Crect x='1' y='6' width='14' height='1'/%3E%3C/g%3E%3Crect x='1' y='7' width='14' height='1' fill='%23cfe8ff'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='9' shape-rendering='crispEdges'%3E%3Cg fill='%23ffffff'%3E%3Crect x='5' y='2' width='6' height='1'/%3E%3Crect x='3' y='3' width='9' height='1'/%3E%3Crect x='2' y='4' width='12' height='1'/%3E%3Crect x='1' y='5' width='14' height='1'/%3E%3Crect x='2' y='6' width='13' height='1'/%3E%3C/g%3E%3Crect x='2' y='7' width='13' height='1' fill='%23cfe8ff'/%3E%3C/svg%3E");
  background-repeat:no-repeat, no-repeat;
  background-size:190px 107px, 150px 84px;
  background-position:8% 22%, 60% 44%;
  animation:pfDrift 60s ease-in-out infinite alternate;}
@keyframes pfDrift{from{transform:translateX(0)}to{transform:translateX(30px)}}
/* ⚠ v0.457 — THERE IS NO SUN. v0.456's .pf-sun rules and the pfSunSvg() sprite behind them are
   deleted at Odaroo's request; the profile sky is clouds only. Do not put one back. */
/* ===== PROFILE — a bright, sunny, glitchy field ===== */
.pf-sky{display:none;}   /* backdrop now lives on #scr-style so it always fills the window */
/* ⭐ v0.456 — EVERYTHING A TAB CAN CHANGE LIVES IN HERE, and nothing else does. renderStyle()
   rewrites this element alone, so the sun and the parade beside it are never detached and their
   animations never restart. Purely structural — it must not introduce a stacking context, or the
   bar/tabs/wrap inside it would stop layering against the backdrop behind it. */
.pf-body{position:static;}
/* the LIVING backdrop — tabletop pieces jog across the sunny green, forever */
.pf-field{position:fixed; left:0; right:0; bottom:0; height:34%; z-index:1; pointer-events:none; overflow:hidden;}
.pf-runner{position:absolute; bottom:var(--py,14px); left:-140px; width:44px; height:48px; opacity:.85; filter:drop-shadow(2px 3px 0 #0003); animation:pfRun var(--pd,12s) linear infinite;}
.pf-runner.big{width:132px; height:144px;}   /* 300% — a proper PARADE, not ants */
.pf-confetti{position:absolute; inset:0; overflow:hidden; pointer-events:none;}
.pf-confetti i{position:absolute; top:-12px; width:6px; height:10px; opacity:.9; animation:pfConf 2.4s linear infinite;}
@keyframes pfConf{to{transform:translateY(40vh) rotate(300deg); opacity:.4;}}
.pf-runner svg{width:100%; height:100%;}
@keyframes pfRun{0%{transform:translateX(0) translateY(0)}10%{transform:translateX(12vw) translateY(-6px)}20%{transform:translateX(24vw) translateY(0)}30%{transform:translateX(36vw) translateY(-6px)}40%{transform:translateX(48vw) translateY(0)}50%{transform:translateX(60vw) translateY(-6px)}60%{transform:translateX(72vw) translateY(0)}70%{transform:translateX(84vw) translateY(-6px)}80%{transform:translateX(96vw) translateY(0)}100%{transform:translateX(120vw) translateY(0)}}
/* real scrollbars on the profile — you can SEE there's more below */
/* ⚠⚠ v0.650 — NO SCROLLBAR GUTTER ON THE PROFILE, AND THAT IS A HUD RULE, NOT A COSMETIC ONE.
   #scr-style is itself the scrolling element, so `scrollbar-gutter:stable` reserved 8px INSIDE it —
   and the house reel now lives inside it too, so the profile bar measured 367px wide while every
   other screen measured 375. Eight pixels is enough for hudNameFit to step the username down one
   more point, so the same name printed at 8.5px here and 9.5px everywhere else: the exact "it
   resizes when I change screens" complaint, hiding in a scrollbar. The bar is thumb-scrolled on a
   phone; hiding it costs nothing and makes the width identical by construction. */
#scr-style::-webkit-scrollbar{width:0; height:0;}
#scr-style::-webkit-scrollbar-thumb{background:#1b1510aa; border-radius:4px; border:2px solid #fdf6e355;}
#scr-style::-webkit-scrollbar-track{background:#0002;}
.pf-sky::before{content:''; position:absolute; left:0; right:0; top:0; height:60%; opacity:.9;
  background:
    radial-gradient(circle at 18% 24%, #fff 0 4%, transparent 5%),
    radial-gradient(circle at 27% 26%, #fff 0 6%, transparent 7%),
    radial-gradient(circle at 71% 16%, #ffffffdd 0 5%, transparent 6%),
    radial-gradient(circle at 82% 30%, #ffffffdd 0 4%, transparent 5%);
  background-size:540px 400px; background-repeat:repeat-x;}
.pf-sky::after{content:''; position:absolute; left:0; right:0; bottom:0; height:42%;
  background:repeating-linear-gradient(93deg,#7ecb58 0 13px,#6cbd4a 13px 26px);}   /* static — no animation */
@keyframes pfClouds{from{background-position:0 0}to{background-position:540px 0}}
@keyframes pfGrass{0%,88%,100%{filter:none}91%{filter:hue-rotate(45deg) saturate(1.5)}95%{filter:hue-rotate(-35deg) brightness(1.2)}}
/* ⭐⭐ v0.423 — THE TABS RUN ACROSS THE TOP. As a vertical rail they dictated the width of the whole
   left column; across the top they cost one line and give the stats the full width of the screen.
   They sit above .pf-wrap, so the wrap's own top padding comes off — the tabs clear the title bar
   instead. */
/* ⚠ v0.456 — the 46px of top padding was clearance for the STICKY bar. The bar is an ordinary
   scrolling block now and takes its own room, so this is plain breathing space instead. */
.pf-tabs-top{position:relative; z-index:3; flex-direction:row !important; gap:6px;
  padding:0 12px; margin:0 0 12px;}
.pf-tabs-top .pf-tab{flex:1 1 0; min-width:0; padding:9px 4px;}
.pf-wrap{position:relative; z-index:2; display:flex; gap:12px; padding:2px 12px 20px; align-items:flex-start;}
/* ⚠ v0.430 — the bottom reservation moved OFF this element and onto #scr-style, where --pfpad is
   measured. It used to be a second, DIFFERENT constant (250px here, 290px there) which is how the
   two drifted apart in the first place. One number, measured, in one place. */

/* ============================================================
   ⭐⭐ v0.430 — THE PLAYER FILE
   The portrait at the top on its own, the way the home menu shows it, and everything under it in
   a confidential file: numbered sections, one subject each, figures in a grid that uses the whole
   width. Two across on the folded phone, four on the unfolded one — never a narrow column beside
   a portrait, which is what was overlapping.
   ============================================================ */
.pf-hero{position:relative; z-index:2; display:flex; flex-direction:column; align-items:center;
  gap:6px; padding:6px 12px 12px; text-align:center;}
/* ⭐⭐ v0.641 — THE PROFILE HERO IS THE SHOWCASE, SO IT IS THE SHOWCASE SIZE. (Odaroo, 2026-08-05:
   "the profile screen centres the inklet to showcase it for editing colour and name.")
   88px was a third hand-typed portrait size, one build after v0.640 collapsed the menu lens and both
   VS corners onto --inkav. It asks for that var now — this is the one screen where the drawing is
   the SUBJECT rather than a corner label, so it takes the shared size and no less. */
/* ⭐⭐⭐ v0.658 — THE SHOWCASE IS A QUARTER BIGGER, AND IT IS DERIVED FROM --inkav-lg RATHER THAN
   RETYPED. (Odaroo, 2026-08-05: "the inkling frame and username should be bigger, probably 25%.")
   A fourth hand-typed portrait size is exactly what the v0.641 note above this was written to stop:
   the moment --inkav-lg moves at a breakpoint, a literal here silently stops being 25% of anything.
   It is a MULTIPLIER of the shared size, so the relationship survives every future change to it. */
.pf-hero{--pfav:calc(var(--inkav-lg,92px) * 1.25);}
.pf-hero .pf-ink{width:var(--pfav); height:var(--pfav); margin:0 auto;}
/* ⚠⚠ THE PORTRAIT STAYS ON THE SCREEN'S CENTRE LINE AND THE PEN HANGS OFF IT. This wrapper is
   exactly the portrait's width, so the absolutely-placed pen beside it costs the layout nothing —
   laying the two out as a flex row would shove the drawing left by half a pen, on the one screen
   whose whole job is to present it centred. */
.pf-heroink{position:relative; width:var(--pfav); height:var(--pfav); margin:0 auto;}
.pf-heroink .pf-ink{position:absolute; inset:0; width:100%; height:100%;}
/* the two PENS — "you can change this". Big enough to read as a button at arm's length, which is
   the whole ask; the 9px .peni beside the rack tabs is a hint, this is an invitation. */
.pf-pen{display:inline-flex; align-items:center; justify-content:center; flex:none; cursor:pointer;
  width:30px; height:30px; padding:0; border:2px solid var(--ink); border-radius:50%;
  background:#f7d84e; box-shadow:0 2px 0 #1b151066, inset 0 0 0 1px #ffffff88;
  animation:pfPenNod 2.4s ease-in-out infinite;}
.pf-pen .peni{width:17px; height:17px; opacity:1;}
.pf-pen:active{transform:translateY(2px); box-shadow:0 0 0 #1b151066;}
/* ⚠ RIGHT SIDE, VERTICALLY CENTRED ON THE FRAME — Odaroo's words exactly. `left:100%` rather than a
   negative right offset so it never overlaps the drawing, whatever --pfav becomes. */
.pf-pen-fr{position:absolute; left:100%; top:50%; transform:translate(6px,-50%);}
.pf-pen-fr:active{transform:translate(6px,calc(-50% + 2px));}
.pf-pen-nm{width:26px; height:26px;}
.pf-pen-nm .peni{width:15px; height:15px;}
@keyframes pfPenNod{0%,88%,100%{rotate:0deg} 92%{rotate:-14deg} 96%{rotate:8deg}}
@media (prefers-reduced-motion:reduce){ .pf-pen{animation:none;} }
.pf-hero-nm{display:flex; align-items:center; justify-content:center; gap:8px; cursor:pointer;
  font-family:var(--fontD); font-size:24px; line-height:1.25; color:var(--ink);
  text-shadow:0 2px 0 #fdf6e3aa; max-width:100%;}
.pf-hero-nm>span{min-width:0; overflow-wrap:anywhere; word-break:break-word;}
/* ============================================================
   ⭐⭐⭐ v0.856 — THE PASSKEY, PERMANENTLY, UNDER THE USERNAME.
   (Odaroo, 2026-08-25: "underneath, we should see the player code at all times.")
   ⚠⚠ IT SETS ONLY `--pksz`. The lettering itself comes from `.pkface` (css/menu.css), which the
   card's readout and its input box also wear — see the note there for why restating a family here
   would be the very fault this was raised to fix.
   ⚠ `overflow-wrap:anywhere` on the value for the same reason the name above it has it: eight
   characters is short, but the rule on this screen is that text WRAPS rather than being cut off.
   ⚠ Deliberately quiet — a label at .55 opacity over a value at full ink. It is a subtitle the
   player can always find, not a second headline competing with their own name. */
.pf-hero-key{
  display:flex; flex-direction:column; align-items:center; gap:2px;
  margin:4px auto 0; padding:3px 10px; max-width:100%;
  background:none; border:0; cursor:pointer; --pksz:clamp(11px,3.4vw,15px);
}
.pf-hero-key .pf-key-lab{font-family:'Press Start 2P',monospace; font-size:7px; letter-spacing:1px;
  color:#1b1510; opacity:.55;}
.pf-hero-key .pf-key-val{color:var(--ink); line-height:1.2; min-width:0;
  overflow-wrap:anywhere; word-break:break-word; text-shadow:1px 1px 0 #fff;}
.pf-hero-key:active{transform:translateY(1px);}
/* ⚠⚠ v0.567 — .pf-hero-lvt (the level box at the head of the name) and .pf-hero-lv (the TOP BILL
   banner) are DELETED with the player level. The hero is the portrait and the name. */
.pf-solo{display:block;}
.pf-solo .pf-main{width:100%;}

.pf-file{background:#f4ecd6; border:3px solid #1b1510; border-radius:3px;
  box-shadow:0 4px 0 #1b151055, inset 0 0 0 1px #ffffff66; overflow:hidden;}
/* ⭐ v0.454 — one piece of text on this bar, centred, allowed to wrap rather than clip.
   It was a space-between row with a CONFIDENTIAL stamp holding a fixed width on the right, so the
   words on the left were the only thing that could give — and they did, mid-word ("PLAYE…").
   With the stamp gone there is nothing to compete with, and normal wrapping is a better failure
   mode than an ellipsis for something a player is meant to be able to read. */
.pf-file-top{display:flex; align-items:center; justify-content:center; gap:8px;
  padding:8px 10px; background:#1b1510; color:#f2e5c9;}
.pf-file-id{font-family:'Press Start 2P',monospace; font-size:7px; letter-spacing:1px; opacity:.9;
  min-width:0; text-align:center; line-height:1.6; overflow-wrap:anywhere;}

.pf-sect{padding:12px 12px 14px; border-top:2px dashed #1b151033;}
.pf-sect:first-of-type{border-top:0;}
.pf-sect-h{display:flex; align-items:center; gap:8px; margin:0 0 10px;
  font-family:'Press Start 2P',monospace; font-size:9px; letter-spacing:1px; color:#1b1510;}
.pf-sect-h .pf-no{flex:0 0 auto; font-style:normal; font-size:8px; color:#f2e5c9; background:#1b1510;
  border-radius:2px; padding:3px 5px;}
.pf-sect-h>span{flex:1 1 auto; min-width:0;}
.pf-sect-h em{flex:0 0 auto; font-style:normal; font-family:var(--fontD); font-size:11px; opacity:.55;}

/* the figures. `i` is one label-over-value cell; they never sit beside prose. */
.pf-cells{display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:8px;}
.pf-cells>i{display:flex; flex-direction:column; gap:3px; min-width:0; font-style:normal;
  background:#fdf6e3; border:2px solid #1b151022; border-left:4px solid #1b1510; border-radius:2px;
  padding:7px 9px;}
.pf-cells>i>span{font-family:'Press Start 2P',monospace; font-size:7px; letter-spacing:.5px;
  color:#1b1510aa; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.pf-cells>i>b{font-family:var(--fontD); font-size:16px; line-height:1.15; color:#1b1510;
  overflow-wrap:anywhere;}
@media (min-width:640px){ .pf-cells{grid-template-columns:repeat(4,minmax(0,1fr));} }

.pf-empty{font-family:'Press Start 2P',monospace; font-size:8px; letter-spacing:1px;
  color:#1b151099; padding:6px 2px;}

/* an empty wardrobe rack explains itself instead of looking broken */
.pf-none{display:flex; flex-direction:column; align-items:center; gap:10px; text-align:center;
  background:#f4ecd6; border:3px solid #1b1510; border-radius:3px; padding:20px 16px;}
.pf-none>b{font-family:'Press Start 2P',monospace; font-size:10px; letter-spacing:1px; color:#1b1510;}
.pf-none>span{font-family:var(--fontD); font-size:13px; line-height:1.5; color:#1b1510cc; max-width:34em;}
/* the left column can be narrower now that it is not holding the tabs up */
.pf-left{flex:0 0 100px; width:100px; display:flex; flex-direction:column; gap:7px;}
/* ⭐ v0.423 — STATS lays the header flat and hands the whole width to the record below it. */
.pf-stack{flex-direction:column; gap:8px;}
.pf-stack .pf-left{flex:0 0 auto; width:100%; flex-direction:row; align-items:center; gap:9px;}
.pf-stack .pf-ink{flex:0 0 56px; width:56px; height:56px; margin:0;}
.pf-stack .pf-name{flex:1 1 auto; text-align:left; min-width:0;}
.pf-stack .pf-lines{flex:0 0 auto; display:grid; grid-template-columns:repeat(2,auto); gap:2px 10px;}
.pf-stack .pf-main{width:100%; flex:1 1 auto;}
@media (max-width:420px){ .pf-stack .pf-lines{grid-template-columns:repeat(2,auto); font-size:9px;} }
.pf-ink{width:64px; height:64px; position:relative; margin:0 auto; filter:drop-shadow(0 3px 5px #0006);}   /* minigame-size inklet */
.pf-ink svg{width:100%; height:100%; display:block; image-rendering:pixelated;}
/* ⭐ v0.386 — the name sits LOWER. Worn frames hang past the 64px inkling box, and at 17px on a tight
   line-height the top of the letters was being clipped by the frame's bottom edge. Extra headroom +
   a real line-height fixes it; the little pen beside it says the name can be tapped and rewritten. */
.pf-name{font-family:var(--fontD); font-size:15px; line-height:1.35; color:var(--ink); text-align:center; cursor:pointer;
  margin-top:15px; padding-top:2px; display:flex; align-items:center; justify-content:center; gap:5px; max-width:100%; text-shadow:1px 1px 0 #fff;}
.pf-name>span{min-width:0; overflow-wrap:anywhere; word-break:break-word;}   /* a long name WRAPS — it is never cut off */
.peni{width:9px; height:9px; flex:0 0 auto; opacity:.55; image-rendering:pixelated; vertical-align:-1px;}
.pf-subtab .peni{opacity:.45; margin-right:4px;}
.pf-subtab.on .peni{opacity:.8;}
.pf-lines{display:flex; flex-direction:column; gap:2px; background:#fffdf4f2; border:2px solid var(--ink); border-radius:5px; padding:5px 7px; box-shadow:2px 2px 0 #0003;}
.pf-line{display:flex; justify-content:space-between; align-items:center; font-family:var(--fontD); font-size:10px; gap:6px;}
.pf-line span{opacity:.6;} .pf-line b{color:var(--red);}
.pf-tabs{display:flex; flex-direction:column; gap:6px; margin-top:2px;}
.pf-tab{font-family:var(--fontD); font-size:12px; text-align:center; padding:10px 6px; background:#fffdf4f2; color:var(--ink); border:2px solid var(--ink); border-radius:5px; cursor:pointer; box-shadow:2px 2px 0 #0003;}
.pf-tab.on{background:var(--gold); color:var(--ink); box-shadow:2px 2px 0 var(--red);}
.pf-main{flex:1; min-width:0; background:#fffdf4f2; border:2px solid var(--ink); border-radius:6px; padding:9px; box-shadow:3px 3px 0 #0003; overflow:visible;}
.pf-main .st-grid{grid-template-columns:repeat(3,1fr); max-height:none; overflow:visible; gap:8px;}   /* 3 cols, no inner scroll — the page scrolls top-to-bottom */
/* ⭐ v0.423 — ONE CARD PER GAME in the arcade log: the name and the play count on top, the record
   underneath in a grid that reflows with the screen. auto-fit means the folded phone gets two
   across and the unfolded one four, with no breakpoint to keep in step. */
.pf-game{border:2px solid var(--ink); border-radius:5px; background:#fffdf4; padding:6px 7px; margin:0 0 6px;
  box-shadow:2px 2px 0 #0002;}
.pf-gname{display:flex; justify-content:space-between; align-items:baseline; gap:8px;
  font-family:var(--fontD); font-size:11px; color:var(--ink); border-bottom:1px dashed #1b151044; padding-bottom:3px;}
.pf-gname em{font-style:normal; font-size:9px; color:var(--red); flex:0 0 auto;}
.pf-gcells{display:grid; grid-template-columns:repeat(auto-fit,minmax(74px,1fr)); gap:3px 8px; margin-top:4px;}
.pf-gcells i{display:flex; justify-content:space-between; align-items:baseline; gap:5px; font-style:normal;
  font-family:var(--fontD); font-size:9px; min-width:0;}
.pf-gcells i span{opacity:.65; white-space:nowrap;}
.pf-gcells i b{color:var(--red); white-space:nowrap;}
.pf-statgrid{display:flex; flex-direction:column; gap:5px;}
.pf-statrow{display:flex; justify-content:space-between; align-items:center; font-family:var(--fontD); font-size:12px; border-bottom:1px dashed #1b151033; padding:4px 2px;}
.pf-statrow{gap:8px;}   /* label and value never touch, even on the folded Fold */
.pf-statrow span{opacity:.7;} .pf-statrow b{color:var(--red); text-align:right;}   /* long values (arcade log) wrap instead of spilling out of the panel */
.pf-statrow.pf-al b{flex:0 0 72px; text-align:left;}   /* the boost rows — every + starts in the SAME column */
.pf-subtabs{display:flex; gap:5px; margin:0 0 9px; flex-wrap:wrap;}
/* ⭐ v0.386 — PLAIN RACK TABS. Odaroo: no house colours on GEAR / COSTUMES / FRAMES — colour belongs to
   the inkling, and you get it by tapping the inkling. The selected tab is marked by paper + a thick
   ink underline instead of a red fill. Subtle, and it keeps the ink-on-cream contrast at ~13:1. */
.pf-subtab{font-family:var(--fontD); font-size:10px; padding:6px 9px; background:#efe6cf; color:var(--ink);
  border:2px solid var(--ink); border-bottom-width:2px; border-radius:4px; cursor:pointer; display:inline-flex; align-items:center; opacity:.72;}
.pf-subtab.on{background:#fffdf4; opacity:1; border-bottom-width:5px; padding-bottom:3px;}
/* ⭐ v0.376 — THE D-PAD TAB. Cream card, ink type, one big honest switch. Contrast checked against
   the cream panel: ink #1b1510 on #efe6cf, and the ON pill is CREAM ON GREEN, never on the house red
   (which measures 3.2:1 there and fails). */
.pf-padpane{font-family:var(--fontD); color:var(--ink); max-width:420px;}
.pf-padrow{display:flex; align-items:center; justify-content:space-between; gap:10px; margin:2px 0 8px; flex-wrap:wrap;}
.pf-padlab{font-size:12px; line-height:1.25;}
.pf-padlab i{font-style:normal; font-size:8px; opacity:.75; margin-left:5px;}
.pf-padsw{display:flex; align-items:center; gap:7px; min-width:96px; padding:6px 10px; cursor:pointer;
  font-family:var(--fontD); font-size:11px; color:#fdf6e3; background:#6b625a; border:2px solid var(--ink); border-radius:5px;}
.pf-padsw.on{background:#2f6b52;}                      /* deep green — 6.6:1 against the cream label */
.pf-padsw b{font-weight:normal; letter-spacing:.5px;}
.pf-padknob{width:13px; height:13px; background:#fdf6e3; border:2px solid var(--ink); border-radius:2px; flex:0 0 auto;}
.pf-padsw.on .pf-padknob{background:#ffe9a8;}
.pf-padnote{font-family:var(--fontB,inherit); font-size:10px; line-height:1.5; color:#33291f;
  background:#efe6cf; border:2px solid var(--ink); border-radius:4px; padding:8px 9px; margin:0 0 12px;}
.pf-padnote b{color:#1b1510;}
.pf-padop{width:100%; max-width:320px;}

.pf-colors{display:grid; grid-template-columns:repeat(3,1fr); gap:8px;}
.pf-colors.circ{grid-template-columns:repeat(auto-fill,minmax(42px,1fr)); gap:10px; padding:4px 2px;}   /* 32 circles — swatches, not inklings */
.pf-color{background:#efe6cf; border:3px solid var(--ink); border-radius:4px; padding:6px; cursor:pointer;}
.pf-color svg{width:100%; height:auto; display:block; image-rendering:pixelated;}
.pf-color.on{border-color:var(--gold); background:#fff4d0;}
.pf-cir{aspect-ratio:1; width:100%; max-width:52px; margin:0 auto; border-radius:50%; padding:0; position:relative; box-shadow:2px 3px 0 #0005; transition:transform .1s;}
.pf-cir:active{transform:scale(.9);}
.pf-cir.on{box-shadow:0 0 0 3px var(--gold), 2px 3px 0 #0005;}
.pf-colors.circ{row-gap:16px;}
/* ⭐ GLITCHED INK (v0.546) — the 16th swatch wears NO number, NO rainbow and NO animation of its
   own. It is a plain white circle; after ten seconds `inkGlitchWatch` (js/ui.js) writes a random
   flat colour and a twitch into its INLINE style every 70ms. Deliberately not a CSS animation —
   a smooth cycle reads as "a pretty colour", and this has to read as a corrupted pixel. */
.pf-cir.pfglitch{background:#f4f4f7;}
.pf-vip{margin-left:auto; font-family:var(--fontD); font-size:9px; letter-spacing:.5px; padding:6px 10px; background:var(--ink); color:var(--gold); border:2px solid var(--gold); border-radius:4px; cursor:pointer; box-shadow:2px 2px 0 #0006;}
.pf-vip.own{background:var(--gold); color:var(--ink); border-color:var(--ink); animation:vipGlow 1.6s ease-in-out infinite;}
@keyframes vipGlow{0%,100%{box-shadow:2px 2px 0 #0006}50%{box-shadow:0 0 14px var(--gold), 2px 2px 0 #0006}}
/* ⭐ v0.656 — WEAR ALL / UNWEAR ALL, under the rack. Two equal halves so neither reads as the
   default; the category name is a second line so the pair never has to shrink its own words to fit
   the folded 375px screen (GEAR is short, COSTUMES is not). */
.pf-allrow{display:flex; gap:8px; margin:12px 2px 4px;}
.pf-all{flex:1 1 0; min-width:0; font-family:var(--fontD); font-size:10px; letter-spacing:.5px; line-height:1.35;
        padding:8px 6px; border-radius:4px; cursor:pointer; box-shadow:2px 2px 0 #0006;
        display:flex; flex-direction:column; align-items:center; gap:2px;}
.pf-all em{font-style:normal; font-size:8px; letter-spacing:1px; opacity:.75;}
.pf-all.on{background:var(--gold); color:var(--ink); border:2px solid var(--ink);}
.pf-all.off{background:var(--ink); color:var(--paper,#fdf6e3); border:2px solid var(--paper,#fdf6e3);}
.pf-all:active{transform:translate(2px,2px); box-shadow:none;}
/* ===== album COLLECTED count — swells toward the full set, gold + shiny at 37/37 ===== */
.coll-lab{font-family:var(--fontD); font-size:9px; letter-spacing:1px; opacity:.55; vertical-align:middle;}
.coll-count{font-family:var(--fontD); font-weight:bold; color:var(--ink); vertical-align:middle; line-height:1; transition:font-size .35s ease; display:inline-block;}
.coll-count .coll-slash{font-size:.5em; opacity:.55;}
.coll-count.coll-full{color:var(--gold); text-shadow:0 0 8px var(--gold2,#f4c542), 1px 1px 0 var(--ink); animation:collShine 1.3s ease-in-out infinite;}
@keyframes collShine{0%,100%{filter:brightness(1) saturate(1)}50%{filter:brightness(1.55) saturate(1.5)}}
.st-bg{position:absolute; inset:0; z-index:0; background:repeating-linear-gradient(115deg,#17090f 0 40px,#1e0b13 40px 80px); animation:stBg 14s linear infinite;}
.st-bg::after{content:''; position:absolute; inset:-20%; background:radial-gradient(circle at 25% 28%,#e01b4d38,transparent 45%),radial-gradient(circle at 82% 74%,#ff2d6e22,transparent 50%);}
@keyframes stBg{to{background-position:80px 0;}}
/* ⭐ v0.456 — THE BAR SCROLLS WITH THE PAGE, like the Album's and the Mart's. It used to be
   `position:sticky` with a negative margin that pulled the tabs up underneath it, so it hung there
   while everything moved past it and the tabs had to wear 46px of padding to clear it. Now it is an
   ordinary block at the top of the scroller: it takes its own room, it leaves the page when the page
   leaves, and the gap under it is a real margin instead of a compensation. */
.st-bar{position:relative; height:38px; margin:0 0 12px;   z-index:6; display:flex; align-items:center; gap:8px; padding:0 14px; background:#0a0509; border-bottom:2px solid #e01b4d; font-family:var(--fontD); transform:skewX(-6deg); box-shadow:0 3px 0 #e01b4d55;}
.st-bar>*{transform:skewX(6deg);}
/* ⚠ v0.658 — the bar holds ONE thing now: the marquee, hard right, below the film reel. `overflow`
   must stay visible or the skewed bar clips the top and bottom bulb rows off the sign. The
   `>*{skewX(6deg)}` rule above is what stands the sign back up straight inside the slanted bar. */
/* ⚠⚠ HEIGHT BECOMES A MINIMUM. Every rule that sets `.st-bar{height:…}` was written for a line of
   text; a sign with bulbs standing off its outside edge is taller than that at both folds, and a
   fixed height would crop it rather than grow. min-height keeps the bar's presence, auto lets the
   sign decide. */
.st-bar{justify-content:flex-end; overflow:visible; height:auto!important; min-height:38px;
  padding-top:5px; padding-bottom:5px;}
.st-bar .inkmq{margin-right:0;}
.st-bar .st-close{font-size:9px; letter-spacing:1px; color:#ff8fb0; cursor:pointer;}
.st-bar .st-nm{margin-left:4px; font-size:15px; letter-spacing:2px; color:#fff; text-shadow:2px 2px 0 #e01b4d;}
.st-bar .st-jp{margin-left:auto; font-size:11px; letter-spacing:2px; color:#ff8fb0;}
.st-stage{position:absolute; top:38px; left:0; right:0; bottom:236px; z-index:2; display:flex; gap:8px; padding:10px; overflow:hidden;}
.st-hero{position:relative; width:40%; max-width:180px; display:flex; flex-direction:column; align-items:center; justify-content:center; background:linear-gradient(160deg,#e01b4d22,#0a0509aa); border:2px solid #e01b4d; border-radius:10px; padding:8px; animation:stHeroIn .5s var(--pop) both;}
.st-heroink{width:86%; max-width:140px; position:relative; animation:stHeroFloat 3s ease-in-out infinite; filter:drop-shadow(0 6px 12px #000a);}
.st-heroink svg{width:100%; height:auto; display:block; image-rendering:pixelated;}
@keyframes stHeroFloat{50%{transform:translateY(-8px) rotate(-1.5deg);}}
@keyframes stHeroIn{0%{opacity:0; transform:translateX(-40px) rotate(-8deg);}100%{opacity:1; transform:none;}}
.st-boosts{display:flex; flex-wrap:wrap; gap:3px 8px; justify-content:center; margin-top:8px;}
.st-bst{font-family:var(--fontD); font-size:8px; color:#ff8fb0; letter-spacing:.5px;}
.st-bst b{color:#fff; font-size:10px;}
.st-panel{flex:1; display:flex; flex-direction:column; min-width:0;}
.st-tabs{display:flex; gap:6px; margin-bottom:8px;}
.st-tab{flex:1; font-family:var(--fontD); font-size:10px; letter-spacing:1px; padding:8px 4px; background:#1a0c12; border:2px solid #3a1420; color:#ff8fb0; cursor:pointer; transform:skewX(-8deg); transition:.12s;}
.st-tab>i{display:block; font-size:7px; opacity:.6; font-style:normal;}
.st-tab.on{background:#e01b4d; color:#fff; border-color:#fff; box-shadow:3px 3px 0 #0007; transform:skewX(-8deg) translateY(-2px);}
.st-grid{flex:1; display:grid; grid-template-columns:repeat(3,1fr); gap:7px; overflow-y:auto; align-content:start; padding-bottom:6px;}
.st-grid::-webkit-scrollbar{width:0}
.st-tile{position:relative; background:#160a10; border:2px solid #3a1420; border-radius:8px; padding:6px 4px 4px; cursor:pointer; display:flex; flex-direction:column; align-items:center; gap:3px; font-family:var(--fontD); animation:stTileIn .3s ease-out both; animation-delay:calc(var(--i)*.028s); transition:transform .1s,border-color .1s;}
@keyframes stTileIn{0%{opacity:0; transform:translateY(12px) scale(.9);}100%{opacity:1; transform:none;}}
.st-tile:active{transform:scale(.95);}
.st-tile.worn{border-color:#f4c542; box-shadow:0 0 10px #f4c54255;}
.st-tile.locked{opacity:.82;}
.st-tile-art{width:44px; height:44px; display:flex; align-items:center; justify-content:center;}
.st-tile-art svg,.st-tile-art .fr-prev{width:100%; height:100%;}
.st-tile-nm{font-size:7px; color:#fdf6e3; letter-spacing:.5px; text-align:center; line-height:1.2; min-height:15px;}
.st-tile-tag{font-size:7px; letter-spacing:.5px; color:#8a8296;}
.st-tile-tag.on{color:#f4c542;} .st-tile-tag.buy{color:#6bd38a;}
/* ===== THE RACK — a big shelf of items, icons only; tap to zoom the dossier ===== */
.st-grid.rack{grid-template-columns:repeat(4,1fr); gap:10px 8px; row-gap:18px; padding:6px 2px 14px;}
.pf-main .st-grid.rack{grid-template-columns:repeat(4,1fr);}
@media (min-width:640px){ .st-grid.rack, .pf-main .st-grid.rack{grid-template-columns:repeat(6,1fr);} }   /* unfolded Fold = a wider shelf */
.st-tile.rack{padding:8px 4px 10px; background:linear-gradient(#241019,#160a10); border:2px solid #3a1420; min-height:64px; justify-content:center;}
.st-tile.rack::after{content:''; position:absolute; left:-8px; right:-8px; bottom:-8px; height:7px; background:linear-gradient(#7a5433,#54371f); border-top:2px solid #93693f; border-radius:2px; pointer-events:none;}   /* the shelf board under every row */
.st-tile.rack .st-tile-art{width:52px; max-width:88%; height:52px;}   /* shrinks on the folded Fold — never overflows its cell */
.st-dot{position:absolute; top:4px; right:4px; width:8px; height:8px; border-radius:50%; background:#5a5a66; border:1px solid #0008;}
.st-dot.on{background:var(--gold); box-shadow:0 0 6px var(--gold);}
.st-pricechip{position:absolute; bottom:3px; right:4px; font-family:var(--fontD); font-size:7px; color:#6bd38a; text-shadow:1px 1px 0 #000;}
.pf-statrow.pf-sec{margin-top:10px; border-bottom:2px solid var(--ink); padding-bottom:3px;}
.pf-statrow.pf-sec span{font-family:var(--fontD); font-size:10px; letter-spacing:1px; color:var(--red);}
/* the unclaimed BUG FINDER frame SHINES on the shelf until it's tapped */
/* THE GLITCH INK — the whole drop cycles RGB non-stop and twitches; one colour at a time, always moving */
/* ⭐⭐⭐ v0.502 — THE GLITCH INK IS NO LONGER A CSS ANIMATION, AND THAT IS THE WHOLE FIX.
   (Odaroo, for the second time: "it's one colour in the top-left avatar and a DIFFERENT colour on
   the field.") v0.497 answered this by phasing each sprite with a negative `animation-delay` off one
   shared epoch. That is the textbook trick and it is still not enough here, because a CSS animation
   does not begin when the element is CREATED — it begins at the first style resolution after it is
   inserted, and two sprites inserted in the same function can reach that at very different moments.
   Measured on the rink with the delays agreeing to within one millisecond: the corner avatar was
   sitting at hue-rotate(331deg) while the skater on the ice was at 360deg — a fifth of a second
   apart on a 2.2s wheel, which to the eye is simply two different colours.
   ⚠⚠ SO NOTHING PER-ELEMENT DECIDES THE COLOUR ANY MORE. `inkGlitchPump()` (js/rink.js) writes the
   SAME filter and the SAME jitter onto every `svg.glink` on screen from one clock. Two sprites
   cannot disagree because there is only one answer, recomputed for all of them together.
   ⚠ The class is now purely a MARKER for the pump to find. Do not give it an animation again. */
svg.glink{will-change:filter;}
/* TETR-INK side stats — clean stacked boxes, aligned, spaced */
#tetStatBox .gs{display:flex; flex-direction:column; align-items:flex-start; gap:2px; margin:0 0 9px; padding:5px 7px; background:#0a140acc; border:2px solid #1b1510; border-radius:4px; min-width:74px;}
#tetStatBox .gs b{font-size:12px; line-height:1;}
/* ⭐ v3.67 — THE WELCOME LINE, ALWAYS READABLE. It used to sit in the frame's top bulb row at a tiny 8-11px,
   so letters were punched out of it ("SWELL AFTERNOON FOR A RUMBLE" read as "W_L_ _F_E_N_O_..."). It now
   owns a clear band of its own between the reel and the tiles, at z-index 64 — ABOVE the frame (63) and the
   tiles (5) — so nothing can ever cover it. Bigger type, and it wraps to a second line rather than clipping. */
/* v0.383 — the home welcome line is retired (too much text). The wrapper stays in the markup so
   nothing that looks for it throws, but it takes no room and shows nothing. */
#greetWrap{display:none !important;}
#greetWrap.on{display:block; position:relative; z-index:64; text-align:center; padding:4px 0 2px; pointer-events:none;}
#greet{padding:0 8px; margin:0 auto; max-width:min(84vw,520px); white-space:normal; overflow-wrap:break-word;
  line-height:1.3; font-size:clamp(11px,3.4vw,14px); color:var(--ink);
  text-shadow:0 1px 0 var(--paper), 0 0 6px var(--paper);}   /* the paper halo keeps it legible over any backdrop */
@media (min-width:640px){ #greet{font-size:15px;} }
/* the inklet sits SMALLER inside every frame — tall hats and stacked costumes never get cut off at the rim */
/* ⭐⭐⭐ v0.659 — THE EXCLUSION IS THE CLASS `.frsvg`, NEVER A LIST OF FRAME IDS (Odaroo, 2026-08-05:
   "the frame is sliding on the inklet — it should sit on it and rotate like all the other frames").
   ⚠⚠⚠ THIS RULE IS FOR THE INKLET SPRITE, and it used to say `:not(.fr-pixring)` — naming ONE frame
   by hand. The 24-BIT ring is the only OTHER frame drawn as a bare <svg> child, so it was caught by
   this rule and given the sprite's `scale(.75)`. Its own `frSpin` then interpolated FROM that scale
   instead of from `translate(-50%,-50%)`, and the browser matrix-decomposed the mismatched transform
   lists into a diagonal slide that grew 0.75→0.94 — measured at 36.6px of drift in a 64px box, with
   NO ROTATION IN IT AT ALL. That is the "sliding frame", and v0.656's size/steps fix could not touch
   it because the fault was never in the frame's own CSS.
   Every frame SVG now carries `.frsvg` and is excluded on that, so a frame added later is exempt by
   construction rather than by somebody remembering to extend a list. Same family as the `animation`
   and `transform` shorthand traps in CLAUDE.md: a second rule REPLACES a property, it does not add. */
.gink>svg:not(.frsvg), .pf-ink>svg:not(.frsvg), .hud-av>svg:not(.frsvg),
.fr-prev>svg:not(.frsvg), .st-pfr>svg:not(.frsvg), .file-av>svg:not(.frsvg){transform:scale(.75); transform-origin:50% 62%;}
/* ⭐⭐⭐ v0.761 — THE **PHOTOGRAPH** IS ANCHORED ON ITS MIDDLE, NOT ON 62% (Odaroo, 2026-08-07:
   "really make it so that it's in the middle position… especially in the frames that we have").
   That 62% origin is a leftover from the DRAWN pixel drop, whose head sat high in a 30x32 grid and
   which therefore had to be shrunk about a point below its centre to stay off the rim. The v0.760
   mascot is a photograph whose content the viewBox already centres (INKLET_VB, js/core.js), so
   shrinking it about 62% pushed it ~3% of the frame DOWN again — a lean you can see the moment two
   framed inklets sit side by side.
   ⚠⚠ IT MUST NOT BE A BARE `svg.inkpng` RULE. The rules above are (0,2,1) selectors; a (0,1,1) one
      loses to them and the origin silently stays at 62%. Matched pair for pair instead, so the
      specificity is equal and source order decides — which is why this block sits BELOW them. */
.gink>svg.inkpng, .pf-ink>svg.inkpng, .hud-av>svg.inkpng,
.fr-prev>svg.inkpng, .st-pfr>svg.inkpng, .file-av>svg.inkpng{transform-origin:50% 50%;}
.gink,.pf-ink,.hud-av,.fr-prev,.st-pfr,.file-av{overflow:visible;}
/* the LIVING inkling — the idle hop every featured inklet does */
.inkhop{animation:ginkBob .42s ease-in-out;}
/* ⭐⭐⭐ v0.654 — THE POP IS A POP, NOT A JUMP (Odaroo, 2026-08-05: "even if he acts out the largest
   animation that he has, zoomed in, it shouldn't impact the actual name on top").
   ⚠⚠⚠ THIS RULE USED TO READ `.gink.hype svg{transform:scale(var(--gpop,1))}` — and `transform` is
   a single property, so it REPLACED the base `.gink>svg{transform:scale(.75)}` above rather than
   adding to it. Every reacting inklet therefore snapped from 0.75 to 1.0 before the pop even
   started, making the real ceiling 1.5/0.75 = TWO TIMES the resting size — which is why a good
   combo grew the face up over the username and 13px off the left edge of a folded phone.
   The .75 is multiplied in now, and the origin matches the base rule so the art does not JUMP to a
   different anchor the instant a combo lands. Same family as the `animation`-shorthand trap in
   CLAUDE.md: a second rule replaces a property, it does not add to it. */
.gink.hype>svg:not(.frsvg){transform:scale(calc(.75 * var(--gpop,1))); transform-origin:50% 62%;}
/* ⚠ v0.761 — and the pop keeps the photograph's own anchor, or a reacting inklet JUMPS from the
   middle to 62% the instant a combo lands. Must sit after the rule above: same specificity. */
.gink.hype>svg.inkpng{transform-origin:50% 50%;}
/* ⭐⭐⭐ v0.787 — combo ≥25: the inkling (frame and all) is LOSING IT — but it BREAKS UP, it does not
   strobe. This used to be `ginkGlitch .22s steps(2) infinite`: the same five-pose keyframe list run
   at ten hard states every fifth of a second, FOR AS LONG AS THE CHAIN LASTS. That is the flicker
   Odaroo filmed, and being infinite is what made it the worst of the two — a 0.18s burst you can
   dismiss, thirty seconds of buzz you cannot.
   Same keyframes as the one-shot (see css/rhythm.css for why they are the way they are), just given
   room: 1.7s, so the bursts land about a second apart with the inklet standing still between them.
   ⚠ It stays on the SAME animation name deliberately — one glitch in this game, seen at two speeds.
   ⚠ And it must NOT be shortened back "because ≥25 should feel wilder". The wildness at 25 is the
   scale pop and the corrupted face; the fault is the punctuation. */
.gink.wild{animation:ginkGlitch 1.7s steps(1,end) infinite;}
.st-detail{position:fixed; left:0; right:0; top:0; bottom:238px; z-index:9985; display:none; align-items:center; justify-content:center; background:#0a0509d0;}   /* FIXED + stops above the gamepad — the dossier card never sinks behind the D-pad, no matter how far you scrolled */
.st-detail.on{display:flex; animation:stFade .2s ease-out;}
@keyframes stFade{from{opacity:0}to{opacity:1}}
.st-card{position:relative; width:min(86%,320px); max-height:94%; overflow-y:auto; background:linear-gradient(165deg,#1e0b13,#0a0509); border:3px solid #e01b4d; border-radius:14px; padding:16px 16px 18px; text-align:center; box-shadow:8px 10px 0 #0007; animation:stCardIn .3s var(--pop);}
@keyframes stCardIn{0%{opacity:0; transform:translateX(50px) rotate(6deg) scale(.9);}100%{opacity:1; transform:none;}}
.st-x{position:absolute; top:8px; right:10px; background:none; border:none; color:#ff8fb0; font-size:16px; cursor:pointer;}
.st-prev{width:118px; height:118px; margin:8px auto 10px; position:relative;}
.st-prev svg,.st-prev .fr-prev,.st-prev .st-pfr{width:100%; height:100%; display:block;}
.st-cardnm{font-family:var(--fontD); font-size:14px; color:#fff; letter-spacing:1px; text-shadow:2px 2px 0 #e01b4d, 0 0 6px #0a0509; margin-bottom:8px; position:relative; z-index:3;}   /* rides ABOVE the preview — big gear art was hiding the item's name */
.st-cardnm i{font-size:9px; opacity:.5; font-style:normal;}
.st-statrow{display:flex; flex-wrap:wrap; gap:6px 10px; justify-content:center; margin-bottom:6px; font-family:var(--fontD); font-size:11px; color:#f4c542;}
.st-d{font-family:var(--fontD); font-size:8px; color:#cbb8d8; letter-spacing:1px; line-height:1.7; margin-bottom:12px;}
.st-act .btn{min-width:180px;}
@media (max-height:680px){ .st-stage{bottom:210px;} .st-prev{width:96px; height:96px;} }
/* MART premium pack — the 8-star whale */
.packbox.prem{background:#3d2166; border-color:#c98af5!important; box-shadow:0 0 20px #b06af077, 6px 6px 0 #1b1510;}
.packbox.prem .pname{color:#eddaff;}
/* v0.734 — no stars anywhere (Odaroo, 2026-08-07). The premiere box is marked by its gold corner
   pip now, not by a sparkle. */
.packbox.prem::after{content:''; position:absolute; top:4px; left:6px; color:#f4c542; font-size:12px; z-index:5; text-shadow:0 0 6px #f4c542;}
#packStage .packrow .packbox{min-width:120px;}

/* ==========================================================================
   ⭐⭐ v0.389 — THE MART REBUILT: black purse box, free-pack slot, one pack per row
   ========================================================================== */
/* ⭐⭐ v0.405 — THE MART'S EXTENDED TOP BAR. Row one is the Album's bar, verbatim: BACK hard left,
   title hard right. Row two carries the three currencies across the full width. The whole thing is
   one black bar, so the purse can never scroll away from the prices it belongs to. */
/* ⭐⭐⭐ v0.650 — THE MART'S CURRENCY ROW LIVES IN THE SUBBAR NOW, NOT IN THE HUD.
   The Mart used to BE its own bar: #hudPacks was flipped to a column and carried BACK, the title
   and the three-currency row, which is exactly why the shop's top looked nothing like the home
   screen's. The film reel (.hudreel) is the top of every screen now and is identical everywhere;
   everything a screen wants to say for itself goes in the .subbar underneath it (css/base.css).
   ⚠ Do NOT put padding-left back on the reel for this screen. The reel always carries the inklet,
   on every screen, so the room it reserves on the left is never spare. */
.subbar .mbar-top{display:flex; align-items:center; gap:8px; width:100%;}
.subbar .mbar-top .bar-title{margin-left:auto;}
.mbar-cur{display:flex; flex-direction:row; flex-wrap:nowrap; gap:calc(5px * var(--gs,1)); width:100%;}
.mbar-c{flex:1 1 0; min-width:0; display:flex; align-items:center; justify-content:center;
  gap:calc(4px * var(--gs,1)); padding:calc(3px * var(--gs,1)) calc(4px * var(--gs,1));
  background:#00000055; border:calc(2px * var(--gs,1)) solid #3a3128;}
.mbar-c svg{width:calc(16px * var(--gs,1)); height:calc(16px * var(--gs,1)); flex:none;}
.mbar-n{font-family:var(--fontD); font-size:calc(12px * var(--gs,1)); color:#ffe9a0; letter-spacing:.5px; white-space:nowrap;}
.mbar-n.lng{font-size:calc(9px * var(--gs,1)); letter-spacing:0;}
.mbar-l{font-family:var(--fontD); font-size:calc(7px * var(--gs,1)); color:#d6cbb6; letter-spacing:1px; white-space:nowrap;}
@media (max-width:430px){
  .mbar-c{gap:calc(3px * var(--gs,1)); padding:calc(3px * var(--gs,1)) calc(2px * var(--gs,1));}
  .mbar-n{font-size:calc(10px * var(--gs,1));} .mbar-n.lng{font-size:calc(8px * var(--gs,1));}
  .mbar-l{font-size:calc(6px * var(--gs,1)); letter-spacing:.5px;}
  .mbar-c svg{width:calc(14px * var(--gs,1)); height:calc(14px * var(--gs,1));}
}
.mv-box{width:100%; max-width:520px; margin:0 auto; background:#12100e; border:3px solid #1b1510;
  box-shadow:5px 5px 0 #1b151099; padding:8px; display:flex; flex-wrap:wrap; gap:6px;
  justify-content:center; align-items:stretch; box-sizing:border-box; position:relative; z-index:5;}
.mv-c{flex:1 1 132px; min-width:112px; display:flex; align-items:center; gap:7px;
  background:#221e1a; border:2px solid #4a423a; padding:5px 8px; box-sizing:border-box;}
.mv-c svg{width:19px; height:19px; flex:none;}
.mv-t{display:flex; flex-direction:column; line-height:1.15; min-width:0;}
.mv-n.lng{font-size:11px; letter-spacing:0;}
.mv-n{font-family:var(--fontD); font-size:14px; color:#ffe9a0; letter-spacing:.5px; white-space:nowrap;}
.mv-l{font-family:var(--fontD); font-size:8px; color:#d6cbb6; letter-spacing:1.6px;}
/* --- the free pack slot --- */
.mfree{width:100%; max-width:520px; margin:8px auto 0; box-sizing:border-box; border:3px solid #1b1510;
  background:#1e6b4a; box-shadow:5px 5px 0 #1b1510; padding:8px 12px; display:flex; align-items:center;
  gap:11px; cursor:pointer; position:relative; z-index:5; transition:transform .16s var(--pop);}
.mfree:hover{transform:translateY(-2px);}
.mfree.off{background:#2a2622; cursor:default; box-shadow:4px 4px 0 #1b1510;}
.mfree.off:hover{transform:none;}
.mfree.off .mfree-art{opacity:.42; filter:grayscale(1);}
.mfree-art{width:46px; height:54px; flex:none; position:relative; overflow:visible;}
.mfree-art svg{width:100%; height:100%; display:block;}
.mfree-dot{position:absolute; top:7px; left:50px; width:12px; height:12px; background:#e01b4d;
  border:2px solid #1b1510; border-radius:50%; animation:mfPulse 1s ease-in-out infinite;}
@keyframes mfPulse{0%,100%{transform:scale(1)}50%{transform:scale(1.28)}}
.mfree-t{flex:1; min-width:0;}
.mfree-h{font-family:var(--fontD); font-size:13px; color:#fff8e0; letter-spacing:1px; text-shadow:2px 2px 0 #1b1510;}
.mfree-s{font-family:var(--fontD); font-size:9px; color:#e8e2d0; letter-spacing:.6px; margin-top:3px;}
.mfree-pips{display:flex; align-items:center; gap:4px; margin-top:5px;}
.mfree-pips span{width:9px; height:9px; background:#1b151088; border:2px solid #1b1510; box-sizing:border-box;}
.mfree-pips span.on{background:#f4c542;}
.mfree-pips em{font-family:var(--fontD); font-style:normal; font-size:8px; color:#e8e2d0; letter-spacing:1px; margin-left:3px;}
.mfree-go{font-family:var(--fontD); font-size:12px; color:#1b1510; background:#f4c542; border:2px solid #1b1510;
  padding:5px 10px; flex:none; letter-spacing:1px;}
.mfree.off .mfree-go{background:#4a423a; color:#9a8f80;}
/* --- ⭐⭐ v0.617 — THREE OF THE SAME PACK, SIDE BY SIDE: 1 · 2 · 3 ---
   ⚠ The shelf is a GRID of three equal columns, never a flex row: three tiles with names of
   different lengths in a flex row come out three different widths, and the ladder stops reading as
   "the same pack, three grades". `1fr 1fr 1fr` makes them identical by construction.
   ⚠ The tile carries the ART, the NAME and the PRICE and nothing else — everything a purchase
   needs to know lives in the showcase, so the shelf can never outgrow the folded screen. */
.pkshelf{display:grid; grid-template-columns:repeat(3,1fr); gap:9px; width:100%; max-width:520px;
  margin:8px auto 0; align-items:stretch;}
#packStage .pkshelf .packbox.pktile{width:auto; min-width:0; height:auto; box-sizing:border-box;
  flex-direction:column; align-items:center; justify-content:flex-start; gap:6px;
  padding:11px 6px 10px; text-align:center; overflow:hidden;}
.packbox.pktile:hover{transform:translateY(-4px) rotate(0deg) scale(1.03);}
.pktier{position:absolute; top:4px; left:5px; z-index:5; font-family:var(--fontD); font-size:11px;
  line-height:1; padding:3px 5px; background:#1b1510; color:#e8e2d0; border:2px solid #e8e2d0;}
.packbox.pktile.gold .pktier{background:#1b1510; color:#ffe9a0;}
.packbox.pktile.prem .pktier{background:#f4c542; color:#1b1510; border-color:#1b1510;}
.pkart{width:78px; height:86px; flex:none; position:relative; z-index:2; max-width:100%;}
.pkart svg{width:100%; height:100%; display:block;}
.pkinfo{width:100%; min-width:0; position:relative; z-index:2; container-type:inline-size;
  display:flex; flex-direction:column; align-items:center; gap:3px;}
/* ⚠⚠ THE PRICE IS SIZED BY ITS COLUMN, NOT BY THE VIEWPORT. Three prices of different lengths
   ("2,400 INK" is 9 characters, "8 LUNES" is 7) share one tile width, so a fixed pixel size that
   fits the short one silently overruns the long one — and `.pprice` in css/menu.css is
   `font-size:12px!important`, which is exactly what pushed 118px of text through a 92px column.
   Press Start 2P is one em per character, so 9 chars fit in `(column − padding) / 9`. */
#packStage .packbox.pktile .pprice{font-size:min(14px, calc((100cqw - 14px) / 9))!important;}
#packStage .packbox.pktile .pname{font-size:10px; line-height:1.2; padding:0; text-align:center;}
.packbox.pktile .pname i{display:block; font-style:normal; font-family:var(--fontJ); font-size:7px; opacity:.65; margin-top:2px;}
#packStage .packbox.pktile .pprice{margin-top:4px; padding:3px 6px; white-space:nowrap;}
.packbox.pktile.gold .pprice{background:var(--ink); color:#ffe9a0;}
.packbox.pktile.prem .pprice{background:#1b1510; color:#f4c542;}
/* --- the showcase: the pack itself, zoomed and tilted, with everything it comes with --- */
.pkshow{text-align:center; max-width:340px; margin:0 auto;}
/* ⚠ THE STAGE IS BIGGER THAN THE PACK ON PURPOSE. The pack is tilted and scaled up, so its painted
   box is ~20% wider and taller than its layout box; a stage cut to the pack's own size reads as
   overflowing to the pre-flight (scrollWidth > clientWidth) even though nothing is visibly clipped.
   The art takes 80% × 82% of the stage and the tilt spends the margin that is left. */
.pkshow-stage{position:relative; width:140px; height:150px; margin:2px auto 6px; overflow:visible;
  display:flex; align-items:center; justify-content:center;
  animation:pkShowIn .42s var(--pop) both;}
.pkshow-art{width:80%; height:82%; transform:rotate(-7deg) scale(1.06); filter:drop-shadow(6px 7px 0 #1b1510);}
.pkshow-art svg{width:100%; height:100%; display:block;}
@keyframes pkShowIn{0%{transform:scale(.55) rotate(0deg); opacity:0}100%{transform:scale(1) rotate(0deg); opacity:1}}
.pkshow-ttl{font-size:13px; line-height:1.2; margin:0;}
.pkshow-ttl i{display:block; font-style:normal; font-family:var(--fontJ); font-size:8px; opacity:.7; margin-top:3px;}
.pkshow-price{display:inline-block; font-family:var(--fontD); font-size:14px; margin:6px 0 2px;
  padding:3px 11px; background:var(--ink); color:var(--cream); transform:skewX(-8deg);}
.pkshow.gold .pkshow-price{color:#ffe9a0;} .pkshow.prem .pkshow-price{color:#f4c542;}
.pkshow-blurb{margin:6px 8px; line-height:1.7;}
.pkshow-odds{display:flex; justify-content:center; flex-wrap:wrap; gap:5px; margin:6px 0;}
.pkodd{font-family:var(--fontD); font-size:8px; letter-spacing:.5px; line-height:1.3;
  padding:4px 6px; border:2px solid var(--ink); background:#00000018;}
.pkodd b{display:block; font-size:11px; margin-top:2px;}
.pkshow-pity{opacity:.7; margin:2px 8px 6px; line-height:1.6;}
.pkshow-ask{font-size:14px; margin:6px 0 4px;}
.pkshow-purse{margin-bottom:8px;}
/* --- the shine ladder: prem sparkles, feat gets a soft sweep, news stays plain paper --- */
.pksheen{position:absolute; inset:0; overflow:hidden; pointer-events:none; z-index:3;}
.pksheen::before{content:''; position:absolute; top:-25%; bottom:-25%; width:34%; left:0;
  background:linear-gradient(90deg,transparent,#ffffff4d,transparent); animation:pkSheen 3.6s ease-in-out infinite;}
@keyframes pkSheen{0%{transform:translateX(-160%) skewX(-18deg)}100%{transform:translateX(340%) skewX(-18deg)}}
.packbox.prem .pksheen::before,.pkshow.prem .pksheen::before{background:linear-gradient(90deg,transparent,#ffffff8c,transparent); animation-duration:2.5s;}
.pkspk{position:absolute; inset:0; pointer-events:none; z-index:4;}
.pkspk i{position:absolute; width:9px; height:9px; opacity:0;
  background:radial-gradient(circle,#ffffff 0 26%,#f4c542 26% 52%,transparent 56%);
  animation:pkTwinkle 2s ease-in-out infinite;}
@keyframes pkTwinkle{0%,100%{opacity:0; transform:scale(.35)}50%{opacity:1; transform:scale(1.2)}}
/* the unfolded Fold gets the room it has — the shelf grows rather than sitting marooned in the middle */
@media (min-width:720px){
  .mv-box,.mfree,.pkshelf{max-width:700px;}
  .mv-c{padding:7px 12px;}
  .mv-n{font-size:17px;} .mv-l{font-size:9px;} .mv-c svg{width:23px; height:23px;}
  .mfree{padding:11px 16px; gap:14px;}
  .mfree-art{width:58px; height:68px;} .mfree-dot{left:62px; top:10px;}
  .mfree-h{font-size:16px;} .mfree-s{font-size:11px;} .mfree-go{font-size:14px; padding:7px 14px;}
  .pkshelf{gap:14px;}
  .pkart{width:140px; height:158px;}
  #packStage .packbox.pktile{padding:16px 10px 14px; gap:9px;}
  #packStage .packbox.pktile .pname{font-size:15px;}
  .packbox.pktile .pname i{font-size:9px;}
  .pktier{font-size:14px; padding:4px 7px;}
  #packStage .packbox.pktile .pprice{padding:4px 10px;}
  .pkshow{max-width:420px;}
  .pkshow-stage{width:150px; height:166px;}
  .pkshow-ttl{font-size:17px;} .pkshow-price{font-size:17px;} .pkshow-ask{font-size:17px;}
  .pkodd{font-size:10px;} .pkodd b{font-size:13px;}
}
/* ⚠ THE FOLDED SHELF IS THE TIGHT CASE — three tiles inside ~347px of usable width, so the tile is
   about 105px wide. Everything on it must be sized against THAT, not against a fresh look at 375. */
@media (max-width:400px){
  .mv-c{flex:1 1 44%; min-width:0; padding:5px 7px; gap:6px;}
  .mv-n{font-size:13px;}
  .pkshelf{gap:6px;}
  .pkart{width:62px; height:69px;}
  #packStage .packbox.pktile{padding:10px 4px 9px; gap:5px;}
  #packStage .packbox.pktile .pname{font-size:9px;}
  .packbox.pktile .pname i{font-size:6px;}
  .pktier{font-size:9px; padding:2px 4px; top:3px; left:3px;}
  #packStage .packbox.pktile .pprice{padding:2px 5px;}
}
/* ============================================================
   ⭐⭐ v0.452 — THE PROFILE BATCH (Odaroo's screenshots).
   ============================================================ */
/* A — ROOM TO BREATHE. "BEST INK 140TIME 5m" was two cells sharing a 74px column with no gutter and
   no edge between them, so the eye read one run-on string. Wider minimum, a real gutter, and each
   cell now sits in its own tinted box so a label can never appear to belong to the figure beside it. */
.pf-gcells{grid-template-columns:repeat(auto-fit,minmax(104px,1fr)); gap:6px 10px; margin-top:7px;}
.pf-gcells i{background:#1b15100a; border:1px solid #1b151022; border-radius:4px;
  padding:4px 7px; gap:10px; min-width:0;}
.pf-gcells i span{overflow:hidden; text-overflow:ellipsis;}
.pf-game{padding:9px 10px; margin:0 0 10px;}
.pf-gname{padding-bottom:5px; border-bottom:1px dashed #1b151033;}
/* and air between every stat line and every section */
.pf-statgrid{gap:8px;}
.pf-statrow{padding:7px 4px;}
.pf-statrow.pf-sec{margin-top:18px; padding-bottom:6px;}
.pf-sect-h{margin-top:20px;}
.pf-main{padding-bottom:10px;}

/* B — THE HEADER MATCHES THE MART AND THE ALBUM: a proper bar with weight, not a thin strip. */
.st-bar{height:54px; margin:0 0 14px; padding:0 16px; border-bottom:4px solid #e01b4d;
  box-shadow:0 5px 0 #e01b4d55, 0 8px 14px -6px #000a;}
.st-bar .st-close{font-size:11px;}
.st-bar .st-nm{font-size:15px;}
.st-bar .st-jp{font-size:10px;}
@media (min-width:560px){
  .st-bar{height:64px; margin:0 0 16px;}
  .st-bar .st-nm{font-size:19px;} .st-bar .st-close{font-size:13px;} .st-bar .st-jp{font-size:12px;}
}

/* C — THE CONTENT CLEARS THE CONTROLS. --pfpad is measured off the real pad in pfPadClear(); the
   scroller has to actually SPEND it, or the last section (FAVOURITE GAME) sits under the D-pad. */
#scr-style{padding-bottom:var(--pfpad,268px)!important; box-sizing:border-box;}
.pf-wrap{padding-bottom:14px;}

/* ⭐ B (second batch) — THE PARADE WALKS. It used to hop: the keyframes stepped translateY between
   0 and -6px at every 10%, which on a linear timing function is a jerk, not a bounce — Odaroo read
   it as characters teleporting. One smooth slide, and the walk is carried by the sprite itself. */
@keyframes pfRun{from{transform:translateX(0)} to{transform:translateX(126vw)}}
.pf-runner{animation-timing-function:linear;}
/* ⚠⚠ v0.635 — THE DOSSIER CARD IS DARK, THE MART'S MODAL IS CREAM, AND THE SHARED COST/EFFECT ROWS
   LIVE IN BOTH. Left to inherit, the rows took the page's dark --ink colour and drew ink-on-ink at
   1.1:1 inside the profile card (crReadable caught it). The row is pinned to cream HERE only, so
   the Mart's light modal keeps its own dark text. */
.st-card .ivcard-row{color:#fdf6e3; background:#ffffff14;}
.st-card .ivcard-row>span{color:#fdf6e3; opacity:.62;}
.st-card .ivcard-row>b{color:#fdf6e3;}

/* ⭐⭐⭐ v0.650 — THE PROFILE'S COPY OF THE HOUSE REEL.
   #scr-style is the scrolling element itself, so the bar sticks rather than sitting still in a
   flex column the way it does on Home, the Album, the Mart and Gigs. Everything else about it —
   size, sprockets, portrait, name, chips — comes from `.hudreel` and is untouched here on purpose.
   ⚠ z-index has to beat the sky (::before, z-index 1), the parade (.pf-field, z-index 1) and the
   pink .st-bar (z-index 6), or the clouds paint straight over the username — the v0.483 paint-order
   trap, which neither hit-testing nor crReadable can see. */
/* ⚠ IT STICKS AT --hudlift, NOT AT 0. That var is the inklet's OVERHANG — hudLift() measures how
   far the lens hangs above the strip and writes it onto the bar — so sticking at 0 pinned the strip
   to the top of the scroll port and sheared the top of the portrait's head off as soon as the file
   scrolled. Sticking at the overhang keeps the whole inklet on screen and costs no extra air. */
#hudStyle{position:sticky; top:var(--hudlift,17px); z-index:20; flex:none;}
