:root{
  --bg:#0A0B10;
  --surface:#14161F;
  --surface-2:#1B1E29;
  --line:#272B38;
  --line-strong:#3A4055;
  --text:#EEF0F6;
  --text-dim:#9AA1B4;
  --text-faint:#7B8296;
  --accent:#F97316;
  --accent-ink:#1A0E03;
  --focus:#5B9BFF;
  --gold:#F0C24B;
  --silver:#C7CCD8;
  --bronze:#C4834E;
  --r-tile:18px;
  --shot:440/524;   /* crop aspect */
  --gap:14px;
  --ease:cubic-bezier(.22,.61,.36,1);
  --dur:220ms;
  --font-display:"Archivo",ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
  --font-body:"Public Sans",ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
}
*{box-sizing:border-box}
/* the UA rule for [hidden] is display:none, which any class-based display value
   silently beats. Without this, hidden elements with a display rule still show. */
[hidden]{display:none !important}
html,body{margin:0;padding:0}
body{
  min-height:100dvh;
  background:var(--bg);
  color:var(--text);
  font-family:var(--font-body);
  font-size:16px;
  line-height:1.5;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  overflow-x:hidden;
}
body::before{
  content:"";position:fixed;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(900px 500px at 12% -10%, rgba(249,115,22,.10), transparent 60%),
    radial-gradient(1000px 600px at 92% 0%, rgba(91,155,255,.10), transparent 62%);
}
.wrap{position:relative;z-index:1;max-width:1560px;margin:0 auto;padding:0 clamp(14px,2.4vw,32px) 72px}

/* ---------- header ---------- */
header{
  position:sticky;top:0;z-index:60;
  margin:0 calc(-1 * clamp(14px,2.4vw,32px));
  padding:18px clamp(14px,2.4vw,32px) 16px;
  background:rgba(10,11,16,.82);
  backdrop-filter:blur(14px) saturate(160%);
  -webkit-backdrop-filter:blur(14px) saturate(160%);
  border-bottom:1px solid var(--line);
}
.head-row{display:flex;align-items:flex-end;gap:24px;flex-wrap:wrap}
.brand{min-width:0;flex:1 1 320px}
.eyebrow{
  font-family:var(--font-display);font-size:11px;font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;color:var(--accent);margin:0 0 5px
}
h1{
  font-family:var(--font-display);font-weight:700;
  font-size:clamp(22px,2.7vw,34px);line-height:1.1;letter-spacing:-.02em;margin:0
}
.sub{margin:7px 0 0;font-size:13.5px;color:var(--text-dim);max-width:62ch}
.sub kbd{
  font:inherit;font-size:11.5px;font-weight:600;
  background:var(--surface-2);border:1px solid var(--line-strong);border-bottom-width:2px;
  border-radius:5px;padding:1px 5px;color:var(--text);
}
.actions{display:flex;gap:9px;flex-wrap:wrap;align-items:center}
.hint-coarse{display:none}
@media (pointer:coarse){.hint-fine{display:none}.hint-coarse{display:inline}}

