/* Compete: the pairing shell, the opponent card, the profile panel, and the
 * leaderboard page.
 *
 * ONE FILE FOR TWO CONTEXTS, deliberately. It is loaded by index.html on top of
 * styles.css (the app) and by leaderboard.html on top of seo.css (a document).
 * Both of those sheets publish the same custom properties -- --card, --rule,
 * --brand, --dim, --ink, --radius, --shelf -- so everything here is written
 * against those and nothing here declares a colour of its own. Where a property
 * exists in only one of them (--crown, --streak, --tap) it is used with a
 * fallback, or the leaderboard page would render one component off-palette.
 *
 * There is no new type scale and no new palette. Every accent below is one of
 * the section colours styles.css already defines, picked for one reason: the
 * two ladders have to be told apart at a glance, everywhere they appear
 * together, and they appear together on three different screens.
 *
 *     duel  -> --sec-attack   (pink; fast, reflexive)
 *     game  -> --sec-endgames (teal; slow, considered)
 *
 * The chunky-solid-with-a-shelf rule from styles.css holds throughout: anything
 * pressable is a solid with a hard-edged bottom shadow in a darker tint of its
 * own colour, and it slides down by the shelf height on :active. No blurred
 * drop shadows on controls.
 */

/* ---------- the compete surface ---------- */

/* Desktop parity. styles.css centres a named list of .scroll children at 64rem
   and this screen's blocks are not on that list, so a reveal or a searching
   state would run the full width of the pane while the section above it stayed
   in a column. Harmless on a phone: the shell is already narrower than this. */
.compete > *:not(.tail) {
  max-width: 34rem;
  margin-left: auto;
  margin-right: auto;
}

.compete .practice-head { padding: .4rem 0 .2rem; }
.compete .practice-head h1 { margin-bottom: .35rem; }
.compete .practice-head .sub { margin-bottom: 0; max-width: 30rem; }

/* A pairing that failed says so where the button that failed is, not in an
   alert. Tinted rather than loud: the usual cause is a network blip and the
   fix is pressing the same button again. */
.compete-error {
  margin: .9rem 0 0;
  padding: .7rem .9rem;
  border-radius: var(--radius);
  font-size: .88rem; font-weight: 600;
  color: var(--bad);
  background: color-mix(in srgb, var(--bad) 14%, var(--card));
  box-shadow: 0 3px 0 color-mix(in srgb, var(--bad) 28%, var(--rule));
}

/* ---------- the two ratings ----------
   Side by side and the same size, because they are peers. Stacking one above
   the other would say the top one is the real rating and the other is a
   footnote, which is exactly the misreading the two-ladder design exists to
   avoid. */
.rate-block { margin-top: 1.3rem; }

.rate-pair { display: grid; grid-template-columns: 1fr 1fr; gap: .55rem; }

.rtile {
  background: color-mix(in srgb, var(--tint, var(--brand)) 6%, var(--card));
  border: 2px solid color-mix(in srgb, var(--tint, var(--brand)) 34%, var(--rule));
  border-radius: var(--radius);
  box-shadow: 0 3px 0 color-mix(in srgb, var(--tint, var(--brand)) 22%, var(--rule));
  padding: .85rem .7rem;
  display: flex; flex-direction: column; gap: .1rem;
  text-align: center;
  animation: cellIn .4s cubic-bezier(.34,1.5,.64,1) both;
}
.rtile.duel { --tint: var(--sec-attack); }
.rtile.game { --tint: var(--sec-endgames); animation-delay: .06s; }

.rtile-label {
  font-size: .64rem; font-weight: 700; color: var(--dim);
  text-transform: uppercase; letter-spacing: .06em;
}
.rtile-val {
  font-size: 1.7rem; font-weight: 700; letter-spacing: -.03em;
  color: var(--tint, var(--ink));
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}
.rtile-rec { font-size: .72rem; font-weight: 600; color: var(--dim); }
.rate-note { margin: .6rem 0 0; text-align: center; }

