/* ui/Hud — contract: ./Hud.spec.md. Tokens from ui/Theme. */

.qc-hud {
  --ship: var(--qc-accent);
  --banner-size: clamp(26px, 4.4vw, 54px); /* banner text size; the respawn overlay clears it */
  position: fixed;
  inset: 0;
  z-index: 10;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  /* top readouts · centre (banner, respawn) · minimap · bottom panels.
     The centre row shrinks first, so nothing is pushed off-screen. */
  grid-template-rows: auto minmax(0, 1fr) auto auto;
  gap: 16px 24px;
  padding: 20px 24px 22px;
  font-size: 13px;
  pointer-events: none; /* never block mouse aim… */
  user-select: none;
}

/* Identity colours for anything tagged with a ship or weapon id. */
.qc-hud,
.qc-hud [data-ship] { --ship: var(--qc-accent); }
.qc-hud[data-ship="laser"],
.qc-hud [data-ship="laser"] { --ship: var(--qc-ship-laser); }
.qc-hud[data-ship="plasma"],
.qc-hud [data-ship="plasma"] { --ship: var(--qc-ship-plasma); }
.qc-hud[data-ship="rocket"],
.qc-hud [data-ship="rocket"] { --ship: var(--qc-ship-rocket); }

/* Streak tiers: cyan → violet → gold → white-hot singularity. */
.qc-hud [data-tier] { --tier: var(--qc-text-dim); }
.qc-hud [data-tier="1"],
.qc-hud [data-tier="2"] { --tier: var(--qc-accent-2); }
.qc-hud [data-tier="3"],
.qc-hud [data-tier="4"] { --tier: var(--qc-accent); }
.qc-hud [data-tier="5"],
.qc-hud [data-tier="6"] { --tier: var(--qc-rarity-legendary); }
.qc-hud [data-tier="7"] { --tier: var(--qc-text); }

/* Announcement kinds (after the ladder, so they win). A tier 0 banner reads in
   full text colour. Arena reveals are cyan, arena event warnings gold, and
   kinds from custom rules stay neutral whatever their tier. */
.qc-banner[data-tier="0"] { --tier: var(--qc-text); }
.qc-banner[data-kind]:not([data-kind="streak"], [data-kind="level"], [data-kind="arena"], [data-kind="event"]) { --tier: var(--qc-text); }
.qc-log__entry[data-kind]:not([data-kind="streak"], [data-kind="level"], [data-kind="arena"], [data-kind="event"]) { --tier: var(--qc-text-dim); }
.qc-banner[data-kind="arena"],
.qc-log__entry[data-kind="arena"] { --tier: var(--qc-accent-2); }
.qc-banner[data-kind="event"],
.qc-log__entry[data-kind="event"] { --tier: var(--qc-rarity-legendary); }

/* ------------------------------------------------------ announcer log --- */

.qc-hud__announcer {
  grid-column: 1;
  grid-row: 1;
  display: flex;
  flex-direction: column;
  gap: 3px;
  max-width: 360px;
}

.qc-log__entry {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 5px 12px 5px 12px;
  background: linear-gradient(90deg, color-mix(in oklab, var(--qc-space-0) 72%, transparent), transparent);
  border-left: 2px solid var(--tier);
  animation: qc-hud-in-left var(--qc-dur) var(--qc-ease-out) both;
}

.qc-log__entry:nth-child(n + 4) {
  opacity: 0.5;
}

.qc-log__text {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--tier);
}

.qc-log__entry[data-tier="7"]:is(:not([data-kind]), [data-kind="streak"]) .qc-log__text {
  text-shadow: 0 0 12px var(--qc-accent);
}

.qc-log__subject {
  font-size: 12px;
  color: var(--qc-text-dim);
}

.qc-log__entry[data-local] .qc-log__subject {
  color: var(--qc-text);
}

/* ---------------------------------------------------------- kill feed --- */

/* Rows stay on one line. The feed fills its column (a size container, so it
   cannot size itself from its rows) and each row is capped to it, so when
   space runs out the names truncate; badges and weapon labels never give. */
