/* ── OCTOPONG · match results ────────────────────────────────
   Built from References/Results. The sheets are unanimous about
   the hierarchy and an earlier pass had it exactly inverted:

     THE VERDICT IS THE HERO. On every reference card the word is
     the biggest object by a wide margin (~150px against ~14px
     labels), the character sits underneath it reacting to the
     result, and the tables are support. We shipped a 46px verdict
     in a corner next to a table 38x its area.

   So the screen is now three bands: a full-width STAGE (verdict,
   the two flanking cards, the mascot standing in a lit pool), a
   two-column GRID of support panels that share a baseline, and the
   action row.

   Structure comes from theme.css (.panel, .panel-head, .btn,
   .btn.play, .chip). Everything below is either new or scoped
   inside .res.

   FOUR TRAPS, all of which have cost a review round here or in
   pause.css:

     1. Never outline a clipped shape with a z-index:-1 ::before —
        clip-path makes a stacking context and the outline layer
        floods the face. Use the chained zero-blur drop-shadows.
     2. clip-path clips `outline`, so focus is drawn with a
        brighter --edge in that chain instead.
     3. A gradient reports background-color rgba(0,0,0,0). An
        automated contrast sweep walks up for an opaque parent,
        finds none, and scores the gold row about 1.1:1. The real
        worst case is 4.89:1 — measure against the endpoints.
     4. The row entrance keyframe TRANSLATES rows, and results.js
        measures row rects in `_revealMe()`. That is why the body
        is held at opacity 0 (which moves nothing) until `.anim`
        lands, and why `.anim` is added after the measurement.
   ─────────────────────────────────────────────────────────── */