/* The signed-out stand-in for the pair. Same footprint, so signing in does not
   move the two mode cards up the screen. */
.rate-invite {
  display: flex; align-items: center; gap: .85rem;
  background: var(--card);
  border: 2px solid var(--rule);
  border-radius: var(--radius);
  box-shadow: 0 3px 0 var(--rule);
  padding: .9rem 1rem;
}
.rate-invite > div { display: flex; flex-direction: column; gap: .15rem; min-width: 0; }
.rate-invite b { font-size: .98rem; font-weight: 800; letter-spacing: -.01em; }
.rate-invite .sub { font-size: .8rem; line-height: 1.35; margin: 0; }
.rate-invite-mark {
  flex: none;
  width: 2.7rem; height: 2.7rem; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--brand); color: #fff;
  box-shadow: 0 3px 0 var(--brand-dk);
}
.rate-invite-mark svg { width: 55%; height: 55%; }

/* ---------- the mode cards ----------
   The same three-column solid as a rush card, tinted per ladder. Two identical
   indigo slabs would make the one decision on this screen invisible. */
.mode-card {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center; gap: .85rem;
  text-align: left; width: 100%;
}
.mode-card.duel {
  background: var(--sec-attack);
  box-shadow: 0 var(--shelf) 0 var(--sec-attack-dk);
}
.mode-card.duel:active { box-shadow: 0 0 0 var(--sec-attack-dk); }
.mode-card.game {
  background: var(--sec-endgames);
  box-shadow: 0 var(--shelf) 0 var(--sec-endgames-dk);
}
.mode-card.game:active { box-shadow: 0 0 0 var(--sec-endgames-dk); }

.mode-icon {
  width: 2.2rem; height: 2.2rem; border-radius: 50%;
  display: grid; place-items: center; flex: none;
  background: rgb(255 255 255 / .2);
}
.mode-icon svg { width: 1.25rem; height: 1.25rem; }
.mode-body { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.mode-body b { font-size: 1rem; }
.mode-body .dim {
  font-size: .78rem; font-weight: 600;
  color: rgb(255 255 255 / .78);
  line-height: 1.35;
}
.mode-arrow { display: grid; place-items: center; opacity: .8; }
.mode-arrow svg { width: 1.1rem; height: 1.1rem; }

/* The time controls. Chips rather than a select: a native select on a phone is
   a full-screen wheel for six values that fit on two lines.

   A GRID, not the wrapping flex row .goals gives it. Six chips of unequal width
   wrap to five and one, and a lone chip on a second row reads as an option that
   got left behind rather than as the last of a set. Auto-fit columns land three
   to a row on a phone and six on a wide pane, and every chip is the same width
   either way. */
.tc-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(4.6rem, 1fr));
  gap: .5rem;
  margin-bottom: .8rem;
}
.tc-row .chip { padding-left: .5rem; padding-right: .5rem; text-align: center; }
.chip.tc { font-variant-numeric: tabular-nums; }
.chip.tc.on {
  background: var(--sec-endgames);
  border-color: var(--sec-endgames-dk);
  box-shadow: 0 3px 0 var(--sec-endgames-dk);
  color: #04262a;
}

.compete-wall { text-align: center; padding-top: .5rem; }
.compete-wall h1 { margin-top: .4rem; }

/* ---------- searching ----------
   Centred in the pane with nothing else on the screen, because the only thing
   the player can do here is wait or cancel, and a search sitting under a menu
   they can still press reads as though it is not really searching. */
.searching {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; gap: .3rem;
  /* Fills the pane between the header and the tab bar rather than a flat share
     of the viewport: at 62vh the block centred itself in the top two thirds and
     left a third of the screen visibly empty under the cancel button, which
     reads as the screen having finished rather than as one that is working. */
  min-height: calc(100dvh - 16rem);
}
.searching h2 { margin: .8rem 0 .2rem; font-size: 1.25rem; }
.searching .sub { max-width: 22rem; }
.searching .btn { margin-top: 1.1rem; min-width: 11rem; }