.qc-hud__feed {
  grid-column: 3;
  grid-row: 1;
  justify-self: stretch;
  container-type: inline-size;
  container-name: qc-feed;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.qc-feed__entry {
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 100%;
  padding: 5px 10px;
  background: var(--qc-glass);
  border: 1px solid var(--qc-line);
  border-radius: 7px;
  backdrop-filter: blur(6px);
  animation: qc-hud-in-right var(--qc-dur) var(--qc-ease-out) both;
}

.qc-feed__entry:has([data-local]) {
  border-color: color-mix(in oklab, var(--qc-accent-2) 45%, transparent);
}

.qc-feed__player {
  min-width: 0;
  max-width: 12em;
  overflow: hidden;
  /* Room below the text for the local player's underline, which the overflow
     would otherwise clip; the negative margin keeps the row height unchanged. */
  padding-bottom: 6px;
  margin-bottom: -6px;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: color-mix(in oklab, var(--ship) 80%, var(--qc-text));
}

.qc-feed__player[data-local] {
  color: var(--qc-text);
  text-decoration: underline;
  text-decoration-color: var(--ship);
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
}

.qc-feed__weapon {
  --weapon: var(--qc-text-dim);
  flex: none;
  padding: 3px 6px;
  font: 600 10px/1 var(--qc-font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--weapon);
  border: 1px solid color-mix(in oklab, var(--weapon) 45%, transparent);
  border-radius: 4px;
}

.qc-feed__weapon[data-weapon="laser"] { --weapon: var(--qc-ship-laser); }
.qc-feed__weapon[data-weapon="plasma"] { --weapon: var(--qc-ship-plasma); }
.qc-feed__weapon[data-weapon="rocket"] { --weapon: var(--qc-ship-rocket); }

/* Mastery badge: a small hexagon holding the pilot's level, tucked against the
   name it follows and tinted by tier. A tinted rim (::before) frames a dark
   face (::after); both are clipped to the hexagon. Static: no animation. */
.qc-feed__mastery {
  --mastery: var(--qc-rarity-common);
  --notch: 4px;
  position: relative; /* contains the visually hidden text */
  isolation: isolate;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  height: 16px;
  margin-left: -5px; /* half the row gap: the badge belongs to its name */
  padding: 0 calc(var(--notch) + 2px);
  font: 700 10px/1 var(--qc-font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: color-mix(in oklab, var(--mastery) 68%, var(--qc-text));
}

.qc-feed__mastery::before,
.qc-feed__mastery::after {
  content: "";
  position: absolute;
  z-index: -1;
  clip-path: polygon(
    var(--notch) 0, calc(100% - var(--notch)) 0, 100% 50%,
    calc(100% - var(--notch)) 100%, var(--notch) 100%, 0 50%
  );
}

.qc-feed__mastery::before {
  inset: 0;
  background: color-mix(in oklab, var(--mastery) 75%, transparent);
}

.qc-feed__mastery::after {
  inset: 1px;
  background: color-mix(in oklab, var(--mastery) 16%, var(--qc-space-0));
}

.qc-feed__mastery[data-tier="common"] { --mastery: var(--qc-rarity-common); }
.qc-feed__mastery[data-tier="rare"] { --mastery: var(--qc-rarity-rare); }
.qc-feed__mastery[data-tier="epic"] { --mastery: var(--qc-rarity-epic); }
.qc-feed__mastery[data-tier="legendary"] {
  --mastery: var(--qc-rarity-legendary);
  filter: drop-shadow(0 0 3px color-mix(in oklab, var(--qc-rarity-legendary) 60%, transparent));
}

/* A very narrow feed column: even fully truncated names would leave no room,
   so badges step aside. Visually only, like .qc-visually-hidden: their
   ", mastery n" text stays for screen readers. */
@container qc-feed (width < 240px) {
  .qc-feed__mastery {
    position: absolute;
    width: 1px;
    min-width: 0;
    height: 1px;
    margin: 0;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
  }
}

/* ---------------------------------------------------- standing readout --- */

/* Where you stand is the whole point of the match, so the place is the biggest
   type in the HUD's top row; the clock, which no pilot can change, sits above
   it small, and the two gaps that decide the place sit under it.

   The width is fixed, not fluid: `ui/Settings` parks the gear 92px right of
   centre over the arena and this panel is centred, so 168px keeps 8px clear of
   it at every viewport. Long names truncate rather than push into the gear. */
.qc-hud__match {
  grid-column: 2;
  grid-row: 1;
  justify-self: center;
  align-self: start;
  display: grid;
  justify-items: stretch;
  gap: 3px;
  width: 168px;
  padding: 8px 13px 9px;
  background: var(--qc-glass);
  border: 1px solid var(--qc-line);
  border-radius: var(--qc-radius);
  backdrop-filter: blur(8px);
  transition: border-color var(--qc-dur), box-shadow var(--qc-dur);
}

.qc-hud__match:has([data-warning]) {
  border-color: color-mix(in oklab, var(--qc-danger) 50%, transparent);
  box-shadow: 0 0 28px -14px var(--qc-danger);
}

/* Clock on the left, whoever is leading on the right. */
.qc-match__top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
}

.qc-match__clock {
  font-family: var(--qc-font-mono);
  font-size: 15px;
  font-weight: 500;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.06em;
  color: var(--qc-text-dim);
  transition: color var(--qc-dur), text-shadow var(--qc-dur);
}

.qc-match__clock[data-warning] {
  font-weight: 700;
  color: var(--qc-danger);
  text-shadow: 0 0 18px color-mix(in oklab, var(--qc-danger) 55%, transparent);
  animation: qc-clock-warn 1s ease-in-out infinite alternate;
}

.qc-match__leader {
  display: flex;
  align-items: baseline;
  gap: 4px;
  min-width: 0;
  font-size: 10px;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--qc-rarity-legendary);
}