.res{
  --r-label:#a9c7dd;          /* 8.7:1 on panel */
  --r-rose:#ff8fbc;           /* 5.8:1 on panel */
  --r-line:rgba(140,190,230,.20);
  --r-row:#101f42;

  /* the verdict ramp. Gold on magenta = you won; hot pink on violet
     = you did not. It used to be the other way round: DEFEATED
     rendered in the teal that means victory on every reference
     sheet, and VICTORY! in gold, both on the same magenta shard. */
  --v-hi:#fff3bd; --v-mid:#ffc63d; --v-lo:#ef8a18;
  --v-shard:linear-gradient(160deg,#ff6fb0,#e02a76 55%,#a5164f);
  --v-shard-b:linear-gradient(160deg,#37d8ff,#1a6fa8);
  --v-sub:var(--aqua-hi);

  /* the stage tint, kept in step with POSE_TINT in mascot-poses.js */
  --pool:#ffb020; --rim:#ffd76a;

  font-size:calc(16px * var(--op-ui-scale, 1));
  /* minmax(0,…), never a bare 1fr — see the .screen note in theme.css */
  grid-template-rows:auto minmax(0,1fr) auto;
}
.res.lost{
  --v-hi:#ffd6e6; --v-mid:#ff4d8f; --v-lo:#bd0f42;
  --v-shard:linear-gradient(160deg,#5544b4,#2c2069 55%,#160f3c);
  --v-shard-b:linear-gradient(160deg,#ff6fb0,#a5164f);
  --v-sub:#ffb3cd;
}
.res[data-mood="near"]{ --pool:#4f9be0; --rim:#9fd4ff; }
.res[data-mood="lost"]{ --pool:#b0246a; --rim:#ff5f9b; }

/* theme.css's .panel-head is white on #ff3d8b, which measures 3.34:1.
   Same shape, darker face, 5.4:1. Scoped so no other screen shifts. */
.res .panel-head{
  background:linear-gradient(180deg,#c8206a,#8f0f4a);
  font-size:13px; letter-spacing:.13em; padding:10px 18px;
}

/* The match is over — the arena behind is atmosphere, not information.
   But atmosphere is the point: the round-2 critic measured the old
   .86-.96 scrim burying the arena entirely, leaving the bottom two
   thirds of the screen as UI floating on a navy void while every
   reference sheet sits its panels inside a lit world. The sim is
   frozen under this overlay, so what glows through is a still frame
   of the arena you just played — dimmed and blurred hard enough that
   the standings stay the loudest thing, present enough that the
   screen has a floor. Panels carry their own opaque plates, so text
   contrast does not lean on this layer. */
.res::before{
  content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
  background:
    /* The linear used to bottom out at .46, which with the two radials
       over it left ~20% of the arena reading through the FINAL
       STANDINGS table at the sides. The blur softened it without
       darkening it, so eight rows of 14px names sat on moving colour.
       .78 at the same stop keeps the tinted depth and takes the
       competition away. */
    radial-gradient(ellipse 70% 46% at 50% 30%, rgba(28,48,104,.55), rgba(9,15,38,0) 72%),
    radial-gradient(ellipse 90% 80% at 50% 38%, rgba(12,26,60,.50), rgba(4,7,18,.62) 70%),
    linear-gradient(180deg, rgba(4,7,18,.86), rgba(4,7,18,.78) 40%, rgba(4,7,18,.90));
  backdrop-filter:blur(4px) saturate(.82) brightness(.82);
}

/* ── header ──────────────────────────────────────────────────
   Deliberately thin. It used to spend 90px of a phone screen on
   MODE / BOTS / FIELD / SEED chips — a debug seed string sitting
   above the match outcome. The seed now lives in the footer next
   to COPY RESULT, which is the only place it is any use. */
.res-top{ display:flex; align-items:center; gap:14px; padding:10px 22px 2px;
          zoom:var(--op-ui-scale,1); }
.res-brand{ display:flex; align-items:center; gap:11px; }
.res-brand .mk{ width:38px; height:38px; color:var(--aqua);
                filter:drop-shadow(0 3px 0 rgba(3,6,16,.6)); }
.res-brand .mk svg{ width:100%; height:100%; }
.res-brand b{ font:900 18px/1 var(--display); letter-spacing:.06em; text-transform:uppercase;
              -webkit-text-stroke:3px var(--outline); paint-order:stroke fill; color:var(--ink); }
.res-brand small{ display:block; margin-top:3px; font:800 11px/1 var(--ui);
                  letter-spacing:.16em; text-transform:uppercase; color:var(--r-label); }
.res-tags{ display:flex; gap:8px; flex-wrap:wrap; }

/* ── body ────────────────────────────────────────────────────
   The stage takes the slack. There used to be 352px of untouched
   navy between the last panel and the action row on a tall
   desktop, 27.7% of the viewport; giving that space to the hero is
   the whole point of the band. */
.res-body{
  display:flex; flex-direction:column; gap:16px;
  padding:4px 22px 0; min-height:0; overflow:auto;
  zoom:var(--op-ui-scale,1);
  scrollbar-width:thin; scrollbar-color:#2c4a86 transparent;
}
.res-body::-webkit-scrollbar{ width:10px; }
.res-body::-webkit-scrollbar-thumb{ background:#2c4a86; border-radius:99px; }

/* held flat until `.anim` — opacity moves no rect, so `_revealMe()`
   can still measure the table underneath it. See trap 4 up top. */
.res:not(.anim) .res-body{ opacity:0; }
.res.anim .res-body{ animation:resFade .3s ease both; }

/* ── the stage ───────────────────────────────────────────── */
.res-stage{
  /* 1 0 auto, not 1 1 auto: the mascot has a fixed height, so a
     shrinking stage clips it instead of letting the body scroll */
  position:relative; flex:1 0 auto; min-height:0;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:2px; padding:4px 0 0;
}
/* the arena floor the mascot is standing on. Without it the figure
   floats on a flat rectangle, which is an automatic fail. */
.res-stage::after{
  content:""; position:absolute; left:50%; bottom:-6px; width:min(92%,980px); height:38%;
  transform:translateX(-50%); pointer-events:none; z-index:0;
  background:
    radial-gradient(ellipse 50% 62% at 50% 100%, rgba(120,190,255,.16), rgba(120,190,255,0) 70%),
    radial-gradient(ellipse 34% 40% at 50% 96%, color-mix(in srgb, var(--pool) 26%, transparent), transparent 72%);
}

/* Both ambient layers live in one clipped box. The ray fan is 1.5x
   the stage wide and square; loose in the flow its untransformed
   border box added 144px of phantom scroll to the body. */
.res-fx{ position:absolute; inset:0; overflow:hidden; pointer-events:none; z-index:0; }

/* the rising bubbles. Nine, transform + opacity only, so they cost
   compositing and nothing else. */
.res-bubbles{ position:absolute; inset:0; pointer-events:none; }
.res-bubbles span{
  position:absolute; left:var(--x); bottom:-30px; width:var(--s); height:var(--s);
  border-radius:50%; opacity:0;
  background:radial-gradient(circle at 34% 30%, rgba(255,255,255,.9), rgba(150,220,255,.22) 58%, rgba(120,190,255,.05) 72%);
  box-shadow:inset 0 0 0 1px rgba(190,235,255,.45);
}
.res.anim .res-bubbles span{ animation:resBub var(--t) linear var(--d) infinite; }

/* the victory burst. Win only — a loss does not get god rays. */
.res-rays{ position:absolute; left:50%; top:52%; width:150%; aspect-ratio:1;
           transform:translate(-50%,-50%); pointer-events:none; opacity:0; }
/* .14, not .30. At a third of a stop brighter the fan stopped being
   light behind the character and became a grey pinwheel drawn over
   the whole band — and the ball is the only thing in this game
   allowed to compete for the eye. */
.res[data-mood="win"] .res-rays{
  opacity:.14;
  background:repeating-conic-gradient(from 0deg at 50% 50%,
    rgba(255,206,96,.95) 0deg 4.5deg, rgba(255,206,96,0) 4.5deg 15deg);
  -webkit-mask-image:radial-gradient(circle at 50% 50%, #000 5%, rgba(0,0,0,.45) 20%, transparent 44%);
  mask-image:radial-gradient(circle at 50% 50%, #000 5%, rgba(0,0,0,.45) 20%, transparent 44%);
}
.res.anim[data-mood="win"] .res-rays{ animation:resSpin 46s linear infinite; }

/* ── the verdict ─────────────────────────────────────────── */
.res-banner{ position:relative; z-index:2; display:inline-block;
             padding:16px 44px 22px; margin:0 auto; }
.res.anim .res-banner{ animation:resBanner .62s cubic-bezier(.16,1.5,.4,1) both; }
.res.anim.lost .res-banner{ animation:resBannerLost .70s cubic-bezier(.22,1.1,.36,1) both; }

.res-shard{
  position:absolute; inset:-14% -6%;
  background:var(--v-shard);
  clip-path:polygon(
    1% 34%, 8% 13%, 14% 28%, 23% 5%, 32% 23%, 41% 7%, 50% 21%, 59% 3%,
    68% 23%, 77% 9%, 85% 27%, 93% 15%, 99% 35%, 92% 47%, 100% 61%,
    90% 71%, 96% 88%, 83% 81%, 77% 97%, 67% 83%, 57% 99%, 49% 83%,
    39% 97%, 31% 81%, 21% 95%, 14% 79%, 5% 87%, 9% 69%, 0% 59%, 6% 45%);
  --edge:var(--outline);
  filter:
    drop-shadow(3px 0 0 var(--edge))  drop-shadow(-3px 0 0 var(--edge))
    drop-shadow(0 3px 0 var(--edge))  drop-shadow(0 -3px 0 var(--edge))
    drop-shadow(0 12px 30px rgba(0,0,0,.6));
}
.res-shard.back{ inset:-24% -11%; background:var(--v-shard-b);
                 opacity:.46; transform:rotate(-1.6deg); }

/* the arrival ring. It reads as impact, and it is one element
   animating transform and opacity. */
.res-shock{
  position:absolute; left:50%; top:52%; width:34%; aspect-ratio:1;
  transform:translate(-50%,-50%) scale(.25); opacity:0; pointer-events:none;
  border-radius:50%; box-shadow:0 0 0 10px var(--rim), 0 0 42px 12px var(--rim);
}
.res.anim .res-shock{ animation:resShock .78s cubic-bezier(.15,.75,.3,1) .16s both; }

.res-word{
  position:relative; display:block; margin:0;   /* it is an <h2>: kill the UA margin */
  font:900 clamp(46px,8.4vw,132px)/0.9 var(--display);
  letter-spacing:.005em; text-transform:uppercase;
  color:var(--v-mid);                          /* fallback if text-clip fails */
  -webkit-text-stroke:.07em var(--outline); paint-order:stroke fill;
  background:linear-gradient(180deg,var(--v-hi) 8%,var(--v-mid) 50%,var(--v-lo) 96%);
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent;
  filter:drop-shadow(0 .06em 0 rgba(3,6,16,.85)) drop-shadow(0 .1em .12em rgba(0,0,0,.5));
}
.res-sub{
  position:relative; z-index:2; margin:10px 0 0;
  font:900 clamp(13px,1.35vw,20px)/1.2 var(--display);
  letter-spacing:.14em; text-transform:uppercase;
  color:var(--v-sub); text-shadow:0 2px 0 rgba(0,0,0,.65);
}
.res.anim .res-sub{ animation:resRise .44s ease-out .46s both; }

/* ── the cast: you, the mascot, the rival ─────────────────── */
.res-cast{
  position:relative; z-index:2; margin-top:6px;
  display:grid; grid-template-columns:1fr auto 1fr; align-items:end;
  gap:clamp(10px,2.4vw,42px); width:min(100%,1080px);
}

.res-figure{ position:relative; display:flex; justify-content:center; align-items:flex-end;
             padding-bottom:14px; }
.res-mascot{ position:relative; z-index:2; display:block; }
.res-mascot svg,
.res-mascot .res-art{
  display:block; width:auto; height:clamp(168px,25vh,318px);
  /* a REAL contact shadow. This used to be drop-shadow(… 0 6px 0),
     a zero-blur sticker offset, which is why the figure read as
     pasted onto the panel rather than standing on it. On the painted
     poses the same filter follows the alpha silhouette, so the
     tentacles cast the shadow rather than a bounding box. */
  filter:drop-shadow(0 16px 14px rgba(2,5,14,.62))
         drop-shadow(0 5px 4px rgba(2,5,14,.6));
}
/* The painted canvas carries headroom the drawn mascot never had —
   confetti, sparks and a pearl held aloft all live above the head,
   so the octopus itself is about 90% of the file's height against
   the SVG's ~100%. Measured on the delivered set: art heights run
   428-543 in a 571 canvas. The 1.10 here buys that back so the two
   sources read at the same size, and is the ONLY reason these
   numbers differ from the rule above. */
.res-mascot .res-art{ height:clamp(185px,27.5vh,350px); }
.res.anim .res-mascot{ animation:resPop .58s cubic-bezier(.2,1.5,.4,1) .2s both,
                                 resBob 3.6s ease-in-out 1.0s infinite; }
.res.anim[data-mood="lost"] .res-mascot{
  animation:resSlump .66s cubic-bezier(.3,1.1,.4,1) .2s both,
            resSag 5.4s ease-in-out 1.1s infinite; }

/* the pool of light it stands in, and the tight ambient occlusion
   where the body meets the floor. Soft gradient stops rather than
   filter:blur() — same look, no per-frame blur pass. */
.res-pool{
  /* the 92vw term is the phone cap: 132% of a narrow figure still ran
     ~40px past the screen edge on a 375px handset, where .res clipped
     the far side of the disc and left the rim looking sliced */
  position:absolute; left:50%; bottom:0; width:min(132%,450px,92vw); height:96px;
  transform:translateX(-50%); z-index:0; opacity:.6; pointer-events:none;
  border-radius:50%;
  background:
    radial-gradient(ellipse 50% 50% at 50% 52%,
      color-mix(in srgb, var(--pool) 96%, transparent) 0%,
      color-mix(in srgb, var(--pool) 42%, transparent) 40%, transparent 70%);
}
/* the disc rim. Every reference card puts the character on a lit
   platform; without an edge the pool reads as fog, not as floor. */
.res-pool::after{
  content:""; position:absolute; inset:24% 12%; border-radius:50%;
  border:3px solid color-mix(in srgb, var(--rim) 85%, transparent);
  box-shadow:0 0 18px color-mix(in srgb, var(--rim) 45%, transparent);
  opacity:.85;
}
.res-ao{
  position:absolute; left:50%; bottom:14px; width:min(60%,206px); height:32px;
  transform:translateX(-50%); z-index:1; pointer-events:none;
  background:radial-gradient(ellipse 50% 50% at 50% 50%,
    rgba(2,4,12,.96) 0%, rgba(2,4,12,.66) 40%, rgba(2,4,12,0) 72%);
}
.res.anim .res-pool{ animation:resPool .5s ease-out .28s backwards,
                               resPoolPulse 3.8s ease-in-out 1.2s infinite; }
.res.anim .res-ao{ animation:resRise .4s ease-out .3s both; }

/* the two flanking cards. Same shape either side so the eye reads
   them as a pair; the tier tints the frame. Coming last of eight
   used to award the victory-gold plinth. */
.res-card{
  position:relative; display:flex; flex-direction:column; align-items:center; gap:2px;
  padding:14px 12px 12px; min-width:0;
  background:linear-gradient(180deg,#1d3f7a,#0a1631);
  clip-path:polygon(16px 0,calc(100% - 16px) 0,100% 16px,100% calc(100% - 16px),
                    calc(100% - 16px) 100%,16px 100%,0 calc(100% - 16px),0 16px);
  --edge:var(--outline); --lift:0 6px 0 rgba(3,6,16,.6);
  filter:
    drop-shadow(2.5px 0 0 var(--edge))  drop-shadow(-2.5px 0 0 var(--edge))
    drop-shadow(0 2.5px 0 var(--edge))  drop-shadow(0 -2.5px 0 var(--edge))
    drop-shadow(var(--lift)) drop-shadow(0 14px 26px rgba(0,0,0,.5));
}
.res-card.ghost{ visibility:hidden; }
.res-card.gold  { --edge:#ffc63d; background:linear-gradient(180deg,#3a2a06,#140c00); }
.res-card.silver{ --edge:#d5e4f2; background:linear-gradient(180deg,#26313f,#0d1219); }
.res-card.bronze{ --edge:#e09a52; background:linear-gradient(180deg,#3a2412,#150c04); }
.res-card.steel { --edge:#4a6d9e; background:linear-gradient(180deg,#17284c,#080f22); }
.res.anim .res-card.mine { animation:resCardL .5s cubic-bezier(.2,1.2,.4,1) .34s both; }
.res.anim .res-card.rival{ animation:resCardR .5s cubic-bezier(.2,1.2,.4,1) .42s both; }

.res-card .lbl{
  font:800 11px/1 var(--ui); letter-spacing:.2em; text-transform:uppercase;
  color:var(--r-label);
}
.res-card.gold   .lbl{ color:#ffd97a; }
.res-card.silver .lbl{ color:#e2eefb; }
.res-card.bronze .lbl{ color:#f0b579; }
.res-card .who{ display:flex; align-items:center; justify-content:center; gap:8px;
                max-width:100%; margin-top:4px; }
.res-card .nm{
  font:900 clamp(17px,1.55vw,24px)/1.1 var(--display); letter-spacing:.03em;
  text-transform:uppercase; color:var(--ink); white-space:nowrap;
  overflow:hidden; text-overflow:ellipsis;
  -webkit-text-stroke:3px var(--outline); paint-order:stroke fill;
}
.res-card .ord{
  display:block; margin-top:2px; line-height:.88;
  font:900 clamp(34px,3.6vw,64px)/0.88 var(--display); letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;
  color:var(--edge); -webkit-text-stroke:.07em var(--outline); paint-order:stroke fill;
  filter:drop-shadow(0 4px 0 rgba(3,6,16,.75));
}
.res-card .ord sup{ font-size:.42em; vertical-align:super; letter-spacing:0; }
.res-card .sub{ margin-top:4px; font:800 11px/1.2 var(--ui); letter-spacing:.16em;
                text-transform:uppercase; color:var(--r-label); }
.res-seat{
  width:18px; height:18px; flex:none; border-radius:50%;
  box-shadow:0 0 0 2.5px var(--outline), 0 0 12px rgba(0,0,0,.6);
}

/* ── the support grid ────────────────────────────────────────
   Two columns that END TOGETHER. They used to finish 89px apart
   with no shared baseline; `.panel.grow` eats the difference. */
.res-grid{
  flex:0 0 auto; display:grid; align-items:stretch; gap:16px;
  grid-template-columns:minmax(300px,.88fr) minmax(0,1.32fr);
}
.res-col{ display:flex; flex-direction:column; gap:14px; min-width:0; }
.res-col > .panel{ display:flex; flex-direction:column; }
.res-col > .panel.grow{ flex:1 1 auto; }
.res.anim .res-col > .panel{ animation:resRise .5s ease-out both; }
.res.anim .res-col:first-child > .panel:nth-child(1){ animation-delay:.44s; }
.res.anim .res-col:first-child > .panel:nth-child(2){ animation-delay:.52s; }
.res.anim .res-col:last-child  > .panel{ animation-delay:.40s; }

/* ── highlights ──────────────────────────────────────────── */
.hi{ margin:0; display:grid; grid-template-columns:1fr auto; gap:11px 14px; align-items:center; }
.hi dt{ margin:0; font:800 11px/1.25 var(--ui); letter-spacing:.14em;
        text-transform:uppercase; color:var(--r-label); }
.hi dd{ margin:0; text-align:right; font:900 20px/1.1 var(--display);
        letter-spacing:.02em; color:var(--ink); text-shadow:0 2px 0 rgba(0,0,0,.5);
        font-variant-numeric:tabular-nums; }
.hi dd.gold{ color:var(--gold); } .hi dd.lime{ color:var(--lime); }
.hi dd.rose{ color:var(--r-rose); }
.hi dd small{ display:block; margin-top:3px; font:700 11px/1 var(--ui);
              letter-spacing:.06em; color:var(--r-label); }
.hi .sep{ grid-column:1 / -1; height:0; border-top:2px solid var(--r-line); }

/* ── standings ───────────────────────────────────────────── */
.res-table-wrap{ flex:1 1 auto; overflow:auto; min-height:0; margin:0 -4px; padding:0 4px;
                 scrollbar-width:thin; scrollbar-color:#2c4a86 transparent; }
.res-table-wrap::-webkit-scrollbar{ width:10px; }
.res-table-wrap::-webkit-scrollbar-thumb{ background:#2c4a86; border-radius:99px; }

table.res-table{
  width:100%; border-collapse:separate; border-spacing:0 6px;
  font:800 14px/1 var(--ui); font-variant-numeric:tabular-nums;
}
table.res-table thead th{
  position:sticky; top:0; z-index:2;
  padding:6px 10px 8px; text-align:right; white-space:nowrap;
  background:linear-gradient(180deg,#16295a,#12234d);
  font:800 11px/1.2 var(--ui); letter-spacing:.14em; text-transform:uppercase;
  color:var(--r-label);
}
table.res-table thead th:nth-child(1){ text-align:center; width:56px; }
table.res-table thead th:nth-child(2){ text-align:left; }

table.res-table tbody td{
  padding:8px 10px; background:var(--r-row); text-align:right; white-space:nowrap;
  color:var(--ink); text-shadow:0 1px 0 rgba(0,0,0,.55);
  border-top:2px solid var(--outline); border-bottom:2px solid var(--outline);
}
table.res-table tbody td:first-child{ border-left:2px solid var(--outline); width:56px; text-align:center; }
table.res-table tbody td:last-child{ border-right:2px solid var(--outline); }
table.res-table tbody td.name{ text-align:left; width:99%; }

/* the seat colour is the row's identity: a tinted bar plus a solid chip */
/* a FLAT tint, not a gradient: `background` is painted per-cell, so a
   left-to-right wash restarts in every column and the row reads striped */
table.res-table tbody tr.seat td{
  background:linear-gradient(0deg, var(--seat-wash), var(--seat-wash)), var(--r-row); }
table.res-table tbody tr.seat td:first-child{ box-shadow:inset 5px 0 0 var(--seat); }

/* eight rows landing one after another. --i is written per row by
   results.js; the whole run is over in 0.8s. */
.res.anim table.res-table tbody tr{
  animation:resRow .4s cubic-bezier(.2,.85,.3,1) both;
  animation-delay:calc(.46s + var(--i,0) * .045s);
}

.rank-chip{
  display:inline-grid; place-items:center; width:32px; height:32px;
  background:linear-gradient(180deg,#2a4780,#16294f); color:var(--ink);
  font:900 15px/1 var(--display);
  clip-path:polygon(50% 0,100% 27%,100% 73%,50% 100%,0 73%,0 27%);
  box-shadow:0 0 0 2px var(--outline);
}
tr.m1 .rank-chip{ background:linear-gradient(180deg,#ffe08a,#d1811a); color:#2a1500; }
tr.m2 .rank-chip{ background:linear-gradient(180deg,#eaf4ff,#8ba3bd); color:#141c2b; }
tr.m3 .rank-chip{ background:linear-gradient(180deg,#f7b479,#a75f22); color:#2a1200; }

/* flex-wrap is doing the real work, and the reason is worth writing
   down because `min-width:0` alone LOOKS like it should be enough and
   is not: min-width:0 removes an item's automatic minimum, but the
   item's MIN-CONTENT CONTRIBUTION — which is what the table's column
   algorithm actually reads — is still the longest unbreakable word.
   "DRIFTWOOD" therefore held the PLAYER column at 242px and the three
   surviving columns summed to 344px inside a 308px panel on a 360px
   phone, where .res-table-wrap is deliberately overflow:visible so the
   spill was simply cut off. A single-line flex container's min-content
   is the SUM of its items; a wrapping one's is the WIDEST item, which
   is what makes the column collapsible. Same fix, same reason, in
   lobby.css — the two tables are deliberate siblings. */
.who-cell{ display:flex; align-items:center; flex-wrap:wrap;
           gap:4px 10px; min-width:0; }
.who-cell .dot{ width:18px; height:18px; flex:none; border-radius:50%;
                box-shadow:0 0 0 2px var(--outline); }
/* min-width:0 is what lets the ellipsis on the next line fire. Without
   it the flex item's automatic minimum is the whole name, so a nine
   letter wall ("DRIFTWOOD") set an unshrinkable floor and the three
   surviving columns ran 14px past a 360px screen — with .res-table-wrap
   deliberately overflow:visible on phones, that 14px was simply gone. */
.who-cell .nm{ font:900 15px/1.15 var(--display); letter-spacing:.03em;
               min-width:0;
               text-transform:uppercase; overflow:hidden; text-overflow:ellipsis; }
.who-cell .pn{ flex:none; font:700 11px/1.2 var(--ui); letter-spacing:.1em; color:var(--r-label);
               text-transform:uppercase; }
.you-tag{
  flex:none; padding:3px 8px; background:linear-gradient(180deg,var(--gold),var(--gold-lo));
  color:#2a1500; font:900 11px/1 var(--ui); letter-spacing:.14em;
  clip-path:polygon(5px 0,100% 0,calc(100% - 5px) 100%,0 100%);
  box-shadow:0 0 0 2px var(--outline);
}

/* your row — the one gold bar on the table */
table.res-table tbody tr.me td{ background:linear-gradient(180deg,#ffd766,#e9a72c); color:#2a1500;
          text-shadow:0 1px 0 rgba(255,255,255,.35);
          border-top-color:#7a4a00; border-bottom-color:#7a4a00; }
table.res-table tbody tr.me td:first-child{ border-left-color:#7a4a00; box-shadow:inset 5px 0 0 var(--seat); }
table.res-table tbody tr.me td:last-child{ border-right-color:#7a4a00; }
tr.me .who-cell .pn{ color:#5c3300; }
tr.me .rank-chip{ background:linear-gradient(180deg,#2a1500,#150a00); color:var(--gold-hi);
                  box-shadow:0 0 0 2px #7a4a00; }
tr.me .you-tag{ background:linear-gradient(180deg,#3b1e00,#1f0f00); color:var(--gold-hi);
                box-shadow:0 0 0 2px #7a4a00; }
table.res-table tbody tr.out td{ opacity:.96; }

/* ── stats strip ─────────────────────────────────────────── */
.stat-grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(112px,1fr)); gap:10px;
            flex:1 1 auto; align-content:center; }
.stat{
  position:relative; padding:11px 10px 9px; text-align:center;
  display:flex; flex-direction:column; align-items:center; gap:3px;
  background:linear-gradient(180deg,#1c3667,#101f42);
  clip-path:polygon(9px 0,calc(100% - 9px) 0,100% 9px,100% calc(100% - 9px),
                    calc(100% - 9px) 100%,9px 100%,0 calc(100% - 9px),0 9px);
  --edge:var(--outline); --lift:0 4px 0 rgba(3,6,16,.55);
  filter:
    drop-shadow(2px 0 0 var(--edge))  drop-shadow(-2px 0 0 var(--edge))
    drop-shadow(0 2px 0 var(--edge))  drop-shadow(0 -2px 0 var(--edge))
    drop-shadow(var(--lift));
}
.res.anim .stat{ animation:resTile .36s cubic-bezier(.2,1.3,.4,1) both;
                 animation-delay:calc(.6s + var(--i,0) * .05s); }
.stat .i{
  display:grid; place-items:center; width:26px; height:26px; margin-bottom:1px;
  background:linear-gradient(180deg,#2b4d8f,#16305f); border-radius:6px;
  font-size:14px; line-height:1; color:var(--ink);
  box-shadow:inset 0 0 0 2px rgba(0,0,0,.4);
}
.stat .i svg{ display:block; width:18px; height:18px; }
.res .btn .ico svg{ display:block; width:20px; height:20px; }
.res-foot .btn.play > span:first-child svg{ display:block; width:26px; height:26px; }
/* tabular figures + a floor width, so a number counting up from 0
   cannot make its tile breathe in and out on the way */
.stat .v{ font:900 24px/1 var(--display); letter-spacing:.01em; color:var(--gold);
          text-shadow:0 2px 0 rgba(0,0,0,.6);
          font-variant-numeric:tabular-nums; min-width:2.6ch; }
.stat .k{ font:800 11px/1.25 var(--ui); letter-spacing:.13em; text-transform:uppercase;
          color:var(--r-label); }
.stat.aqua .v{ color:var(--aqua-hi); }
.stat.lime .v{ color:var(--lime); }
.stat.rose .v{ color:var(--r-rose); }

/* ── footer actions ──────────────────────────────────────── */
.res-foot{
  display:flex; flex-direction:column; align-items:center; gap:2px;
  padding:6px 22px 14px; zoom:var(--op-ui-scale,1);
}
.res-acts{ display:flex; gap:12px; justify-content:center; flex-wrap:wrap; }
.res-foot .btn{ padding:13px 30px 13px 20px; font-size:clamp(14px,1.15vw,17px); }
.res-foot .btn.play{
  padding:13px 36px 13px 24px; font-size:clamp(17px,1.7vw,23px); gap:12px;
  clip-path:polygon(0 0, calc(100% - 20px) 0, 100% 50%, calc(100% - 20px) 100%, 0 100%);
}
.res-foot .btn .ico{ width:28px; height:28px; font-size:15px; }
.res.anim .res-acts .btn{ animation:resRise .4s ease-out both; }
.res.anim .res-acts .btn:nth-child(1){ animation-delay:.66s; }
.res.anim .res-acts .btn:nth-child(2){ animation-delay:.72s; }
.res.anim .res-acts .btn:nth-child(3){ animation-delay:.78s; }
.res.anim .res-acts .btn:nth-child(4){ animation-delay:.84s; }
.res-say{
  margin:6px 0 0; width:100%; text-align:center; min-height:15px;
  font:800 12px/1.3 var(--ui); letter-spacing:.1em; text-transform:uppercase; color:var(--gold);
}
/* the seed is diagnostic. It belongs beside COPY RESULT, not above
   the match outcome where it used to sit. */
.res-seed{ margin:0; font:700 10.5px/1 var(--ui); letter-spacing:.22em;
           text-transform:uppercase; color:#6f90b4; }

/* ── motion ──────────────────────────────────────────────────
   Everything here animates TRANSFORM and OPACITY only. Nothing
   touches a layout property, because this screen has to hold 60fps
   with a WebGL arena still rendering behind it.

   The inventory, so the next person can see it without reading the
   whole file: body fade, verdict entrance (two flavours), impact
   ring, subtitle rise, mascot entrance (two flavours) plus an idle
   loop, light pool grow plus a slow pulse, two card slides, panel
   rises, an eight-row stagger, a six-tile stagger, four button
   rises, nine looping bubbles and — on a win only — a slow ray
   rotation. The numbers themselves count up in results.js.
   ─────────────────────────────────────────────────────────── */
@keyframes resFade{ from{ opacity:0 } to{ opacity:1 } }

/* anticipation, overshoot, settle */
@keyframes resBanner{
  0%  { opacity:0; transform:scale(1.55) translateY(-30px) rotate(-5deg); }
  55% { opacity:1; transform:scale(.94) translateY(0) rotate(1.6deg); }
  76% { transform:scale(1.045) rotate(-1deg); }
  100%{ opacity:1; transform:none; }
}
/* a loss lands heavier and later, and does not spring back as far */
@keyframes resBannerLost{
  0%  { opacity:0; transform:scale(1.3) translateY(-52px) rotate(3.5deg); }
  46% { opacity:1; transform:scale(1.03) translateY(8px) rotate(-1.6deg); }
  70% { transform:scale(.985) translateY(-4px) rotate(.7deg); }
  100%{ opacity:1; transform:none; }
}
@keyframes resShock{
  0%  { opacity:0; transform:translate(-50%,-50%) scale(.22); }
  14% { opacity:.8; }
  100%{ opacity:0; transform:translate(-50%,-50%) scale(2.7); }
}
@keyframes resRise{ from{ opacity:0; transform:translateY(16px) } to{ opacity:1; transform:none } }
@keyframes resRow { from{ opacity:0; transform:translateY(14px) } to{ opacity:1; transform:none } }
@keyframes resTile{ from{ opacity:0; transform:scale(.84) } to{ opacity:1; transform:none } }
@keyframes resCardL{ from{ opacity:0; transform:translateX(-34px) scale(.93) } to{ opacity:1; transform:none } }
@keyframes resCardR{ from{ opacity:0; transform:translateX(34px) scale(.93) } to{ opacity:1; transform:none } }

@keyframes resPop{
  0%  { opacity:0; transform:translateY(30px) scale(.6); }
  62% { opacity:1; transform:translateY(-8px) scale(1.07); }
  82% { transform:translateY(3px) scale(.985); }
  100%{ opacity:1; transform:none; }
}
/* the loser drops in, squashes on landing, and does not bounce */
@keyframes resSlump{
  0%  { opacity:0; transform:translateY(-34px) scaleX(.96) scaleY(1.06); }
  58% { opacity:1; transform:translateY(8px) scaleX(1.07) scaleY(.9); }
  80% { transform:translateY(-2px) scaleX(.99) scaleY(1.02); }
  100%{ opacity:1; transform:none; }
}
@keyframes resBob{ 0%,100%{ transform:translateY(0) rotate(-1.1deg) }
                   50%    { transform:translateY(-10px) rotate(1.1deg) } }
@keyframes resSag{ 0%,100%{ transform:translateY(0) rotate(.5deg) }
                   50%    { transform:translateY(5px) rotate(-.5deg) } }

@keyframes resPool{ from{ opacity:0; transform:translateX(-50%) scaleX(.3) }
                    to  { opacity:.6; transform:translateX(-50%) scaleX(1) } }
@keyframes resPoolPulse{ 0%,100%{ opacity:.5 } 50%{ opacity:.74 } }

@keyframes resBub{
  0%  { opacity:0; transform:translate3d(0,0,0) scale(.55); }
  12% { opacity:.55; }
  78% { opacity:.34; }
  100%{ opacity:0; transform:translate3d(16px,-300px,0) scale(1); }
}
@keyframes resSpin{ from{ transform:translate(-50%,-50%) rotate(0) }
                    to  { transform:translate(-50%,-50%) rotate(360deg) } }

/* ── focus. clip-path eats `outline`, so light up the edge. ── */
/* :focus, not :focus-visible — see the note in pause.css */
.res :is(.btn,.pill):focus{
  outline:none; --edge:#8ff9ee;
  /* the inset ring is the guaranteed one — clip-path cannot eat it */
  box-shadow:inset 0 0 0 3px #8ff9ee, inset 0 0 0 7px rgba(2,8,18,.92);
  filter:
    drop-shadow(3px 0 0 var(--edge))  drop-shadow(-3px 0 0 var(--edge))
    drop-shadow(0 3px 0 var(--edge))  drop-shadow(0 -3px 0 var(--edge))
    drop-shadow(0 0 16px rgba(143,249,238,.95))
    drop-shadow(var(--lift));
}
.res .res-table-wrap:focus{ outline:3px solid #8ff9ee; outline-offset:2px; }

/* ── accessibility flags, driven by the pause screen ─────── */
.seat-glyph{ display:none; margin-left:5px; font-size:.85em; }
html.op-a11y-glyphs .res .seat-glyph{ display:inline; }

html.op-a11y-contrast .res::before{ background:rgba(3,5,14,.97); backdrop-filter:none; }
html.op-a11y-contrast .res .panel{ background:linear-gradient(180deg,#0f1f42,#060d22); }
html.op-a11y-contrast .res .btn::after{ display:none; }
html.op-a11y-contrast .res{ --r-label:#dff7ff; --r-row:#08132c; }
html.op-a11y-contrast .res .res-rays,
html.op-a11y-contrast .res .res-bubbles{ display:none; }

/* Everything above degrades to an instant, static, correct screen.
   `.res-body` is opacity-gated on `.anim`, and results.js always
   adds `.anim` — so killing the animations leaves it visible at its
   finished state rather than blank. The count-up checks the same
   two conditions in JS and jumps straight to the final numbers. */
html.op-a11y-still .res *,
html.op-a11y-still .res *::before,
html.op-a11y-still .res *::after{ transition:none !important; animation:none !important; }
html.op-a11y-still .res .btn:hover{ transform:none; }

@media (prefers-reduced-motion:reduce){
  .res *,.res *::before,.res *::after{ transition:none !important; animation:none !important; }
  .res .btn:hover{ transform:none; }
}

/* ── short and narrow: ONE scroller, not three ───────────────
   At 1400x560 the two columns used to hide 289px and 218px of
   content inside nested scrollers with no visible affordance, and
   the document itself did not scroll. At 375x812, 76% of the body
   sat behind a nested scroller and reaching your own row meant
   finding a SECOND scroller inside the table.

   Below, the screen itself is the only scroller, the header and the
   action row are sticky, and the table wrap stops clipping. Note
   that `_revealMe()` and the smoke test both compare the local row
   against the WRAP's rect — with the wrap grown to fit the table,
   the row is inside it by construction, so both still hold. */
@media (max-height:900px), (max-width:1180px){
  .res{ display:block; overflow-y:auto; overflow-x:hidden; overscroll-behavior:contain;
        scrollbar-width:thin; scrollbar-color:#3a63aa transparent; }
  .res::-webkit-scrollbar{ width:12px; }
  .res::-webkit-scrollbar-thumb{ background:#3a63aa; border-radius:99px; }
  .res::before{ position:fixed; }
  .res-top{ position:sticky; top:0; z-index:8;
            background:linear-gradient(180deg,rgba(5,9,24,.97),rgba(5,9,24,.72) 78%,rgba(5,9,24,0)); }
  .res-body{ overflow:visible; min-height:0; }
  .res-stage{ flex:0 0 auto; padding:10px 0 4px; }
  .res-table-wrap{ max-height:none; overflow:visible; }
  .res-foot{
    position:sticky; bottom:0; z-index:8; padding-top:12px;
    /* The transparent top third let MATCH HIGHLIGHTS text show between
       the wrapped buttons mid-scroll on a phone — reads as unfinished.
       A short fade is enough softness; the body must be solid.
       THE STOP IS IN PIXELS, NOT PER CENT, and that is the fix: 14% of
       a foot that has wrapped to three rows on a 375px phone is a 26px
       transparent band, and PLAY AGAIN sits inside it — screenshotted
       with the YOUR MATCH tile labels reading straight through the
       gold. A fixed 10px fade is the same softness at every foot
       height, which is the whole point of a sticky bar. */
    background:linear-gradient(180deg,rgba(5,9,24,0) 0,rgba(5,9,24,.97) 10px,rgba(5,9,24,1) 100%);
  }
}

/* ── narrow ──────────────────────────────────────────────── */
@media (max-width:1180px){
  .res-grid{ grid-template-columns:1fr; }
  .res-mascot svg{ height:clamp(150px,22vh,240px); }
  .res-mascot .res-art{ height:clamp(165px,24vh,264px); }
}
@media (max-width:900px){
  /* chips are duplicated in the brand subline; on a phone they are
     just a band of debug text above the outcome */
  .res-tags{ display:none; }
  .res-cast{ grid-template-columns:1fr auto 1fr; gap:8px; }
  .res-card{ padding:10px 8px 9px; }
}
@media (max-width:640px){
  .res-banner{ padding:12px 24px 16px; }
  .res-word{ font-size:clamp(52px,15vw,74px); }
  .res-sub{ font-size:12px; letter-spacing:.1em; }
  .res-cast{ grid-template-columns:1fr; gap:12px; }
  .res-figure{ order:-1; }
  .res-card{ flex-direction:row; justify-content:space-between; align-items:center;
             padding:8px 14px; gap:10px; }
  .res-card .ord{ margin-top:0; font-size:32px; }
  .res-card .sub{ display:none; }
  .res-body,.res-top,.res-foot{ padding-left:12px; padding-right:12px; }
  .res-acts{ gap:8px; }
  .res-foot .btn{ padding:11px 22px 11px 14px; }
  table.res-table thead th:nth-child(n+4),
  table.res-table tbody td:nth-child(n+4){ display:none; }
  table.res-table tbody td:nth-child(3){ border-right:2px solid var(--outline); }
}

/* ── the two secondary stat columns ──────────────────────────
   Taken and Best are context, not the headline: KOs / Deflects /
   Damage are what the placement was earned with. Dimming them keeps
   the eye on the first three while the numbers stay available.
   Measured, not assumed: 10.64:1 on the dark seat plates, and 4.89:1
   against the dark end of the gold YOU-row gradient, which is the
   worst case in the table. Both clear AA for normal text. Note that
   a gradient reports background-color rgba(0,0,0,0), so an automated
   contrast sweep that walks up for an opaque parent will score this
   row about 1.1:1 and cry wolf — measure against the endpoints. */
table.res-table tbody td.dim{ color:#a9c6e8; }
table.res-table tbody tr.me td.dim{ color:#5b3a05; }

/* Seven columns do not fit a narrow pane. Shed the secondary pair
   first, then fall back to the existing three-column rule at 640. */
@media (max-width:980px){
  table.res-table thead th:nth-child(n+6),
  table.res-table tbody td:nth-child(n+6){ display:none; }
  table.res-table tbody td:nth-child(5){ border-right:2px solid var(--outline); }
}

/* The picked character on a standings row. Sized to the seat dot it
   sits beside so the pair reads as one identity chip, and ringed in
   the seat colour so the face never floats free of the colour that
   actually identifies the player. */
.res-face{
  width:26px; height:26px; flex:0 0 auto;
  border-radius:50%; object-fit:cover;
  background:rgba(8,14,32,.6);
  box-shadow:0 0 0 2px var(--outline,#050814),
             0 0 0 4px var(--seat,#8fb3c9);
  margin-right:2px;
}
@media (prefers-reduced-motion:reduce){ .res-face{ transition:none; } }

/* ── medal numerals: light emboss, not a black smear ─────────
   `table.res-table tbody td` sets `text-shadow:0 1px 0 rgba(0,0,0,.55)`
   for the light-on-dark rows, and the three medal chips inherited it
   while overriding their ink to near-black on gold/silver/bronze. Dark
   on dark is not a shadow, it is a doubled glyph — same family as the
   .panel-head fix in theme.css. Fills are unchanged (they measure
   12.9:1, 11.4:1 and 9.8:1 on their own plates); only the shadow flips. */
table.res-table tbody tr:is(.m1,.m2,.m3) .rank-chip{
  text-shadow:0 1px 0 rgba(255,255,255,.45);
}