/* Two rings leaving the mark, half a cycle apart, so there is always one in
   flight. Scale and opacity only: nothing here triggers layout, and the whole
   thing is switched off by the reduced-motion rule in styles.css. */
.search-orb {
  position: relative;
  width: 5.5rem; height: 5.5rem;
  display: grid; place-items: center;
}
.search-orb::before,
.search-orb::after {
  content: "";
  position: absolute; inset: 0;
  border-radius: 50%;
  border: 3px solid var(--brand);
  animation: searchPing 2s ease-out infinite;
}
.search-orb::after { animation-delay: 1s; }
.search-orb-face {
  position: relative;
  width: 3.6rem; height: 3.6rem; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--brand); color: #fff;
  box-shadow: 0 var(--shelf) 0 var(--brand-dk);
}
.search-orb-face svg { width: 55%; height: 55%; }

@keyframes searchPing {
  0%   { transform: scale(.66); opacity: .7; }
  100% { transform: scale(1.15); opacity: 0; }
}

/* ---------- the reveal ---------- */

.reveal {
  display: flex; flex-direction: column;
  padding-top: .6rem;
  min-height: calc(100dvh - 18rem);
  animation: cardPop .3s cubic-bezier(.34,1.7,.64,1) both;
}
/* Auto margin above, so the identity sits under the header and the decision
   sits under the thumb, with the slack between them rather than below both. */
.reveal-cta { margin-top: auto; padding-bottom: .4rem; }

/* What is at stake, in the same three-cell shape the results screen uses for a
   score -- deliberately, because it is the same kind of statement made before
   the match instead of after it. */
.stakes { display: flex; gap: .55rem; }
.stake {
  flex: 1;
  background: var(--card);
  border: 2px solid var(--rule);
  border-radius: var(--radius);
  box-shadow: 0 3px 0 var(--rule);
  padding: .75rem .4rem;
  display: flex; flex-direction: column; gap: .1rem;
  text-align: center;
  animation: cellIn .4s cubic-bezier(.34,1.5,.64,1) both;
}
.stake:nth-child(2) { animation-delay: .06s }
.stake:nth-child(3) { animation-delay: .12s }
.stake b {
  font-size: 1.15rem; font-weight: 700; letter-spacing: -.02em;
  text-transform: capitalize;
}
.stake span {
  font-size: .62rem; font-weight: 700; color: var(--dim);
  text-transform: uppercase; letter-spacing: .05em;
}