.qc-match__leader::before {
  content: "★";
  flex: none;
  font-size: 9px;
  line-height: 1;
}

.qc-match__leader-name {
  min-width: 0;
  max-width: 7em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: color-mix(in oklab, var(--qc-rarity-legendary) 55%, var(--qc-text));
}

/* The place, then the field size over the kills beside it. `display: contents`
   on the rank wrapper puts all three in this one grid. */
.qc-match__standing {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  column-gap: 9px;
  margin: 1px 0 2px;
  line-height: 1;
  white-space: nowrap;
}

.qc-match__rank {
  display: contents;
}

.qc-match__place {
  grid-area: 1 / 1 / span 2 / auto;
  font-family: var(--qc-font-mono);
  font-size: 38px;
  font-weight: 300;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--qc-text);
  animation: qc-place-in 420ms var(--qc-ease-out) both;
}

/* First place is worth looking at. */
.qc-match__place[data-rank="1"] {
  color: var(--qc-rarity-legendary);
  text-shadow: 0 0 24px color-mix(in oklab, var(--qc-rarity-legendary) 45%, transparent);
}

.qc-match__suffix {
  font-size: 0.42em;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.qc-match__field,
.qc-match__kills {
  overflow: hidden;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-overflow: ellipsis;
  text-transform: uppercase;
  color: var(--qc-text-faint);
}

.qc-match__field {
  grid-area: 1 / 2;
  align-self: end;
  padding-bottom: 2px;
}

.qc-match__kills {
  grid-area: 2 / 2;
  align-self: start;
  padding-top: 2px;
  font-family: var(--qc-font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--qc-text-dim);
}

/* The two gaps that decide the place: who is above, and who is closing in. */
/* Two rows' worth of height whether or not both rivals exist, so the panel
   (and the latency pill under it) never jumps as you take the lead or last. */
.qc-match__rivals {
  display: grid;
  gap: 2px;
  width: 100%;
  min-height: 36px; /* two rows, plus this list's own padding and rule */
  margin: 1px 0 0;
  padding: 6px 0 0;
  border-top: 1px solid color-mix(in oklab, var(--qc-line) 85%, transparent);
  list-style: none;
}

.qc-match__rival {
  --side: var(--qc-text-faint);
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-size: 10px;
  font-weight: 600;
  line-height: 1.35;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* Decorative: the words "ahead" and "behind" carry which side a rival is on. */
.qc-match__rival::before {
  content: "▲";
  flex: none;
  font-size: 8px;
  color: var(--side);
}

.qc-match__rival[data-side="ahead"] { --side: var(--qc-accent-2); }

.qc-match__rival[data-side="behind"] { --side: var(--qc-danger); }

.qc-match__rival[data-side="behind"]::before {
  content: "▼";
}

.qc-match__rival-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--qc-text-dim);
}

