/* Puzzle duels. Loaded after styles.css and owning nothing outside `.duel*`
 * and `.final-*`.
 *
 * Borrowed, not restated: --brand, --good, --bad, --card, --rule, --radius and
 * the 4px shelf all come from styles.css, and `.board-wrap`, `.btn` and
 * `.icon-btn` are used as they are. Both themes work for the same reason every
 * other sheet here does -- there is no hard-coded colour in this file at all.
 *
 * THE TWO BARS ARE THE SCREEN. Everything else is arranged around keeping them
 * comparable at a glance, because a race that has to be read as numbers is not
 * a race. Three rules follow from that and none of them is a preference:
 *
 *   1. THEY ARE STACKED AND IDENTICAL. Two bars growing from opposite edges
 *      cannot be compared without reading the numbers off the ends, which is
 *      exactly the work the bars exist to remove. Same origin, same direction,
 *      same length, one above the other.
 *
 *   2. EACH BAR IS TWO BARS. The solid fill is what that side has SOLVED; the
 *      pale ghost behind it is how far through the fifteen they are. An
 *      opponent grinding a hard puzzle would otherwise look frozen, and a
 *      player who has missed four would look level with somebody who has
 *      solved four. The gap between ghost and fill is the misses, which is a
 *      whole extra fact for no extra ink.
 *
 *   3. NOTHING IN THE RACE STRIP CHANGES SIZE. Same reason rush.css pins its
 *      HUD: this sits directly above the board, so a name or a score that
 *      resizes walks the board down the page mid-drag. The score is a fixed
 *      column with tabular figures and the name gets an ellipsis.
 *
 * The lead cue is a colour on the strip, not an animation. It is on screen for
 * most of a duel -- anything that moves for that long stops being noticed and
 * starts being noise.
 *
 * MOTION is one-shot everywhere except the urgent clock, which only exists in
 * the last fifteen seconds. The block at the end repeats styles.css's
 * reduced-motion kill so this sheet is still correct if it is ever loaded
 * without its parent.
 */

/* ---------- frame ----------
   The same column as .rush and .lesson: fixed header, a body that owns the
   rest, nothing that scrolls. A duel that scrolls is a duel where the board is
   half off screen when the clock runs out. */

/* `.duel-screen`, not `.duel`. A bare `.duel` is far too generic a name to own
   a full-height flex column: index.html loads this stylesheet on every route,
   and compete.css uses `duel` as a MODIFIER -- `.rtile.duel`, `.mode-card.duel`,
   `.lb-tab.duel`, `.lb-list.duel`. Every one of those elements also matched this
   rule, so the rating tiles on the Compete tab were silently inheriting
   height:100% and a column direction they never asked for. A third surface hit
   the same trap with a legend key and had to rename its own classes to escape.
   The root of this screen is the only thing that wants these four lines. */
