/* ============================================================================
   STORY CARDS — the standardized share-card system for the Mojo Dojo ladder.

   TWO ANATOMIES, one family:

   · STORY CARD (a report between two players, or a player and the ladder):
       date strip → crest → [eyebrow + VERDICT BANNER] → subject (VS matchup /
       rank tokens) → [SECTION BAND] → stats → pills → url.
       Banners and bands are full-bleed horizontal strips that DELIMIT space —
       nothing floats, everything hangs from a strip.

   · ACHIEVEMENT CARD (make the player feel the grind paid off):
       date strip → crest → [LOGRO band] → BIG ILLUSTRATION STAGE (badge art
       lives here, generous air around it) → title → one-line desc → url.

   TYPE SCALE (PP Monument Extended for display, Avenir for body):
     13 strip · 13 eyebrow · 38 banner · 24 vs-name · 28 score · 12 band
     48 riv-number · 15 status · 12 meta · 13 pill · 34 ach-title · 15 note

   RHYTHM: sections are separated by a fixed 30px beat (--beat); banners are
   full-bleed; inner blocks carry the 44px side gutter (--gutter).

   html2canvas-safe: gradients/borders/shadows + inline SVG only. No masks,
   no blend modes, no clip-path.
   ========================================================================== */
.storycard {
  --gutter: 44px;
  --beat: 30px;
  position: fixed; left: -99999px; top: 0; width: 660px; min-height: 1173px;
  display: flex; flex-direction: column; overflow: hidden;
  background: #0b0a08; color: var(--text); font-family: var(--font-body);
}
.storycard * { box-sizing: border-box; }
/* html2canvas paints a transparency checkerboard inside any image that carries a
   CSS `filter` — so strip filters from every image once it's on a rasterized card
   (avatar hats, medals, badges). The live UI keeps its shadows; only the poster
   render drops them. */
.storycard img, .sharecard img { filter: none !important; }
/* The OTHER html2canvas rendering bug lives in app.js: it cannot draw `inset`
   box-shadows and floods most of the element with the shadow colour instead —
   which is what turned every gold plate (banner, score, pill, rank token) into
   a pale checkerboard. renderCardToFile strips the inset segments for the
   duration of the render, so the insets below are safe to keep for the screen. */
.storycard > *:not(.sc2-glow):not(.sc2-dots):not(.sc2-hatch) { position: relative; z-index: 2; }