.qc-match__rival-gap {
  flex: none;
  font-family: var(--qc-font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.06em;
  color: color-mix(in oklab, var(--side) 55%, var(--qc-text));
}

/* A place taken rises into its slot and flares; a place lost settles in from
   above, smaller and without the glow — the same asymmetry as the sound. */
.qc-match__place[data-move="up"] {
  animation-name: qc-place-up;
  animation-duration: 620ms;
}

.qc-match__place[data-move="down"] {
  animation-name: qc-place-down;
  animation-duration: 380ms;
}

@keyframes qc-clock-warn {
  to { opacity: 0.6; }
}

@keyframes qc-place-in {
  from { opacity: 0; }
}

@keyframes qc-place-up {
  0% { opacity: 0; transform: translateY(0.36em) scale(0.82); color: var(--qc-accent-2); text-shadow: 0 0 32px var(--qc-accent-2); }
  45% { opacity: 1; transform: translateY(-0.05em) scale(1.14); color: var(--qc-accent-2); text-shadow: 0 0 26px color-mix(in oklab, var(--qc-accent-2) 70%, transparent); }
  100% { opacity: 1; transform: none; }
}

@keyframes qc-place-down {
  0% { opacity: 0.15; transform: translateY(-0.22em); color: var(--qc-danger); }
  55% { color: color-mix(in oklab, var(--qc-danger) 60%, var(--qc-text)); }
  100% { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------- banner --- */

.qc-hud__banner-slot {
  grid-column: 1 / -1;
  grid-row: 2;
  justify-self: center;
  align-self: start;
  margin-top: 8vh;
}

.qc-banner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  text-align: center;
  animation: qc-banner-in 720ms var(--qc-ease-out) both;
}

.qc-banner__text {
  font-size: var(--banner-size);
  font-weight: 300;
  line-height: 1;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--tier);
  text-shadow: 0 0 28px color-mix(in oklab, var(--tier) 55%, transparent);
}

.qc-banner__subject {
  display: flex;
  align-items: center;
  gap: 14px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--qc-text-dim);
}

.qc-banner__subject::before,
.qc-banner__subject::after {
  content: "";
  width: 90px;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--tier));
  animation: qc-banner-rule 720ms var(--qc-ease-out) both;
}

.qc-banner__subject::after {
  transform: scaleX(-1);
}

/* Streak tiers 5+ get heavier type; the singularity collapses inward with a
   halo. Other kinds never take these treatments. */
.qc-banner[data-tier="5"]:is(:not([data-kind]), [data-kind="streak"]) .qc-banner__text,
.qc-banner[data-tier="6"]:is(:not([data-kind]), [data-kind="streak"]) .qc-banner__text {
  font-weight: 400;
}

.qc-banner[data-tier="7"]:is(:not([data-kind]), [data-kind="streak"]) .qc-banner__text {
  font-weight: 200;
  letter-spacing: 0.44em;
  text-indent: 0.44em;
  text-shadow:
    0 0 18px var(--qc-accent),
    0 0 60px var(--qc-accent);
}

.qc-banner[data-tier="7"]:is(:not([data-kind]), [data-kind="streak"]) {
  animation-name: qc-banner-collapse;
  animation-duration: 900ms;
}

/* Arena reveal: calm. Light type, very wide tracking, a slow blur-in. */
.qc-banner[data-kind="arena"] {
  animation: qc-banner-reveal 1600ms var(--qc-ease-out) both;
}

.qc-banner[data-kind="arena"] .qc-banner__text {
  font-size: calc(var(--banner-size) * 0.8);
  font-weight: 200;
  letter-spacing: 0.56em;
  text-indent: 0.56em;
  text-shadow: 0 0 36px color-mix(in oklab, var(--tier) 45%, transparent);
}

/* Longer rules, should an arena announcement ever carry a subject. */
.qc-banner[data-kind="arena"] .qc-banner__subject::before,
.qc-banner[data-kind="arena"] .qc-banner__subject::after {
  width: 150px;
}

/* Arena event warning: heavier, smaller gold type that pulses gently once it
   has landed. The words ("… incoming") carry the warning; colour reinforces. */
.qc-banner[data-kind="event"] .qc-banner__text {
  font-size: calc(var(--banner-size) * 0.72);
  font-weight: 400;
  letter-spacing: 0.24em;
  text-indent: 0.24em;
  text-shadow: 0 0 26px color-mix(in oklab, var(--tier) 50%, transparent);
  animation: qc-event-pulse 1200ms ease-in-out 720ms infinite alternate;
}

@keyframes qc-banner-in {
  0% { opacity: 0; transform: scale(1.22); filter: blur(10px); }
  55% { opacity: 1; filter: blur(0); }
  100% { opacity: 1; transform: scale(1); }
}