.versus {
  display: grid; grid-template-columns: 1fr auto 1fr;
  align-items: center; gap: .6rem;
  margin-bottom: .9rem;
}
.vs-side { display: flex; flex-direction: column; align-items: center; gap: .1rem; min-width: 0; }
.vs-name {
  font-size: .9rem; font-weight: 700; letter-spacing: -.01em;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.vs-rating {
  font-size: 1.35rem; font-weight: 700; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.vs-v {
  width: 2.4rem; height: 2.4rem; border-radius: 50%;
  display: grid; place-items: center; flex: none;
  background: var(--card); color: var(--dim);
  box-shadow: 0 3px 0 var(--rule);
}
.vs-v svg { width: 1.2rem; height: 1.2rem; }

/* THE CARD IS THE DOORWAY TO THE PANEL, so it has to read as pressable: same
   card body as a tool row, with the shelf and the press travel of a button. */
.opp-card {
  display: grid; grid-template-columns: auto 1fr auto;
  align-items: center; gap: .85rem;
  width: 100%; text-align: left;
  font: inherit; color: var(--ink);
  background: var(--card);
  border: 2px solid var(--rule);
  border-radius: var(--radius-lg, 1.6rem);
  padding: .9rem 1rem;
  box-shadow: 0 var(--shelf) 0 var(--rule);
  cursor: pointer;
  transition: transform .1s ease, box-shadow .1s ease, border-color .1s ease;
}
.opp-card:hover { border-color: var(--brand); }
.opp-card:active { transform: translateY(var(--shelf)); box-shadow: 0 0 0 var(--rule); }

.opp-avatar {
  width: 2.9rem; height: 2.9rem; border-radius: 50%;
  display: grid; place-items: center; flex: none;
  font-size: 1.2rem; font-weight: 800;
  background: var(--brand); color: #fff;
  box-shadow: 0 3px 0 var(--brand-dk);
}
.opp-body { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.opp-name {
  font-size: 1.02rem; font-weight: 800; letter-spacing: -.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.opp-flag { font-size: .95rem; }
.opp-rec { font-size: .78rem; font-weight: 600; color: var(--dim); }
.opp-rating {
  display: flex; flex-direction: column; align-items: center;
  min-width: 3.4rem; flex: none;
  font-variant-numeric: tabular-nums;
}
.opp-rating b { font-size: 1.35rem; line-height: 1; letter-spacing: -.02em; }
.opp-rating span {
  font-size: .6rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--dim);
}
.opp-hint { text-align: center; margin: .5rem 0 1.1rem; }
.reveal .btn { width: 100%; margin-bottom: .6rem; }
.reveal .btn:last-child { margin-bottom: 0; }

/* ---------- the profile panel ----------
 *
 * A PANEL, NOT A PAGE. Several hundred thin pages about fabricated people is
 * the doorway pattern Google's spam policy names, and it would put the whole
 * domain's indexing at risk to chase nothing. There is no route behind this and
 * there must never be one. See docs/COMPETE.md.
 *
 * Positioned inside #app, which already establishes a containing block and
 * clips, so the tab bar stays reachable underneath it -- the same rule the
 * welcome overlay follows. Never trap somebody in a modal. */
.cp {
  position: absolute; inset: 0; z-index: 60;
  display: flex; align-items: flex-end; justify-content: center;
  padding: 1rem;
  background: color-mix(in srgb, var(--bg) 55%, transparent);
  animation: sheetUp .28s cubic-bezier(.21,.9,.3,1.05) both;
}
.cp-card {
  position: relative;
  width: 100%; max-width: 24rem;
  max-height: 100%; overflow-y: auto;
  background: var(--card);
  border-radius: var(--radius-lg, 1.6rem);
  padding: 1.2rem 1.15rem 1.3rem;
  box-shadow: 0 10px 40px rgb(0 0 0 / .45), 0 0 0 1px var(--rule);
}
.cp-x {
  position: absolute; top: .55rem; right: .55rem;
  width: 2.2rem; height: 2.2rem; border-radius: 50%;
  display: grid; place-items: center;
  background: none; border: 0; cursor: pointer;
  color: var(--dim);
}
.cp-x svg { width: 1rem; height: 1rem; }
.cp-x:active { background: var(--surface); }

.cp-head { display: flex; align-items: center; gap: .8rem; margin-bottom: 1rem; padding-right: 2rem; }
.cp-avatar {
  width: 3.2rem; height: 3.2rem; border-radius: 50%;
  display: grid; place-items: center; flex: none;
  font-size: 1.35rem; font-weight: 800;
  background: var(--brand); color: #fff;
  box-shadow: 0 3px 0 var(--brand-dk);
}
.cp-who { min-width: 0; }
.cp-who h2 { margin: 0; font-size: 1.25rem; letter-spacing: -.02em; }
.cp-who .sub { font-size: .82rem; }
.cp-h3 { margin: 1.3rem 0 .5rem; font-size: .95rem; font-weight: 800; }

/* One row per result. The dot carries the outcome so the column can be read
   without reading it -- three greens in a row is a run, and that is the whole
   point of showing recent form at all. */
.cres-list { display: grid; gap: .4rem; }
.cres {
  display: grid; grid-template-columns: auto 1fr auto;
  align-items: center; gap: .7rem;
  background: var(--card-2, var(--surface));
  border-radius: .8rem;
  padding: .55rem .8rem;
  animation: rowIn .3s cubic-bezier(.21,.9,.3,1.1) both;
}
.cres:nth-child(2) { animation-delay: .04s }
.cres:nth-child(3) { animation-delay: .08s }
.cres:nth-child(4) { animation-delay: .12s }
.cres:nth-child(5) { animation-delay: .16s }
.cres-dot {
  width: .6rem; height: .6rem; border-radius: 50%;
  background: var(--dim);
}
.cres.win .cres-dot  { background: var(--good); }
.cres.loss .cres-dot { background: var(--bad); }
.cres-what { display: flex; flex-direction: column; gap: .05rem; min-width: 0; }
.cres-what b { font-size: .88rem; font-weight: 800; }
.cres-what .dim { font-size: .74rem; }
.cres-when { font-size: .72rem; font-weight: 600; color: var(--dim); white-space: nowrap; }

/* ---------- the leaderboard page ----------
 *
 * This one IS a page and is meant to be indexed: it is a single list of the top
 * of a ladder, which is a thing people search for and a thing that is worth
 * reading. It is also the only public surface where a rating appears, which is
 * why the switch between the two boards is on the page rather than being two
 * pages -- the ladders share a reader, not a URL each. */
.lb-switch {
  display: flex; gap: .5rem; flex-wrap: wrap;
  margin: 0 0 1.2rem;
}
.lb-tab {
  font: inherit; font-size: .9rem; font-weight: 600;
  background: var(--card); color: var(--ink);
  border: 2px solid var(--rule); border-radius: 999px;
  padding: .5rem 1.1rem; cursor: pointer;
  box-shadow: 0 3px 0 var(--rule);
  transition: transform .1s ease, box-shadow .1s ease;
}
.lb-tab:active { transform: translateY(3px); box-shadow: none; }
.lb-tab[aria-selected="true"] {
  color: #fff;
  background: var(--tint, var(--brand));
  border-color: transparent;
  box-shadow: 0 3px 0 color-mix(in srgb, var(--tint, var(--brand)) 70%, #000);
}
.lb-tab.duel { --tint: var(--sec-attack); }
.lb-tab.game { --tint: var(--sec-endgames); }

.lb-list { display: grid; gap: .4rem; margin: 0 0 1.4rem; }

/* A grid of rows rather than a table. At 390px a five-column table either
   scrolls sideways or squeezes the name to three characters, and the name is
   the thing the row is about. Rank, name and rating are the row; the record is
   a second line under the name where there is always space for it. */
.lb-row {
  display: grid;
  grid-template-columns: 2.2rem 1fr auto;
  align-items: center; gap: .8rem;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: .6rem .85rem;
}
.lb-rank {
  font-size: .95rem; font-weight: 800; color: var(--dim);
  text-align: right; font-variant-numeric: tabular-nums;
}
/* The top three are the only rows anybody looks for by position. */
.lb-row:nth-child(1) .lb-rank { color: var(--crown, #ffc531); }
.lb-row:nth-child(2) .lb-rank { color: var(--dim); }
.lb-row:nth-child(3) .lb-rank { color: var(--sec-tactics-2, #ff8f3f); }

.lb-who { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.lb-name {
  font-size: .98rem; font-weight: 700; letter-spacing: -.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lb-rec { font-size: .74rem; font-weight: 600; color: var(--dim); }
.lb-rating {
  font-size: 1.2rem; font-weight: 800; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  color: var(--tint, var(--brand));
}
.lb-list.duel { --tint: var(--sec-attack); }
.lb-list.game { --tint: var(--sec-endgames); }

.lb-note { font-size: .85rem; color: var(--dim); }

@media (min-width: 34rem) {
  .lb-row { grid-template-columns: 2.6rem 1fr auto; padding: .7rem 1rem; }
}