/* --- mood washes ---------------------------------------------------------- */
.sc2-glow { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.storycard.m-gold  .sc2-glow { background:
  radial-gradient(96% 34% at 50% 0%, rgba(246,194,67,.18), transparent 62%),
  radial-gradient(120% 42% at 50% 100%, rgba(246,194,67,.14), transparent 60%); }
.storycard.m-crown .sc2-glow { background:
  radial-gradient(100% 40% at 50% 0%, rgba(246,194,67,.28), transparent 64%),
  radial-gradient(130% 46% at 50% 100%, rgba(224,165,44,.2), transparent 60%); }
.storycard.m-ember .sc2-glow { background:
  radial-gradient(92% 30% at 50% 0%, rgba(246,194,67,.09), transparent 60%),
  radial-gradient(128% 48% at 50% 100%, rgba(224,52,72,.26), transparent 62%); }
.storycard.m-fire  .sc2-glow { background:
  radial-gradient(92% 30% at 50% 0%, rgba(246,194,67,.12), transparent 60%),
  radial-gradient(130% 50% at 50% 100%, rgba(255,59,48,.24), transparent 60%); }

/* --- background textures (a whisper — never competing with content) ------- */
.sc2-dots, .sc2-hatch {
  position: absolute; left: 0; right: 0; top: 0; height: 620px; z-index: 1; pointer-events: none;
  background-position: center, center; opacity: .2;
}
.sc2-dots {
  background:
    radial-gradient(76% 62% at 50% 26%, rgba(11,10,8,0), rgba(11,10,8,0) 40%, #0b0a08 82%),
    radial-gradient(rgba(246,194,67,.6) 1.3px, transparent 1.7px);
  background-size: 100% 100%, 21px 21px;
}
.sc2-hatch {
  background:
    radial-gradient(80% 66% at 50% 24%, rgba(11,10,8,0), rgba(11,10,8,0) 36%, #0b0a08 84%),
    repeating-linear-gradient(48deg, rgba(224,52,72,.22) 0 1.5px, transparent 1.5px 25px);
  background-size: 100% 100%, auto;
}

/* --- header: quiet date strip + crest ------------------------------------- */
.sc2-head { padding: 38px var(--gutter) 0; text-align: center; }
.sc2-strip {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 8px;
  font-family: var(--font-head); font-weight: 900; letter-spacing: .22em;
  font-size: 13px; color: rgba(255,255,255,.6); text-transform: uppercase;
}
.sc2-strip span:first-child { text-align: left; }
.sc2-strip span:last-child { text-align: right; }
/* The crest carries ~14% empty pixels top and bottom (see .sc-logo in style.css):
   cancel that dead space so these margins are the air you actually see, and the
   crest sits centred under the date strip instead of riding high. */
.sc2-logo {
  width: 158px; height: auto; display: block;
  --void: 22.2px;                       /* 14.07% of 158px */
  margin: calc(30px - var(--void)) auto calc(30px - var(--void));
}

/* --- middle: a fixed-beat column; banners bleed, blocks keep the gutter --- */
.sc2-mid { flex: 1; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: var(--beat); padding: 34px 0; text-align: center; width: 100%; }
.sc2-mid > * { width: 100%; }
.sc2-gutter { padding-left: var(--gutter); padding-right: var(--gutter); }

/* --- VERDICT BANNER: the event, anchored in a full-bleed strip ------------ */
.sc2-banner { text-align: center; }
.sc2-banner .bn-eyebrow {
  font-family: var(--font-head); font-weight: 900; text-transform: uppercase;
  letter-spacing: .32em; font-size: 13px; color: #b9a877; margin: 0 0 12px .32em;
}
.sc2-banner .bn-band {
  padding: 18px 28px;
  background: linear-gradient(180deg, var(--gold-row), var(--gold-row-2));
  border-top: 1px solid rgba(255,243,206,.6); border-bottom: 1px solid #8a5c14;
  box-shadow: inset 0 1px 0 rgba(255,251,232,.5), 0 10px 26px rgba(0,0,0,.45);
}
.sc2-banner .bn-band span {
  display: block; margin: 0 auto; max-width: 100%; overflow: hidden;
  font-family: 'PP Monument Extended', var(--font-head); font-weight: 900;
  text-transform: uppercase; line-height: 1; letter-spacing: -0.01em;
  font-size: 38px; color: var(--ink); white-space: nowrap;
}
/* conflict banner: dark strip, ember rails, cream text */
.storycard.m-ember .sc2-banner .bn-band, .storycard.m-fire .sc2-banner .bn-band {
  background: linear-gradient(180deg, #1d1512, #150f0d);
  border-top: 1px solid rgba(224,52,72,.55); border-bottom: 1px solid rgba(224,52,72,.35);
  box-shadow: 0 10px 26px rgba(0,0,0,.45);
}
.storycard.m-ember .sc2-banner .bn-band span { color: var(--cream); }
.storycard.m-fire .sc2-banner .bn-band span { color: #ff6a5f; }
.storycard.m-ember .sc2-banner .bn-eyebrow, .storycard.m-fire .sc2-banner .bn-eyebrow { color: #d98a86; }

/* --- SECTION BAND: a thin labeled strip that opens a stats section -------- */
.sc2-band {
  padding: 10px 20px;
  background: rgba(246,194,67,.06);
  border-top: 1px solid rgba(246,194,67,.3); border-bottom: 1px solid rgba(246,194,67,.18);
  font-family: var(--font-head); font-weight: 900; text-transform: uppercase;
  letter-spacing: .3em; font-size: 12px; color: var(--gold); text-indent: .3em;
}
.storycard.m-ember .sc2-band, .storycard.m-fire .sc2-band {
  background: rgba(224,52,72,.07);
  border-top-color: rgba(224,52,72,.4); border-bottom-color: rgba(224,52,72,.2);
  color: #ff8079;
}

/* --- VS matchup (both fighters vivid; gold marks the winner) -------------- */
.vs { display: grid; grid-template-columns: minmax(0,1fr) auto minmax(0,1fr); align-items: start;
  gap: 12px; width: 100%; }
.vs-side { display: flex; flex-direction: column; align-items: center; gap: 11px; min-width: 0; }
.vs-ava { width: 112px; height: 112px; border-radius: 50%; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center; position: relative;
  border: 3px solid rgba(255,255,255,.16); box-shadow: 0 8px 22px rgba(0,0,0,.5); }
.vs-ava img { width: 100%; height: 100%; object-fit: cover; }
.vs-ava .hatmark { width: 66%; height: 66%; object-fit: contain; }
.vs-name { font-family: 'PP Monument Extended', var(--font-head); font-weight: 900;
  text-transform: uppercase; font-size: 24px; line-height: 1.05; color: var(--text);
  max-width: 100%; }
.vs-crew { font-family: var(--font-head); font-weight: 800; letter-spacing: .2em;
  font-size: 10px; color: #8d7f5c; }
.vs-side.win .vs-ava { border-color: var(--gold);
  box-shadow: 0 0 0 4px rgba(246,194,67,.16), 0 10px 26px rgba(0,0,0,.55); }
.vs-side.win .vs-name { color: var(--gold); }
.vs-side.win .vs-crew { color: #9c8a54; }
.vs-side.lose .vs-name { color: #cbb98f; }
.storycard.m-ember .vs-side.win .vs-ava { border-color: var(--red);
  box-shadow: 0 0 0 4px rgba(224,52,72,.18), 0 10px 26px rgba(0,0,0,.55); }
.storycard.m-ember .vs-side.win .vs-name { color: #ff6a5f; }

/* the score plate sits between the two fighters, aligned to the avatars */
.vs-mid { display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-height: 112px; }
.vs-vs { font-family: var(--font-head); font-weight: 900; font-size: 11px;
  letter-spacing: .22em; color: #7d7052; margin-bottom: 8px; text-indent: .22em; }
.vs-score { font-family: 'PP Monument Extended', var(--font-head); font-weight: 900;
  color: var(--ink); font-size: 28px; line-height: 1; letter-spacing: .01em;
  padding: 13px 15px; border-radius: 12px;
  background: linear-gradient(180deg, var(--gold-row), var(--gold-row-2));
  box-shadow: inset 0 1px 0 rgba(255,251,232,.55), 0 6px 16px rgba(0,0,0,.5);
  border: 1px solid rgba(255,243,206,.5); }
.storycard.m-ember .vs-score { color: #fff;
  background: linear-gradient(180deg, #2a2320, #1c1714);
  border-color: #4a3f39; box-shadow: 0 6px 16px rgba(0,0,0,.5); }

/* challenge face-off: both lit, a red swords plate instead of a score */
.storycard.m-fire .vs-side.win .vs-ava { border-color: var(--red);
  box-shadow: 0 0 0 4px rgba(224,52,72,.18), 0 10px 26px rgba(0,0,0,.55); }
.storycard.m-fire .vs-side.win .vs-name { color: #ff6a5f; }
.reto-plate { display: inline-flex; align-items: center; justify-content: center;
  width: 64px; height: 64px; border-radius: 14px; color: #fff;
  /* flat solid face — html2canvas checkerboards gradients + inset shadows + the
     transparent PNG's own box; the swords carry the SAME solid bg so their box
     vanishes into the plate and nothing renders as a transparency checkerboard. */
  background: #171310;
  border: 2px solid var(--red);
  box-shadow: 0 0 18px rgba(224,52,72,.4), 0 6px 16px rgba(0,0,0,.5); }
.reto-plate .swordsicon { width: 44px; height: 44px; object-fit: contain; background: #171310; }
.sc2-oath { font-family: var(--font-body); font-style: italic; font-size: 15px; color: #d9b6b0; }

/* --- rivalry stats (opened by its section band) --------------------------- */
.riv-wrap { width: 100%; }
.riv { width: 100%; margin-top: 20px; }
.riv-rec { display: grid; grid-template-columns: minmax(0,1fr) auto minmax(0,1fr);
  align-items: center; gap: 10px; }
.riv-num { font-family: 'PP Monument Extended', var(--font-head); font-weight: 900;
  font-size: 48px; line-height: 1; color: var(--text); }
.riv-col.me .riv-num { color: var(--gold); }
.storycard.m-ember .riv-col.me .riv-num, .storycard.m-fire .riv-col.me .riv-num { color: #ff6a5f; }
.riv-dash { font-family: 'PP Monument Extended', var(--font-head); font-weight: 900;
  font-size: 24px; color: #6f6448; }
.riv-tag { font-family: var(--font-head); font-weight: 900; text-transform: uppercase;
  font-size: 12px; letter-spacing: .08em; color: #b6a577; margin-top: 8px;
  max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding: 0 6px; }
.riv-col.me .riv-tag { color: var(--gold); }
.riv-meta { margin-top: 16px; display: flex; flex-direction: column; gap: 8px; align-items: center; }
.riv-status { font-family: 'PP Monument Extended', var(--font-head); font-weight: 900;
  text-transform: uppercase; letter-spacing: .03em; font-size: 15px; color: var(--gold); }
.storycard.m-ember .riv-status, .storycard.m-fire .riv-status { color: #ff8079; }
.riv-line { font-family: var(--font-head); font-weight: 800; text-transform: uppercase;
  letter-spacing: .14em; font-size: 12px; color: #a89670; }
.riv-first { font-family: 'PP Monument Extended', var(--font-head); font-weight: 900;
  text-transform: uppercase; font-size: 22px; color: var(--gold); }
.storycard.m-fire .riv-first { color: #ff8079; }
.riv-first-sub { font-family: var(--font-body); font-style: italic; font-size: 14px; color: #b9a877; margin-top: 8px; }

/* --- pills ---------------------------------------------------------------- */
.pill-row { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
.pill { display: inline-flex; align-items: center; gap: 7px; border-radius: 10px;
  padding: 9px 14px; font-family: var(--font-head); font-weight: 900;
  text-transform: uppercase; letter-spacing: .05em; font-size: 13px; white-space: nowrap; }
.pill .tri { font-size: 11px; }
.pill.gold { color: var(--ink);
  background: linear-gradient(180deg, var(--gold-row), var(--gold-row-2));
  box-shadow: inset 0 1px 0 rgba(255,251,232,.5), 0 5px 14px rgba(0,0,0,.45);
  border: 1px solid rgba(255,243,206,.5); }
.pill.ghost { color: var(--gold); background: rgba(246,194,67,.07);
  border: 2px solid rgba(246,194,67,.5); }
.pill.fire { color: #fff; background: rgba(224,52,72,.12); border: 2px solid var(--red); }
.pill.fire .streakflame { width: 15px; height: 15px; }

/* --- rank tokens ---------------------------------------------------------- */
.rankmove { display: flex; align-items: center; justify-content: center; gap: 18px; }
.rk-tok { font-family: 'PP Monument Extended', var(--font-head); font-weight: 900;
  color: var(--ink); font-size: 40px; line-height: 1; padding: 15px 20px; border-radius: 14px;
  background: linear-gradient(180deg, var(--gold-row), var(--gold-row-2));
  box-shadow: inset 0 1px 0 rgba(255,251,232,.5), 0 8px 20px rgba(0,0,0,.5);
  border: 1px solid rgba(255,243,206,.5); }
.rk-tok.from { color: #7d7052; background: linear-gradient(180deg,#211d17,#171310);
  border: 1px solid #37301f; box-shadow: 0 8px 20px rgba(0,0,0,.5); font-size: 34px; }
.rk-arrow { font-size: 32px; color: var(--gold); }
.storycard.m-ember .rk-arrow { color: var(--red); }
.storycard.m-ember .rk-tok { color: #fff; background: linear-gradient(180deg,#2a2320,#1c1714);
  border-color: #4a3f39; box-shadow: 0 8px 20px rgba(0,0,0,.5); }
.rk-medal { width: 118px; height: 140px; object-fit: contain; display: block; margin: 0 auto; }

/* --- ACHIEVEMENT anatomy: the illustration is the hero -------------------- */
/* a generous stage where badge art lives (swappable for commissioned PNGs) */
.ach-stage { position: relative; height: 380px; display: flex;
  align-items: center; justify-content: center; }
.ach-halo { position: absolute; left: 50%; top: 50%; width: 480px; height: 480px;
  transform: translate(-50%,-50%); pointer-events: none;
  background: radial-gradient(circle, rgba(246,194,67,.22), rgba(246,194,67,.05) 46%, transparent 68%); }
.ach-art { position: relative; width: 340px; height: 340px; }
.ach-art .badge-img { width: 100%; height: 100%; object-fit: contain; display: block; }
.ach-title { font-family: 'PP Monument Extended', var(--font-head); font-weight: 900;
  text-transform: uppercase; line-height: 1; font-size: 34px; color: var(--gold);
  max-width: 100%; overflow: hidden; white-space: nowrap; }
.ach-desc { font-family: var(--font-body); font-style: italic; font-size: 15px;
  color: #cdbd8f; max-width: 440px; margin: 0 auto; line-height: 1.45; }

/* --- notes / footer ------------------------------------------------------- */
.sc2-note { font-family: var(--font-body); font-style: italic; font-size: 15px;
  color: #cdbd8f; max-width: 460px; margin: 0 auto; line-height: 1.45; }
.sc2-foot { text-align: center; padding: 18px 20px 38px; }
.sc2-url { font-family: var(--font-head); font-weight: 900; text-transform: uppercase;
  letter-spacing: .24em; font-size: 13px; color: var(--cream); margin-left: .24em; }

/* ===================== profile trophy case (badge grid) ==================== */
.trophycase { display: grid; grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); gap: 12px; margin-top: 4px; }
.tc-tile { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 14px 6px 11px;
  background: rgba(246,194,67,.05); border: 1px solid rgba(246,194,67,.22); border-radius: 14px;
  color: var(--text); font: inherit; text-align: center; }
button.tc-tile { cursor: pointer; }
button.tc-tile:hover { background: rgba(246,194,67,.1); border-color: rgba(246,194,67,.42); }
button.tc-tile:active { transform: translateY(1px); }
.tc-art { position: relative; width: 78px; height: 78px; display: inline-flex; align-items: center; justify-content: center; }
.tc-art .badge-img { width: 100%; height: 100%; object-fit: contain; display: block; }
.tc-name { font-family: var(--font-head); font-weight: 900; text-transform: uppercase; letter-spacing: .04em;
  font-size: 10px; color: #cbb98f; line-height: 1.2; }

/* ===================== end-of-set screen (inline hero) ===================== */
.resultscreen { max-width: 620px; margin: 0 auto; padding: 2px 0 20px; }
.res-actions { display: flex; flex-direction: column; gap: 10px; margin-top: 20px; }
.res-actions .btn.block { width: 100%; }
.res-actions .rematchbox { margin-top: 2px; }
.res-shares { display: flex; flex-direction: column; gap: 10px; }
.res-shares .btn.block { width: 100%; }
/* the bought painting — set apart from the free share stack, because it costs */
.res-art:not(:empty) {
  display: flex; flex-direction: column; gap: 8px;
  padding: 12px; border: 1px dashed #46381b; border-radius: 13px; background: #14110c;
}
.res-art .btn.block { width: 100%; }
.res-art .artbtn { border-color: #6a5324; }
.art-note {
  color: #b9aa8b; font-size: 11px; line-height: 1.35; text-align: center;
}
/* the "this costs a MOJO" line in the one-time notice */
.art-once {
  margin-top: 10px; color: var(--cream); font-family: var(--font-head);
  font-size: 11px; letter-spacing: .04em; text-transform: uppercase; text-align: center;
}
/* which movement the painting came out of, under its preview */
.share-note { margin-top: 10px; color: #b9aa8b; font-size: 12px; text-align: center; }

/* ---- THE EASEL: what a painting looks like before it exists -----------------
   A Gemini painting is ~40 seconds of nothing, and a disabled button reading
   "Pintando…" was not enough to hold anybody there — the crew was leaving the
   screen (or reloading it) mid-generation and losing the share sheet they had
   just paid a MOJO for. So the wait gets the SHAPE of what is coming: the exact
   9:16 frame the arte lands in, a gilt sweep travelling across it like light
   crossing wet varnish, and three strokes building up underneath. It occupies
   the space the finished painting will occupy, so nothing jumps when it swaps in.
   Reused verbatim by the result screen, the share sheet and the carousel. */
.art-easel {
  position: relative; width: 100%; aspect-ratio: 9 / 16;
  max-height: min(52vh, 460px); margin: 0 auto;
  border: 1px solid rgba(246,194,67,.26); border-radius: 12px; overflow: hidden;
  background:
    radial-gradient(120% 80% at 50% 0%, rgba(246,194,67,.07), transparent 60%),
    linear-gradient(180deg, #16120c, #0d0b08);
}
/* the sweep — one band of gold light crossing the canvas on a slow diagonal */
.art-easel::before {
  content: ''; position: absolute; inset: -40%;
  background: linear-gradient(115deg,
    transparent 34%, rgba(246,194,67,.2) 46%, rgba(253,237,182,.34) 50%,
    rgba(246,194,67,.2) 54%, transparent 66%);
  animation: easel-sweep 2.4s linear infinite;
}
@keyframes easel-sweep {
  0% { transform: translate3d(-46%, -30%, 0); }
  100% { transform: translate3d(46%, 30%, 0); }
}
/* three blocked-in strokes, the way a canvas is actually laid in: the two
   figures, then the ground under them. They pulse out of phase so the frame
   never reads as a static placeholder somebody forgot to remove. */
.ee-stroke {
  position: absolute; border-radius: 6px; background: rgba(246,194,67,.16);
  animation: easel-pulse 1.9s ease-in-out infinite;
}
.ee-stroke.a { left: 12%; top: 26%; width: 30%; height: 34%; }
.ee-stroke.b { right: 12%; top: 32%; width: 30%; height: 28%; animation-delay: .32s; }
.ee-stroke.c { left: 12%; right: 12%; bottom: 14%; height: 9%; animation-delay: .64s; }
@keyframes easel-pulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
/* the frame's own gold corners — it should read as a canvas on an easel, not a
   grey box, even in the first frame before anything has animated */
.art-easel::after {
  content: ''; position: absolute; inset: 8px; border-radius: 8px;
  border: 1px dashed rgba(246,194,67,.22); pointer-events: none;
}
/* what the house is doing right now, stepping through the stages of a painting.
   Fixed height so the line changing never nudges the frame above it. */
.art-phase {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 1.5em; text-align: center;
  font-family: var(--font-head); font-weight: 900; font-size: 10px;
  letter-spacing: .16em; text-transform: uppercase; color: var(--cream);
}
.art-phase .dot {
  width: 5px; height: 5px; border-radius: 50%; background: var(--gold);
  animation: easel-pulse 1.2s ease-in-out infinite;
}
/* the honest part: this takes a while, and the MOJO is safe if it doesn't land */
.art-wait {
  color: #8d7f5c; font-size: 11px; line-height: 1.45; text-align: center;
}
@media (prefers-reduced-motion: reduce) {
  .art-easel::before, .ee-stroke, .art-phase .dot { animation: none; }
}

/* The easel inside the share sheet. The finished .share-preview caps at 46vh, so
   the easel is sized to land on the same footprint — the sheet must not jump when
   the painting swaps in. HEIGHT drives it here, not width: `width:auto` against a
   bare aspect-ratio has nothing to resolve against and collapses the frame to
   nothing (which is exactly what it did the first time). */
.art-wait-sheet {
  display: flex; flex-direction: column; align-items: center; gap: 10px; margin-top: 10px;
}
.art-wait-sheet .art-easel {
  height: min(46vh, 420px); height: min(46dvh, 420px);
  width: auto; max-height: none;
}
.art-wait-sheet .art-wait { max-width: 300px; }
/* the result screen's easel is narrower still — it sits in the share column, not
   in a modal, and a full-width 9:16 frame there would be most of a phone screen */
.res-art .art-easel { max-width: 190px; }

/* A tile whose image hasn't decoded yet: the same gilt sweep at tile scale, so a
   slow 2K PNG shows something arriving instead of a black hole. Dropped the
   instant the <img> fires `load` (see artImg in feed.js / app.js). */
.art-loading { position: relative; }
.art-loading::before {
  content: ''; position: absolute; inset: 0; z-index: 1;
  background:
    linear-gradient(115deg, transparent 34%, rgba(246,194,67,.26) 50%, transparent 66%),
    linear-gradient(180deg, #16120c, #0d0b08);
  background-size: 260% 260%, 100% 100%;
  animation: tile-sweep 1.5s linear infinite;
}
@keyframes tile-sweep {
  0% { background-position: 130% 130%, 0 0; }
  100% { background-position: -130% -130%, 0 0; }
}
.art-loading img { opacity: 0; }
.art-loading img.ready { opacity: 1; transition: opacity .35s ease; }
@media (prefers-reduced-motion: reduce) { .art-loading::before { animation: none; } }

/* owned on the result screen: a free view button + a paid "de nuevo" beside it */
.art-owned { display: flex; align-items: stretch; gap: 8px; }
.art-owned .btn.block { width: auto; flex: 1; }
.art-owned .artbtn.again { flex: 0 0 auto; white-space: nowrap; }

/* ===================== timeline: the public art strip on a match post ===================== */
/* sits between the plate and the reaction bar; collapses to nothing when
   there's no painting yet for this match */
.fp-art:empty { display: none; }
/* one inline row, neither wrapping nor scrolling: each painter's arte takes the
   next slot to the right, and once the column runs out the row is clipped by
   fitArtStrip() and the last slot becomes the "+N · VER TODOS" chip */
.fp-art {
  display: flex; align-items: center; gap: 8px; flex-wrap: nowrap;
  overflow: hidden;
  /* The row must never be what decides how wide a post is — otherwise a battle
     everybody painted widens the column and the phone scrolls sideways. A hard
     width of 0 keeps the tiles out of the column's intrinsic size; min-width
     hands the row the column's full width back once that is settled, which is
     also what fitArtStrip() then measures. */
  width: 0; min-width: 100%;
  padding: 10px 14px; border-top: 1px solid #2b2418; border-bottom: 1px solid #2b2418;
  background: #100d09;
}
/* the tiles the row had no space for, and the chip while everything fits */
.fp-art [hidden] { display: none; }
.art-tile {
  position: relative; flex: 0 0 auto; width: 54px; height: 96px; padding: 0;
  border: 1px solid rgba(246,194,67,.28); border-radius: 8px; overflow: hidden;
  background: #0b0a08; cursor: pointer;
}
.art-tile:hover { border-color: rgba(246,194,67,.55); }
.art-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.art-tile-tag {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 3px 4px;
  background: linear-gradient(0deg, rgba(0,0,0,.85), transparent);
  font-family: var(--font-head); font-weight: 900; font-size: 7px; letter-spacing: .04em;
  color: var(--cream); text-transform: uppercase; text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* ---- a painter's stack: one slot, however many artes are in it --------------
   Every MOJO adds to the pile instead of writing over it, so a slot in the row
   is a PERSON, not a picture: their newest face-up, the rest as leaves peeking
   out behind the top-right corner. Two leaves is all it ever draws — a pile of
   nine doesn't need nine edges to say "there are more", the count says that.
   The face is inset the same 6px whether there are leaves behind it or not, so
   every painting in the row is the same size no matter how deep the stack. */
.art-stack { border: 0; background: none; overflow: visible; }
.art-stack .as-face {
  position: absolute; left: 0; bottom: 0; right: 6px; top: 6px; z-index: 3;
  border: 1px solid rgba(246,194,67,.28); border-radius: 8px; overflow: hidden;
  background: #0b0a08;
}
.art-stack:hover .as-face { border-color: rgba(246,194,67,.55); }
.as-leaf {
  position: absolute; border: 1px solid rgba(246,194,67,.22); border-radius: 8px;
  background: #17130c;
}
.as-leaf.l1 { left: 3px; bottom: 3px; right: 3px; top: 3px; z-index: 2; }
.as-leaf.l2 { left: 6px; bottom: 6px; right: 0; top: 0; z-index: 1; }
.art-stack:hover .as-leaf { border-color: rgba(246,194,67,.4); }
/* how many are in the pile — only drawn past the first */
.as-n {
  position: absolute; top: 3px; left: 3px; z-index: 2; min-width: 13px;
  padding: 1px 3px; border-radius: 999px;
  background: rgba(11,10,8,.86); border: 1px solid rgba(246,194,67,.45);
  font-family: var(--font-head); font-weight: 900; font-size: 8px; line-height: 1.35;
  color: var(--gold); text-align: center;
}
/* the chip in the last slot: exactly a tile's footprint, but empty and dashed so
   it reads as "there is more behind this" rather than as another painting */
.art-more {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  border-style: dashed; border-color: rgba(246,194,67,.4); background: #0e0b07;
  font-family: var(--font-head); text-transform: uppercase; color: var(--gold);
}
.art-more:hover { border-color: var(--gold); background: #141009; }
.art-more .am-n { font-weight: 900; font-size: 15px; line-height: 1; letter-spacing: .02em; }
.art-more .am-label {
  font-weight: 900; font-size: 6.5px; letter-spacing: .06em; color: #8d7f5c;
  padding: 0 3px; text-align: center; line-height: 1.2;
}
/* every painter's stack on one battle, laid out the way the profile's Museo lays
   out a player's — the way out of a row that ran out of column */
.art-all {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 10px;
  max-height: 62vh; overflow-y: auto; margin-top: 4px; padding-right: 2px;
}
/* ---- one painter's stack, opened out ---------------------------------------
   Their artes of this battle one at a time, newest first. The frame is fixed to
   the tallest the modal allows and the painting is contained inside it, so the
   arrows and the movement line hold still while you leaf through pieces the
   model may not have returned at identical proportions. */
.art-car { display: flex; flex-direction: column; gap: 10px; }
.ac-stage {
  position: relative; height: min(56vh, 420px);
  border: 1px solid rgba(246,194,67,.24); border-radius: 12px; overflow: hidden;
  background: #0b0a08;
}
.ac-stage img { width: 100%; height: 100%; object-fit: contain; display: block; }
.ac-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 32px; height: 46px; padding: 0; border-radius: 8px;
  background: rgba(11,10,8,.72); border: 1px solid rgba(246,194,67,.3);
  color: var(--cream); font-size: 22px; line-height: 1; cursor: pointer;
  transition: color .1s ease, border-color .1s ease, background .1s ease;
}
.ac-nav:hover { color: var(--gold); border-color: var(--gold); background: rgba(11,10,8,.92); }
/* above the loading sweep, which is z-index 1 — leafing through a stack must not
   go dead while the next 2K painting decodes */
.ac-nav { z-index: 2; }
.ac-nav.prev { left: 8px; }
.ac-nav.next { right: 8px; }
.ac-dots { display: flex; justify-content: center; flex-wrap: wrap; gap: 6px; }
.ac-dot {
  width: 7px; height: 7px; padding: 0; border-radius: 50%; cursor: pointer;
  background: #2b2418; border: 1px solid rgba(246,194,67,.3);
}
.ac-dot.on { background: var(--gold); border-color: var(--gold); }
.ac-label {
  font-size: 12px; color: #8d7f5c; text-align: center; line-height: 1.45; min-height: 1.45em;
}
/* the paint/repaint action, sized and placed like the other bar icons */
.fp-act.paint.on { color: var(--gold); }
.fp-act.paint.busy { opacity: .5; }
.fp-act.paint.busy svg { animation: paint-spin 1s linear infinite; }
@keyframes paint-spin { to { transform: rotate(360deg); } }

/* the paint popup: no title, no cancel — a cost line, one button, and an X */
.modal-x {
  position: absolute; top: 8px; right: 10px; width: 30px; height: 30px; padding: 0;
  background: none; border: none; color: #8d7f5c; font-size: 24px; line-height: 1;
  cursor: pointer; transition: color .1s ease;
}
.modal-x:hover { color: var(--gold); }

/* ===================== profile: "Museo de {tag}" ===================== */
.art-gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 12px; margin-top: 4px; }
.ag-cell { position: relative; }
.ag-tile {
  position: relative; display: block; width: 100%;
  aspect-ratio: 9 / 16; padding: 0; overflow: hidden;
  border: 1px solid rgba(246,194,67,.22); border-radius: 12px; background: #0b0a08; cursor: pointer;
}
/* throw your own arte away — own profile only */
.ag-del {
  position: absolute; top: 5px; right: 5px; z-index: 2;
  width: 22px; height: 22px; padding: 0; border-radius: 50%;
  background: rgba(11,10,8,.8); border: 1px solid rgba(246,194,67,.3);
  color: #cbb98f; font-size: 13px; line-height: 1; cursor: pointer;
  transition: color .1s ease, border-color .1s ease, background .1s ease;
}
.ag-del:hover { color: #fff; border-color: var(--red); background: rgba(224,52,72,.85); }
.ag-tile:hover { border-color: rgba(246,194,67,.5); }
.ag-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* the matchup, which can be two long tags — wrap it rather than clip it, since
   the owner may not be in the fight and "GUDBOI VS PAR…" tells you nothing */
.ag-opp {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 6px 5px;
  background: linear-gradient(0deg, rgba(0,0,0,.92), rgba(0,0,0,.75) 60%, transparent);
  font-family: var(--font-head); font-weight: 900; font-size: 8.5px; letter-spacing: .02em;
  color: var(--cream); text-transform: uppercase; text-align: center;
  line-height: 1.3; overflow-wrap: anywhere;
}

.storycard.result-inline {
  --gutter: 22px;
  position: relative; left: auto; top: auto; width: 100%; min-height: 0;
  overflow: hidden; border-radius: 22px;
  background: #100d09; border: 1px solid #2b2418; box-shadow: var(--shadow);
}
.storycard.result-inline .sc2-mid { flex: none; padding: 30px 0 32px; gap: 24px; }
/* responsive clamps — these must out-specify the mood rules (.storycard.m-*) */
.storycard.result-inline .sc2-banner .bn-band span { font-size: clamp(21px, 6vw, 34px); }
.storycard.result-inline .sc2-banner .bn-eyebrow { font-size: clamp(11px, 3vw, 13px); }
.storycard.result-inline .vs-ava { width: clamp(72px, 19vw, 108px); height: clamp(72px, 19vw, 108px); }
.storycard.result-inline .vs-mid { min-height: clamp(72px, 19vw, 108px); }
.storycard.result-inline .vs-name { font-size: clamp(15px, 4.2vw, 22px); white-space: normal; overflow: visible; }
.storycard.result-inline .vs-score { font-size: clamp(20px, 5.6vw, 27px); padding: 11px 12px; }
.storycard.result-inline .riv-num { font-size: clamp(32px, 10vw, 46px); }
.storycard.result-inline .riv-first { font-size: clamp(18px, 5.4vw, 22px); }
.storycard.result-inline .sc2-note { font-size: clamp(13px, 3.5vw, 15px); padding: 0 12px; }
.storycard.result-inline .pill { font-size: clamp(12px, 3.1vw, 13px); }
.storycard.result-inline .sc2-band { font-size: clamp(10px, 2.8vw, 12px); }
/* (The achievement/milestone anatomies only ever render at poster size — the
   timeline shows its own compact plate, see feed.css.) */