@keyframes qc-banner-collapse {
  0% { opacity: 0; transform: scale(2.2); filter: blur(18px); }
  60% { opacity: 1; transform: scale(0.96); filter: blur(0); }
  100% { opacity: 1; transform: scale(1); }
}

@keyframes qc-banner-rule {
  from { width: 0; opacity: 0; }
}

@keyframes qc-banner-reveal {
  0% { opacity: 0; transform: scale(1.04); filter: blur(14px); }
  100% { opacity: 1; transform: scale(1); filter: blur(0); }
}

@keyframes qc-event-pulse {
  to { opacity: 0.62; }
}

/* ------------------------------------------------------------ respawn --- */

/* Shares the banner's row and sits clear below the tallest banner, so the two
   never overlap. No transform on this element: its ::before is fixed to the
   viewport. */
.qc-respawn {
  grid-column: 1 / -1;
  grid-row: 2;
  justify-self: center;
  align-self: start;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  margin-top: calc(8vh + var(--banner-size) + 84px);
  padding: 26px 64px;
  text-align: center;
  /* A soft dark halo keeps the text legible over busy arena effects. */
  background: radial-gradient(closest-side, color-mix(in oklab, var(--qc-space-0) 78%, transparent), transparent);
  animation: qc-respawn-fade 600ms ease-out both;
}

/* A faint danger vignette while destroyed, behind every HUD region. */
.qc-respawn::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background: radial-gradient(
    ellipse 75% 70% at 50% 50%,
    transparent 45%,
    color-mix(in oklab, var(--qc-danger) 9%, color-mix(in oklab, var(--qc-space-0) 55%, transparent))
  );
  pointer-events: none;
}

.qc-respawn__title {
  margin: 0;
  font-size: clamp(18px, 2.2vw, 28px);
  font-weight: 200;
  line-height: 1;
  letter-spacing: 0.34em;
  text-indent: 0.34em; /* balances the trailing letter-spacing */
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--qc-text);
  text-shadow: 0 0 24px color-mix(in oklab, var(--qc-space-0) 80%, transparent);
}

.qc-respawn__killer {
  font-weight: 400;
  color: color-mix(in oklab, var(--qc-danger) 70%, var(--qc-text));
}

.qc-respawn__rule {
  display: block;
  width: min(380px, 70vw);
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--qc-danger) 50%, transparent);
  filter: drop-shadow(0 0 5px var(--qc-danger));
  animation: qc-banner-rule 720ms var(--qc-ease-out) both;
}

.qc-respawn__countdown {
  margin: 0;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.26em;
  text-indent: 0.26em;
  text-transform: uppercase;
  color: var(--qc-text-dim);
}

.qc-respawn__seconds {
  display: inline-block;
  min-width: 1.2em;
  font: 500 15px/1 var(--qc-font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  color: var(--qc-text);
}

/* The ship is gone: its own panels step back. */
.qc-hud:has(> .qc-respawn) .qc-hud__vitals,
.qc-hud:has(> .qc-respawn) .qc-hud__special {
  opacity: 0.55;
}

@keyframes qc-respawn-fade {
  from { opacity: 0; }
}

/* --------------------------------------------------------- connection --- */

/* Latency readout: a small pill in the standing readout's cell, hanging just
   under it (120px tall: 17px top row, 40px place, 37px rivals, 6px gaps, 17px
   padding, 2px border), so it reads as part of it and stays clear of the
   announcer log and kill feed on either side. With no readout it moves up. */
.qc-hud__net {
  --net: var(--qc-danger);
  grid-column: 2;
  grid-row: 1;
  justify-self: center;
  align-self: start;
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 128px;
  padding: 5px 11px 5px 10px;
  font: 500 11px/1 var(--qc-font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--qc-text-dim);
  background: var(--qc-glass);
  border: 1px solid var(--qc-line);
  border-radius: 999px;
  backdrop-filter: blur(8px);
}

.qc-hud:not(:has(> .qc-hud__match)) .qc-hud__net {
  margin-top: 0;
}

.qc-hud__net[data-quality="good"] { --net: var(--qc-accent-2); }
.qc-hud__net[data-quality="fair"] { --net: var(--qc-rarity-legendary); }
.qc-hud__net[data-quality="poor"] { --net: var(--qc-danger); }

/* Three ascending bars. How many are lit says the quality without colour. */
.qc-net__bars {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 10px;
}

.qc-net__bar {
  width: 3px;
  height: 40%;
  background: color-mix(in oklab, var(--qc-text) 16%, transparent);
  border-radius: 1px;
}

.qc-net__bar:nth-child(2) { height: 70%; }
.qc-net__bar:nth-child(3) { height: 100%; }

.qc-net__bar[data-lit] {
  background: var(--net);
  box-shadow: 0 0 6px color-mix(in oklab, var(--net) 55%, transparent);
}

.qc-hud__net[data-quality="poor"] .qc-net__latency {
  color: color-mix(in oklab, var(--qc-danger) 55%, var(--qc-text));
}

.qc-hud__net[data-state="reconnecting"] .qc-net__bars {
  animation: qc-clock-warn 800ms ease-in-out infinite alternate;
}

/* Reconnecting: a glass panel in the banner's place over a faint veil, short
   enough to clear the respawn overlay beneath it. The glass lives on the inner
   status element: a backdrop-filter here would trap the fixed ::before veil. */
.qc-reconnecting {
  grid-column: 1 / -1;
  grid-row: 2;
  justify-self: center;
  align-self: start;
  margin-top: 8vh;
  animation: qc-respawn-fade 400ms ease-out both;
}

/* The world is on hold: dim the whole arena, behind every HUD region. */
.qc-reconnecting::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background: color-mix(in oklab, var(--qc-space-0) 45%, transparent);
  pointer-events: none;
}