.duel-screen {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

.duel-top {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: max(.6rem, env(safe-area-inset-top)) var(--pad) .5rem;
}

.duel-title {
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: -.02em;
  flex: 1 1 auto;
  text-align: center;
}
/* Balances the close button so the title is optically centred without a grid. */
.duel-spacer { width: var(--tap); flex: none; }

.duel-clock {
  flex: 1 1 auto;
  text-align: center;
  font-weight: 700;
  font-size: 1.5rem;
  line-height: 1;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  /* Four characters plus the colon, fixed, so 1:00 rolling to 0:59 does not
     resize the header. */
  min-width: 4ch;
  transition: color .3s ease;
}

.duel-count-cell {
  flex: none;
  /* Wider than the close button it balances, because "15 / 15" does not fit in
     a 44px tap target -- and it has to be a fixed width rather than fitting its
     content, or the clock re-centres itself as the count grows a digit. The
     10px of asymmetry that costs is invisible; a clock that shuffles sideways
     mid-duel is not. */
  width: 3.4rem;
  text-align: right;
  font-size: .8rem;
  font-weight: 700;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
}

/* The last fifteen seconds. The digits change colour and pulse slowly; nothing
   else on the screen moves, because the bars are being read at exactly this
   moment and a flashing layout is unreadable. */
.duel-top.urgent .duel-clock {
  color: var(--bad);
  animation: duelUrgent .9s ease-in-out infinite;
}

.duel-body {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0 var(--pad) .6rem;
}

/* Two elastic spacers, unequal, exactly as rush.css splits its slack.
   Centring the whole column instead drops the race strip into the middle of the
   screen with a hand's worth of nothing above it -- and the race strip is the
   one thing that should sit near the clock, because they are read together. So
   the strip stays at the top, and the slack under it is split 1:1.6, which
   leaves the board a little above centre where a thumb reaches it without the
   hand moving. Both collapse to their min-height on a short screen, so nothing
   is ever pushed off. */
.duel-lead { flex: 1 1 auto; min-height: .35rem; }
.duel-foot { flex: 1.6 1 auto; min-height: .5rem; }

/* ---------- the race ---------- */

.duel-race {
  width: 100%;
  max-width: min(30rem, 100% - 1rem);
  display: flex;
  flex-direction: column;
  gap: .4rem;
  padding: .55rem .7rem;
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: 0 3px 0 var(--rule);
  /* The lead cue. A ring rather than a fill, so the card behind the bars keeps
     the same contrast whoever is ahead. */
  box-shadow: 0 3px 0 var(--rule), inset 0 0 0 2px transparent;
  transition: box-shadow .3s ease;
}
/* Mixed down rather than used at full strength. This ring is on screen for
   most of a duel, and at full --bad it competes with the urgent clock in the
   last fifteen seconds -- which is the one moment the clock has to win. */
.duel-race.ahead {
  box-shadow: 0 3px 0 var(--rule),
              inset 0 0 0 2px color-mix(in srgb, var(--good) 70%, transparent);
}
.duel-race.behind {
  box-shadow: 0 3px 0 var(--rule),
              inset 0 0 0 2px color-mix(in srgb, var(--bad) 55%, transparent);
}

.duel-side {
  display: grid;
  /* Name column, bar, score. `minmax(0, 1fr)` on the bar and not `1fr`, or a
     long username refuses to shrink and pushes the score off the card. */
  grid-template-columns: 7.5rem minmax(0, 1fr) 1.6rem;
  align-items: center;
  gap: .5rem;
}

.duel-who {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.duel-name {
  font-weight: 700;
  font-size: .85rem;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.duel-flag { font-size: .8em; }
.duel-meta {
  font-size: .68rem;
  font-weight: 600;
  color: var(--dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.duel-track {
  position: relative;
  display: block;
  height: .55rem;
  border-radius: 999px;
  background: var(--rule);
  overflow: hidden;
}
.duel-track > i {
  position: absolute;
  inset: 0 auto 0 0;
  display: block;
  height: 100%;
  width: 0;
  border-radius: 999px;
  /* Linear and barely longer than the 100ms tick. Anything springy reads as the
     opponent hesitating, which is a lie about a number that is already fixed. */
  transition: width .12s linear;
}
/* How far through they are, behind how many they have solved. */
.duel-track > i.ghost { background: color-mix(in srgb, var(--dim) 45%, transparent); }
.duel-track > i.fill { background: var(--brand); }
.duel-side.them .duel-track > i.fill { background: var(--streak); }

.duel-score {
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* ---------- the prompt ----------
   Fixed height, always present, invisible when empty. A line that appears and
   disappears is a line that moves the board. */

.duel-prompt {
  min-height: 1.6rem;
  margin: 0;
  padding: .22rem .8rem;
  border-radius: 999px;
  font-weight: 700;
  font-size: .9rem;
  line-height: 1.15;
  color: var(--dim);
}
.duel-prompt.good { color: #fff; background: var(--good); }
.duel-prompt.bad {
  color: #fff;
  background: var(--bad);
  animation: duelShake .42s cubic-bezier(.36,.07,.19,.97);
}

/* ---------- waiting ---------- */

.duel-wait { height: 100%; }
.duel-wait-body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  padding: 2rem var(--pad);
  text-align: center;
}
.duel-spinner {
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 50%;
  border: 3px solid var(--rule);
  border-top-color: var(--brand);
  animation: duelSpin .8s linear infinite;
}
.duel-err { color: var(--bad); font-weight: 700; margin: 0; }
.duel-wait-body .btn { width: 100%; max-width: 20rem; }

/* ---------- the countdown ---------- */

.duel-go { height: 100%; }
.duel-go-body {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  padding: 2rem var(--pad);
  text-align: center;
}
.duel-go-body .duel-who { align-items: center; }
.duel-go-body .duel-name { font-size: 1.15rem; }

.duel-count {
  font-size: 4.5rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -.03em;
  margin: .8rem 0;
  color: var(--brand);
  font-variant-numeric: tabular-nums;
}
:root[data-theme="dark"] .duel-count { color: var(--brand-lt); }
.duel-count.pop { animation: duelPop .5s cubic-bezier(.34,1.6,.64,1); }

.duel-rules { font-size: .82rem; font-weight: 600; color: var(--dim); margin: 0; }

/* ---------- the result ---------- */

.duel-over {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .7rem;
  padding: 2rem var(--pad) max(2rem, env(safe-area-inset-bottom));
  text-align: center;
}
.duel-over h1 {
  font-size: 1.9rem;
  font-weight: 700;
  letter-spacing: -.02em;
  margin: 0;
}
.duel-over.win h1 { color: var(--good); }
.duel-over.loss h1 { color: var(--bad); }

.duel-how { margin: 0; font-weight: 600; color: var(--dim); }

.duel-final {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .9rem;
  width: 100%;
  max-width: 24rem;
  margin: .3rem 0;
}
.final-cell {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .1rem;
  padding: .7rem .5rem;
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: 0 3px 0 var(--rule);
}
.final-cell .duel-who { align-items: center; max-width: 100%; }
.final-cell b {
  font-size: 2rem;
  font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.final-cell > span {
  font-size: .72rem;
  font-weight: 700;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
}
.final-v { font-weight: 700; color: var(--dim); flex: none; }

.duel-delta {
  margin: 0;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.duel-delta.up { color: var(--good); }
.duel-delta.down { color: var(--bad); }
.duel-note { margin: 0; font-size: .85rem; font-weight: 600; color: var(--dim); }

.duel-cta { width: 100%; max-width: 24rem; margin-top: .5rem; }

/* ---------- motion ---------- */

@keyframes duelUrgent {
  0%, 100% { opacity: 1; }
  50% { opacity: .45; }
}
@keyframes duelSpin { to { transform: rotate(360deg); } }
@keyframes duelPop {
  0% { transform: scale(.6); opacity: 0; }
  60% { transform: scale(1.08); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}
@keyframes duelShake {
  10%, 90% { transform: translateX(-1px); }
  20%, 80% { transform: translateX(2px); }
  30%, 50%, 70% { transform: translateX(-4px); }
  40%, 60% { transform: translateX(4px); }
}

@media (prefers-reduced-motion: reduce) {
  .duel-count.pop,
  .duel-prompt.bad,
  .duel-spinner,
  .duel-top.urgent .duel-clock { animation: none !important; }
  .duel-track > i { transition: none !important; }
}

/* A short screen. The race strip is the first thing to give up its padding,
   because the board is the thing that must not shrink. */
@media (max-height: 700px) {
  .duel-race { padding: .4rem .6rem; gap: .3rem; }
  .duel-body { gap: .4rem; }
  .duel-prompt { min-height: 1.4rem; }
}