button{font-family:inherit;cursor:pointer}
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  min-height:44px;padding:0 16px;border-radius:11px;
  font-size:14px;font-weight:600;letter-spacing:-.005em;
  border:1px solid var(--line-strong);background:var(--surface-2);color:var(--text);
  transition:background var(--dur) var(--ease),border-color var(--dur) var(--ease),
             transform 120ms var(--ease),color var(--dur) var(--ease);
  white-space:nowrap;
}
.btn:hover{background:#232739;border-color:#4A5169}
.btn:active{transform:translateY(1px)}
.btn svg{width:17px;height:17px;flex:none;stroke-width:2}
.btn-primary{
  background:var(--accent);border-color:var(--accent);color:var(--accent-ink);
  box-shadow:0 6px 18px -10px rgba(0,0,0,.9)
}
.btn-primary:hover{background:#FB8B3C;border-color:#FB8B3C}
.btn-icon{padding:0 13px;min-width:44px}
.btn[aria-pressed="true"]{background:rgba(91,155,255,.16);border-color:#4A6FA8;color:#CFE0FF}
:where(button,[tabindex],a):focus-visible{outline:3px solid var(--focus);outline-offset:2px;border-radius:11px}

/* ---------- grid ---------- */
.board{
  display:grid;grid-template-columns:repeat(5,minmax(0,1fr));
  gap:var(--gap);margin-top:22px;justify-content:center;
}
body.fit .board{grid-template-columns:repeat(5,var(--tile-w,180px))}
@media (max-width:1180px){
  body.fit .board{grid-template-columns:repeat(4,var(--tile-w,120px))}
}
/* Until rank.js measures the screen there is no --tile-w, and the fixed fallbacks
   above would run off the side of a phone. While the placeholders are still up,
   share the row out evenly instead; the inline style from sizeFit() then wins. */
body.fit .board:has(.skel){grid-template-columns:repeat(4,minmax(0,1fr))}

/* ---------- invite prompt ---------- */
/* Only appears for someone who arrived on a mate's link. It is the reason they
   are here, so it reads as a challenge rather than a notice. */
.invite{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  margin:10px 0 0;padding:10px 13px;
  background:rgba(249,115,22,.10);
  border:1px solid rgba(249,115,22,.34);
  border-radius:12px;
  font-size:14px;line-height:1.35;color:var(--text);
}
.invite svg{width:17px;height:17px;stroke-width:2;flex:none;color:var(--accent)}
.invite strong{font-weight:700}
.invite-go{
  margin-left:auto;display:inline-flex;align-items:center;min-height:36px;padding:0 13px;
  border-radius:9px;background:var(--accent);color:var(--accent-ink);
  font-size:13.5px;font-weight:700;text-decoration:none;white-space:nowrap;
}
.invite-go:hover{filter:brightness(1.07)}
@media (max-width:700px){
  .invite{font-size:13px;padding:8px 11px;gap:8px}
  .invite-go{margin-left:0;width:100%;justify-content:center}
}
/* Tighten it in fit mode, but never hide the tail. In fit the header's own
   instruction line is already hidden, so this sentence is the only thing on a
   phone that explains why a stranger's link is worth finishing — which is
   exactly the visitor it was written for. It costs one line of grid height and
   KitList.refit() gives that back. */
@media (max-width:860px){
  body.fit .invite{margin-top:8px;padding:7px 10px;font-size:12.5px}
}

/* ---------- first-paint skeleton ---------- */
/* Geometry copied from .tile/.shot/.club so nothing jumps when the real tiles
   replace these. Removed by render() on the first frame the script runs. */
.skel{
  border-radius:var(--r-tile);border:1px solid var(--line);
  background:var(--surface);overflow:hidden;position:relative;
}
.skel i{display:block;width:100%;aspect-ratio:var(--shot);background:#0E1017}
.skel u{display:block;min-height:40px}
body.fit .skel u{display:none}
.skel::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(100deg,transparent 22%,rgba(255,255,255,.05) 45%,transparent 68%);
  transform:translateX(-100%);
  animation:skel-sweep 1.3s var(--ease) infinite;
}
@keyframes skel-sweep{to{transform:translateX(100%)}}

@media (prefers-reduced-motion:reduce){
  .skel::after{animation:none}
}

.tile{
  position:relative;display:flex;flex-direction:column;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-tile);
  overflow:hidden;cursor:grab;user-select:none;-webkit-user-select:none;
  touch-action:manipulation;-webkit-tap-highlight-color:transparent;
  transition:border-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease),
             background var(--dur) var(--ease);
  will-change:transform;
}
.tile:hover{border-color:var(--line-strong);background:var(--surface-2);box-shadow:0 10px 30px -14px rgba(0,0,0,.9)}
.tile:focus-visible{outline:3px solid var(--focus);outline-offset:3px}
.shot{
  position:relative;width:100%;aspect-ratio:var(--shot);background:#0E1017;overflow:hidden;
}
/* Photos fade in rather than snapping, so 20 images landing at different moments
   read as one board filling up instead of a scattergun of pop-ins. */
.shot img{
  display:block;width:100%;height:100%;object-fit:cover;
  opacity:0;
  transition:opacity 260ms var(--ease),transform 420ms var(--ease);
}
.shot.ready img{opacity:1}
/* Same sweep as the placeholders, so a tile whose photo has not arrived yet still
   reads as loading rather than as an empty box. The rank badge sits above it. */
.shot:not(.ready)::before{
  content:"";position:absolute;inset:0;z-index:1;
  background:linear-gradient(100deg,transparent 22%,rgba(255,255,255,.05) 45%,transparent 68%);
  transform:translateX(-100%);
  animation:skel-sweep 1.3s var(--ease) infinite;
}
@media (prefers-reduced-motion:reduce){
  .shot img{opacity:1;transition:none}
  .shot:not(.ready)::before{animation:none}
}
.tile:hover .shot img{transform:scale(1.035)}
.rank{
  position:absolute;top:9px;right:9px;z-index:2;
  min-width:30px;height:30px;padding:0 8px;border-radius:9px;
  display:flex;align-items:center;justify-content:center;
  font-family:var(--font-display);font-size:14px;font-weight:700;
  font-variant-numeric:tabular-nums;letter-spacing:-.02em;
  background:rgba(8,9,13,.82);color:var(--text);
  border:1px solid rgba(255,255,255,.16);
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
  box-shadow:0 3px 10px rgba(0,0,0,.5);
}
.tile[data-medal="1"] .rank{background:var(--gold);color:#241A03;border-color:#FFE39B}
.tile[data-medal="2"] .rank{background:var(--silver);color:#1B1E26;border-color:#EAEEF6}
.tile[data-medal="3"] .rank{background:var(--bronze);color:#1E1206;border-color:#E5B489}
.club{
  display:flex;align-items:center;justify-content:center;gap:7px;
  padding:9px 10px 10px;min-height:40px;
  font-size:13px;font-weight:600;letter-spacing:-.01em;text-align:center;
  color:var(--text);line-height:1.25;
}
.grip{display:none;width:15px;height:15px;flex:none;opacity:.5}
/* In fit mode the name sits over the shot on a scrim, so a row costs no extra
   height and every kit renders about 20% larger. */
body.fit .club{
  position:absolute;left:0;right:0;bottom:0;z-index:2;
  min-height:0;padding:18px 6px 6px;font-size:11.5px;
  text-shadow:0 1px 3px rgba(0,0,0,.9);
  background:linear-gradient(to top,rgba(6,7,11,.95) 55%,rgba(6,7,11,.82) 85%,transparent);
}
body.fit .rank{min-width:24px;height:24px;font-size:11.5px;border-radius:7px;top:6px;right:6px}

/* drag states */
.tile.is-source{
  background:rgba(249,115,22,.07);
  border:1px dashed rgba(249,115,22,.65);
  box-shadow:inset 0 0 0 1px rgba(249,115,22,.10);
}
.tile.is-source .shot img,.tile.is-source .club{opacity:0;transition:none}
.tile.is-source .rank{background:var(--accent);color:var(--accent-ink);border-color:#FFB273}
.tile.floating{
  /* top/left MUST be pinned: with `auto` a fixed box resolves to its static
     position (bottom of the document) and translate() would offset from there. */
  position:fixed;top:0;left:0;z-index:200;margin:0;pointer-events:none;cursor:grabbing;
  border-color:rgba(255,255,255,.28);
  box-shadow:0 28px 60px -18px rgba(0,0,0,.95),0 0 0 1px rgba(255,255,255,.07);
  transition:none;
}
.tile.floating .shot img{transform:none}
body.dragging{cursor:grabbing;user-select:none;-webkit-user-select:none}
body.dragging .tile:not(.floating){cursor:grabbing}
.tile.settling{transition:transform 200ms var(--ease)}
.tile.reflow{transition:transform 260ms var(--ease)}
/* keyboard grab */
.tile.grabbed{
  border-color:var(--focus);
  box-shadow:0 0 0 3px rgba(91,155,255,.35),0 18px 40px -18px rgba(0,0,0,.9);
  transform:scale(1.03);
}

/* ---------- lightbox ---------- */
.lb{
  position:fixed;inset:0;z-index:300;display:none;
  align-items:center;justify-content:center;padding:clamp(12px,3vw,40px);
  background:rgba(5,6,10,.86);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
}
.lb.open{display:flex;animation:fade 180ms var(--ease)}
@keyframes fade{from{opacity:0}to{opacity:1}}
.lb-card{
  position:relative;display:flex;flex-direction:column;
  max-width:min(560px,92vw);width:100%;
  background:var(--surface);border:1px solid var(--line-strong);border-radius:20px;
  overflow:hidden;box-shadow:0 40px 90px -30px #000;
  animation:pop 220ms var(--ease);
}
@keyframes pop{from{opacity:0;transform:scale(.94) translateY(10px)}to{opacity:1;transform:none}}
.lb-card img{display:block;width:100%;height:auto;max-height:calc(100dvh - 200px);object-fit:contain;background:#0E1017}
.lb-meta{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:14px 18px 16px}
.lb-name{font-family:var(--font-display);font-size:19px;font-weight:700;letter-spacing:-.01em;margin:0}
.lb-rank{font-size:12.5px;color:var(--text-dim);margin:2px 0 0;font-variant-numeric:tabular-nums}
.lb-nav{display:flex;gap:7px;align-items:center;flex-wrap:wrap;justify-content:flex-end}
.lb-sep{width:1px;height:26px;background:var(--line-strong);margin:0 2px}
.lb-close{
  position:absolute;top:10px;right:10px;z-index:2;
  width:44px;height:44px;border-radius:12px;display:grid;place-items:center;
  background:rgba(8,9,13,.75);border:1px solid rgba(255,255,255,.18);color:#fff;
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
}
.lb-close:hover{background:rgba(30,33,44,.9)}
.lb-close svg{width:18px;height:18px;stroke-width:2.2}

/* ---------- toast ---------- */
.toast{
  position:fixed;left:50%;bottom:30px;transform:translate(-50%,18px);
  z-index:400;display:flex;align-items:center;gap:10px;
  padding:13px 18px;border-radius:13px;
  background:#EDEFF5;color:#12141B;font-size:14px;font-weight:600;
  box-shadow:0 20px 46px -16px rgba(0,0,0,.85);
  opacity:0;pointer-events:none;transition:opacity 200ms var(--ease),transform 260ms var(--ease);
}
.toast.show{opacity:1;transform:translate(-50%,0)}
.toast svg{width:18px;height:18px;stroke-width:2.4;color:#12855A;flex:none}
.toast.err svg{color:#B4341F}

.sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.foot{margin-top:34px;font-size:12.5px;color:var(--text-faint);text-align:center;line-height:1.7}

/* ---------- responsive ---------- */
@media (max-width:1180px){ .board{grid-template-columns:repeat(4,minmax(0,1fr))} }
@media (max-width:860px){ .board{grid-template-columns:repeat(3,minmax(0,1fr))} .head-row{align-items:stretch} .actions{width:100%} }
@media (max-width:820px){
  /* the fixed submit bar is the persistent control on a phone, not the header */
  header{position:static}
}
@media (max-width:560px){
  :root{--gap:8px;--r-tile:14px}
  .board{grid-template-columns:repeat(3,minmax(0,1fr))}
  .club{font-size:11.5px;padding:7px 4px 8px;min-height:44px}
  .btn{flex:1 1 auto}
  .btn-icon{flex:0 0 auto}
}

/* On a touch screen the browser claims the gesture for scrolling unless
   touch-action is none, so only the name bar can start a drag. The rest of the
   tile keeps scrolling and tap-to-enlarge. Last in the sheet so the width
   breakpoints cannot shrink the handle below the 44px touch target. */
@media (pointer:coarse){
  .club{touch-action:none;min-height:46px;cursor:grab}
  .grip{display:block}
  /* Everything fits on one screen in fit mode, so there is no page scroll left
     to protect: the whole tile becomes the drag target and the handle is dropped. */
  body.fit .tile{touch-action:none}
  body.fit .grip{display:none}
  body.fit .club{min-height:0}
}
/* fit mode on a phone: give the grid the room the header was using */
@media (max-width:860px){
  body.fit .sub{display:none}
  body.fit header{padding-top:12px;padding-bottom:10px}
  /* Save image stays. Hiding it here meant that once phones started landing in
     fit mode, no phone visitor could see it at all — and saving is the one thing
     we most want them to be able to do. The rest can go; they are all recoverable
     by turning fit off. */
  body.fit #copy,body.fit #shuffle,body.fit #reset{display:none}
  /* The season eyebrow and a 34px headline cost ~46px of grid. The title is
     already in the tab, the share preview and the results page, so on the one
     screen where every pixel becomes tile size it goes quiet. */
  body.fit .eyebrow{display:none}
  body.fit h1{font-size:22px}
  /* The wrap keeps a submit-bar-sized bottom padding for browse mode. In fit the
     grid already stops above the bar, so that padding is the only thing left
     making the page scroll a couple of centimetres when it claims to fit. */
  body.fit .wrap{padding-bottom:0}
}
/* At four-across on a phone a tile is ~85px, where a wrapped club name covers
   the kit it is meant to label. Every crop already carries the club badge in its
   corner, so the shirt does the identifying and the name is dropped. */
@media (max-width:640px){
  /* Four gaps of 14px cost 56px of the height the tiles are competing for, and
     the grid is height-bound on a phone, so every pixel here becomes tile. */
  body.fit{--gap:8px}
  /* nothing scrolls in fit mode, so the footer would sit under the fixed bar and
     be sliced mid-sentence. It is on the default view, which is where people land. */
  body.fit .foot{display:none}
  body.fit .club{display:none}
  body.fit .rank{min-width:20px;height:20px;font-size:10px;border-radius:6px;top:4px;right:4px}
}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important}
  .tile:hover .shot img{transform:none}
}