.qc-reconnecting__status {
  display: grid;
  justify-items: center;
  gap: 10px;
  padding: 16px 32px 17px;
  text-align: center;
  background: var(--qc-glass);
  border: 1px solid var(--qc-line-strong);
  border-radius: var(--qc-radius);
  backdrop-filter: blur(10px);
}

.qc-reconnecting__title {
  margin: 0;
  font-size: clamp(16px, 1.7vw, 21px);
  font-weight: 300;
  line-height: 1;
  letter-spacing: 0.34em;
  text-indent: 0.34em; /* balances the trailing letter-spacing */
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--qc-text);
}

/* Indeterminate progress: a bright segment sweeping along a hairline. */
.qc-reconnecting__scan {
  position: relative;
  display: block;
  width: min(240px, 60vw);
  height: 2px;
  overflow: hidden;
  background: var(--qc-line);
  border-radius: 1px;
}

.qc-reconnecting__scan::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 36%;
  background: linear-gradient(90deg, transparent, var(--qc-accent-2), transparent);
  animation: qc-reconnect-scan 1300ms ease-in-out infinite;
}

.qc-reconnecting__hint {
  margin: 0;
  font-size: 12px;
  line-height: 1.3;
  color: var(--qc-text-dim);
}

/* While reconnecting the overlay takes the banner's place (visually only: the
   banner is aria-hidden anyway), and the ship's own panels step back. */
.qc-hud:has(> .qc-reconnecting) .qc-hud__banner-slot {
  visibility: hidden;
}

.qc-hud:has(> .qc-reconnecting) .qc-hud__vitals,
.qc-hud:has(> .qc-reconnecting) .qc-hud__special {
  opacity: 0.55;
}

@keyframes qc-reconnect-scan {
  from { transform: translateX(-100%); }
  to { transform: translateX(280%); }
}

/* ------------------------------------------------------------ minimap --- */

/* Its own grid row, directly above the vitals. The app sizes the canvas's
   backing store and draws into it; only its CSS size is set here. */
.qc-hud__minimap {
  --minimap-size: 176px;
  grid-column: 1;
  grid-row: 3;
  align-self: end;
  justify-self: start;
  display: grid;
  gap: 10px;
  padding: 12px 14px 14px;
}

.qc-minimap__name {
  max-width: var(--minimap-size);
  overflow: hidden;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.2em;
  text-overflow: ellipsis;
  text-transform: uppercase;
  white-space: nowrap;
  color: color-mix(in oklab, var(--qc-accent-2) 75%, var(--qc-text));
}

.qc-minimap__canvas {
  display: block;
  width: var(--minimap-size);
  height: var(--minimap-size);
  background: color-mix(in oklab, var(--qc-space-0) 60%, transparent);
  border-radius: var(--qc-radius-sm);
}

/* ------------------------------------------------------------- vitals --- */

.qc-hud__minimap,
.qc-hud__vitals,
.qc-hud__special {
  background: var(--qc-glass);
  border: 1px solid var(--qc-line);
  border-radius: var(--qc-radius);
  backdrop-filter: blur(8px);
}

.qc-hud__vitals {
  grid-column: 1;
  grid-row: 4;
  align-self: end;
  justify-self: start;
  display: grid;
  gap: 9px;
  width: min(360px, 100%);
  padding: 14px 16px 15px;
}

.qc-hud__ship {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ship);
}

.qc-hud__row {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
}

.qc-hud__row-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--qc-text-faint);
}

.qc-hud__row-value {
  min-width: 92px;
  font: 500 12px/1 var(--qc-font-mono);
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--qc-text-dim);
}

/* -------------------------------------------------------------- meter --- */

.qc-meter {
  --fill: 0;
  --meter: var(--qc-text);
  position: relative;
  height: 8px;
  overflow: hidden;
  background: color-mix(in oklab, var(--qc-text) 8%, transparent);
  border-radius: 4px;
}

.qc-meter__fill {
  position: absolute;
  inset: 0;
  background: var(--meter);
  border-radius: inherit;
  transform: scaleX(var(--fill));
  transform-origin: left center;
  transition: transform var(--qc-dur) var(--qc-ease-out), background-color var(--qc-dur);
}

.qc-meter[data-kind="hull"] { --meter: var(--qc-hull); }

.qc-meter[data-kind="hull"][data-low] {
  --meter: var(--qc-danger);
  animation: qc-low-hull 850ms ease-in-out infinite alternate;
}

.qc-meter[data-kind="xp"] .qc-meter__fill {
  background: linear-gradient(90deg, var(--qc-accent), var(--qc-xp));
}

.qc-meter[data-kind="special"] {
  height: 5px;
}

.qc-meter[data-kind="special"] { --meter: var(--ship); }

@keyframes qc-low-hull {
  from { box-shadow: 0 0 0 0 transparent; }
  to { box-shadow: 0 0 14px 1px color-mix(in oklab, var(--qc-danger) 70%, transparent); }
}

/* ------------------------------------------------------------ special --- */

.qc-hud__special {
  grid-column: 2;
  grid-row: 4;
  align-self: end;
  display: grid;
  gap: 10px;
  width: 240px;
  padding: 12px 14px;
  transition: border-color var(--qc-dur), box-shadow var(--qc-dur);
}

.qc-hud__special[data-state="ready"] {
  border-color: color-mix(in oklab, var(--ship) 55%, transparent);
  box-shadow: 0 0 26px -14px var(--ship);
}

.qc-hud__special[data-state="active"] {
  border-color: var(--ship);
}

.qc-hud__special-head,
.qc-hud__special-body {
  display: flex;
  align-items: center;
  gap: 10px;
}

.qc-hud__special-body .qc-meter {
  flex: 1;
}

.qc-hud__special-name {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.qc-hud__special-status {
  margin-left: auto;
  font: 600 11px/1 var(--qc-font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--qc-text-dim);
}

.qc-hud__special[data-state="cooldown"] .qc-hud__special-status {
  text-transform: none; /* "4s", not "4S" */
}

.qc-hud__special[data-state="ready"] .qc-hud__special-status,
.qc-hud__special[data-state="active"] .qc-hud__special-status {
  color: var(--ship);
}

/* --------------------------------------------------------------- tray --- */

.qc-hud__tray {
  grid-column: 3;
  grid-row: 3 / 5; /* level with the minimap and vitals, bottom-aligned */
  justify-self: end;
  align-self: end;
  display: grid;
  gap: 6px;
  width: min(320px, 100%);
  pointer-events: auto; /* …except the tray, which is clickable */
  animation: qc-hud-in-right 420ms var(--qc-ease-out) both;
}

.qc-tray__header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 2px 2px;
}

.qc-tray__title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--qc-rarity-legendary);
}

.qc-tray__title::before {
  content: "";
  width: 6px;
  height: 6px;
  background: currentColor;
  border-radius: 50%;
  box-shadow: 0 0 10px currentColor;
  animation: qc-tray-pulse 1.4s ease-in-out infinite;
}

.qc-tray__more {
  margin-left: auto;
  font: 600 11px/1 var(--qc-font-mono);
  color: var(--qc-text-dim);
}

.qc-tray__cards {
  display: grid;
  gap: 5px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.qc-tray__details {
  justify-self: end;
  margin-top: 2px;
  background: var(--qc-glass);
}

@keyframes qc-tray-pulse {
  50% { opacity: 0.35; }
}

/* ------------------------------------------------------------ shared --- */

@keyframes qc-hud-in-left {
  from { opacity: 0; transform: translateX(-14px); }
}

@keyframes qc-hud-in-right {
  from { opacity: 0; transform: translateX(14px); }
}

/* Small viewports: the clock gets its own row, and the left column stacks
   minimap, vitals and special beside the tray. */
@media (max-width: 860px) {
  .qc-hud {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto auto minmax(0, 1fr) auto auto auto;
  }

  .qc-hud__match { grid-column: 1 / -1; grid-row: 1; }
  .qc-hud__announcer { grid-row: 2; }
  .qc-hud__feed { grid-column: 2; grid-row: 2; }
  .qc-hud__banner-slot,
  .qc-respawn,
  .qc-reconnecting { grid-row: 3; }
  .qc-hud__net { grid-column: 1 / -1; } /* still under the clock, in its full-width row */
  .qc-hud__minimap { grid-row: 4; --minimap-size: 128px; }
  .qc-hud__vitals { grid-row: 5; }
  .qc-hud__special { grid-column: 1; grid-row: 6; width: auto; }
  .qc-hud__tray { grid-column: 2; grid-row: 4 / 7; }
}

/* Very narrow, or narrow and short: centred text is wider than the gap beside
   the minimap, or the centre row has little height, so the minimap, banner and
   respawn type all compact to stay clear of each other. */
@media (max-width: 700px), (max-width: 860px) and (max-height: 760px) {
  .qc-hud { --banner-size: 22px; }
  .qc-hud__minimap { --minimap-size: 104px; }

  .qc-minimap__name {
    font-size: 9px;
    letter-spacing: 0.12em;
  }

  .qc-respawn {
    margin-top: calc(8vh + var(--banner-size) + 56px);
    padding: 12px 16px;
  }

  .qc-respawn__title {
    font-size: 15px;
    letter-spacing: 0.2em;
    text-indent: 0.2em;
  }

  /* Short enough to stay clear of the compacted respawn overlay. */
  .qc-reconnecting__status {
    gap: 7px;
    padding: 10px 16px 11px;
  }

  .qc-reconnecting__title {
    font-size: 14px;
    letter-spacing: 0.2em;
    text-indent: 0.2em;
  }

  .qc-reconnecting__hint {
    font-size: 11px;
  }
}

/* A landscape phone: wide enough for the three-column layout, but too short
   for the stacked one, and its bottom corners belong to the player's thumbs
   (client/TouchControls draws its sticks there). So the HUD stays in three
   columns, compacts hard, and keeps the minimap off the left corner. */
@media (max-height: 520px) and (min-width: 560px) {
  .qc-hud {
    --banner-size: 20px;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr) auto;
    gap: 6px 12px;
    padding: 8px 12px 10px;
    font-size: 11px;
  }

  .qc-hud__announcer { grid-column: 1; grid-row: 1; }
  .qc-hud__match { grid-column: 2; grid-row: 1; }
  .qc-hud__net { grid-column: 2; grid-row: 1; }
  .qc-hud__feed { grid-column: 3; grid-row: 1; }
  .qc-hud__banner-slot,
  .qc-respawn,
  .qc-reconnecting { grid-column: 1 / -1; grid-row: 2; }

  /* Both bottom corners are a thumb, so nothing may sit in one. The minimap
     goes up to the top-left, and the two panels a player reads mid-fight flank
     the centre, inboard of where the sticks are drawn. */
  .qc-hud__minimap { --minimap-size: 76px; grid-column: 1; grid-row: 1; justify-self: start; align-self: start; }
  .qc-hud__announcer { grid-column: 1; grid-row: 2; align-self: start; }
  .qc-hud__vitals { grid-column: 1; grid-row: 3; justify-self: end; }
  .qc-hud__special { grid-column: 3; grid-row: 3; justify-self: start; width: auto; }
  .qc-hud__tray { grid-column: 3; grid-row: 2; justify-self: end; align-self: end; }

  .qc-minimap__name { font-size: 8px; letter-spacing: 0.1em; }
  .qc-respawn { margin-top: 0; padding: 8px 14px; }
  .qc-respawn__title { font-size: 14px; letter-spacing: 0.18em; text-indent: 0.18em; }
}
