:root {
  --navy-deep: #171b2e;
  --navy: #2f3b63;
  --shirt: #fffaf6;
  --white: #ffffff;
  --hair-blonde: #f4c875;
  --hair-blonde-soft: #ffe8b7;
  --hair-pink: #ef8fa8;
  --hair-pink-dark: #b9345b;
  --hair-pink-soft: #f9ceda;
  --tie-red: #b82f42;
  --skirt-blue: #8fcde0;
  --skirt-blue-soft: #d8eff6;
  --shadow: rgba(23, 27, 46, 0.22);
  --shadow-strong: rgba(23, 27, 46, 0.58);
  --shirt-glow: rgba(255, 250, 246, 0.95);
  --white-glow: rgba(255, 255, 255, 0.8);
  --navy-pattern: rgba(23, 27, 46, 0.16);
  --hair-pink-pattern: rgba(239, 143, 168, 0.1);
  --hair-pink-haze: rgba(239, 143, 168, 0.16);
  --shirt-stripe: rgba(255, 250, 246, 0.34);
  --hair-pink-ring: rgba(239, 143, 168, 0.12);
  --dialog-backdrop: rgba(23, 27, 46, 0.98);
  --casino-panel: rgba(23, 27, 46, 0.94);
  --casino-light-glow: rgba(244, 200, 117, 0.72);
  --casino-pink-glow: rgba(239, 143, 168, 0.58);
  --achievement-stage-clear: rgba(23, 27, 46, 0.08);
  --achievement-stage-shade: rgba(23, 27, 46, 0.24);
  --jackpot-shade: rgba(23, 27, 46, 0.86);
  --jackpot-clear: rgba(23, 27, 46, 0.08);
  --confetti-gold: #ffd66f;
  --confetti-pink: #ff7faa;
  --confetti-blue: #7dd9f2;
  --confetti-white: #fffaf6;
  --holo-pink: #ff8fd1;
  --holo-gold: #ffe27a;
  --holo-mint: #8ff0c8;
  --holo-sky: #8fd3ff;
  --holo-violet: #c39bff;
  --holo-glow: rgba(255, 143, 209, 0.72);

  /* Nana Collection naval palette (base naval de dia). */
  --kc-navy-deep: #0e1c36;
  --kc-navy: #1d3461;
  --kc-steel: #4b6a8f;
  --kc-sea: #9fc1d6;
  --kc-sea-soft: #dcebf2;
  --kc-paper: #f4ecd8;
  --kc-paper-dark: #e2d3ad;
  --kc-ink: #1f2430;
  --kc-brass: #c8a04a;
  --kc-brass-light: #ecd48d;
  --kc-brass-dark: #8a6a26;
  --kc-silver: #c9d2dc;
  --kc-signal: #b0303b;
  /* Signal red for text on the night plates, where --kc-signal reads at 2.6:1. */
  --kc-signal-light: #ec7a83;
  /* What a character's pieces add on her ficha, the green beside that red. */
  --kc-gain: #8fd3a2;
  --kc-hex: rgba(29, 52, 97, 0.08);
  --kc-hex-light: rgba(244, 236, 216, 0.08);
  --kc-shadow: rgba(14, 28, 54, 0.45);
  --kc-scrim: rgba(14, 28, 54, 0.72);
  --kc-brass-glow: rgba(236, 212, 141, 0.7);
  --kc-sea-glow: rgba(159, 193, 214, 0.6);
  --kc-night: rgba(14, 28, 54, 0.92);
  --kc-paper-glow: rgba(244, 236, 216, 0.85);
  /* The white the Vinicius Pirasoft logo is drawn on (1.120), so the screen
     under the logo is the logo's own and its square never shows. */
  --kc-splash-paper: #fdfdfd;
  --kc-font-mincho: "Shippori Mincho B1", "Yu Mincho", "Hiragino Mincho ProN", Georgia, serif;
  --kc-font-ui: "Segoe UI", system-ui, sans-serif;

  /* Type scale for the collection's small text, where the floor matters: no
     text a visitor reads in the collection renders below 1rem (16px), which
     the owner asked for in 1.86 after the manual read too small. The two
     smallest steps are the floor itself; the others grow a little on a large
     screen. Larger headings keep their own per-rule clamps. */
  --kc-type-3xs: 1rem;
  --kc-type-2xs: 1rem;
  --kc-type-xs: clamp(1rem, 2vmin, 1.06rem);
  --kc-type-s: clamp(1rem, 2.2vmin, 1.12rem);

  /* Radius: the naval skin is flat; only counts and meters stay round. */
  --kc-radius-flat: 0.25rem;
  --kc-radius: 0.3rem;
  --kc-radius-nanavio: 0.55rem;
  --kc-radius-pill: 999px;

  /* Controls: 44px is the hit-target floor, relaxed only on short viewports
     where the dialog must stay scrollbar-free. */
  --kc-control-min: 2.75rem;
  --kc-control-compact: 2.25rem;
  --kc-focus-width: 0.18rem;
  --kc-select-width: 0.32rem;

  /* Stacking inside the collection dialog, named so the layering is auditable. */
  --kc-z-banner: 20;
  --kc-z-tutorial: 30;
  --kc-z-loading: 40;
  /* The shared close button, which the boot and the tutorial scrim above must
     both stay under: it is the one control either of them leaves live, and an
     overlay painted over it shuts a first visitor inside the dialog. It is a
     bare 50 in the site-wide .dialog-close-form rule, so it is restated here
     rather than moved, to keep the ceiling visible in the scale it caps. */
  --kc-z-close: 50;
  --kc-z-cinematic: 45;
  --kc-z-viewer: 60;
  --kc-z-tooltip: 70;
  /* The Vinicius Pirasoft opening, over everything: it is the way in, and while
     it shows the close button, the language menu and the login's music plate
     are taken off the screen rather than painted over (.is-splash). */
  --kc-z-splash: 80;
}

/* The collection's languages the webfont does not write (1.118): the build
   ships Shippori Mincho B1's latin subsets alone, so Russian, Japanese and
   Chinese take system stacks of their own, keyed on the lang the dialog
   carries (and on an option of the language menu marked with its own).
   Russian drops the mincho altogether, which has no Cyrillic and would set the
   Latin of a word apart from the rest of it; Japanese and Chinese keep it in
   front for their Latin. The Chinese stack never names a Japanese font, which
   draws many Han characters another way — the reason shipping the webfont's
   Japanese subsets would mean taking it out of the Chinese stack. */
.collection-dialog:lang(ru),
.collection-dialog :lang(ru) {
  --kc-font-mincho: Georgia, "Noto Serif", "PT Serif", "Times New Roman", serif;
}

.collection-dialog:lang(ja),
.collection-dialog :lang(ja) {
  --kc-font-mincho: "Shippori Mincho B1", "Yu Mincho", YuMincho, "Hiragino Mincho ProN", "Noto Serif JP", "Noto Serif CJK JP", serif;
  --kc-font-ui: "Segoe UI", "Yu Gothic UI", "Hiragino Sans", Meiryo, "Noto Sans JP", "Noto Sans CJK JP", system-ui, sans-serif;
}

.collection-dialog:lang(zh),
.collection-dialog :lang(zh) {
  --kc-font-mincho: "Shippori Mincho B1", "Noto Serif SC", "Noto Serif CJK SC", "Source Han Serif SC", "Songti SC", STSong, SimSun, serif;
  --kc-font-ui: "Segoe UI", "PingFang SC", "Microsoft YaHei UI", "Microsoft YaHei", "Noto Sans SC", "Noto Sans CJK SC", system-ui, sans-serif;
}

/* Japanese and Chinese have no italic and spread no wider for tracking: a
   slanted or spaced-out ideograph reads as a fault. The tracked labels of the
   collection are many and some are specific, so the reset takes !important,
   as the reduced-motion block, [hidden] and .visually-hidden already do: it is
   a blanket rule about a script, not a tweak of one component. */
.collection-dialog:is(:lang(ja), :lang(zh)) * {
  font-style: normal !important;
  letter-spacing: 0 !important;
}

.collection-dialog:is(:lang(ja), :lang(zh)) :is(em, i) {
  font-weight: bolder;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

[hidden] {
  display: none !important;
}

html {
  width: 100%;
  height: 100%;
  min-width: 20rem;
  overflow: hidden;
  color-scheme: light;
}

body {
  width: 100%;
  height: 100%;
  margin: 0;
  overflow: hidden;
  color: var(--navy-deep);
  font-family: "Arial Rounded MT Bold", "Trebuchet MS", "Segoe UI", sans-serif;
  background:
    radial-gradient(circle at 14% 18%, var(--shirt-glow) 0 0.45rem, transparent 0.5rem),
    radial-gradient(circle at 88% 78%, var(--white-glow) 0 0.3rem, transparent 0.35rem),
    linear-gradient(135deg, var(--hair-pink-soft), var(--hair-blonde-soft) 48%, var(--skirt-blue-soft));
}

body::before,
body::after {
  position: fixed;
  inset: 0;
  z-index: -2;
  content: "";
  pointer-events: none;
}

body::before {
  background-image:
    radial-gradient(circle, var(--navy-pattern) 0 0.08rem, transparent 0.09rem),
    linear-gradient(45deg, transparent 47%, var(--hair-pink-pattern) 48% 52%, transparent 53%);
  background-size: 1.15rem 1.15rem, 4rem 4rem;
  opacity: 0.55;
}

body::after {
  z-index: -1;
  background: radial-gradient(circle at center, transparent 35%, var(--hair-pink-haze));
}

button,
input {
  font: inherit;
}

button {
  color: inherit;
}

.visually-hidden {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  overflow: hidden !important;
  clip: rect(0 0 0 0) !important;
  clip-path: inset(50%) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

.page {
  position: relative;
  display: grid;
  width: 100%;
  height: 100svh;
  height: 100dvh;
  min-height: 0;
  padding: clamp(0.45rem, 2.6vmin, 1.5rem);
  overflow: hidden;
  place-items: center;
}

.card {
  position: relative;
  z-index: 1;
  display: grid;
  width: min(100%, 61rem);
  max-height: 100%;
  padding: clamp(0.7rem, 2.5vmin, 1.7rem) clamp(0.65rem, 3.8vw, 3rem);
  overflow: hidden;
  align-content: center;
  gap: clamp(0.45rem, 1.8dvh, 1.15rem);
  text-align: center;
  background:
    linear-gradient(var(--shirt), var(--shirt)) padding-box,
    linear-gradient(115deg, var(--navy-deep), var(--hair-pink), var(--tie-red), var(--navy)) border-box;
  border: 0.24rem solid transparent;
  border-radius: clamp(1rem, 3vmin, 2.4rem);
  box-shadow: 0.45rem 0.5rem 0 var(--navy-deep), 0 1rem 2.5rem var(--shadow);
  isolation: isolate;
}

.card::before {
  position: absolute;
  inset: 0 0 auto;
  height: clamp(0.55rem, 1.6dvh, 1rem);
  background:
    linear-gradient(135deg, transparent 25%, var(--shirt-stripe) 25% 50%, transparent 50% 75%, var(--shirt-stripe) 75%) 0 0 / 1rem 1rem,
    var(--navy);
  content: "";
}

.card::after {
  position: absolute;
  right: -3rem;
  bottom: -3rem;
  z-index: -1;
  width: 9rem;
  height: 9rem;
  border: 1.25rem solid var(--hair-pink-ring);
  border-radius: 50%;
  content: "";
}

.main-primary,
.main-details {
  display: grid;
  min-width: 0;
  align-content: center;
  justify-items: center;
}

.main-primary {
  gap: clamp(0.35rem, 1.2dvh, 0.7rem);
}

.main-details {
  gap: clamp(0.3rem, 1dvh, 0.65rem);
}

.sparkle {
  position: absolute;
  color: var(--hair-pink);
  font-size: clamp(1.1rem, 4vmin, 2.5rem);
  line-height: 1;
  user-select: none;
  animation: twinkle 2.2s ease-in-out infinite;
}

.sparkle-left {
  top: clamp(0.8rem, 3vmin, 2rem);
  left: clamp(0.8rem, 4vw, 3.5rem);
  transform: rotate(-12deg);
}

.eyebrow {
  display: inline-block;
  margin: 0;
  padding: 0.28rem 0.72rem;
  color: var(--shirt);
  font-size: clamp(0.56rem, 1.6vmin, 0.78rem);
  font-weight: 900;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  background: var(--navy);
  border-radius: 999px;
  transform: rotate(-1deg);
}

h1 {
  max-width: 18ch;
  margin: 0;
  font-size: clamp(1.7rem, 7.2vmin, 4.6rem);
  line-height: 0.98;
  letter-spacing: -0.05em;
  overflow-wrap: anywhere;
  text-wrap: balance;
}

.verdict {
  position: relative;
  display: inline-grid;
  margin: clamp(0.1rem, 0.8dvh, 0.55rem) 0;
  place-items: center;
  transform: rotate(-2deg);
  animation: float 3.4s ease-in-out infinite;
  isolation: isolate;
}

.verdict::before {
  position: absolute;
  z-index: 0;
  width: 112%;
  height: 86%;
  background: var(--hair-blonde);
  border: 0.18rem solid var(--navy-deep);
  border-radius: 48% 52% 45% 55% / 52% 44% 56% 48%;
  box-shadow: 0.25rem 0.28rem 0 var(--navy-deep);
  content: "";
}

.verdict::after {
  position: absolute;
  z-index: 0;
  width: 145%;
  aspect-ratio: 1;
  opacity: 0;
  background: repeating-conic-gradient(
    from 0deg,
    var(--hair-blonde-soft) 0 10deg,
    transparent 10deg 22deg
  );
  border-radius: 50%;
  content: "";
  pointer-events: none;
}

.answer {
  position: relative;
  z-index: 2;
  margin: 0;
  padding: 0.03em 0.3em 0.1em;
  color: var(--tie-red);
  font-size: clamp(3.8rem, 16vmin, 9.5rem);
  font-weight: 1000;
  line-height: 0.86;
  letter-spacing: -0.08em;
  text-shadow: 0.035em 0.035em 0 var(--shirt), 0.06em 0.06em 0 var(--navy-deep);
  appearance: none;
  background: transparent;
  border: 0;
  cursor: pointer;
}

.answer[aria-disabled="true"] {
  cursor: wait;
}

.dramatic-paper {
  position: absolute;
  z-index: 1;
  width: 28%;
  height: 34%;
  opacity: 0;
  background:
    linear-gradient(var(--navy-pattern), var(--navy-pattern)) 50% 34% / 62% 0.08rem no-repeat,
    linear-gradient(var(--hair-pink-pattern), var(--hair-pink-pattern)) 50% 58% / 72% 0.08rem no-repeat,
    var(--shirt);
  border: 0.08rem solid var(--navy-deep);
  border-radius: 0.12rem;
  box-shadow: 0.08rem 0.1rem 0 var(--shadow-strong);
  pointer-events: none;
}

.dramatic-paper-one {
  top: 6%;
  left: -22%;
  rotate: -14deg;
}

.dramatic-paper-two {
  top: -22%;
  right: -13%;
  rotate: 11deg;
}

.dramatic-paper-three {
  right: -25%;
  bottom: -10%;
  rotate: 17deg;
}

.dramatic-no-status {
  min-height: 1.35em;
  margin: -0.12rem 0 0;
  color: var(--navy);
  font-size: clamp(0.56rem, 1.7vmin, 0.82rem);
  font-weight: 1000;
  line-height: 1.2;
  letter-spacing: 0.025em;
  text-wrap: balance;
}

.question-heading[data-name-size="long"] {
  max-width: 22ch;
  font-size: clamp(1.45rem, 6.2vmin, 3.75rem);
}

.editable-name {
  padding: 0 0.07em;
  cursor: text;
  border-radius: 0.1em;
  outline: 0 solid transparent;
  box-decoration-break: clone;
  caret-color: var(--tie-red);
  overflow-wrap: anywhere;
  text-decoration-color: var(--hair-pink);
  text-decoration-line: underline;
  text-decoration-style: dotted;
  text-decoration-thickness: 0.07em;
  text-underline-offset: 0.13em;
  transition: background-color 140ms ease, outline-color 140ms ease;
  -webkit-box-decoration-break: clone;
}

.editable-name:hover {
  background: var(--hair-pink-pattern);
}

.editable-name:focus {
  background: var(--hair-blonde-soft);
  outline: 0.08em solid var(--skirt-blue);
  outline-offset: 0.06em;
  text-decoration-color: transparent;
}

.question-suffix {
  white-space: nowrap;
}

.verdict[data-stage="1"] .answer {
  animation: audit-wobble 360ms ease-in-out;
}

.verdict[data-stage="2"]::before {
  animation: audit-pulse 520ms ease-in-out;
}

.verdict[data-stage="3"] .dramatic-paper {
  opacity: 1;
  animation: audit-paper 620ms ease-out both;
}

.verdict[data-stage="4"]::after,
.verdict[data-stage="5"]::after {
  opacity: 0.72;
  animation: audit-rays 1.4s linear infinite;
}

.verdict[data-stage="4"] .answer {
  animation: audit-spotlight 560ms ease-in-out both;
}

.verdict[data-stage="5"] {
  animation: audit-final 460ms cubic-bezier(0.2, 1.4, 0.45, 1) both;
}

.verdict[data-stage="5"] + .dramatic-no-status {
  width: min(100%, 28rem);
  padding: 0.28rem 0.55rem;
  color: var(--shirt);
  background: var(--tie-red);
  border: 0.12rem solid var(--navy-deep);
  border-radius: 0.35rem;
  box-shadow: 0.14rem 0.16rem 0 var(--navy-deep);
  transform: rotate(-1deg);
}

.main-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: clamp(0.45rem, 1.6vmin, 0.8rem);
}

.rich-button,
.achievements-button,
.coffee-oracle-button,
.certificate-button,
.collection-button,
.dialog-close,
.casino-music-toggle,
.jackpot-continue,
.classroom-open-button,
.classroom-answer,
.classroom-continue,
.certificate-action,
.collection-action,
.release-page-button {
  min-height: 2.5rem;
  font-weight: 1000;
  border: 0.14rem solid var(--navy-deep);
  border-radius: 999px;
  box-shadow: 0.18rem 0.22rem 0 var(--navy-deep);
  cursor: pointer;
}

.rich-button,
.achievements-button,
.coffee-oracle-button,
.certificate-button,
.collection-button {
  padding: 0.52rem 0.9rem;
  font-size: clamp(0.68rem, 2vmin, 0.92rem);
  letter-spacing: 0.04em;
}

.rich-button {
  background: var(--hair-pink);
  transform: rotate(-1deg);
}

.achievements-button {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  color: var(--shirt);
  background: var(--navy);
  transform: rotate(1deg);
}

.coffee-oracle-button {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  background: var(--hair-blonde-soft);
  transform: rotate(-0.35deg);
}

.certificate-button {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  color: var(--shirt);
  background: var(--tie-red);
  transform: rotate(0.45deg);
}

.collection-button {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  color: var(--navy-deep);
  background: linear-gradient(120deg, var(--hair-blonde), var(--hair-pink));
  transform: rotate(-0.6deg);
}

/* Birthday: the collection button is all the card holds, so the card hugs it. */
.is-nanaversario .card {
  width: auto;
  grid-template-columns: auto;
  padding-inline: clamp(3rem, 12vw, 6rem);
}

.is-nanaversario .collection-button {
  padding: 0.75rem 1.4rem;
  font-size: clamp(1rem, 3.4vmin, 1.45rem);
}

.collection-nanacote-count {
  min-width: 1.6rem;
  padding: 0.12rem 0.32rem;
  color: var(--shirt);
  font-size: 0.66rem;
  background: var(--tie-red);
  border: 0.08rem solid var(--navy-deep);
  border-radius: 999px;
}

.coffee-oracle-button[aria-expanded="true"] {
  background: var(--hair-blonde);
  transform: translateY(0.08rem);
}

.achievements-count {
  min-width: 2.5rem;
  padding: 0.16rem 0.35rem;
  color: var(--navy-deep);
  font-size: 0.66rem;
  background: var(--hair-pink-soft);
  border: 0.08rem solid var(--navy-deep);
  border-radius: 999px;
}

.rich-button:hover,
.achievements-button:hover,
.coffee-oracle-button:hover,
.certificate-button:hover,
.collection-button:hover {
  color: var(--navy-deep);
  background: var(--hair-blonde);
}

.coffee-oracle-panel {
  display: grid;
  width: min(100%, 44rem);
  padding: clamp(0.42rem, 1.25vmin, 0.7rem) clamp(0.55rem, 1.8vmin, 0.95rem);
  gap: clamp(0.16rem, 0.55dvh, 0.3rem);
  color: var(--navy-deep);
  background:
    linear-gradient(125deg, var(--hair-blonde-soft), var(--shirt), var(--hair-pink-soft));
  border: 0.12rem solid var(--navy-deep);
  border-radius: clamp(0.55rem, 1.5vmin, 0.85rem);
  box-shadow: 0.16rem 0.19rem 0 var(--navy-deep);
}

.coffee-oracle-heading,
.coffee-oracle-message {
  margin: 0;
}

.coffee-oracle-heading {
  color: var(--hair-pink-dark);
  font-size: clamp(0.5rem, 1.45vmin, 0.7rem);
  font-weight: 1000;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

.coffee-oracle-message {
  font-size: clamp(0.68rem, 1.85vmin, 0.94rem);
  font-weight: 850;
  line-height: 1.25;
  text-wrap: balance;
}

.counter-heading {
  margin: 0;
  color: var(--hair-pink-dark);
  font-size: clamp(0.62rem, 1.8vmin, 0.92rem);
  font-weight: 900;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.counter {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: clamp(0.2rem, 1vmin, 0.65rem);
  width: 100%;
}

.counter-part {
  min-width: 0;
  padding: clamp(0.38rem, 1.4vmin, 0.85rem) 0.1rem;
  background: var(--white);
  border: 0.11rem solid var(--navy-deep);
  border-radius: clamp(0.45rem, 1.5vmin, 0.85rem);
  box-shadow: 0.12rem 0.15rem 0 var(--navy-deep);
}

.counter-part:nth-child(2) {
  background: var(--hair-pink-soft);
}

.counter-part:nth-child(3) {
  background: var(--hair-blonde);
}

.counter-part:nth-child(4) {
  background: var(--skirt-blue);
}

.counter-part:nth-child(5) {
  color: var(--shirt);
  background: var(--navy);
}

.counter-value {
  display: block;
  overflow: hidden;
  font-size: clamp(1rem, 4vmin, 2.2rem);
  font-variant-numeric: tabular-nums;
  font-weight: 1000;
  letter-spacing: -0.04em;
  line-height: 1;
}

.counter-unit {
  display: block;
  margin-top: 0.18rem;
  overflow: hidden;
  font-size: clamp(0.43rem, 1.35vmin, 0.68rem);
  font-weight: 900;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

.site-footer {
  display: grid;
  gap: clamp(0.12rem, 0.5dvh, 0.3rem);
  justify-items: center;
}

.site-footer p {
  margin: 0;
}

.since {
  font-size: clamp(0.58rem, 1.65vmin, 0.82rem);
  font-weight: 800;
}

.credit,
.donation,
.version-label {
  font-size: clamp(0.5rem, 1.45vmin, 0.7rem);
  font-weight: 750;
}

.credit {
  opacity: 0.66;
}

.donation a {
  color: var(--hair-pink-dark);
  text-decoration-color: var(--hair-pink);
  text-underline-offset: 0.14em;
}

.version-label {
  letter-spacing: 0.05em;
}

.marin-decorations {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
}

.marin-gif-frame {
  position: absolute;
  display: grid;
  width: clamp(4.5rem, 8vw, 7.2rem);
  overflow: hidden;
  place-items: center;
  background: var(--shirt);
  border: 0.12rem solid var(--navy-deep);
  border-radius: 0.75rem;
  box-shadow: 0.2rem 0.23rem 0 var(--navy-deep);
  pointer-events: auto;
}

.marin-gif-frame a,
.marin-gif-frame img {
  display: block;
  width: 100%;
  height: 100%;
}

.marin-gif-frame img {
  object-fit: cover;
}

.marin-gif-left {
  top: 16%;
  left: max(0.35rem, calc(50% - 38rem));
  transform: rotate(-4deg);
}

.marin-gif-right {
  right: max(0.35rem, calc(50% - 38rem));
  bottom: 16%;
  transform: rotate(4deg);
}

.answer:focus-visible,
.rich-button:focus-visible,
.achievements-button:focus-visible,
.coffee-oracle-button:focus-visible,
.certificate-button:focus-visible,
.collection-button:focus-visible,
.dialog-close:focus-visible,
.casino-music-toggle:focus-visible,
.casino-volume:focus-visible,
.jackpot-continue:focus-visible,
.classroom-open-button:focus-visible,
.classroom-answer:focus-visible,
.classroom-continue:focus-visible,
.certificate-action:focus-visible,
.release-page-button:focus-visible,
.cheat-input:focus-visible,
.cheat-submit:focus-visible,
.donation a:focus-visible {
  outline: 0.18rem solid var(--tie-red);
  outline-offset: 0.16rem;
}

.site-dialog {
  inset: 0;
  width: 100vw;
  max-width: none;
  height: 100svh;
  height: 100dvh;
  max-height: none;
  margin: 0;
  padding: 0;
  overflow: hidden;
  color: var(--navy-deep);
  background: var(--navy-deep);
  border: 0;
}

.site-dialog::backdrop {
  background: var(--dialog-backdrop);
}

.dialog-close-form {
  position: absolute;
  top: max(0.55rem, env(safe-area-inset-top));
  right: max(0.55rem, env(safe-area-inset-right));
  z-index: 50;
  margin: 0;
  pointer-events: auto;
}

.dialog-close {
  display: grid;
  width: 2.7rem;
  min-height: 2.7rem;
  padding: 0;
  place-items: center;
  color: var(--navy-deep);
  font-size: 1.35rem;
  background: var(--hair-pink-soft);
}

/* Fullscreen WebGL casino. Text-bearing layers remain untransformed. */
.slot-machine {
  position: relative;
  isolation: isolate;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background:
    radial-gradient(circle at 50% 42%, var(--hair-pink-haze), transparent 42%),
    linear-gradient(145deg, var(--navy), var(--navy-deep));
}

.casino-canvas {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: block;
  width: 100%;
  height: 100%;
  opacity: 0;
  pointer-events: auto;
  touch-action: none;
  transition: opacity 180ms ease-out;
}

.slot-machine.is-ready .casino-canvas {
  opacity: 1;
}

.casino-interface {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
}

.casino-marquee {
  position: absolute;
  top: max(0.65rem, env(safe-area-inset-top));
  right: clamp(5rem, 18vw, 14rem);
  left: clamp(5rem, 18vw, 14rem);
  z-index: 4;
  display: grid;
  min-height: clamp(2.8rem, 9dvh, 4.5rem);
  padding: 0.45rem 0.8rem;
  align-content: center;
  color: var(--shirt);
  text-align: center;
  background: var(--casino-panel);
  border: 0.1rem solid var(--hair-blonde);
  border-radius: 0.85rem;
  box-shadow: 0 0.3rem 0 var(--hair-pink-dark), inset 0 0 0.75rem var(--shadow-strong);
}

.casino-marquee.is-logo-ready {
  padding: 0;
  background: transparent;
  border-color: transparent;
  box-shadow: none;
}

.casino-title {
  display: grid;
  margin: 0;
  place-items: center;
  font-size: clamp(1.2rem, 4.3vmin, 2.6rem);
  line-height: 1;
  text-shadow: 0.08rem 0.08rem 0 var(--hair-pink-dark);
}

.casino-title-fallback,
.casino-logo {
  grid-area: 1 / 1;
}

.casino-logo {
  display: block;
  width: 100%;
  height: clamp(2.8rem, 9dvh, 4.5rem);
  object-fit: contain;
}

.casino-marquee.is-logo-ready .casino-title-fallback {
  display: none;
}

.casino-token-hud {
  position: absolute;
  top: max(0.65rem, env(safe-area-inset-top));
  left: max(0.65rem, env(safe-area-inset-left));
  z-index: 7;
  display: grid;
  min-width: clamp(4.1rem, 10vw, 6.8rem);
  padding: 0.45rem 0.65rem;
  place-items: center;
  color: var(--navy-deep);
  background: linear-gradient(145deg, var(--hair-blonde-soft), var(--hair-blonde));
  border: 0.12rem solid var(--navy-deep);
  border-radius: 0.75rem;
  box-shadow: 0.18rem 0.22rem 0 var(--navy-deep), 0 0 0.75rem var(--casino-light-glow);
}

.casino-token-label {
  font-size: clamp(0.48rem, 1.3vmin, 0.68rem);
  font-weight: 1000;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.casino-token-value {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: clamp(0.18rem, 0.7vmin, 0.35rem);
}

.casino-chip-icon {
  display: block;
  width: clamp(1.15rem, 3.2vmin, 2rem);
  height: clamp(1.15rem, 3.2vmin, 2rem);
  aspect-ratio: 1;
  object-fit: contain;
}

.casino-token-balance {
  font-size: clamp(1.35rem, 4vmin, 2.6rem);
  font-variant-numeric: tabular-nums;
  line-height: 0.95;
}

.casino-token-storage-note {
  max-width: 9rem;
  margin: 0.2rem 0 0;
  color: var(--tie-red);
  font-size: 0.48rem;
  font-weight: 900;
}

.casino-loading {
  position: absolute;
  top: 45%;
  left: 50%;
  z-index: 5;
  margin: 0;
  padding: 0.7rem 1rem;
  color: var(--navy-deep);
  font-size: 0.78rem;
  font-weight: 900;
  background: var(--hair-blonde-soft);
  border: 0.12rem solid var(--navy-deep);
  border-radius: 0.75rem;
  translate: -50% -50%;
}

.slot-machine.is-ready .casino-loading,
.slot-machine.is-fallback .casino-loading {
  display: none;
}

.casino-reel-fallback {
  position: absolute;
  top: 34%;
  right: 24%;
  left: 24%;
  z-index: 5;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.5rem;
  padding: 0.65rem;
  background: var(--navy-deep);
  border: 0.15rem solid var(--hair-blonde);
  border-radius: 0.85rem;
}

.casino-reel-fallback:not([hidden]) {
  display: grid;
}

.fallback-reel {
  display: grid;
  min-width: 0;
  min-height: clamp(4rem, 18dvh, 8rem);
  place-items: center;
  font-size: clamp(2rem, 8vmin, 4rem);
  background: linear-gradient(var(--shirt), var(--skirt-blue-soft));
  border: 0.12rem solid var(--navy-deep);
  border-radius: 0.65rem;
}

.fallback-reel-image {
  display: block;
  width: min(84%, 6.5rem);
  max-height: clamp(3.5rem, 16dvh, 7rem);
  object-fit: contain;
}

/* Keyboard-only lever control. Pointer input is handled by WebGL raycasting. */
.slot-lever {
  position: absolute;
  top: 50%;
  right: 1px;
  z-index: 8;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  opacity: 0;
  pointer-events: none;
  background: transparent;
  border: 0;
}

.slot-lever:focus,
.slot-lever:focus-visible {
  outline: 0;
}

.fallback-lever-visual {
  display: none;
}

.slot-machine.is-fallback .slot-lever {
  top: 29%;
  right: 4%;
  width: clamp(3rem, 10vw, 5.5rem);
  height: clamp(8rem, 34dvh, 14rem);
  overflow: visible;
  clip: auto;
  clip-path: polygon(30% 100%, 70% 100%, 66% 24%, 88% 18%, 88% 0, 12% 0, 12% 18%, 34% 24%);
  opacity: 1;
  pointer-events: auto;
  cursor: pointer;
}

.slot-machine.is-fallback .fallback-lever-visual,
.fallback-lever-arm,
.fallback-lever-knob {
  position: absolute;
  display: block;
}

.slot-machine.is-fallback .fallback-lever-visual {
  inset: 0;
}

.fallback-lever-arm {
  top: 13%;
  bottom: 8%;
  left: 43%;
  width: 14%;
  background: var(--shirt);
  border: 0.08rem solid var(--navy-deep);
  border-radius: 999px;
}

.fallback-lever-knob {
  top: 0;
  left: 22%;
  width: 56%;
  aspect-ratio: 1;
  background: var(--hair-pink);
  border: 0.1rem solid var(--navy-deep);
  border-radius: 50%;
  box-shadow: 0 0.16rem 0 var(--navy-deep);
}

.slot-machine.is-fallback .slot-lever:focus-visible .fallback-lever-arm,
.slot-machine.is-fallback .slot-lever:focus-visible .fallback-lever-knob {
  box-shadow: 0 0 0.8rem var(--casino-light-glow);
}

.casino-control-deck {
  position: absolute;
  right: clamp(7.5rem, 17vw, 13rem);
  bottom: max(0.65rem, env(safe-area-inset-bottom));
  left: clamp(4.5rem, 17vw, 13rem);
  z-index: 6;
  display: grid;
  gap: clamp(0.24rem, 0.8dvh, 0.48rem);
  padding: clamp(0.42rem, 1.2vmin, 0.68rem);
  align-content: center;
  background: var(--casino-panel);
  border: 0.1rem solid var(--hair-pink);
  border-radius: 0.8rem;
  box-shadow: 0 0.3rem 0 var(--navy-deep), inset 0 0 0.65rem var(--shadow-strong);
  pointer-events: auto;
}

.casino-result,
.casino-rules {
  margin: 0;
  text-align: center;
}

.casino-result {
  min-height: 2.2em;
  padding: 0.38rem 0.55rem;
  color: var(--hair-pink-dark);
  font-size: clamp(0.62rem, 1.65vmin, 0.84rem);
  font-weight: 900;
  line-height: 1.2;
  background: var(--shirt);
  border: 0.09rem solid var(--navy-deep);
  border-radius: 0.52rem;
  box-shadow: inset 0 -0.1rem 0 var(--hair-pink);
}

.casino-result.is-prize,
.casino-result.is-token {
  color: var(--navy-deep);
  background: var(--hair-blonde-soft);
  box-shadow: inset 0 -0.1rem 0 var(--hair-pink), 0 0 0.65rem var(--casino-light-glow);
}

.casino-result-flash {
  position: absolute;
  inset: 0;
  z-index: 20;
  display: grid;
  width: min(82vw, 36rem);
  height: max-content;
  min-height: clamp(5rem, 18dvh, 8.5rem);
  margin: auto;
  padding: clamp(0.8rem, 3vmin, 1.45rem);
  place-items: center;
  color: var(--hair-pink-dark);
  font-size: clamp(1.15rem, 4.8vmin, 2.8rem);
  font-weight: 1000;
  line-height: 1.05;
  text-align: center;
  text-wrap: balance;
  background: var(--shirt);
  border: clamp(0.12rem, 0.35vmin, 0.2rem) solid var(--navy-deep);
  border-radius: clamp(0.75rem, 2vmin, 1.2rem);
  box-shadow: 0 0.4rem 0 var(--navy-deep), 0 0 1.2rem var(--shadow-strong);
  pointer-events: none;
}

.casino-rule-line {
  display: flex;
  min-width: 0;
  align-items: center;
  justify-content: center;
  gap: clamp(0.15rem, 0.5vmin, 0.3rem);
}

.casino-rule-icon,
.casino-rule-icon-fallback {
  display: block;
  width: clamp(0.8rem, 2vmin, 1.15rem);
  height: clamp(0.8rem, 2vmin, 1.15rem);
  aspect-ratio: 1;
  flex: 0 0 auto;
  object-fit: contain;
  line-height: 1;
}

.casino-result-flash.is-token {
  color: var(--navy-deep);
  background: var(--hair-blonde-soft);
  border-color: var(--hair-blonde);
}

.casino-result-flash.is-spinning {
  color: var(--navy);
  background: var(--skirt-blue-soft);
  border-color: var(--skirt-blue);
}

.casino-result-flash.is-loss {
  color: var(--tie-red);
  border-color: var(--tie-red);
}

.casino-rules {
  position: absolute;
  right: max(0.65rem, env(safe-area-inset-right));
  bottom: max(0.65rem, env(safe-area-inset-bottom));
  z-index: 6;
  display: grid;
  width: clamp(6.2rem, 10vw, 8.25rem);
  aspect-ratio: 1;
  padding: clamp(0.45rem, 1.2vmin, 0.7rem);
  align-content: center;
  gap: 0.3rem;
  color: var(--hair-blonde-soft);
  font-size: clamp(0.48rem, 1.3vmin, 0.66rem);
  font-weight: 850;
  line-height: 1.25;
  background: var(--casino-panel);
  border: 0.1rem solid var(--hair-pink);
  border-radius: 0.7rem;
  box-shadow: 0 0.25rem 0 var(--navy-deep), inset 0 0 0.55rem var(--shadow-strong);
  pointer-events: none;
}

.casino-audio-controls {
  position: absolute;
  top: calc(
    max(0.65rem, env(safe-area-inset-top)) +
    clamp(2.8rem, 9dvh, 4.5rem) +
    0.65rem
  );
  right: max(0.55rem, env(safe-area-inset-right));
  z-index: 9;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 0.55rem;
  width: clamp(14rem, 22vw, 18rem);
  padding: 0.32rem 0.52rem;
  background: var(--skirt-blue-soft);
  border: 0.09rem solid var(--navy-deep);
  border-radius: 0.55rem;
  box-shadow: 0.16rem 0.2rem 0 var(--navy-deep);
  pointer-events: auto;
}

.casino-music-toggle {
  min-height: 2.1rem;
  padding: 0.3rem 0.55rem;
  color: var(--navy-deep);
  font-size: clamp(0.52rem, 1.3vmin, 0.66rem);
  background: var(--hair-blonde-soft);
}

.casino-music-toggle[aria-pressed="true"] {
  color: var(--shirt);
  background: var(--navy);
}

.casino-volume-control {
  display: grid;
  grid-template-columns: auto minmax(4rem, 1fr);
  align-items: center;
  gap: 0.4rem;
  min-width: 0;
  color: var(--navy-deep);
  font-size: 0.58rem;
  font-weight: 900;
  text-transform: uppercase;
}

.casino-volume {
  width: 100%;
  accent-color: var(--hair-pink-dark);
  cursor: pointer;
}

.casino-empty {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 15;
  display: grid;
  min-width: min(84vw, 24rem);
  padding: 1rem;
  gap: 0.65rem;
  place-items: center;
  color: var(--shirt);
  text-align: center;
  background: var(--casino-panel);
  border: 0.15rem solid var(--hair-pink);
  border-radius: 1rem;
  box-shadow: 0 0 1.4rem var(--casino-pink-glow);
  pointer-events: auto;
  translate: -50% -50%;
}

.casino-empty strong {
  color: var(--hair-blonde);
  font-size: clamp(1.45rem, 6vmin, 3rem);
}

.classroom-open-button {
  min-height: 2.75rem;
  padding: 0.55rem 1rem;
  color: var(--navy-deep);
  font-size: clamp(0.65rem, 2vmin, 0.9rem);
  letter-spacing: 0.05em;
  background: var(--hair-blonde);
}

.casino-jackpot {
  position: absolute;
  inset: 0;
  z-index: 40;
  display: grid;
  overflow: hidden;
  place-items: center;
  background: radial-gradient(circle at 50% 48%, var(--jackpot-clear) 0 22%, var(--jackpot-shade) 58%);
  pointer-events: auto;
}

.slot-machine.is-ready .casino-jackpot {
  background: transparent;
}

.slot-machine.is-ready .jackpot-flash,
.slot-machine.is-ready .jackpot-rays,
.slot-machine.is-ready .jackpot-confetti {
  display: none;
}

.slot-machine.is-jackpot .casino-close-form,
.slot-machine.is-jackpot .casino-marquee,
.slot-machine.is-jackpot .casino-token-hud,
.slot-machine.is-jackpot .casino-audio-controls,
.slot-machine.is-jackpot .casino-control-deck,
.slot-machine.is-jackpot .casino-result-flash,
.slot-machine.is-jackpot .casino-rules,
.slot-machine.is-jackpot .casino-loading,
.slot-machine.is-jackpot .casino-reel-fallback,
.slot-machine.is-jackpot .casino-empty {
  visibility: hidden;
}

.jackpot-flash,
.jackpot-rays,
.jackpot-confetti {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.jackpot-flash {
  z-index: 1;
  background: var(--shirt);
  animation: jackpot-flash 850ms ease-out both;
}

.jackpot-rays {
  top: 50%;
  left: 50%;
  z-index: 1;
  width: 120vmax;
  height: 120vmax;
  background: repeating-conic-gradient(var(--casino-light-glow) 0 5deg, transparent 5deg 13deg);
  translate: -50% -50%;
  animation: jackpot-rays 8s linear infinite;
}

.jackpot-copy {
  position: relative;
  z-index: 4;
  display: grid;
  width: min(94vw, 62rem);
  height: min(94dvh, 46rem);
  padding: clamp(0.8rem, 3vmin, 2rem);
  grid-template-rows: auto auto 1fr auto;
  gap: clamp(0.2rem, 0.8dvh, 0.55rem);
  align-items: start;
  justify-items: center;
  color: var(--shirt);
  text-align: center;
  pointer-events: none;
}

.jackpot-badge,
.jackpot-title,
.jackpot-prize {
  margin: 0;
}

.jackpot-badge {
  padding: 0.34rem 0.8rem;
  color: var(--navy-deep);
  font-size: clamp(0.62rem, 2vmin, 0.9rem);
  font-weight: 1000;
  letter-spacing: 0.14em;
  background: var(--hair-blonde);
  border: 0.11rem solid var(--navy-deep);
  border-radius: 999px;
}

.jackpot-title {
  margin-top: clamp(0.3rem, 1.2dvh, 0.75rem);
  color: var(--shirt);
  font-size: clamp(2.5rem, 10vmin, 7rem);
  line-height: 0.88;
  letter-spacing: -0.06em;
  text-shadow: 0.05em 0.05em 0 var(--hair-pink-dark), 0 0 1rem var(--casino-light-glow);
}

.jackpot-card {
  position: relative;
  display: grid;
  width: min(82vw, calc(54dvh * 0.75), 22rem);
  min-width: 0;
  min-height: 0;
  aspect-ratio: 3 / 4;
  align-self: center;
  isolation: isolate;
  margin: 0;
  place-items: center;
}

.jackpot-card-image,
.jackpot-card-fallback {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

.jackpot-card-image {
  z-index: 2;
  object-fit: contain;
  transform-origin: 50% 76%;
}

.jackpot-card-fallback {
  z-index: 1;
  display: grid;
  place-items: center;
  color: var(--hair-blonde-soft);
  font-size: clamp(3rem, 14vmin, 7rem);
  font-weight: 1000;
  background:
    radial-gradient(circle at 50% 35%, var(--casino-pink-glow), transparent 38%),
    linear-gradient(160deg, var(--navy), var(--hair-pink-dark));
  border: 0.18rem solid var(--hair-blonde);
  border-radius: 6%;
  box-shadow: 0 0 1.4rem var(--casino-light-glow);
}

.jackpot-card.is-card-ready .jackpot-card-fallback {
  opacity: 0;
}

.jackpot-card.is-card-loading .jackpot-card-fallback {
  animation: card-loading-pulse 900ms ease-in-out infinite alternate;
}

.jackpot-card.is-new-prize .jackpot-card-image,
.jackpot-card.is-new-prize:not(.is-card-ready) .jackpot-card-fallback {
  animation: jackpot-card-reveal 1100ms cubic-bezier(0.16, 0.82, 0.28, 1) both;
}

.jackpot-card.is-repeat-prize .jackpot-card-image,
.jackpot-card.is-repeat-prize:not(.is-card-ready) .jackpot-card-fallback {
  animation: jackpot-card-repeat 450ms ease-out both;
}

.jackpot-prize {
  position: absolute;
  right: 9%;
  bottom: 3.2%;
  left: 9%;
  z-index: 3;
  display: grid;
  min-height: 11.5%;
  padding: 0.16rem 0.3rem;
  place-items: center;
  color: var(--hair-blonde-soft);
  font-size: clamp(0.68rem, 2.2vmin, 1.15rem);
  font-weight: 1000;
  line-height: 1;
  background: linear-gradient(90deg, transparent, var(--jackpot-shade) 12% 88%, transparent);
  text-shadow: 0.07em 0.07em 0 var(--navy-deep), 0 0 0.55rem var(--casino-pink-glow);
  text-transform: uppercase;
  text-wrap: balance;
}

.jackpot-continue {
  min-width: min(80vw, 14rem);
  min-height: 3rem;
  padding: 0.65rem 1.2rem;
  color: var(--navy-deep);
  font-size: clamp(0.8rem, 2.4vmin, 1.05rem);
  background: var(--hair-pink);
  pointer-events: auto;
}

.jackpot-confetti {
  z-index: 3;
}

.jackpot-confetti i {
  --confetti-x: 50%;
  --confetti-delay: 0ms;
  --confetti-turn: 420deg;
  position: absolute;
  top: -8%;
  left: var(--confetti-x);
  width: clamp(0.28rem, 0.8vmin, 0.55rem);
  height: clamp(0.7rem, 1.8vmin, 1.25rem);
  background: var(--confetti-gold);
  border-radius: 0.1rem;
  animation: jackpot-confetti 2.8s var(--confetti-delay) linear infinite;
}

.jackpot-confetti i:nth-child(4n + 2) { background: var(--confetti-pink); }
.jackpot-confetti i:nth-child(4n + 3) { background: var(--confetti-blue); }
.jackpot-confetti i:nth-child(4n + 4) { background: var(--confetti-white); }
.jackpot-confetti i:nth-child(1) { --confetti-x: 3%; --confetti-delay: -900ms; --confetti-turn: 500deg; }
.jackpot-confetti i:nth-child(2) { --confetti-x: 8%; --confetti-delay: -1800ms; --confetti-turn: -420deg; }
.jackpot-confetti i:nth-child(3) { --confetti-x: 13%; --confetti-delay: -300ms; }
.jackpot-confetti i:nth-child(4) { --confetti-x: 18%; --confetti-delay: -1300ms; --confetti-turn: -620deg; }
.jackpot-confetti i:nth-child(5) { --confetti-x: 23%; --confetti-delay: -2100ms; }
.jackpot-confetti i:nth-child(6) { --confetti-x: 28%; --confetti-delay: -700ms; }
.jackpot-confetti i:nth-child(7) { --confetti-x: 33%; --confetti-delay: -1600ms; --confetti-turn: -480deg; }
.jackpot-confetti i:nth-child(8) { --confetti-x: 38%; --confetti-delay: -100ms; }
.jackpot-confetti i:nth-child(9) { --confetti-x: 43%; --confetti-delay: -1200ms; }
.jackpot-confetti i:nth-child(10) { --confetti-x: 47%; --confetti-delay: -2300ms; --confetti-turn: -560deg; }
.jackpot-confetti i:nth-child(11) { --confetti-x: 52%; --confetti-delay: -550ms; }
.jackpot-confetti i:nth-child(12) { --confetti-x: 57%; --confetti-delay: -1450ms; }
.jackpot-confetti i:nth-child(13) { --confetti-x: 61%; --confetti-delay: -2500ms; --confetti-turn: -430deg; }
.jackpot-confetti i:nth-child(14) { --confetti-x: 65%; --confetti-delay: -800ms; }
.jackpot-confetti i:nth-child(15) { --confetti-x: 69%; --confetti-delay: -1900ms; }
.jackpot-confetti i:nth-child(16) { --confetti-x: 73%; --confetti-delay: -400ms; --confetti-turn: -510deg; }
.jackpot-confetti i:nth-child(17) { --confetti-x: 77%; --confetti-delay: -1500ms; }
.jackpot-confetti i:nth-child(18) { --confetti-x: 81%; --confetti-delay: -2450ms; }
.jackpot-confetti i:nth-child(19) { --confetti-x: 85%; --confetti-delay: -650ms; --confetti-turn: -680deg; }
.jackpot-confetti i:nth-child(20) { --confetti-x: 89%; --confetti-delay: -1750ms; }
.jackpot-confetti i:nth-child(21) { --confetti-x: 93%; --confetti-delay: -50ms; }
.jackpot-confetti i:nth-child(22) { --confetti-x: 96%; --confetti-delay: -1100ms; --confetti-turn: -470deg; }
.jackpot-confetti i:nth-child(23) { --confetti-x: 35%; --confetti-delay: -2600ms; }
.jackpot-confetti i:nth-child(24) { --confetti-x: 70%; --confetti-delay: -2200ms; }

/* Fullscreen visual-novel classroom. */
.classroom-screen {
  position: relative;
  display: grid;
  width: 100%;
  height: 100%;
  min-height: 0;
  padding: max(0.55rem, env(safe-area-inset-top)) max(0.55rem, env(safe-area-inset-right)) max(0.55rem, env(safe-area-inset-bottom)) max(0.55rem, env(safe-area-inset-left));
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: clamp(0.35rem, 1.4dvh, 0.8rem);
  overflow: hidden;
  color: var(--navy-deep);
  background:
    radial-gradient(circle at 18% 18%, var(--white-glow), transparent 22%),
    linear-gradient(145deg, var(--skirt-blue-soft), var(--hair-pink-soft) 58%, var(--hair-blonde-soft));
}

.classroom-screen::before {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, var(--shirt-stripe), transparent 24% 72%, var(--hair-pink-haze));
  content: "";
  pointer-events: none;
}

.classroom-background,
.classroom-background img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

.classroom-background {
  z-index: 0;
  overflow: hidden;
}

.classroom-background img {
  object-fit: cover;
  object-position: center;
}

.classroom-finale,
.classroom-finale img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

.classroom-finale {
  z-index: 3;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  transition: opacity 300ms ease-out;
  will-change: opacity;
}

.classroom-finale img {
  object-fit: cover;
  object-position: center;
}

.classroom-finale.is-visible {
  opacity: 1;
}

.classroom-header {
  z-index: 4;
  display: grid;
  width: min(100%, 36rem);
  padding: clamp(0.22rem, 0.8vmin, 0.42rem) 3rem;
  justify-items: center;
  text-align: center;
  background: var(--shirt-glow);
  border: 0.1rem solid var(--navy-deep);
  border-radius: 0.8rem;
  box-shadow: 0.16rem 0.2rem 0 var(--navy-deep);
  justify-self: center;
}

.classroom-title,
.classroom-progress,
.classroom-speaker,
.classroom-dialogue-text {
  margin: 0;
}

.classroom-title {
  margin-top: 0.08rem;
  color: var(--navy-deep);
  font-size: clamp(1.35rem, 5vmin, 3.1rem);
  line-height: 0.98;
  text-wrap: balance;
}

.classroom-progress {
  margin-top: 0.12rem;
  color: var(--navy);
  font-size: clamp(0.54rem, 1.55vmin, 0.8rem);
  font-weight: 900;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.classroom-stage {
  position: relative;
  z-index: 2;
  isolation: isolate;
  width: 100%;
  min-height: 0;
  justify-self: center;
  overflow: hidden;
}

.classroom-art-status {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 3;
  margin: 0;
  padding: 0.42rem 0.7rem;
  color: var(--shirt);
  font-size: clamp(0.52rem, 1.5vmin, 0.72rem);
  font-weight: 900;
  letter-spacing: 0.06em;
  text-align: center;
  text-transform: uppercase;
  background: var(--casino-panel);
  border: 0.1rem solid var(--hair-blonde);
  border-radius: 999px;
  box-shadow: 0 0.2rem 0 var(--navy-deep);
  translate: -50% -50%;
}

.gojo-character {
  position: absolute;
  bottom: max(-5.5rem, -18%);
  left: 50%;
  z-index: 2;
  display: block;
  width: auto;
  max-width: min(90vw, 36rem);
  height: min(118%, 48rem);
  object-fit: contain;
  object-position: center bottom;
  opacity: 0;
  filter: drop-shadow(0 0.35rem 0.5rem var(--shadow-strong));
  pointer-events: none;
  translate: -50% 0;
}

.gojo-character.is-visible {
  opacity: 1;
}

.gojo-character.is-entering {
  animation: gojo-pose-in 220ms ease-out both;
}

.classroom-dialogue-box {
  position: relative;
  z-index: 5;
  display: grid;
  width: min(100%, 72rem);
  min-height: 0;
  padding: clamp(0.5rem, 1.8vmin, 0.9rem);
  gap: clamp(0.28rem, 0.9dvh, 0.48rem);
  justify-self: center;
  overflow: hidden;
  background: var(--shirt);
  border: 0.15rem solid var(--navy-deep);
  border-radius: clamp(0.65rem, 2vmin, 1rem);
  box-shadow: 0.24rem 0.28rem 0 var(--navy-deep), 0 0 1rem var(--casino-pink-glow);
}

.classroom-screen:not(.is-finale) .classroom-dialogue-box {
  margin-block-start: clamp(-3.5rem, -8dvh, -1.5rem);
}

.classroom-screen.is-finale .classroom-dialogue-box {
  position: absolute;
  right: max(0.55rem, env(safe-area-inset-right));
  bottom: max(0.55rem, env(safe-area-inset-bottom));
  width: min(48vw, 34rem);
  max-height: calc(100% - 5rem);
}

.classroom-screen.is-finale .classroom-dialogue-text {
  min-height: 0;
}

@media (orientation: portrait) {
  .classroom-screen.is-finale .classroom-dialogue-box {
    right: max(0.35rem, env(safe-area-inset-right));
    bottom: max(0.35rem, env(safe-area-inset-bottom));
    left: max(0.35rem, env(safe-area-inset-left));
    width: auto;
    justify-self: stretch;
  }
}

.classroom-speaker {
  justify-self: start;
  padding: 0.2rem 0.62rem;
  color: var(--shirt);
  font-size: clamp(0.72rem, 2vmin, 1rem);
  font-weight: 1000;
  letter-spacing: 0.04em;
  background: var(--hair-pink-dark);
  border-radius: 999px;
}

.classroom-dialogue-text {
  min-height: 2.7em;
  color: var(--navy-deep);
  font-size: clamp(0.88rem, 2.5vmin, 1.25rem);
  font-weight: 750;
  line-height: 1.35;
  text-align: left;
  text-wrap: pretty;
}

.classroom-answers {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(0.3rem, 1.2vw, 0.65rem);
}

.classroom-answer,
.classroom-continue {
  min-height: 2.55rem;
  padding: 0.42rem 0.75rem;
  color: var(--navy-deep);
  font-size: clamp(0.85rem, 2.25vmin, 1.125rem);
  background: var(--hair-blonde-soft);
}

.classroom-answer:hover,
.classroom-continue:hover,
.classroom-open-button:hover {
  background: var(--skirt-blue);
}

.classroom-continue {
  min-width: min(100%, 13rem);
  justify-self: end;
  background: var(--hair-pink);
}

/* Fullscreen illustrated-card achievement showroom. */
.achievements-screen {
  position: relative;
  display: grid;
  width: 100%;
  height: 100%;
  min-height: 0;
  padding: max(0.55rem, env(safe-area-inset-top)) max(0.55rem, env(safe-area-inset-right)) max(0.55rem, env(safe-area-inset-bottom)) max(0.55rem, env(safe-area-inset-left));
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: clamp(0.35rem, 1.4dvh, 0.8rem);
  overflow: hidden;
  color: var(--shirt);
  background:
    radial-gradient(circle at 50% 38%, var(--casino-pink-glow), transparent 38%),
    linear-gradient(145deg, var(--hair-pink-dark), var(--navy-deep) 68%);
}

.achievements-background {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: block;
  opacity: 0;
  pointer-events: none;
  transition: opacity 220ms ease-out;
}

.achievements-background img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

.achievements-screen.is-background-ready .achievements-background {
  opacity: 0.94;
}

.achievements-screen.is-complete.is-background-ready .achievements-background {
  opacity: 0.82;
}

.achievements-header {
  z-index: 4;
  display: grid;
  justify-items: center;
  text-align: center;
}

.achievements-title,
.achievements-progress {
  margin: 0;
}

.achievements-title {
  font-size: clamp(1.75rem, 5.5vmin, 3.6rem);
  line-height: 0.95;
  text-shadow: 0.08rem 0.08rem 0 var(--navy-deep);
}

.achievements-progress {
  margin-top: 0.2rem;
  color: var(--hair-pink-soft);
  font-size: clamp(0.6rem, 1.6vmin, 0.86rem);
  font-weight: 900;
}

.achievement-stage {
  position: relative;
  z-index: 1;
  isolation: isolate;
  min-height: 0;
  overflow: hidden;
  background: linear-gradient(180deg, var(--achievement-stage-clear), var(--achievement-stage-shade));
  border: 0.14rem solid var(--hair-blonde);
  border-radius: clamp(0.75rem, 2vmin, 1.25rem);
  box-shadow: inset 0 0 1.5rem var(--shadow-strong), 0 0.45rem 0 var(--navy-deep);
}

.achievement-grid {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: clamp(0.3rem, 1vmin, 0.75rem);
  margin: 0;
  padding: clamp(0.45rem, 1.5vmin, 0.9rem);
  list-style: none;
  pointer-events: none;
}

.achievement-slot {
  position: relative;
  display: grid;
  min-width: 0;
  min-height: 0;
  padding: clamp(0.16rem, 0.55vmin, 0.36rem);
  place-items: center;
  color: var(--hair-pink-soft);
  background: transparent;
  border: 0;
  border-radius: clamp(0.55rem, 1.4vmin, 0.9rem);
  container-type: size;
}

.achievement-slot.is-unlocked {
  color: var(--shirt);
  filter: drop-shadow(0 0 0.55rem var(--casino-light-glow));
}

.achievement-card-shell {
  position: relative;
  display: grid;
  width: min(100cqw, 75cqh);
  max-width: 100%;
  height: min(100cqh, 133.333cqw);
  max-height: 100%;
  min-height: 0;
  aspect-ratio: 3 / 4;
  isolation: isolate;
  place-items: center;
}

.achievement-card-image,
.achievement-card-fallback {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

.achievement-card-image {
  z-index: 2;
  object-fit: contain;
  transform-origin: 50% 76%;
}

.achievement-card-fallback {
  z-index: 1;
  display: grid;
  place-items: center;
  color: var(--hair-pink-soft);
  font-size: clamp(1.6rem, 5vmin, 3rem);
  font-weight: 1000;
  background:
    radial-gradient(circle at 50% 35%, var(--casino-pink-glow), transparent 40%),
    linear-gradient(160deg, var(--navy), var(--hair-pink-dark));
  border: 0.1rem solid var(--hair-blonde);
  border-radius: 6%;
  box-shadow: 0 0.28rem 0 var(--navy-deep), 0 0 0.7rem var(--casino-pink-glow);
}

.achievement-slot.is-card-ready .achievement-card-fallback {
  opacity: 0;
}

.achievement-slot.is-card-loading .achievement-card-fallback {
  animation: card-loading-pulse 900ms ease-in-out infinite alternate;
}

.achievement-slot.is-unlocked.is-card-ready:not(.is-new-reveal):not(.is-repeat-reveal) .achievement-card-image {
  animation: achievement-card-float 3.8s ease-in-out infinite alternate;
}

.achievement-slot.is-new-reveal .achievement-card-image,
.achievement-slot.is-new-reveal:not(.is-card-ready) .achievement-card-fallback {
  animation: achievement-card-reveal 1100ms cubic-bezier(0.16, 0.82, 0.28, 1) both;
}

.achievement-slot.is-repeat-reveal .achievement-card-image,
.achievement-slot.is-repeat-reveal:not(.is-card-ready) .achievement-card-fallback {
  animation: achievement-card-repeat 450ms ease-out both;
}

.achievement-name {
  position: absolute;
  right: 8%;
  bottom: 3.4%;
  left: 8%;
  z-index: 3;
  display: grid;
  min-height: 11%;
  padding: 0.12rem 0.2rem;
  place-items: center;
  color: var(--hair-blonde-soft);
  font-size: clamp(0.48rem, 1.25vmin, 0.72rem);
  font-weight: 1000;
  line-height: 1;
  background: linear-gradient(90deg, transparent, var(--jackpot-shade) 12% 88%, transparent);
  text-align: center;
  text-shadow: 0.07em 0.07em 0 var(--navy-deep), 0 0 0.42rem var(--casino-pink-glow);
  text-transform: uppercase;
  text-wrap: balance;
}

.achievement-status {
  z-index: 4;
  display: grid;
  gap: 0.22rem;
  justify-self: center;
  justify-items: center;
}

.achievement-complete-plaque,
.achievement-storage-note {
  margin: 0;
  text-align: center;
}

.achievement-complete-plaque {
  padding: 0.38rem 0.75rem;
  color: var(--navy-deep);
  font-size: clamp(0.58rem, 1.5vmin, 0.78rem);
  font-weight: 1000;
  letter-spacing: 0.08em;
  background: var(--hair-blonde);
  border: 0.1rem solid var(--navy-deep);
  border-radius: 0.5rem;
  box-shadow: 0 0.18rem 0 var(--navy-deep), 0 0 0.7rem var(--casino-light-glow);
}

.achievement-storage-note {
  color: var(--hair-blonde-soft);
  font-size: 0.62rem;
  font-weight: 850;
}

.achievements-screen.is-complete {
  background:
    radial-gradient(circle at 50% 38%, var(--casino-light-glow), transparent 42%),
    linear-gradient(145deg, var(--hair-blonde), var(--hair-pink-dark) 45%, var(--navy-deep));
}

/* Fullscreen official certificate with a responsive HTML preview. */
.certificate-screen {
  position: relative;
  display: grid;
  width: 100%;
  height: 100%;
  min-height: 0;
  padding:
    max(0.55rem, env(safe-area-inset-top))
    max(0.55rem, env(safe-area-inset-right))
    max(0.55rem, env(safe-area-inset-bottom))
    max(0.55rem, env(safe-area-inset-left));
  grid-template-rows: auto minmax(0, 1fr) auto auto;
  gap: clamp(0.28rem, 1.3dvh, 0.7rem);
  place-items: center;
  overflow: hidden;
  color: var(--navy-deep);
  background:
    radial-gradient(circle at 15% 18%, var(--hair-blonde-soft), transparent 26%),
    radial-gradient(circle at 86% 82%, var(--hair-pink-soft), transparent 30%),
    linear-gradient(145deg, var(--navy), var(--hair-pink-dark));
}

.certificate-header {
  text-align: center;
}

.certificate-header p,
.certificate-header h2 {
  margin: 0;
}

.certificate-header p {
  color: var(--hair-blonde-soft);
  font-size: clamp(0.48rem, 1.3vmin, 0.68rem);
  font-weight: 900;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.certificate-header h2 {
  color: var(--shirt);
  font-size: clamp(1rem, 3.2vmin, 2rem);
  line-height: 1;
}

.certificate-card {
  /* The rule under the name is drawn outside this element's own font
     size, so the scale lives in a property both can read. */
  --certificate-scale: clamp(0.34rem, 1.35vmin, 0.9rem);
  position: relative;
  display: grid;
  width: auto;
  max-width: 92vw;
  height: min(100%, calc(92vw * 0.706875));
  max-height: 50rem;
  min-height: 0;
  aspect-ratio: 1600 / 1131;
  padding: 3.4em 4.5em 2.8em;
  align-content: center;
  gap: 0.52em;
  overflow: hidden;
  color: var(--navy-deep);
  font-size: var(--certificate-scale);
  text-align: center;
  background:
    linear-gradient(var(--hair-pink-soft), var(--hair-pink-soft)) top / 100% 6% no-repeat,
    linear-gradient(var(--skirt-blue-soft), var(--skirt-blue-soft)) bottom / 100% 6% no-repeat,
    var(--shirt);
  border: 0.72em solid var(--navy-deep);
  box-shadow:
    0 0 0 0.28em var(--hair-pink),
    0.5em 0.6em 0 var(--shadow-strong);
}

/* The rule under the name, where the exported PNG puts it. It rides the name in
   the flow because a share of the card's height tracked the card box while the
   text keeps its own rhythm, so it crossed the description at every required
   viewport and struck through the name itself on a desktop.

   Its font-size is pinned back to the card's, because `em` and `%` here would
   otherwise resolve against the name's own 2.75em and its narrower content box:
   that made the rule roughly three times thicker and a tenth shorter than the
   one this card drew before. The 5.94em added to the width is 66% of the card's
   9em of side padding, so the whole stays the 66% of the card it always was.

   The blonde rule that sat at 20% from the bottom is gone rather than moved --
   the export has no counterpart for it, and in the preview its only effect at
   the two smallest viewports was to cross the signatures. */
.certificate-name::after {
  display: block;
  width: calc(66% + 5.94em);
  height: 0.38em;
  margin: 0.34em auto 0;
  font-size: var(--certificate-scale);
  background: var(--hair-pink);
  content: "";
}

.certificate-corner {
  position: absolute;
  width: 9em;
  height: 9em;
  border: 1.1em solid var(--hair-blonde);
  border-radius: 50%;
  opacity: 0.48;
  pointer-events: none;
}

.certificate-corner-top {
  top: -5.2em;
  left: -5.2em;
}

.certificate-corner-bottom {
  right: -5.2em;
  bottom: -5.2em;
  border-color: var(--hair-pink);
}

.certificate-authority,
.certificate-title,
.certificate-intro,
.certificate-description,
.certificate-record p,
.certificate-signatures p {
  margin: 0;
}

.certificate-authority {
  color: var(--hair-pink-dark);
  font-size: 1em;
  font-weight: 1000;
  letter-spacing: 0.1em;
}

.certificate-title {
  font-size: 3.05em;
  line-height: 0.95;
  letter-spacing: -0.04em;
  text-wrap: balance;
}

.certificate-intro,
.certificate-description {
  font-weight: 750;
  line-height: 1.25;
}

.certificate-intro {
  font-size: 1.18em;
}

.certificate-name {
  color: var(--tie-red);
  font-size: 2.75em;
  line-height: 1;
  overflow-wrap: anywhere;
  text-wrap: balance;
}

.certificate-description {
  max-width: 64em;
  justify-self: center;
  font-size: 1.08em;
}

.certificate-verdict {
  justify-self: center;
  padding: 0.34em 1.15em;
  color: var(--tie-red);
  font-size: 2.1em;
  line-height: 1;
  background: var(--hair-blonde-soft);
  border: 0.14em solid var(--navy-deep);
  box-shadow: 0.13em 0.15em 0 var(--navy-deep);
  transform: rotate(-1deg);
}

.certificate-record {
  display: flex;
  gap: 0.45em 2em;
  justify-content: center;
  flex-wrap: wrap;
  color: var(--navy);
  font-size: 0.92em;
  font-weight: 850;
}

.certificate-signatures {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 1.3em;
  align-items: center;
  margin-top: 0.25em;
}

.certificate-signatures p {
  display: grid;
  min-width: 0;
  gap: 0.08em;
}

.certificate-signature-mark {
  display: block;
  padding: 0 0.2em 0.06em;
  color: var(--tie-red);
  font-family: "Segoe Script", "Brush Script MT", cursive;
  font-size: 1.45em;
  font-weight: 700;
  line-height: 0.86;
  white-space: nowrap;
  border-bottom: 0.08em solid var(--navy-deep);
  transform: rotate(-2deg);
}

.certificate-signature-nanabet {
  color: var(--hair-pink-dark);
  transform: rotate(1deg);
}

.certificate-signature-authority {
  font-size: 0.9em;
  font-weight: 1000;
}

.certificate-signatures strong {
  color: var(--navy);
  font-size: 0.72em;
}

.certificate-seal {
  display: grid;
  width: 4.6em;
  aspect-ratio: 1;
  place-items: center;
  font-size: 1.7em !important;
  background: var(--hair-blonde);
  border: 0.32em double var(--navy-deep);
  border-radius: 50%;
}

.certificate-actions {
  display: flex;
  gap: clamp(0.45rem, 1.8vw, 0.85rem);
  justify-content: center;
}

.certificate-action {
  min-width: clamp(8.5rem, 22vw, 12rem);
  padding: 0.48rem 0.8rem;
  color: var(--navy-deep);
  font-size: clamp(0.62rem, 1.7vmin, 0.82rem);
  background: var(--hair-blonde);
}

.certificate-action:disabled {
  opacity: 0.6;
  cursor: wait;
}

.certificate-status {
  min-height: 1.2em;
  margin: 0;
  color: var(--shirt);
  font-size: clamp(0.5rem, 1.4vmin, 0.7rem);
  font-weight: 850;
  text-align: center;
}

/* Unadvertised fullscreen achievement terminal opened only by F3. */
.cheat-screen {
  position: relative;
  display: grid;
  width: 100%;
  height: 100%;
  min-height: 0;
  padding:
    max(0.65rem, env(safe-area-inset-top))
    max(0.65rem, env(safe-area-inset-right))
    max(0.65rem, env(safe-area-inset-bottom))
    max(0.65rem, env(safe-area-inset-left));
  overflow: hidden;
  place-items: center;
  background:
    radial-gradient(circle at 50% 42%, var(--casino-pink-glow), transparent 28%),
    linear-gradient(145deg, var(--navy), var(--navy-deep));
}

.cheat-panel {
  display: grid;
  width: min(100%, 28rem);
  max-height: 100%;
  margin: 0;
  padding: clamp(1rem, 4vmin, 2rem);
  gap: clamp(0.45rem, 1.8vmin, 0.85rem);
  overflow: hidden;
  color: var(--navy-deep);
  background: var(--shirt);
  border: 0.18rem solid var(--hair-blonde);
  border-radius: clamp(0.75rem, 2.5vmin, 1.25rem);
  box-shadow:
    0.32rem 0.38rem 0 var(--hair-pink-dark),
    0 0 1.8rem var(--casino-pink-glow);
}

.cheat-kicker,
.cheat-title,
.cheat-status {
  margin: 0;
  text-align: center;
}

.cheat-kicker,
.cheat-label {
  color: var(--hair-pink-dark);
  font-size: clamp(0.58rem, 1.6vmin, 0.78rem);
  font-weight: 1000;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.cheat-title {
  color: var(--navy-deep);
  font-size: clamp(1.5rem, 5vmin, 2.8rem);
  line-height: 0.98;
  text-wrap: balance;
}

.cheat-label {
  margin-top: 0.25rem;
}

.cheat-input {
  width: 100%;
  min-height: 3rem;
  padding: 0.65rem 0.8rem;
  color: var(--navy-deep);
  font-size: clamp(0.8rem, 2.4vmin, 1.05rem);
  font-weight: 850;
  background: var(--hair-blonde-soft);
  border: 0.12rem solid var(--navy-deep);
  border-radius: 0.55rem;
}

.cheat-submit {
  min-height: 3rem;
  padding: 0.65rem 1rem;
  color: var(--shirt);
  font-size: clamp(0.7rem, 2vmin, 0.92rem);
  background: var(--hair-pink-dark);
}

.cheat-status {
  min-height: 1.35em;
  color: var(--tie-red);
  font-size: clamp(0.62rem, 1.8vmin, 0.82rem);
  font-weight: 900;
}

/* Nana Collection: the birthday event's nanavios, backed by the Go API. Long lists
   scroll inside their own panel while the dialog itself stays contained. */
.collection-screen {
  position: relative;
  display: grid;
  width: 100%;
  height: 100%;
  min-height: 0;
  padding:
    max(0.55rem, env(safe-area-inset-top))
    max(0.55rem, env(safe-area-inset-right))
    max(0.55rem, env(safe-area-inset-bottom))
    max(0.55rem, env(safe-area-inset-left));
  --collection-shell-gap: clamp(0.28rem, 1.2dvh, 0.6rem);
  /* One explicit column the width of the screen: an implicit one is sized to
     the widest row, and an admin's account line at 320 wide pushed the tabs and
     every panel 27px past the right edge, where overflow: hidden cut them. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto minmax(0, 1fr) auto auto;
  gap: 0;
  overflow: hidden;
  color: var(--kc-navy-deep);
  background:
    radial-gradient(circle at 12% 16%, var(--kc-paper-dark), transparent 24%),
    radial-gradient(circle at 88% 84%, var(--kc-sea-soft), transparent 28%),
    linear-gradient(145deg, var(--kc-navy-deep), var(--kc-navy) 55%, var(--kc-signal));
}

.collection-header {
  display: grid;
  /* Wide enough that the eyebrow clears the close button, which sits outside
     the header's own box and bleeds past it with the naval skin. */
  padding: 0 3.6rem;
  gap: 0.1rem;
  text-align: center;
}

.collection-kicker,
.collection-title,
.collection-account,
.collection-subtitle,
.collection-nanacote-status,
.collection-status,
.album-progress,
.admin-summary {
  margin: 0;
}

/* The tab bar is hidden on the login form, and PIRACA I.A's row only exists
   during the first visit's tutorial. Without explicit rows the body
   auto-places into the `auto` track and the status line takes the `1fr`, which
   left a panel unbounded, unable to scroll, and clipped by the body's own
   `overflow: hidden`. The spacing rides on the children rather than on `gap`,
   so a collapsed row costs nothing. */
.collection-header {
  grid-row: 1;
}

.collection-tabs {
  grid-row: 2;
  margin-block-start: var(--collection-shell-gap);
}

.collection-body {
  grid-row: 3;
  margin-block-start: var(--collection-shell-gap);
}

.collection-tutorial {
  grid-row: 4;
  margin-block-start: var(--collection-shell-gap);
}

.collection-status {
  grid-row: 5;
  margin-block-start: var(--collection-shell-gap);
}

.collection-kicker {
  color: var(--kc-paper-dark);
  font-size: var(--kc-type-3xs);
  font-weight: 900;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.collection-title {
  color: var(--kc-paper);
  font-size: clamp(1.1rem, 3.6vmin, 2.1rem);
  line-height: 1;
}

/* The account row stays inside the header's padding, which is what keeps it
   clear of the close button and the language switch in the corners: the name
   is the one part that gives way, cut short with an ellipsis (its whole text
   is its title), while the avatar and `sair da conta` keep their size. As a
   plain row that could not shrink, an admin's line at 320 wide ran under the
   close button and a long login pushed the button off the screen. */
.collection-account {
  display: flex;
  min-width: 0;
  max-width: 100%;
  justify-content: center;
  align-items: center;
  gap: 0.5rem;
  color: var(--kc-sea-soft);
  font-size: var(--kc-type-2xs);
  font-weight: 850;
}

.collection-account > * {
  flex: none;
}

.collection-account > #collection-username {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A phone gives the row about 210px between the corners, and `sair da conta`
   takes most of it: the gaps, the picture and the button's own padding give
   some back so the name keeps a few letters. Written straight after the rules
   it overrides, since a media query carries no specificity of its own. */
@media (max-width: 34rem) {
  .collection-account {
    gap: 0.3rem;
  }

  .collection-account .collection-avatar.is-medium {
    --avatar-size: 1.6rem;
  }

  .collection-account > .collection-link-button {
    padding-inline: 0.4rem;
  }
}

.collection-link-button {
  display: inline-flex;
  min-height: var(--kc-control-min);
  padding: 0.12rem 0.7rem;
  white-space: nowrap;
  align-items: center;
  justify-content: center;
  color: var(--kc-navy-deep);
  font: inherit;
  font-family: var(--kc-font-ui);
  font-weight: 900;
  background: var(--kc-paper);
  border: 0.1rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius-flat);
  cursor: pointer;
}

/* One row at every size, scrolling sideways when it does not fit: an admin's
   ten tabs wrapped into a second row at 1366 wide, a lone HISTÓRICO taking a
   row of the panel's height. */
.collection-tabs {
  display: flex;
  flex-wrap: nowrap;
  justify-content: safe center;
  gap: 0.35rem;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
}

.collection-tabs > .collection-tab {
  flex: none;
  padding-inline: 0.55rem;
  letter-spacing: 0.02em;
}

.collection-tab,
.panel-filter {
  min-height: var(--kc-control-min);
  padding: 0.3rem 0.7rem;
  color: var(--kc-navy-deep);
  font-size: var(--kc-type-2xs);
  font-weight: 1000;
  letter-spacing: 0.04em;
  background: var(--kc-paper);
  border: 0.12rem solid var(--kc-navy-deep);
  border-radius: var(--kc-radius-pill);
  box-shadow: 0.12rem 0.15rem 0 var(--kc-navy-deep);
  cursor: pointer;
}

.collection-tab[aria-pressed="true"],
.panel-filter[aria-pressed="true"] {
  background: var(--kc-brass);
  box-shadow: none;
  transform: translateY(0.1rem);
}

.collection-body {
  position: relative;
  display: grid;
  min-height: 0;
  overflow: hidden;
}

.collection-body > [data-view-panel] {
  min-width: 0;
  min-height: 0;
}

.collection-panel {
  padding: clamp(0.4rem, 1.5vmin, 0.8rem);
  background: var(--kc-paper);
  border: 0.14rem solid var(--kc-navy-deep);
  border-radius: clamp(0.6rem, 1.8vmin, 1rem);
  box-shadow: 0.2rem 0.24rem 0 var(--kc-shadow);
}

.collection-action {
  min-height: var(--kc-control-min);
  padding: 0.45rem 1rem;
  color: var(--kc-paper);
  font-size: var(--kc-type-xs);
  letter-spacing: 0.04em;
  background: var(--kc-signal);
}

.collection-action-secondary {
  color: var(--kc-navy-deep);
  background: var(--kc-brass);
}

.collection-action:disabled {
  cursor: not-allowed;
}

.collection-label {
  color: var(--kc-signal);
  font-size: var(--kc-type-2xs);
  font-weight: 1000;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.collection-input {
  width: 100%;
  min-height: var(--kc-control-min);
  padding: 0.5rem 0.7rem;
  color: var(--kc-navy-deep);
  font: inherit;
  font-family: var(--kc-font-ui);
  font-size: clamp(1rem, 2.2vmin, 1.1rem);
  font-weight: 800;
  background: var(--kc-paper);
  border: 0.12rem solid var(--kc-navy);
  border-radius: var(--kc-radius-flat);
}

.collection-subtitle {
  color: var(--kc-navy-deep);
  font-size: clamp(1rem, 2.4vmin, 1.15rem);
  text-transform: uppercase;
}

.collection-status {
  min-height: 1.2em;
  color: var(--kc-navy-deep);
  text-shadow: 0 0 0.4rem var(--kc-paper-glow);
  font-size: var(--kc-type-2xs);
  font-weight: 850;
  text-align: center;
}

.collection-login {
  display: grid;
  width: min(100%, 26rem);
  margin: 0;
  padding: clamp(0.9rem, 3.5vmin, 1.8rem);
  gap: clamp(0.35rem, 1.4vmin, 0.7rem);
  align-self: center;
  justify-self: center;
  background: var(--kc-paper);
  border: 0.18rem solid var(--kc-brass);
  border-radius: clamp(0.75rem, 2.5vmin, 1.25rem);
  box-shadow: 0.32rem 0.38rem 0 var(--kc-signal);
}

.nanavio {
  position: relative;
  display: grid;
  width: 100%;
  height: 100%;
  margin: 0;
  overflow: hidden;
  grid-template-rows: minmax(0, 1fr) auto;
  background: var(--kc-paper);
  border: 0.16rem solid var(--kc-navy);
  border-radius: var(--kc-radius-nanavio);
  box-shadow: 0.12rem 0.15rem 0 var(--kc-shadow);
}

.nanavio.is-missing {
  border-style: dashed;
  border-color: var(--kc-navy);
  box-shadow: none;
  opacity: 0.55;
}

.nanavio-art {
  position: relative;
  display: grid;
  min-height: 0;
  overflow: hidden;
  place-items: center;
  background: var(--kc-sea-soft);
}

.nanavio.is-missing .nanavio-art {
  background: var(--kc-sea-soft);
}

/* The blacked-out character already looks unavailable; only soften it a little. */
.nanavio.is-missing.has-art {
  opacity: 0.85;
}

.nanavio-fallback {
  padding: 0.3rem;
  color: var(--kc-navy);
  font-size: var(--kc-type-3xs);
  font-weight: 1000;
  line-height: 1.1;
  text-align: center;
  overflow-wrap: anywhere;
}

.nanavio.is-missing .nanavio-fallback {
  font-size: clamp(1.4rem, 5vmin, 2.8rem);
}

.nanavio-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
}

.nanavio.has-art .nanavio-image {
  opacity: 1;
}

/* A portrait character the account does not own: the character blacked out
   over the first card in grey. That is the whole silhouette, with no file. */
.nanavio.is-missing .nanavio-image[data-layer="background"] {
  filter: grayscale(1);
}

.nanavio.is-missing .nanavio-image[data-layer="portrait"] {
  filter: brightness(0);
}

.nanavio-caption {
  display: flex;
  flex-wrap: wrap;
  min-width: 0;
  padding: 0.14rem 0.3rem;
  gap: 0 0.25rem;
  align-items: center;
  font-size: var(--kc-type-3xs);
  font-weight: 900;
  background: var(--kc-paper);
  border-top: 0.1rem solid var(--kc-navy-deep);
}

.nanavio-name {
  min-width: 0;
  flex: 1 0 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.nanavio-level {
  flex: 1;
  color: var(--kc-signal);
  text-transform: uppercase;
}

/* Once the art has decoded the card is only the picture: the name and level
   live in the tooltip, the ficha and the accessible label. */
.nanavio.has-art {
  grid-template-rows: minmax(0, 1fr);
  border: 0;
  background: transparent;
  box-shadow: 0.12rem 0.15rem 0 var(--kc-shadow);
}

.nanavio.has-art .nanavio-fallback,
.nanavio.has-art .nanavio-caption {
  display: none;
}

.collection-nanacotes {
  display: grid;
  /* One explicit column: an implicit one is sized to its widest child, and at
     320 wide the buy row pushed the whole panel past its own edge, cutting the
     status and the buy form off at the right. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: clamp(0.35rem, 1.4vmin, 0.7rem);
  text-align: center;
}

.collection-nanacote-status {
  font-size: clamp(1rem, 2.2vmin, 1.12rem);
  font-weight: 900;
}

.collection-buy {
  display: flex;
  flex-wrap: wrap;
  margin: 0;
  gap: 0.35rem;
  align-items: center;
  justify-content: center;
}

/* Why the buy button is off, on its own line under the form. */
.collection-buy-hint {
  flex-basis: 100%;
  margin: 0;
  color: var(--kc-signal);
  font-size: var(--kc-type-2xs);
  font-weight: 800;
}

.collection-buy-quantity {
  width: 4.2rem;
  padding: 0.3rem 0.4rem;
  text-align: center;
}

.nanacote-stage {
  display: grid;
  min-height: 0;
  grid-template-rows: minmax(0, 1fr);
  grid-template-columns: minmax(0, 1fr);
}

/* A landscape phone gives the panel no height: stacked, the line and the buy
   form took all of it and the closed nanacote — the only way to open one —
   shrank to nothing. The nanacote takes the left column's whole height and the
   rest stands beside it. */
@media (max-height: 34rem) and (min-width: 34rem) {
  .collection-nanacotes {
    grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
    grid-template-rows: auto minmax(0, 1fr);
    align-content: start;
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  /* The right column scrolls to the buy form; the nanacote stays in sight. */
  .collection-nanacotes > .nanacote-stage {
    position: sticky;
    top: 0;
    grid-column: 1;
    grid-row: 1 / -1;
    height: min(100%, calc(100dvh - 9rem));
  }

  .collection-nanacotes > :not(.nanacote-stage) {
    grid-column: 2;
  }
}

.nanacote-stage > * {
  grid-area: 1 / 1;
}

.nanacote-wrapper {
  position: relative;
  justify-self: center;
  align-self: center;
  height: 100%;
  max-height: calc(60vw * 1.5);
  max-width: 100%;
  aspect-ratio: 2 / 3;
  padding: 0;
  background: none;
  border: 0;
  border-radius: var(--kc-radius-nanavio);
  filter: drop-shadow(0 0 1.2rem var(--kc-brass-glow));
  cursor: pointer;
  animation: nanacote-wrapper-float 3.2s ease-in-out infinite alternate;
}

/* The glow and its hover stay here, before `.is-empty`: restated after it at
   its weight, the hover lit up a faded nanacote nobody could open. */
.nanacote-wrapper:hover,
.nanacote-wrapper:focus-visible {
  filter: drop-shadow(0 0 1.8rem var(--kc-brass-glow)) brightness(1.08);
}

.nanacote-wrapper:focus-visible {
  outline: 0.2rem solid var(--kc-signal);
  outline-offset: 0.2rem;
}

.nanacote-wrapper:disabled {
  cursor: default;
}

.nanacote-wrapper.is-empty {
  opacity: 0.6;
  filter: grayscale(0.7) brightness(0.8);
  cursor: not-allowed;
  animation: none;
}

.nanacote-art {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.nanacote-wrapper-count {
  position: absolute;
  top: 4%;
  right: 2%;
  padding: 0.1rem 0.55rem;
  color: var(--kc-paper);
  font-size: clamp(1rem, 2.4vmin, 1.2rem);
  font-weight: 1000;
  background: var(--kc-signal);
  border: 0.12rem solid var(--kc-paper);
  border-radius: var(--kc-radius-pill);
  box-shadow: 0.1rem 0.12rem 0 var(--kc-navy-deep);
}

.collection-buy-nanacote {
  display: block;
  height: 2.4rem;
  aspect-ratio: 2 / 3;
}

@keyframes nanacote-wrapper-float {
  from { transform: translateY(0) rotate(-1deg); }
  to { transform: translateY(-0.35rem) rotate(1deg); }
}

/* A nanacote lays out in rows of up to five nanavios (JS sets the columns and rows),
   each nanavio as tall as its row allows without overflowing the width. */
.nanacote-reveal {
  --reveal-gap: clamp(0.25rem, 1.2vmin, 0.7rem);
  display: grid;
  justify-self: center;
  width: min(100%, calc(var(--reveal-columns, 3) * 10rem));
  min-height: 0;
  height: 100%;
  margin: 0;
  padding: 0.3rem;
  grid-template-columns: repeat(auto-fill, minmax(clamp(4.5rem, 22vmin, 9rem), 1fr));
  grid-auto-rows: auto;
  gap: var(--reveal-gap);
  align-content: start;
  overflow-y: auto;
  overscroll-behavior: contain;
  list-style: none;
}

.nanacote-slot {
  width: 100%;
  aspect-ratio: 327 / 450;
}

.nanacote-nanavio-back {
  display: grid;
  width: 100%;
  height: 100%;
  padding: 0;
  place-items: center;
  color: var(--kc-brass-light);
  font-size: clamp(1.4rem, 6vmin, 3.2rem);
  background:
    repeating-linear-gradient(45deg, var(--kc-navy) 0 0.5rem, var(--kc-navy-deep) 0.5rem 1rem);
  border: 0.2rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
  box-shadow: 0.16rem 0.2rem 0 var(--kc-shadow);
  cursor: pointer;
}

.nanacote-nanavio-back:hover,
.nanacote-nanavio-back:focus-visible {
  outline: 0.2rem solid var(--kc-brass-light);
  outline-offset: 0.15rem;
  transform: translateY(-0.2rem);
}

.nanacote-slot.is-revealed .nanavio {
  animation: nanacote-nanavio-reveal 520ms ease-out both;
}

.collection-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: center;
}

/* Three explicit rows — the toolbar, the note and the slots — spaced with
   margins rather than gap, since the note is hidden most of the time and an
   empty row would still take two gaps. */
.collection-album {
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr);
}

.album-toolbar {
  grid-row: 1;
  margin-bottom: clamp(0.3rem, 1.2vmin, 0.6rem);
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  justify-content: space-between;
  align-items: center;
}

.album-progress,
.admin-summary {
  font-size: var(--kc-type-xs);
  font-weight: 900;
}

/* The nanálbum is one block per ship class, so a visitor looking for a cruiser
   is not reading eleven cards for the type on their tooltips. The scroller is
   the outer block: each class's own grid grows to whatever it holds, and one
   scrollbar runs down the whole nanálbum instead of one per class. */
.album-groups {
  grid-row: 3;
  display: grid;
  min-height: 0;
  padding: 0.3rem;
  /* The class blocks sit side by side where there is width: one under the
     other, a wide screen was a column of two cards and a page of paper. */
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
  gap: clamp(0.35rem, 1.2vmin, 0.7rem) clamp(0.8rem, 2vw, 1.6rem);
  align-content: start;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.album-group-title {
  margin: 0 0 0.2rem;
  color: var(--kc-navy-deep);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-xs);
  font-weight: 900;
  letter-spacing: 0.05em;
}

/* The class letters the navy itself uses: read as a code, never as a word. */
.album-group-code {
  padding: 0.05rem 0.35rem;
  margin-right: 0.35em;
  color: var(--kc-paper);
  background: var(--kc-navy);
  border: 0.1rem solid var(--kc-brass);
  border-radius: var(--kc-radius-flat);
  text-transform: uppercase;
}

.album-grid {
  display: grid;
  min-height: 0;
  margin: 0;
  grid-template-columns: repeat(auto-fill, minmax(clamp(5rem, 13vmin, 8.5rem), 1fr));
  gap: clamp(0.35rem, 1.2vmin, 0.7rem);
  align-content: start;
  list-style: none;
}

.album-slot {
  position: relative;
  aspect-ratio: 327 / 450;
}

/* The solo battle is the same screen shape as a duel — a preparation that
   scrolls inside itself and an arena — so it takes the duels panel's own
   containment rather than a second copy of it. */
.collection-duels,
.collection-battle {
  position: relative;
  display: grid;
  overflow: hidden;
}

/* O campeonato (1.93): a head with its name and where it stands, then one
   column that scrolls inside the panel — the registration, the frozen line,
   the bracket, the entrants and the admin's steps — or, while a match is
   watched, the arena in its place. The preparation scrolls with the column
   rather than inside itself, so its action row sticks to the column's bottom. */
.collection-championship {
  position: relative;
  display: grid;
  /* One explicit column: an implicit one is sized to the widest child, and the
     bracket's row of rounds pushed the whole panel past its right edge. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr);
  gap: clamp(0.25rem, 1vmin, 0.5rem);
  overflow: hidden;
}

.championship-head {
  display: flex;
  flex-wrap: wrap;
  gap: 0.1rem 0.7rem;
  align-items: baseline;
  justify-content: center;
  text-align: center;
}

.championship-head .collection-subtitle {
  margin: 0;
  font-family: var(--kc-font-mincho);
}

.championship-state {
  margin: 0;
  color: var(--kc-navy);
  font-size: var(--kc-type-xs);
  font-weight: 800;
}

.championship-state:empty {
  display: none;
}

/* A column of blocks that never shrink: as grid items of a scroller with
   min-height 0 the preparation's rows were squeezed under their own content. */
.championship-body {
  display: flex;
  min-height: 0;
  flex-direction: column;
  gap: clamp(0.5rem, 2vmin, 0.9rem);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.championship-body > * {
  flex: none;
  min-width: 0;
}

.championship-body > .championship-prep {
  overflow: visible;
}

.championship-arena {
  grid-row: 2;
}

.championship-block {
  display: grid;
  min-width: 0;
  gap: 0.4rem;
}

.championship-heading {
  margin: 0;
  padding-bottom: 0.15rem;
  color: var(--kc-navy-deep);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-s);
  font-weight: 800;
  border-bottom: 0.08rem solid var(--kc-brass-dark);
}

.championship-line {
  display: grid;
  margin: 0;
  padding: 0;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr));
  gap: 0.35rem;
  list-style: none;
}

.championship-line-place {
  --banner-art: var(--kc-control-min);
  display: flex;
  min-width: 0;
  gap: 0.4rem;
  align-items: center;
  padding: 0.2rem 0.4rem;
  background: var(--kc-paper-dark);
  border: 0.1rem solid var(--kc-navy);
  border-radius: var(--kc-radius);
}

.championship-line-place > .ship-banner {
  flex: 1 1 auto;
  min-width: 0;
}

.championship-entrants {
  display: flex;
  flex-wrap: wrap;
  margin: 0;
  padding: 0;
  gap: 0.35rem;
  list-style: none;
}

.championship-entrant,
.arena-side {
  display: flex;
  min-width: 0;
  max-width: 100%;
  gap: 0.35rem;
  align-items: center;
  padding: 0.2rem 0.6rem 0.2rem 0.2rem;
  background: var(--kc-paper-dark);
  border: 0.1rem solid var(--kc-navy);
  border-radius: var(--kc-radius-pill);
}

.championship-player-name {
  min-width: 0;
  overflow: hidden;
  font-size: var(--kc-type-xs);
  font-weight: 900;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Whose column is whose in a replay, the duel's and the championship's: a
   plate over each, the picture and the login, at its own column's edge. The
   columns take what the plates leave, and a plate with nobody in it yet takes
   no room. It is the entrants' pill (.championship-entrant), edged in brass. */
.duel-lines:has(> .arena-side) {
  grid-template-rows: auto minmax(0, 1fr);
  row-gap: 0.3rem;
}

.arena-side {
  justify-self: start;
  max-width: min(100%, 22rem);
  margin: 0;
  color: var(--kc-navy-deep);
  border-color: var(--kc-brass-dark);
}

.arena-side:empty {
  display: none;
}

.arena-side-them {
  justify-self: end;
}

.arena-side[data-winner="true"] {
  background: var(--kc-brass-light);
  box-shadow: inset 0 0 0 0.1rem var(--kc-brass-dark);
}

.arena-side[data-winner="true"]::after {
  content: "✓";
  font-family: var(--kc-font-ui);
  font-weight: 900;
}

.championship-winner {
  margin: 0;
  padding: 0.4rem 0.7rem;
  color: var(--kc-navy-deep);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-s);
  font-weight: 800;
  text-align: center;
  background: var(--kc-brass-light);
  border: 0.12rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius);
}

/* The bracket: one column per round, side by side, scrolling sideways inside
   its own row when the rounds do not fit, and each column spacing its matches
   evenly so a match sits between the two it was fed by. */
.championship-rounds {
  display: grid;
  margin: 0;
  padding: 0 0 0.3rem;
  grid-auto-flow: column;
  grid-auto-columns: minmax(min(15rem, 80%), 1fr);
  gap: 0.6rem;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  list-style: none;
}

.championship-round {
  display: grid;
  min-width: 0;
  grid-template-rows: auto 1fr;
  gap: 0.35rem;
}

.championship-round-title {
  margin: 0;
  color: var(--kc-paper);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-xs);
  font-weight: 800;
  text-align: center;
  background: var(--kc-navy);
  border-radius: var(--kc-radius-flat);
}

.championship-matches {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  gap: 0.5rem;
  justify-content: space-around;
  list-style: none;
}

.championship-match {
  display: grid;
  min-width: 0;
  gap: 0.2rem;
  padding: 0.3rem;
  background: var(--kc-paper);
  border: 0.12rem solid var(--kc-navy-deep);
  border-radius: var(--kc-radius);
  box-shadow: 0.12rem 0.14rem 0 var(--kc-shadow);
}

.championship-match[data-state="ready"] {
  border-color: var(--kc-brass-dark);
}

/* Fought and waiting for the admin to put it on the air: nobody reads a winner
   off it yet, so it stands apart from a match still to be fought. */
.championship-match[data-state="pending"] {
  border-color: var(--kc-brass-dark);
  border-style: dashed;
}

/* The broadcast's mark in the arena's action row: the same signal dot as the
   header's live count, on a navy plate, beside what the replay says. */
.championship-live {
  display: inline-flex;
  gap: 0.3rem;
  align-items: center;
  padding: 0.1rem 0.5rem;
  color: var(--kc-paper);
  background: var(--kc-navy-deep);
  border: 0.1rem solid var(--kc-brass);
  border-radius: var(--kc-radius-flat);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-3xs);
  font-weight: 800;
  letter-spacing: 0.08em;
}

.championship-live-dot {
  color: var(--kc-signal);
  font-family: var(--kc-font-ui);
  font-size: 0.75em;
  line-height: 1;
}

.championship-side {
  display: flex;
  min-width: 0;
  min-height: 2rem;
  gap: 0.35rem;
  align-items: center;
  padding: 0.1rem 0.35rem;
  border-radius: var(--kc-radius-flat);
}

.championship-side[data-empty="true"] .championship-player-name {
  color: var(--kc-steel);
  font-style: italic;
  font-weight: 700;
}

.championship-side[data-me="true"] .championship-player-name {
  text-decoration: underline;
  text-decoration-color: var(--kc-brass-dark);
  text-underline-offset: 0.18em;
}

.championship-side[data-winner="true"] {
  background: var(--kc-brass-light);
  box-shadow: inset 0.25rem 0 0 var(--kc-brass-dark);
}

.championship-match[data-state="played"] .championship-side:not([data-winner="true"]) {
  opacity: 0.7;
}

.championship-match-foot {
  display: flex;
  justify-content: center;
  color: var(--kc-navy);
  font-size: var(--kc-type-3xs);
  font-weight: 800;
}

.championship-watch {
  width: 100%;
}

.championship-admin {
  padding: 0.5rem;
  background: var(--kc-sea-soft);
  border: 0.1rem dashed var(--kc-navy);
  border-radius: var(--kc-radius);
}

.championship-admin-open {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  align-items: center;
}

.championship-admin-open .collection-input {
  flex: 1 1 12rem;
  min-width: 0;
}

.championship-admin-open .collection-label {
  flex-basis: 100%;
}

.championship-admin-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  align-items: center;
}

.championship-admin-actions:not(:has(> :not([hidden]))) {
  display: none;
}

/* The AJUDA card: how a battle works, over the preparation rather than always
   open over the line. It scrolls inside itself on a short screen. */
.battle-help {
  position: absolute;
  inset: 0.6rem;
  z-index: 3;
  display: grid;
  gap: 0.5rem;
  width: min(100% - 1.2rem, 30rem);
  height: fit-content;
  max-height: calc(100% - 1.2rem);
  margin: auto;
  padding: 0.8rem 1rem;
  overflow-y: auto;
  color: var(--kc-navy-deep);
  font-size: var(--kc-type-xs);
  font-weight: 700;
  line-height: 1.35;
  background: var(--kc-paper);
  border: 0.16rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
  box-shadow:
    inset 0 0 0 0.2rem var(--kc-paper),
    inset 0 0 0 0.3rem var(--kc-navy-deep),
    0.2rem 0.3rem 0 var(--kc-navy-deep);
}

.battle-help-title {
  margin: 0;
  color: var(--kc-navy-deep);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-s);
  font-weight: 800;
}

.battle-help-close {
  justify-self: end;
}

/* The battle's result, over the arena: above the replay's plates and trails,
   and its title as loud as the outcome. */
.battle-result {
  z-index: 5;
}

.battle-result-title {
  font-size: clamp(1.3rem, 4vmin, 1.8rem);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.collection-invite {
  position: absolute;
  top: 0.4rem;
  left: 50%;
  z-index: var(--kc-z-banner);
  display: flex;
  flex-wrap: wrap;
  width: min(calc(100% - 0.8rem), 34rem);
  padding: 0.5rem 0.7rem;
  gap: 0.4rem;
  justify-content: center;
  align-items: center;
  background: linear-gradient(180deg, var(--kc-brass-light), var(--kc-brass));
  border: 0.16rem solid var(--kc-navy-deep);
  border-radius: var(--kc-radius);
  box-shadow: 0.22rem 0.26rem 0 var(--kc-navy-deep);
  translate: -50% 0;
}

.collection-invite p {
  flex: 1 1 100%;
  margin: 0;
  font-size: clamp(1rem, 2.2vmin, 1.1rem);
  font-weight: 1000;
  text-align: center;
}

.duel-lobby {
  display: grid;
  min-height: 0;
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  gap: 0.35rem;
}

.duel-lobby-hint,
.fleet-hint,
.panel-heading,
.lobby-outgoing {
  margin: 0;
}

.duel-lobby-hint,
.fleet-hint {
  color: var(--kc-navy);
  font-size: var(--kc-type-xs);
  font-weight: 800;
}

/* The line's warnings, one per bullet and on the left, rather than a run of
   sentences in one paragraph. */
.fleet-hint {
  text-align: start;
}

.fleet-hint-list {
  display: grid;
  gap: 0.15rem;
  margin: 0;
  padding-inline-start: 1.1rem;
  list-style: disc;
}

/* A step not open yet reads as locked, not as a broken chip. */
.duel-chip[data-locked="true"] {
  border-style: dashed;
  font-style: italic;
}

.lobby-players {
  display: grid;
  min-height: 0;
  margin: 0;
  padding: 0;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr));
  gap: 0.35rem;
  align-content: start;
  overflow-y: auto;
  overscroll-behavior: contain;
  list-style: none;
}

/* The row's picture slot, which setCollectionAvatarSlot fills; it lays out as
   the avatar it holds. */
.lobby-player-avatar {
  display: contents;
}

.lobby-player {
  display: flex;
  min-width: 0;
  padding: 0.3rem 0.35rem 0.3rem 0.3rem;
  gap: 0.4rem;
  justify-content: space-between;
  align-items: center;
  background: var(--kc-paper);
  border: 0.12rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius);
}

.lobby-player-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  font-size: var(--kc-type-s);
  font-weight: 1000;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.lobby-player-busy {
  color: var(--kc-signal);
  font-size: var(--kc-type-2xs);
  font-weight: 900;
}

.lobby-invite {
  min-height: var(--kc-control-min);
  padding: 0.2rem 0.75rem;
}

.lobby-outgoing {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  align-items: center;
  font-size: var(--kc-type-xs);
  font-weight: 900;
}

.panel-heading {
  font-size: var(--kc-type-xs);
  font-weight: 1000;
  text-transform: uppercase;
}

.nanavio-grid {
  display: grid;
  min-height: 0;
  margin: 0;
  padding: 0.3rem;
  grid-template-columns: repeat(auto-fill, minmax(clamp(3.6rem, 9vmin, 5.4rem), 1fr));
  gap: 0.4rem;
  align-content: start;
  overflow-y: auto;
  overscroll-behavior: contain;
  list-style: none;
}

.profile-nanavio {
  aspect-ratio: 327 / 450;
}

.picker-card {
  width: 100%;
  padding: 0;
  aspect-ratio: 327 / 450;
  background: none;
  border: 0;
  border-radius: var(--kc-radius-nanavio);
  cursor: pointer;
}

.picker-card:disabled {
  cursor: not-allowed;
}

.picker-card:disabled:not([aria-pressed="true"]) {
  opacity: 0.45;
}

/* Selection is a brass glow and focus is a red outline, so a nanavio that is both
   still reads as both. They used to share the same red ring. */
.picker-card[aria-pressed="true"] {
  box-shadow:
    0 0 0 var(--kc-select-width) var(--kc-brass),
    0 0 0.7rem var(--kc-brass-glow);
}

.picker-card:focus-visible,
.duel-slot-button:focus-visible,
.duel-chip:focus-visible,
.lobby-chat-delete:focus-visible {
  outline: var(--kc-focus-width) solid var(--kc-signal);
  outline-offset: 0.14rem;
}

.collection-empty {
  grid-column: 1 / -1;
  padding: 0.4rem;
  color: var(--kc-navy);
  font-size: var(--kc-type-xs);
  font-weight: 800;
}

.action-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  justify-content: center;
  align-items: center;
}

/* The solo screen's fuel line sits beside ATACAR, since that is the button it
   decides. Plain ink on the parchment, digits in columns so the countdown does
   not shimmer as it ticks. */
.battle-fuel {
  color: var(--kc-ink);
  font-size: var(--kc-type-xs);
  font-variant-numeric: tabular-nums;
}

.battle-fuel:empty {
  display: none;
}

/* The doubled wins still to come (1.87) stand under the fuel line in one
   group rather than as a fourth item of the row: side by side, the bonus
   pushed ATACAR onto a second row at 568x320, where AJUDA, the fuel and
   ATACAR otherwise share one. Stacked, it costs the row at most its own line.
   Bold, since it is the one line there that changes what a win pays. */
.battle-meters {
  display: grid;
  justify-items: center;
}

.battle-meters:not(:has(> :not(:empty))) {
  display: none;
}

.battle-bonus {
  font-weight: 700;
}

.chat-form {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  gap: 0.3rem;
}

.chat-form .collection-input {
  grid-area: 1 / 1;
  padding: 0.3rem 0.5rem;
}

.chat-form .collection-action {
  grid-area: 1 / 3;
  padding: 0.2rem 0.7rem;
}

/* The emoji button, between the field and ENVIAR: a square of the control
   height carrying its face as a ::before, silent for assistive technology
   (its aria-label names it), like the steel mark. */
.chat-form .lobby-chat-emoji-toggle {
  grid-area: 1 / 2;
  min-width: var(--kc-control-min);
  padding: 0;
}

.lobby-chat-emoji-toggle::before {
  font-family: var(--kc-font-ui);
  font-size: 1.35rem;
  font-weight: 400;
  line-height: 1;
  content: "😊";
  content: "😊" / "";
}

.lobby-chat-emoji-toggle[aria-expanded="true"] {
  background: linear-gradient(180deg, var(--kc-brass-light), var(--kc-brass));
}

/* The count near the 200-character cap, laid over the end of the field it
   counts, which makes room for it while it shows (the `:has()` rule below). */
.chat-count {
  grid-area: 1 / 1;
  align-self: center;
  justify-self: end;
  margin-right: 0.5rem;
  color: var(--kc-navy-deep);
  font-size: var(--kc-type-3xs);
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  pointer-events: none;
}

/* The count sits over the end of the field, so the text keeps clear of it
   while it shows instead of running underneath the digits. */
.collection-input:has(+ .chat-count:not([hidden])) {
  padding-right: 4.2rem;
}

.chat-count[data-full="true"] {
  color: var(--kc-signal);
}

.admin-list {
  display: grid;
  min-height: 0;
  margin: 0;
  padding: 0;
  gap: 0.35rem;
  align-content: start;
  overflow-y: auto;
  overscroll-behavior: contain;
  list-style: none;
}

.admin-pull,
.admin-user {
  display: grid;
  padding: 0.35rem 0.45rem;
  gap: 0.2rem;
  font-size: var(--kc-type-2xs);
  background: var(--kc-paper);
  border: 0.1rem solid var(--kc-navy-deep);
  border-radius: var(--kc-radius-nanavio);
}

.chip-row {
  display: flex;
  flex-wrap: wrap;
  margin: 0;
  gap: 0.2rem;
  align-items: center;
}

.name-chip {
  padding: 0.05rem 0.4rem;
  font-weight: 850;
  background: var(--kc-sea-soft);
  border: 0.08rem solid var(--kc-navy);
  border-radius: var(--kc-radius-pill);
}

/* The certificate's panel wears the parchment every panel does (the skin's
   `.collection-panel` below), and its canvas no border of its own. */
.collection-certificate {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  gap: clamp(0.3rem, 1.2vmin, 0.6rem);
  place-items: center;
}

.nanaversario-canvas {
  width: 100%;
  height: 100%;
  min-height: 0;
  object-fit: contain;
}

/* The manual. Two sibling screens share one grid cell: the subject index and
   the reader. Only one is ever shown, and `[hidden] { display: none !important }`
   near the top of this file beats their `display: grid`, so the hidden one costs
   nothing. The panel itself never scrolls — the list and the article do — which
   is what keeps the heading and the VOLTAR bar on screen. TRAVAR FROTA rode
   off the bottom edge for several releases because a scrolling panel put its own
   control past an ancestor's `overflow: hidden`; the same mistake here would
   strand a reader inside a subject with no way back. */
.collection-manual {
  display: grid;
  /* One explicit column: a bare `auto` track is sized to max-content, so the
     longest line of prose would widen the panel instead of wrapping. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  overflow: hidden;
}

.manual-index,
.manual-reader {
  display: grid;
  grid-row: 1;
  grid-column: 1;
  /* One explicit column here too: an implicit one takes the width of the
     widest one-line child, and the replay button's old label (`rever o
     tutorial do PIRACA I.A`) ran 6px past the box at 320 wide, cutting every
     card at the right. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr);
  /* A reading measure, not the panel's whole width: on a desktop the article
     would otherwise run 1300px a line, and nine subject cards would spread into
     six columns of scatter instead of a list. */
  width: min(100%, 52rem);
  min-width: 0;
  min-height: 0;
  padding: clamp(0.55rem, 1.8vmin, 1rem);
  gap: clamp(0.3rem, 1.2vmin, 0.6rem);
  justify-self: center;
  overflow: hidden;
  /* A sheet of plain paper in the theme's brass frame, the AJUDA card's: the
     panel's hexagons stood right behind the prose, between its lines, which the
     owner found odd to read (1.110). */
  background: var(--kc-paper);
  border: 0.16rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
  box-shadow:
    inset 0 0 0 0.2rem var(--kc-paper),
    inset 0 0 0 0.3rem var(--kc-navy-deep),
    0.2rem 0.26rem 0 var(--kc-shadow);
}

.manual-index-head {
  display: grid;
  gap: 0.15rem;
  padding-block-end: 0.35rem;
  border-block-end: 0.1rem solid var(--kc-brass);
}

.manual-replay-tutorial {
  justify-self: start;
}

.manual-index-hint {
  margin: 0;
  color: var(--kc-steel);
  font-family: var(--kc-font-ui);
  font-size: var(--kc-type-3xs);
}

.manual-topics {
  display: grid;
  /* auto-fit rather than a media query: one column on a phone, two on a desk,
     and no override sitting in a block that would lose the cascade. */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
  min-height: 0;
  margin: 0;
  /* Room for the cards' offset shadow, and for the scrollbar beside them. */
  padding: 0 0.5rem 0.2rem 0;
  gap: 0.35rem;
  align-content: start;
  list-style: none;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.manual-topic {
  display: grid;
  width: 100%;
  min-height: var(--kc-control-min);
  padding: 0.4rem 0.6rem;
  gap: 0.1rem;
  text-align: start;
  background: var(--kc-sea-soft);
  border: 0.12rem solid var(--kc-navy-deep);
  border-radius: var(--kc-radius);
  box-shadow: 0.12rem 0.15rem 0 var(--kc-navy-deep);
  cursor: pointer;
}

.manual-topic:hover {
  background: var(--kc-paper-dark);
}

.manual-topic:active {
  box-shadow: none;
  transform: translateY(0.1rem);
}

.manual-topic-name {
  color: var(--kc-navy-deep);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-xs);
  font-weight: 800;
}

.manual-topic-hint {
  color: var(--kc-steel);
  font-family: var(--kc-font-ui);
  font-size: var(--kc-type-3xs);
}

.manual-reader-bar {
  display: flex;
  justify-content: flex-start;
  padding-block-end: 0.35rem;
  border-block-end: 0.1rem solid var(--kc-brass);
}

.manual-article {
  display: grid;
  min-height: 0;
  padding-inline-end: 0.5rem;
  gap: 0.6rem;
  align-content: start;
  color: var(--kc-ink);
  font-family: var(--kc-font-ui);
  font-size: var(--kc-type-2xs);
  line-height: 1.45;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.manual-article p {
  margin: 0;
}

.manual-heading {
  margin: 0.25rem 0 0;
  padding-block-end: 0.15rem;
  border-block-end: 0.08rem solid var(--kc-brass-light);
  color: var(--kc-navy-deep);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-xs);
  font-weight: 800;
}

.manual-list {
  display: grid;
  margin: 0;
  padding-inline-start: 1.1rem;
  gap: 0.25rem;
}

/* Rows read as a name above its numbers, the shape the observability tab
   settled on: a five-column table does not fit 320px without scrolling sideways.
   They drop their markers with the same breath, since a grid item shows none
   anyway and a 1. beside a stat would read as a second count. */
.manual-stats {
  padding-inline-start: 0.7rem;
  list-style: none;
}

.manual-stats li {
  display: grid;
  gap: 0.05rem;
}

.manual-stat-name {
  color: var(--kc-navy-deep);
  font-weight: 800;
}

.manual-stat-value {
  color: var(--kc-steel);
}

.manual-key {
  display: inline-block;
  padding: 0.05rem 0.3rem;
  color: var(--kc-paper);
  font-weight: 800;
  background: var(--kc-navy);
  border-radius: var(--kc-radius-flat);
}

.manual-terms {
  display: grid;
  margin: 0;
  gap: 0.3rem;
}

.manual-terms > div {
  display: grid;
  gap: 0.05rem;
  padding-inline-start: 0.5rem;
  border-inline-start: 0.16rem solid var(--kc-brass);
}

.manual-terms dt {
  color: var(--kc-navy-deep);
  font-family: var(--kc-font-mincho);
  font-weight: 800;
}

.manual-terms dd {
  margin: 0;
}

.manual-ring {
  font-family: var(--kc-font-mincho);
  font-weight: 800;
}

.manual-value {
  color: var(--kc-signal);
  font-weight: 900;
  font-variant-numeric: tabular-nums;
}

.manual-note {
  padding: 0.3rem 0.45rem;
  color: var(--kc-navy-deep);
  background: var(--kc-paper-dark);
  border-inline-start: 0.16rem solid var(--kc-signal);
  border-radius: var(--kc-radius-flat);
}

/* On the note's darker paper the signal red reads at 4.2:1, so a number there
   stands on a chip of the article's own paper, where it reads at 5.3:1 like
   every other value of the manual. */
.manual-note .manual-value {
  padding-inline: 0.15em;
  background: var(--kc-paper);
  border-radius: var(--kc-radius-flat);
}

/* The manual's own short-viewport override, written here right after the rules
   it overrides, which is the only thing that makes an override apply: a media
   query carries no extra specificity of its own. PIRACA I.A's landscape pair
   shipped dead for a release by sitting ABOVE his base rules, so the rule to
   follow is "after its own rules", not "away from the collection's blocks" —
   the collection's own `max-height` and `max-width` blocks sit further down
   this file and declare no `.manual-*` selector.
   Anything they did declare would win, which is the direction to watch here. */
@media (max-height: 34rem) {
  /* Three lines a card is too much when the dialog has room for four cards.
     The name alone still says what the subject is. */
  .manual-topic-hint,
  .manual-index-hint {
    display: none;
  }

  .manual-topics {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), 1fr));
  }

  /* The sheet's frame gives back what a short screen cannot spare. */
  .manual-index,
  .manual-reader {
    padding: 0.4rem;
    gap: 0.25rem;
  }
}

.collection-admin {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  gap: clamp(0.4rem, 1.5vmin, 0.8rem);
  align-content: start;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.admin-card {
  display: grid;
  min-width: 0;
  margin: 0;
  padding: 0.55rem;
  gap: 0.35rem;
  align-content: start;
  background: var(--kc-paper);
  border: 0.12rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius-flat);
}

.admin-feed {
  grid-template-rows: auto minmax(0, 1fr);
  max-height: 22rem;
}

.admin-user {
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
}

.admin-credentials {
  display: grid;
  padding: 0.4rem;
  gap: 0.3rem;
  background: var(--kc-paper-dark);
  border: 0.1rem dashed var(--kc-navy-deep);
  border-radius: var(--kc-radius-nanavio);
}

.admin-credentials pre {
  margin: 0;
  font-size: 1rem;
  font-weight: 800;
  white-space: pre-wrap;
  user-select: all;
}

.admin-user-link {
  margin-inline-end: 0.2rem;
}

.collection-avatar {
  --avatar-size: 1.5rem;
  --avatar-ring: var(--kc-navy);
  position: relative;
  display: inline-grid;
  flex: none;
  width: var(--avatar-size);
  height: var(--avatar-size);
  overflow: hidden;
  place-items: center;
  vertical-align: middle;
  color: var(--kc-navy-deep);
  /* The initial is the one picture an account without one has, so it reads at
     the collection's 16px floor even in the smallest circle, where half the
     circle used to make it 12px. */
  font-size: max(1rem, calc(var(--avatar-size) * 0.5));
  font-weight: 1000;
  line-height: 1;
  background: var(--kc-sea-soft);
  border: 0.12rem solid var(--avatar-ring);
  border-radius: 50%;
  box-shadow: 0 0 0 0.08rem var(--kc-navy-deep);
}

.collection-avatar:not(.has-nanavio)::before {
  content: attr(data-initial);
}

.collection-avatar.is-medium {
  --avatar-size: 2.1rem;
}

.collection-avatar.is-large {
  --avatar-size: 2.9rem;
}

/* The face sits in the upper part of the portrait art. Both layers stay hidden
   until the script marks the circle `has-nanavio`, which it does only once the
   card and the character have both decoded: the initial stands until then,
   rather than the character floating on the crop's plain colour. */
.collection-avatar-image {
  position: absolute;
  inset: 0;
  width: 100%;
  max-width: none;
  height: 100%;
  object-fit: cover;
  object-position: 50% 30%;
  opacity: 0;
  transform: scale(1.55);
  transform-origin: 49% 27%;
}

.collection-avatar.has-nanavio .collection-avatar-image {
  opacity: 1;
}

.collection-ticker .collection-avatar {
  margin-inline-end: 0.3rem;
}

.collection-invite p .collection-avatar {
  margin-inline-end: 0.35rem;
}

.duel-avatars {
  display: flex;
  justify-content: center;
}

.duel-avatars .collection-avatar + .collection-avatar {
  margin-inline-start: -0.6rem;
}

.nanavio-viewer-avatar-actions {
  display: flex;
  flex-wrap: wrap;
  margin: 0.15rem 0 0;
  gap: 0.4rem;
  justify-content: center;
  align-items: center;
}

.nanavio-viewer-avatar-actions .collection-action {
  padding: 0.2rem 0.8rem;
}

.nanavio-viewer-avatar-actions .collection-link-button {
  font-size: var(--kc-type-xs);
}

.admin-user .collection-avatar {
  margin-inline-end: 0.3rem;
}

.collection-online,
.collection-header-steel {
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 0.1rem 0 0;
  gap: 0.3rem;
  color: var(--kc-sea-soft);
  font-size: var(--kc-type-2xs);
  font-weight: 900;
}

/* The ticker floats over the header's bottom edge instead of taking a row of
   its own: toggled in and out of the grid, it grew the header and shoved the
   whole body down and back up every six seconds, under whatever the visitor was
   reading or about to tap. It lets taps through to what it floats over, and
   the header it hangs from is already positioned by the naval skin. */
.collection-ticker {
  position: absolute;
  top: 100%;
  inset-inline: 0;
  z-index: var(--kc-z-banner);
  width: fit-content;
  max-width: min(100%, 36rem);
  margin: 0.15rem auto 0;
  pointer-events: none;
  padding: 0.18rem 0.8rem;
  overflow-wrap: anywhere;
  color: var(--kc-paper);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-xs);
  font-weight: 850;
  background: linear-gradient(180deg, var(--kc-navy), var(--kc-navy-deep));
  border: 0.12rem solid var(--kc-brass);
  border-radius: var(--kc-radius-flat);
  box-shadow: 0 0 1rem var(--kc-brass-glow);
  animation: collection-ticker-in 0.45s ease-out;
}

.collection-ticker strong {
  font-weight: 950;
}

.collection-ticker-mark {
  color: var(--kc-brass-light);
}

.collection-ticker.is-ss-holo {
  background:
    linear-gradient(var(--kc-navy-deep), var(--kc-navy-deep)) padding-box,
    linear-gradient(90deg, var(--holo-pink), var(--holo-gold), var(--holo-mint), var(--holo-sky), var(--holo-violet)) border-box;
  border-color: transparent;
  box-shadow: 0 0 1.2rem var(--holo-glow);
}

@keyframes collection-ticker-in {
  from {
    opacity: 0;
    transform: translateY(-0.4rem) scale(0.96);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

.collection-welcome {
  color: var(--kc-navy-deep);
  background: var(--kc-brass);
  box-shadow: 0 0 0.9rem var(--kc-brass-glow);
}

.collection-online-dot {
  color: var(--kc-steel);
  animation: collection-online-pulse 2.4s ease-in-out infinite;
}

@keyframes collection-online-pulse {
  0%,
  100% {
    opacity: 1;
  }

  50% {
    opacity: 0.35;
  }
}

.collection-observability {
  display: grid;
  /* One explicit column: a bare `auto` track is sized to max-content, so the
     scrollable chip row below would widen the whole panel instead of scrolling. */
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(0.4rem, 1.5vmin, 0.8rem);
  align-content: start;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* The section chips and the freshness stamp share one row, and the stamp drops
   below them when the chips need the whole width. */
.obs-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.3rem 0.5rem;
}

.obs-sections {
  display: flex;
  flex-wrap: wrap;
  min-width: 0;
  gap: 0.3rem;
}

/* A section with something wrong flies a signal dot, so the warning is visible
   from the three sections that are not showing it. The CHAT tab flies the same
   dot while a message from somebody else waits. */
.obs-sections [data-alert="true"]::after,
.collection-tab[data-unread="true"]::after {
  display: inline-block;
  width: 0.45rem;
  height: 0.45rem;
  margin-inline-start: 0.4rem;
  background: var(--kc-signal);
  border-radius: var(--kc-radius-pill);
  content: "";
}

.obs-updated {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  margin: 0;
  color: var(--kc-navy);
  font-size: var(--kc-type-2xs);
  font-weight: 850;
  text-align: end;
}

/* A real aria-hidden span, like the header's dot: a `content` mark is read out
   by Chrome and Firefox, which would put a bullet in the middle of the stamp. */
.obs-dot {
  color: var(--kc-steel);
}

.obs-updated[data-state="live"] .obs-dot {
  animation: collection-online-pulse 2.4s ease-in-out infinite;
}

/* A stale reading looks stale: the last good numbers stay on screen, but the
   stamp stops claiming they are live. */
.obs-updated[data-state="stale"] {
  color: var(--kc-signal);
}

.obs-updated[data-state="stale"] .obs-dot {
  color: var(--kc-signal);
}

.obs-alert-slot:empty {
  display: none;
}

.obs-alert {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: var(--kc-control-min);
  margin: 0;
  padding: 0.3rem 0.6rem;
  color: var(--kc-navy-deep);
  font: inherit;
  font-size: var(--kc-type-2xs);
  font-weight: 850;
  text-align: start;
  background: var(--kc-sea-soft);
  border: 0.12rem solid var(--kc-navy-deep);
  border-radius: var(--kc-radius-flat);
  box-shadow: inset 0.3rem 0 0 var(--kc-steel);
}

.obs-alert.is-warning {
  background: var(--kc-paper-dark);
  box-shadow: inset 0.3rem 0 0 var(--kc-brass-dark);
}

.obs-alert.is-critical {
  color: var(--kc-signal);
  border-color: var(--kc-signal);
  box-shadow: inset 0.3rem 0 0 var(--kc-signal);
}

button.obs-alert {
  cursor: pointer;
}

.obs-tiles {
  display: grid;
  /* Wide enough for the longest word a label prints at the 16px floor:
     "REFORMA" ran out of an 8rem tile, and "REMODELAÇÃO" has stood in
     its place since 1.114. */
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 11.5rem), 1fr));
  margin: 0;
  gap: 0.35rem;
}

.obs-tile {
  display: grid;
  /* Label on top, value pinned to the bottom of the rest: a label that wraps
     no longer pushes its number out of line with the tiles beside it. */
  grid-template-rows: auto minmax(0, 1fr);
  min-width: 0;
  padding: 0.35rem 0.45rem 0.35rem 0.6rem;
  gap: 0.1rem;
  background: var(--kc-paper);
  border: 0.1rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius-flat);
}

.obs-tile dt {
  color: var(--kc-navy);
  font-size: var(--kc-type-3xs);
  font-weight: 900;
  text-transform: uppercase;
}

.obs-tile dd {
  display: grid;
  align-content: end;
  min-width: 0;
  margin: 0;
  color: var(--kc-navy-deep);
}

.obs-tile-value {
  font-size: clamp(1.1rem, 2.6vmin, 1.35rem);
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

/* A version hash or a backup age is read, not compared, so it
   does not need the size the counts use — and stops wrapping into three lines. */
.obs-tile.is-text .obs-tile-value {
  font-size: var(--kc-type-xs);
}

.obs-tile-detail {
  color: var(--kc-navy);
  font-size: var(--kc-type-3xs);
  font-weight: 800;
  overflow-wrap: anywhere;
}

/* The tone reads as a stripe down the side, so a problem is visible without
   comparing fills across a grid of eight tiles. */
.obs-tile.is-warning {
  background: var(--kc-paper-dark);
  box-shadow: inset 0.28rem 0 0 var(--kc-brass-dark);
}

.obs-tile.is-critical {
  background: var(--kc-sea-soft);
  border-color: var(--kc-signal);
  box-shadow: inset 0.28rem 0 0 var(--kc-signal);
}

.obs-tile.is-critical .obs-tile-value {
  color: var(--kc-signal);
}

.obs-section > .collection-link-button {
  justify-self: start;
  font-size: var(--kc-type-2xs);
}

.obs-columns {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
  gap: 0.5rem;
}

/* A block that takes the whole width of its section rather than a column of the
   auto-fit grid: the two blocks of the activity panel, whose rows are as wide
   as their text. They sit outside `.obs-columns` rather than spanning `1 / -1`
   inside it, which would stop auto-fit collapsing its empty tracks and leave
   the two name lists squeezed into 13rem apiece with four dead tracks beside
   them. */
.obs-block {
  min-width: 0;
}

/* Each heading takes a rule the width of its own column: without one the three
   of them read as a single run-on line on a wide screen, where every column is
   far wider than the chips inside it. A rule between the columns instead would
   land in the wrong place as soon as auto-fit wrapped them. */
.obs-columns > * > .panel-heading,
.obs-block > .panel-heading {
  display: block;
  padding-bottom: 0.1rem;
  border-bottom: 0.08rem solid var(--kc-brass-dark);
}

.obs-list {
  display: grid;
  /* One section shows at a time now, so a list can be taller before it starts
     costing the section below it; the viewport still bounds it. */
  max-height: min(20rem, 42vh);
  margin: 0.2rem 0 0;
  padding: 0;
  gap: 0.25rem;
  align-content: start;
  overflow-y: auto;
  overscroll-behavior: contain;
  list-style: none;
}

/* Bare names pack far tighter as a wrapping row of chips than as one stacked
   full-width row each. */
.obs-list.is-inline {
  display: flex;
  flex-wrap: wrap;
}

.obs-item {
  color: var(--kc-navy-deep);
  font-size: var(--kc-type-2xs);
  overflow-wrap: anywhere;
  background: var(--kc-paper);
  border: 0.08rem solid var(--kc-navy);
  border-radius: var(--kc-radius);
}

.obs-item-body {
  display: grid;
  width: 100%;
  padding: 0.25rem 0.4rem;
  gap: 0.05rem;
  text-align: start;
}

/* The whole row is the control, so it keeps the hit target and the focus ring
   without the pill a link button would draw inside its own border. */
button.obs-item-body {
  min-height: var(--kc-control-min);
  align-content: center;
  color: inherit;
  font: inherit;
  background: none;
  border: 0;
  border-radius: var(--kc-radius);
  cursor: pointer;
}

button.obs-item-body:hover {
  background: var(--kc-sea-soft);
}

button.obs-item-body:focus-visible {
  outline: var(--kc-focus-width) solid var(--kc-signal);
  outline-offset: -0.14rem;
}

.obs-item-name {
  font-weight: 900;
}

.obs-item-meta {
  color: var(--kc-navy);
  font-size: var(--kc-type-3xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.obs-meter {
  height: 0.25rem;
  margin-top: 0.15rem;
  background: var(--kc-sea-soft);
  border-radius: var(--kc-radius-pill);
}

.obs-meter::before {
  display: block;
  width: calc(var(--value, 0) * 100%);
  height: 100%;
  background: var(--kc-brass-dark);
  border-radius: inherit;
  content: "";
}

.obs-item.is-empty {
  color: var(--kc-navy);
  border-style: dashed;
}

.obs-item.is-empty .obs-item-name {
  font-weight: 700;
}

.obs-charts {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr));
  gap: 0.55rem;
}

.obs-chart {
  display: grid;
  min-width: 0;
  margin: 0;
  gap: 0.1rem;
  --obs-bar-color: var(--kc-steel);
  --obs-peak-color: var(--kc-navy);
}

.obs-chart.is-critical {
  --obs-bar-color: var(--kc-signal);
  --obs-peak-color: var(--kc-signal);
}

/* A metric with nothing in it steps back so the busy ones read first. */
.obs-chart.is-quiet {
  opacity: 0.55;
}

.obs-chart figcaption {
  display: flex;
  justify-content: space-between;
  gap: 0.4rem;
  color: var(--kc-navy-deep);
  font-size: var(--kc-type-2xs);
  font-weight: 850;
}

.obs-chart-title {
  min-width: 0;
}

.obs-chart figcaption strong {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.obs-chart-note {
  margin: 0;
  color: var(--kc-navy);
  font-size: var(--kc-type-3xs);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

.obs-bars {
  display: grid;
  grid-template-columns: repeat(24, minmax(0, 1fr));
  align-items: end;
  height: 4.2rem;
  margin-top: 0.15rem;
  padding: 0 0 0.08rem;
  gap: 2px;
  border-bottom: 0.08rem solid var(--kc-navy);
}

.obs-bar {
  height: calc(var(--value, 0) * 100%);
  min-height: 2px;
  background: var(--obs-bar-color);
  border-radius: var(--kc-radius-flat) var(--kc-radius-flat) 0 0;
}

.obs-bar.is-zero {
  background: var(--kc-sea-soft);
}

/* The tallest hour is darker, so the shape of the day reads at a glance. */
.obs-bar.is-peak {
  background: var(--obs-peak-color);
}

.obs-bar:hover {
  background: var(--kc-brass-dark);
}

.obs-axis {
  display: flex;
  justify-content: space-between;
  color: var(--kc-navy);
  font-size: var(--kc-type-3xs);
  font-weight: 800;
}

/* The stamp's dot is left to the blanket reduced-motion block at the end of the
   file: its pulse is set by a heavier selector this one could never beat. */
@media (prefers-reduced-motion: reduce) {
  .collection-online-dot,
  .collection-ticker,
  .nanacote-wrapper {
    animation: none;
  }
}

.collection-history {
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  gap: clamp(0.3rem, 1.2vmin, 0.6rem);
  overflow: hidden;
}

/* Placed by hand: the room line is hidden most of the time, and left to
   auto-placement the list took its auto row and grew past the panel while
   "carregar mais" sat in the 1fr one, cut off at the bottom edge. */
.collection-history > .history-filters {
  grid-row: 1;
}

.collection-history > .history-room {
  grid-row: 2;
}

.collection-history > .history-list {
  grid-row: 3;
}

.collection-history > .history-more {
  grid-row: 4;
}

.history-filters {
  display: flex;
  flex-wrap: wrap;
  margin: 0;
  gap: 0.4rem;
  align-items: end;
}

.history-filter {
  display: grid;
  flex: 1 1 9rem;
  gap: 0.1rem;
}

.history-filter .collection-input {
  padding: 0.25rem 0.5rem;
}

.history-filters .collection-action {
  padding: 0.2rem 0.8rem;
}

.history-room {
  display: flex;
  flex-wrap: wrap;
  margin: 0;
  gap: 0.4rem;
  align-items: center;
  font-size: var(--kc-type-xs);
  font-weight: 900;
}

.history-list {
  display: grid;
  min-height: 0;
  margin: 0;
  padding: 0;
  gap: 0.3rem;
  align-content: start;
  overflow-y: auto;
  overscroll-behavior: contain;
  list-style: none;
}

.history-entry {
  display: grid;
  padding: 0.3rem 0.5rem;
  gap: 0.12rem;
  background: var(--kc-paper);
  border: 0.1rem solid var(--kc-brass-dark);
  border-left-width: 0.35rem;
  border-radius: var(--kc-radius-flat);
}

.history-entry[data-category="nanacote"] {
  border-left-color: var(--kc-brass);
}

.history-entry[data-category="admin"] {
  border-left-color: var(--kc-signal);
}

.history-entry[data-category="system"],
.history-entry[data-kind="account.login_failed"] {
  border-left-color: var(--kc-signal);
}

.history-entry.is-chat {
  background: var(--kc-sea-soft);
}

.history-meta,
.history-text {
  margin: 0;
}

.history-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  align-items: center;
  color: var(--kc-navy);
  font-size: var(--kc-type-3xs);
  font-weight: 850;
}

.history-ip {
  font-family: ui-monospace, "Cascadia Mono", monospace;
}

.history-text {
  font-size: var(--kc-type-xs);
  font-weight: 800;
  overflow-wrap: anywhere;
}

.history-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}

.history-actions .collection-link-button,
.history-room .collection-link-button,
.history-more,
.profile-header .collection-link-button,
.profile-section .collection-link-button,
.admin-user-link {
  font-size: var(--kc-type-2xs);
}

.history-more {
  justify-self: center;
}

.collection-profile {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 19rem), 1fr));
  gap: clamp(0.4rem, 1.5vmin, 0.8rem);
  align-content: start;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.profile-header {
  display: flex;
  flex-wrap: wrap;
  grid-column: 1 / -1;
  gap: 0.4rem;
  align-items: center;
}

.profile-summary {
  flex: 1 1 100%;
  margin: 0;
  font-size: var(--kc-type-xs);
  font-weight: 850;
}

.profile-section {
  grid-template-rows: auto minmax(0, 1fr);
  max-height: 24rem;
}

.profile-section .admin-list,
.profile-section .history-list,
.profile-collection {
  overflow-y: auto;
}

/* An owned nanálbum nanavio opens the full-screen viewer, with her art flat
   beside her ficha. */
.album-nanavio-button {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  color: inherit;
  font: inherit;
  background: none;
  border: 0;
  border-radius: var(--kc-radius-nanavio);
  cursor: pointer;
  transition: transform 160ms ease-out;
}

.album-nanavio-button:hover,
.album-nanavio-button:focus-visible {
  transform: translateY(-0.25rem);
}

.album-nanavio-button:focus-visible {
  outline: 0.22rem solid var(--kc-signal);
  outline-offset: 0.12rem;
}

.nanavio-viewer {
  position: absolute;
  inset: 0;
  z-index: var(--kc-z-viewer);
  display: grid;
  padding:
    max(3.6rem, env(safe-area-inset-top))
    max(0.6rem, env(safe-area-inset-right))
    max(0.8rem, env(safe-area-inset-bottom))
    max(0.6rem, env(safe-area-inset-left));
  grid-template-rows: minmax(0, 1fr) auto;
  gap: clamp(0.3rem, 1.2dvh, 0.7rem);
  justify-items: center;
  overflow: hidden;
  color: var(--kc-paper);
  background:
    radial-gradient(ellipse at 50% 108%, var(--kc-sea-glow), transparent 45%),
    linear-gradient(180deg, var(--kc-navy-deep), var(--kc-navy) 72%, var(--kc-navy-deep));
  outline: none;
  animation: cinematic-fade-in 220ms ease-out both;
}

/* The art stands flat, as it is: no turning, no zoom and no sheen. It is the
   grid item itself, centred in its own cell, and neither selects nor drags. */
.nanavio-viewer-front {
  position: relative;
  z-index: 1;
  place-self: center;
  height: min(88%, calc(80vw * 450 / 327), 44rem);
  aspect-ratio: 327 / 450;
  border-radius: var(--kc-radius-nanavio);
  user-select: none;
  -webkit-user-drag: none;
}

/* The card clips itself to its own rounded corners rather than leaving that to
   the front around it. */
.nanavio-viewer-front .nanavio {
  height: 100%;
  overflow: hidden;
  border-radius: inherit;
  box-shadow: none;
}

/* The ficha: the one screen that shows a single character whole. It scrolls
   inside itself on a phone, since a sheet this long is taller than the room a
   landscape phone leaves beside the art, and the dialog may never scroll. */
.nanavio-viewer-info {
  position: relative;
  z-index: 2;
  display: grid;
  align-content: start;
  /* Every row its content's height: the ficha is what scrolls, and an auto
     row shrank under a long section until the chips overlapped it. */
  grid-auto-rows: max-content;
  width: min(100%, 28rem);
  min-height: 0;
  padding: 0.5rem 0.8rem 0.6rem;
  gap: 0.4rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--kc-brass-dark) transparent;
  text-align: start;
  background: var(--kc-night);
  border: 0.12rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
}

.nanavio-sheet-head {
  display: grid;
  gap: 0.1rem;
}

/* The ficha's three sections, one at a time: status, equipment and
   reformas. The row stays in sight at the top of the ficha while a long
   section scrolls under it, on the ficha's own night. It wraps rather than
   scrolling sideways: three chips at the 16px floor are wider than the ficha,
   and a chip cut at the edge read as a missing section. */
.nanavio-sheet-sections {
  position: sticky;
  /* Up over the ficha's own top padding, or the section scrolling under the
     row shows through that strip above it. */
  top: -0.5rem;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.3rem;
  min-width: 0;
  margin: 0 -0.8rem;
  padding: 0.3rem 0.8rem 0.4rem;
  background: var(--kc-night);
}

.nanavio-sheet-sections .panel-filter {
  flex: 1 1 auto;
  /* Tighter than a chip elsewhere, so the three stand on one row from about
     26rem of ficha; narrower, they still wrap. */
  padding-inline: 0.45rem;
}

.nanavio-sheet-panel {
  display: grid;
  min-width: 0;
  gap: 0.4rem;
}

/* The ficha holds the keyboard when the viewer opens so the arrows scroll it.
   The ring shows only when the keyboard is really there, drawn inside the
   brass border rather than over the art beside it. */
.nanavio-sheet:focus {
  outline: none;
}

.nanavio-sheet:focus-visible {
  outline: 0.16rem solid var(--kc-brass-light);
  outline-offset: -0.3rem;
}

.nanavio-viewer-name,
.nanavio-viewer-details,
.nanavio-sheet-tags {
  margin: 0;
}

.nanavio-viewer-name {
  color: var(--kc-brass-light);
  font-size: clamp(1rem, 3vmin, 1.45rem);
  font-weight: 1000;
  line-height: 1.1;
  text-shadow: 0.08rem 0.1rem 0 var(--kc-navy-deep);
}

.nanavio-sheet-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 0.6rem;
  font-size: var(--kc-type-2xs);
}

.nanavio-sheet-type {
  color: var(--kc-paper-dark);
}

.nanavio-viewer-details {
  color: var(--kc-paper-dark);
  font-size: var(--kc-type-xs);
  font-weight: 850;
}

/* "como deixar ela mais forte?": a plain link under the class and the rungs,
   wrapping rather than running past the ficha on a narrow phone. */
.nanavio-sheet-guide-toggle {
  justify-self: start;
  margin-top: 0.3rem;
  font-size: var(--kc-type-xs);
  white-space: normal;
  text-align: start;
}

/* The card that answers it, over the ficha and the art, clear of the viewer's
   own × at the top. The title and FECHAR stay put and only the body scrolls,
   so the way back never falls past an edge. */
.nanavio-viewer-guide {
  position: absolute;
  inset:
    max(3.6rem, env(safe-area-inset-top))
    max(0.6rem, env(safe-area-inset-right))
    max(0.8rem, env(safe-area-inset-bottom))
    max(0.6rem, env(safe-area-inset-left));
  /* Over the ficha, which stands at 2; its top edge clears the × above it. */
  z-index: 3;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: 0.5rem;
  width: min(100% - 1.2rem, 34rem);
  height: fit-content;
  max-height: calc(100% - max(3.6rem, env(safe-area-inset-top)) - max(0.8rem, env(safe-area-inset-bottom)));
  margin: auto;
  padding: 0.7rem 0.9rem;
  color: var(--kc-navy-deep);
  font-size: var(--kc-type-xs);
  font-weight: 700;
  line-height: 1.35;
  background: var(--kc-paper);
  border: 0.16rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
  box-shadow:
    inset 0 0 0 0.2rem var(--kc-paper),
    inset 0 0 0 0.3rem var(--kc-navy-deep),
    0.2rem 0.3rem 0 var(--kc-navy-deep);
}

.nanavio-viewer-guide-title {
  margin: 0;
  color: var(--kc-navy-deep);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-s);
  font-weight: 800;
  line-height: 1.2;
}

.nanavio-viewer-guide-body {
  display: grid;
  gap: 0.6rem;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  outline: none;
}

.nanavio-viewer-guide-body p {
  margin: 0;
}

.nanavio-viewer-guide-block {
  display: grid;
  gap: 0.3rem;
  padding-top: 0.5rem;
  border-top: 0.1rem solid var(--kc-brass-dark);
}

.nanavio-viewer-guide-block .nanavio-sheet-title {
  color: var(--kc-navy-deep);
}

.nanavio-viewer-guide-now {
  color: var(--kc-navy);
  font-weight: 900;
}

/* The ficha's list of what the next reforma gives, in the card's own ink:
   the ficha's light tone would wash out on the card's paper. */
.nanavio-viewer-guide-block .nanavio-sheet-next {
  color: inherit;
  font-size: inherit;
}

.nanavio-viewer-guide-close {
  justify-self: end;
}

.nanavio-sheet-block {
  display: grid;
  gap: 0.25rem;
  padding-top: 0.35rem;
  border-top: 0.08rem solid var(--kc-brass-dark);
}

.nanavio-sheet-title {
  margin: 0;
  color: var(--kc-brass);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-2xs);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* What her class is for and what sets her apart inside it, as running text:
   the paper tone of the details line, with her classmates in brass. */
.nanavio-sheet-class {
  margin: 0;
  color: var(--kc-paper-dark);
  font-size: var(--kc-type-2xs);
  line-height: 1.35;
}

.nanavio-sheet-classmates {
  color: var(--kc-brass-light);
  font-weight: 800;
}

/* The XP line and the six stats. The portrait art prints neither, so this sheet
   is where a player reads what a reforma bought. Every bar is a --meter share the script sets. */
.nanavio-viewer-xp {
  margin: 0;
  color: var(--kc-brass-light);
  font-size: var(--kc-type-xs);
  font-weight: 850;
}

.nanavio-sheet-meter {
  display: block;
  height: 0.45rem;
  overflow: hidden;
  background: var(--kc-navy-deep);
  border: 0.08rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius-pill);
}

.nanavio-sheet-meter > span {
  display: block;
  width: var(--meter, 0%);
  height: 100%;
  background: linear-gradient(90deg, var(--kc-steel), var(--kc-sea));
}

/* The admin's level-up under the XP bar, which a player never sees: the
   paragraph takes no room while its button is hidden. */
.nanavio-viewer-admin-level {
  display: flex;
  justify-content: center;
  margin: 0;
}

.nanavio-viewer-admin-level:has(> button[hidden]) {
  display: none;
}

/* Each stat is three lines — her name, the sum nível 1 + níveis +
   remodelações + equipamentos = total, and the bar — and every line stands
   the same --stat-gap from the next, the next stat's name included, at the
   owner's request. The name and the sum take one line height, the font's own
   size, so the sans of the name and the mincho of the numbers make boxes of
   one size: at their normal heights (20 and 24px) the mincho drew its digits
   low in its taller box and the sum stood visibly further from its name than
   from its bar. The sum's numbers and signs are flex items one
   --stat-term-gap apart, set against the right edge so the totals line up
   there. They stood in six columns shared through a subgrid until the owner
   asked for x + x + x: a column as wide as its widest number left 6 to 32px
   between two terms, with the sign's own narrow text space after it, and the
   row read 6 +5 +2. The term gap is 6px rather than the lines' 8px because
   the widest sum the catalog draws, a carrier's accuracy at the top with two
   caças and a radar, needs 256 of the 260px a landscape phone leaves the
   ficha. Six numbers do not fit beside the name at 320 wide, which is why the
   name stands over them at every size. */
.nanavio-viewer-stats {
  --stat-gap: 0.5rem;
  --stat-term-gap: 0.375rem;
  --stat-line: 1rem;
  display: grid;
  row-gap: var(--stat-gap);
  margin: 0;
  padding: 0;
  list-style: none;
}

.nanavio-viewer-stats li {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: baseline;
  gap: var(--stat-gap) var(--stat-term-gap);
}

/* The legend over the six sums: one word per part, each after a swatch of the
   colour its part takes on the bar, wrapping where the ficha is narrow. The
   pieces' swatch is half brass and half red, since they can add or take. */
.nanavio-viewer-stats-head {
  margin: 0;
  color: var(--kc-paper-dark);
  font-size: var(--kc-type-3xs);
  text-align: end;
}

.nanavio-viewer-stats-key,
.nanavio-viewer-stats-tail {
  white-space: nowrap;
}

.nanavio-viewer-stats-key::before {
  content: "";
  display: inline-block;
  width: 0.7em;
  height: 0.7em;
  margin-inline-end: 0.25em;
  border: 0.08rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius-flat);
  vertical-align: -0.05em;
}

.nanavio-viewer-stats-key[data-part="start"]::before {
  background: var(--kc-steel);
}

.nanavio-viewer-stats-key[data-part="levels"]::before {
  background: var(--kc-sea);
}

.nanavio-viewer-stats-key[data-part="rebuilds"]::before {
  background: var(--holo-violet);
}

.nanavio-viewer-stats-key[data-part="pieces"]::before {
  background: linear-gradient(135deg, var(--kc-gain) 50%, var(--kc-signal-light) 50%);
}

.nanavio-viewer-stat-start,
.nanavio-viewer-stat-op,
.nanavio-viewer-stat-levels,
.nanavio-viewer-stat-rebuilds,
.nanavio-viewer-stat-pieces,
.nanavio-viewer-stat-equals,
.nanavio-viewer-stat-value {
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-xs);
  line-height: var(--stat-line);
  white-space: nowrap;
}

/* Every number and sign of the sum in the ficha's own gold, the colour its XP
   line reads in, at the owner's request. Two cuts coloured them by what the
   pieces did — brass up and red down with the total, then green and red with
   the total white — and a total in red read as a bad number in itself. The
   sign says which way the pieces moved the stat, and the bar under it shows
   it in colour. */
.nanavio-viewer-stat-start,
.nanavio-viewer-stat-op,
.nanavio-viewer-stat-levels,
.nanavio-viewer-stat-rebuilds,
.nanavio-viewer-stat-pieces,
.nanavio-viewer-stat-equals,
.nanavio-viewer-stat-value {
  color: var(--kc-brass-light);
}

/* The stat's bar in the sum's own parts, each whole up to where its running
   total ends: level 1 in steel, the levels in sea, the reformas in the violet
   of their holo card. The pieces' stretch comes after: what they add, in green
   past the reformas, or what they take, hatched in red over the end of the
   ladder, between the total and where the ladder ended, so the colours under
   it still say which part of her the pieces cost. Cut at the total instead,
   as it first shipped, a loss hid the reformas' stretch under nothing. */
.nanavio-viewer-stats .nanavio-sheet-meter {
  position: relative;
  flex-basis: 100%;
  height: 0.625rem;
}

.nanavio-sheet-meter > .nanavio-sheet-meter-start,
.nanavio-sheet-meter > .nanavio-sheet-meter-levels,
.nanavio-sheet-meter > .nanavio-sheet-meter-rebuilds,
.nanavio-sheet-meter > .nanavio-sheet-meter-shift {
  position: absolute;
  inset-block: 0;
  width: auto;
  height: auto;
}

.nanavio-sheet-meter > .nanavio-sheet-meter-start {
  left: 0;
  right: calc(100% - var(--meter-start, 0%));
  background: var(--kc-steel);
}

.nanavio-sheet-meter > .nanavio-sheet-meter-levels {
  left: var(--meter-start, 0%);
  right: calc(100% - var(--meter-levels, 0%));
  background: var(--kc-sea);
}

.nanavio-sheet-meter > .nanavio-sheet-meter-rebuilds {
  left: var(--meter-levels, 0%);
  right: calc(100% - var(--meter-base, 0%));
  background: var(--holo-violet);
}

.nanavio-sheet-meter > .nanavio-sheet-meter-shift {
  left: min(var(--meter, 0%), var(--meter-base, 0%));
  right: calc(100% - max(var(--meter, 0%), var(--meter-base, 0%)));
  background: none;
}

.nanavio-sheet-meter > .nanavio-sheet-meter-shift[data-delta="up"] {
  background: var(--kc-gain);
}

.nanavio-sheet-meter > .nanavio-sheet-meter-shift[data-delta="down"] {
  background: repeating-linear-gradient(
    135deg,
    var(--kc-signal-light) 0 0.12rem,
    transparent 0.12rem 0.3rem
  );
}

.nanavio-viewer-stat-name {
  flex-basis: 100%;
  color: var(--kc-paper-dark);
  font-size: var(--kc-type-xs);
  line-height: var(--stat-line);
}

.nanavio-viewer-stat-value {
  font-weight: 800;
}

/* The equipment slots of her class, one control each, and under them the
   pieces that fit the chosen slot. Every slot and option is a real control, so
   each keeps the 44px floor. */
.nanavio-sheet-gear,
.nanavio-sheet-gear-options {
  display: grid;
  gap: 0.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Two columns of rows of one height: a class carries at most four slots, so
   they stand two by two with the same corners. The automatic grid left one
   slot alone under three, and a piece whose name wrapped ("combustível extra")
   stretched its own button and no other. */
.nanavio-sheet-gear {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-auto-rows: 1fr;
}

.nanavio-sheet-gear > li {
  display: grid;
}

.nanavio-sheet-gear-slot {
  display: grid;
  align-content: start;
  width: 100%;
  height: 100%;
  min-height: var(--kc-control-min);
  padding: 0.2rem 0.4rem;
  gap: 0.05rem;
  color: var(--kc-paper);
  font: inherit;
  text-align: start;
  cursor: pointer;
  background: var(--kc-navy-deep);
  border: 0.1rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius-flat);
}

.nanavio-sheet-gear-slot:hover:not(:disabled) {
  border-color: var(--kc-brass);
}

.nanavio-sheet-gear-slot[aria-pressed="true"] {
  border-color: var(--kc-brass-light);
  box-shadow: 0 0 0.45rem var(--kc-brass);
}

.nanavio-sheet-gear-slot:disabled {
  cursor: default;
  opacity: 0.6;
}

.nanavio-sheet-gear-name {
  color: var(--kc-paper-dark);
  font-size: var(--kc-type-3xs);
}

.nanavio-sheet-gear-piece {
  color: var(--kc-brass-light);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-xs);
  font-weight: 800;
}

/* The equipment picker is a card over the whole viewer, like the "mais forte"
   one: the ficha and the art go inert under it and the rest of the screen
   dims, its title and FECHAR on one row and the list scrolling under them. */
.nanavio-sheet-gear-picker {
  position: absolute;
  inset:
    max(3.6rem, env(safe-area-inset-top))
    max(0.6rem, env(safe-area-inset-right))
    max(0.8rem, env(safe-area-inset-bottom))
    max(0.6rem, env(safe-area-inset-left));
  z-index: 3;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: auto minmax(0, 1fr);
  align-items: center;
  gap: 0.6rem;
  width: min(100% - 1.2rem, 34rem);
  height: fit-content;
  max-height: calc(100% - max(3.6rem, env(safe-area-inset-top)) - max(0.8rem, env(safe-area-inset-bottom)));
  margin: auto;
  padding: 0.8rem 0.9rem;
  background: var(--kc-night);
  border: 0.16rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
  box-shadow:
    inset 0 0 0 0.2rem var(--kc-night),
    inset 0 0 0 0.3rem var(--kc-brass-dark),
    0 0 0 100vmax var(--kc-scrim);
}

.nanavio-sheet-gear-picker-title {
  margin: 0;
  color: var(--kc-brass-light);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-s);
  font-weight: 800;
  line-height: 1.25;
}

/* The list runs the picker's whole width under its title row, and is what
   scrolls when the pieces are taller than the screen. */
.nanavio-sheet-gear-options {
  grid-column: 1 / -1;
  align-self: stretch;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--kc-brass-dark) transparent;
}

/* Each piece a small card, its name and effect over its button: side by side,
   the button took the width and the effect ran under it, one word a line. The
   button spans the card, so the buttons of a list line up. */
.nanavio-sheet-gear-option {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.3rem;
  padding: 0.45rem 0.5rem;
  background: var(--kc-navy);
  border: 0.08rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius-flat);
}

.nanavio-sheet-gear-option > .collection-action,
.nanavio-sheet-gear-option > button {
  justify-self: stretch;
}

.nanavio-sheet-gear-option-text {
  display: grid;
  color: var(--kc-paper-dark);
  font-size: var(--kc-type-3xs);
}

.nanavio-sheet-gear-option-text b {
  color: var(--kc-paper);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-xs);
}

.nanavio-sheet-gear-option .collection-action {
  font-size: var(--kc-type-3xs);
}

.nanavio-sheet-gear-why {
  grid-column: 1 / -1;
  color: var(--kc-brass-light);
  font-size: var(--kc-type-3xs);
}

.nanavio-sheet-gear-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  justify-content: flex-end;
  margin: 0;
}


/* The four reformas as a ladder: made, next, and still to come, each in
   the colour of the card it buys. */
.nanavio-sheet-ladder {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.3rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nanavio-sheet-step {
  display: grid;
  justify-items: center;
  padding: 0.25rem 0.15rem;
  color: var(--kc-paper-dark);
  font-size: var(--kc-type-3xs);
  text-align: center;
  background: var(--kc-navy-deep);
  border: 0.1rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius);
}

.nanavio-sheet-step-label {
  color: var(--kc-paper);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-s);
  font-weight: 800;
}

.nanavio-sheet-step[data-state="reached"][data-reforma="1"] {
  border-color: var(--kc-steel);
}

.nanavio-sheet-step[data-state="reached"][data-reforma="2"] {
  border-color: var(--kc-silver);
}

.nanavio-sheet-step[data-state="reached"][data-reforma="3"] {
  border-color: var(--kc-brass-light);
}

.nanavio-sheet-step[data-state="reached"][data-reforma="4"] {
  border-color: var(--holo-pink);
}

.nanavio-sheet-step[data-state="reached"] .nanavio-sheet-step-label::after {
  content: " ✓";
  color: var(--kc-brass-light);
}

.nanavio-sheet-step[data-state="next"] {
  border-color: var(--kc-brass);
  box-shadow: 0 0 0 0.12rem var(--kc-brass-glow);
}

.nanavio-sheet-step[data-state="locked"] {
  opacity: 0.6;
}

.nanavio-sheet-next {
  display: grid;
  gap: 0.15rem;
  margin: 0;
  color: var(--kc-paper-dark);
  font-size: var(--kc-type-2xs);
}

/* What the next reforma gives, one bullet per thing, under its lead. */
.nanavio-sheet-next-list {
  display: grid;
  gap: 0.15rem;
  margin: 0;
  padding-inline-start: 1.1rem;
  list-style: disc;
}

.nanavio-sheet-block .collection-action {
  justify-self: start;
}

/* What the next reforma is missing, under its button. */
.nanavio-sheet-hint {
  margin: 0;
  color: var(--kc-brass-light);
  font-size: var(--kc-type-2xs);
  font-weight: 800;
}

/* The answer to the ficha's own buttons, on the ficha: the collection's
   status line is under the viewer while it is open. */
.nanavio-sheet-status {
  margin: 0;
  padding: 0.3rem 0.6rem;
  color: var(--kc-navy-deep);
  font-size: var(--kc-type-2xs);
  font-weight: 800;
  background: var(--kc-paper);
  border-inline-start: var(--kc-select-width) solid var(--kc-brass-dark);
  border-radius: var(--kc-radius-flat);
}

.nanavio-sheet-status[data-tone="error"] {
  color: var(--kc-signal);
  border-inline-start-color: var(--kc-signal);
}

.nanavio-viewer-close {
  position: absolute;
  top: max(0.55rem, env(safe-area-inset-top));
  right: max(0.55rem, env(safe-area-inset-right));
  z-index: 2;
}

/* With room for it, the ficha stands beside the art rather than under it: the
   art keeps the height and the sheet reads top to bottom, scrolling inside
   itself when it must. */
@media (min-width: 48rem) and (min-height: 34.01rem) {
  .nanavio-viewer {
    /* The art and the ficha stand as one pair in the middle: on a wide screen
       the ficha used to sit at the right edge with half the screen empty
       between the two. */
    padding-inline: max(0.6rem, calc((100% - 76rem) / 2));
    grid-template-columns: minmax(0, 1fr) minmax(18rem, 26rem);
    grid-template-rows: minmax(0, 1fr);
    column-gap: 1rem;
    justify-items: stretch;
  }

  .nanavio-viewer-front {
    grid-column: 1;
    grid-row: 1;
  }

  .nanavio-viewer-info {
    grid-column: 2;
    grid-row: 1;
    align-self: center;
    width: auto;
    max-height: 100%;
  }
}

/* With room to spare the ficha widens, which is what puts its three section
   chips on one row. */
@media (min-width: 75rem) and (min-height: 34.01rem) {
  .nanavio-viewer {
    grid-template-columns: minmax(0, 1fr) minmax(18rem, 30rem);
  }
}

@media (max-height: 34rem) and (orientation: landscape) {
  .nanavio-viewer {
    padding-top: 0.6rem;
    grid-template-columns: minmax(0, 1fr) minmax(13rem, 18rem);
    grid-template-rows: minmax(0, 1fr);
    align-items: stretch;
  }

  .nanavio-viewer-info {
    width: auto;
  }

  /* The close button sits in the top right corner the sheet now reaches, so
     the head keeps clear of it rather than printing the name under it. */
  .nanavio-sheet-head {
    padding-inline-end: 2.8rem;
  }
}

/* The event notice an admin releases. Its button shares the header's row with
   the active count where there is width for both, so it costs a row of header
   only where the two do not fit side by side. */
.collection-header-live {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  column-gap: 0.7rem;
  row-gap: 0.2rem;
  min-width: 0;
}

.collection-event-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-height: var(--kc-control-min);
  padding: 0.2rem 0.9rem;
  color: var(--kc-navy-deep);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-xs);
  font-weight: 800;
  letter-spacing: 0.08em;
  white-space: nowrap;
  background: linear-gradient(180deg, var(--kc-brass-light), var(--kc-brass));
  border: 0.12rem solid var(--kc-navy-deep);
  border-radius: var(--kc-radius);
  box-shadow: 0.14rem 0.18rem 0 var(--kc-navy-deep);
  cursor: pointer;
}

.collection-event-button:hover {
  background: var(--kc-brass-light);
}

.collection-event-button:active {
  box-shadow: none;
  transform: translate(0.14rem, 0.18rem);
}

.collection-event-button:focus-visible {
  outline: 0.18rem solid var(--kc-signal);
  outline-offset: 0.14rem;
}

/* The same signal the CHAT tab flies for an unread message. */
.collection-event-dot {
  color: var(--kc-signal);
  font-family: var(--kc-font-ui);
  font-size: 0.75em;
  line-height: 1;
}

/* The notice itself: the picture on the left, what it says on the right, over
   the whole screen like the nanavio viewer. The picture is shown whole and the
   text scrolls inside its own card, so the dialog never scrolls. */
.event-notice {
  position: absolute;
  inset: 0;
  z-index: var(--kc-z-viewer);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(16rem, 30rem);
  grid-template-rows: minmax(0, 1fr);
  gap: clamp(0.6rem, 2vmin, 1.2rem);
  padding:
    max(3.6rem, env(safe-area-inset-top))
    max(0.8rem, env(safe-area-inset-right))
    max(0.8rem, env(safe-area-inset-bottom))
    max(0.8rem, env(safe-area-inset-left));
  overflow: hidden;
  background:
    radial-gradient(ellipse at 50% 115%, var(--kc-night), transparent 60%),
    linear-gradient(180deg, var(--kc-navy-deep), var(--kc-navy) 70%, var(--kc-navy-deep));
  animation: cinematic-fade-in 220ms ease-out both;
}

/* A flex box rather than a grid, so the picture's max-height resolves against
   the column's definite height: as a grid item of an auto row it did not, and a
   tall picture ran off the bottom of a landscape screen. */
.event-notice-art {
  display: flex;
  grid-column: 1;
  grid-row: 1;
  min-width: 0;
  min-height: 0;
  align-items: center;
  justify-content: center;
}

.event-notice-image {
  display: block;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  background: var(--kc-navy-deep);
  border: 0.14rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
  box-shadow: 0 0 0 0.14rem var(--kc-navy-deep), 0.3rem 0.35rem 0 var(--kc-shadow);
  user-select: none;
}

.event-notice-text {
  display: grid;
  grid-column: 2;
  grid-row: 1;
  align-self: center;
  align-content: start;
  grid-auto-rows: max-content;
  min-width: 0;
  min-height: 0;
  max-height: 100%;
  padding: 0.9rem 1rem 1rem;
  gap: 0.5rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  color: var(--kc-ink);
  text-align: start;
  background: var(--kc-paper);
  border: 0.14rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius-flat);
  box-shadow:
    inset 0 0 0 0.2rem var(--kc-paper),
    inset 0 0 0 0.3rem var(--kc-navy-deep),
    0.3rem 0.35rem 0 var(--kc-shadow);
  outline: none;
}

.event-notice-text:focus-visible {
  outline: 0.18rem solid var(--kc-signal);
  outline-offset: 0.14rem;
}

.event-notice-kicker {
  margin: 0;
  color: var(--kc-signal);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-2xs);
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.event-notice-title {
  margin: 0;
  color: var(--kc-navy-deep);
  font-family: var(--kc-font-mincho);
  font-size: clamp(1.3rem, 4vmin, 2rem);
  font-weight: 800;
  line-height: 1.15;
  overflow-wrap: anywhere;
}

/* The admin's line breaks are kept as written. */
.event-notice-body {
  margin: 0;
  font-family: var(--kc-font-ui);
  font-size: clamp(1rem, 2.4vmin, 1.15rem);
  font-weight: 700;
  line-height: 1.5;
  white-space: pre-line;
  overflow-wrap: anywhere;
}

/* Without a picture the text takes the middle of the screen alone. */
.event-notice.is-text-only {
  grid-template-columns: minmax(0, 40rem);
  justify-content: center;
}

.event-notice.is-text-only .event-notice-text {
  grid-column: 1;
}

.event-notice-close {
  position: absolute;
  top: max(0.55rem, env(safe-area-inset-top));
  right: max(0.55rem, env(safe-area-inset-right));
  z-index: 2;
}

/* A portrait phone has no width for two columns: the picture stands on top,
   capped so the text keeps most of the screen, and the text under it. */
@media (orientation: portrait) and (max-width: 47.99rem) {
  .event-notice {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
  }

  .event-notice-image {
    max-height: 38dvh;
  }

  .event-notice-text {
    grid-column: 1;
    grid-row: 2;
    align-self: start;
  }

  .event-notice.is-text-only {
    grid-template-rows: minmax(0, 1fr);
  }

  .event-notice.is-text-only .event-notice-text {
    grid-row: 1;
  }
}

/* A landscape phone keeps the two columns but has no height to spare above
   them, so the close button stands beside the text rather than over it. */
@media (max-height: 34rem) and (orientation: landscape) {
  .event-notice {
    grid-template-columns: minmax(0, 1fr) minmax(13rem, 22rem);
    padding-top: max(0.6rem, env(safe-area-inset-top));
    padding-inline-end: calc(var(--kc-control-min) + 1.1rem);
  }
}

/* The admin's editor for the notice. */
.collection-notice {
  display: grid;
  align-content: start;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.notice-editor {
  width: min(100%, 44rem);
  justify-self: center;
}

.notice-editor-retry {
  justify-self: center;
  margin-block-end: 0.5rem;
}

.notice-editor-hint,
.notice-editor-state {
  margin: 0;
  color: var(--kc-navy);
  font-size: var(--kc-type-2xs);
  font-weight: 700;
}

.notice-editor-state {
  font-weight: 850;
}

.notice-editor-toggle {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  min-height: var(--kc-control-min);
  color: var(--kc-navy-deep);
  font-size: var(--kc-type-xs);
  font-weight: 900;
  cursor: pointer;
}

.notice-editor-toggle input {
  width: 1.4rem;
  height: 1.4rem;
  accent-color: var(--kc-signal);
}

.notice-editor-group {
  display: grid;
  min-width: 0;
  margin: 0;
  padding: 0.45rem 0.55rem 0.6rem;
  gap: 0.3rem;
  border: 0.12rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius-flat);
}

.notice-editor-group legend {
  padding-inline: 0.3rem;
  color: var(--kc-navy-deep);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-xs);
  font-weight: 800;
}

.notice-editor-body {
  min-height: 7rem;
  resize: vertical;
}

.notice-editor-thumb {
  max-width: min(100%, 18rem);
  max-height: 12rem;
  object-fit: contain;
  background: var(--kc-navy-deep);
  border: 0.12rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius-flat);
}

.notice-editor-buttons {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

/* Stacked under the art on a portrait phone, the sheet takes a share of the
   height and scrolls inside itself, or the art is squeezed to nothing. */
@media (max-width: 47.99rem) and (orientation: portrait) {
  .nanavio-viewer-info {
    max-height: 46dvh;
  }
}

/* Wish-style nanacote opening: shooting star, flash, one nanavio at a time. */
.nanacote-cinematic {
  --meteor-color: var(--kc-steel);
  --meteor-glow: var(--kc-sea-glow);
  position: absolute;
  inset: 0;
  z-index: var(--kc-z-cinematic);
  display: grid;
  place-items: center;
  overflow: hidden;
  color: var(--kc-paper);
  background:
    radial-gradient(ellipse at 50% 108%, var(--kc-sea-glow), transparent 45%),
    linear-gradient(180deg, var(--kc-navy-deep), var(--kc-navy) 72%, var(--kc-navy-deep));
  cursor: pointer;
  outline: none;
}

/* The overlay is opaque from its first frame; only the sky fades in, so the
   nanacotes tab never shows through while the nanacote opens. */
.nanacote-cinematic > .cinematic-sky {
  animation:
    cinematic-fade-in 300ms ease-out both,
    cinematic-twinkle 2.6s ease-in-out 300ms infinite alternate,
    cinematic-drift 40s linear infinite;
}

.cinematic-sky {
  position: absolute;
  inset: -10%;
  background-image:
    radial-gradient(circle, var(--kc-paper-glow) 0 1px, transparent 1.6px),
    radial-gradient(circle, var(--kc-paper) 0 1.4px, transparent 2.2px),
    radial-gradient(circle, var(--kc-sea-glow) 0 1.2px, transparent 2px);
  background-position: 0 0, 43px 67px, 121px 19px;
  background-size: 90px 90px, 150px 150px, 230px 230px;
  animation:
    cinematic-twinkle 2.6s ease-in-out infinite alternate,
    cinematic-drift 40s linear infinite;
  pointer-events: none;
}

.cinematic-meteor,
.cinematic-meteor::after {
  position: absolute;
  top: 2%;
  left: 96%;
  width: 60vmax;
  height: clamp(0.45rem, 1.6vmin, 0.95rem);
  opacity: 0;
  background:
    radial-gradient(circle at 0.5rem 50%, var(--kc-paper) 0 0.45rem, transparent 0.9rem),
    linear-gradient(90deg, var(--kc-paper), var(--meteor-color) 6%, var(--meteor-glow) 32%, transparent);
  border-radius: var(--kc-radius-pill);
  filter: drop-shadow(0 0 1.4rem var(--meteor-color)) brightness(1.3);
  pointer-events: none;
}

.cinematic-meteor::after {
  top: 3.5rem;
  left: 8rem;
  width: 45vmax;
  content: "";
}

.cinematic-nanacote {
  position: absolute;
  z-index: 1;
  height: min(64dvh, calc(88vw * 1.5));
  aspect-ratio: 2 / 3;
  opacity: 0;
  pointer-events: none;
}

/* While the server draws the nanavios the nanacote rises and keeps shaking. */
.nanacote-cinematic[data-phase="nanacote"] .cinematic-nanacote {
  animation: cinematic-nanacote-rise 450ms ease-out both;
}

.nanacote-cinematic[data-phase="nanacote"] .cinematic-nanacote .nanacote-art {
  animation: cinematic-nanacote-shake 700ms ease-in-out 450ms infinite;
}

.nanacote-cinematic[data-phase="tear"] .cinematic-nanacote {
  opacity: 1;
  animation: cinematic-nanacote-tear 500ms ease-in forwards;
}

@keyframes cinematic-nanacote-rise {
  from { opacity: 0; transform: translateY(10%) scale(0.9); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes cinematic-nanacote-shake {
  0%, 100% { transform: rotate(0); }
  20% { transform: rotate(-3deg); }
  45% { transform: rotate(3deg); }
  70% { transform: rotate(-2deg); }
}

@keyframes cinematic-nanacote-tear {
  0% { opacity: 1; transform: scale(1); filter: brightness(1.1) drop-shadow(0 0 1rem var(--meteor-color)); }
  100% { opacity: 0; transform: scale(1.3); filter: brightness(2.4) drop-shadow(0 0 3rem var(--meteor-color)); }
}

.nanacote-cinematic[data-phase="meteor"] .cinematic-meteor {
  animation: cinematic-meteor 1.8s cubic-bezier(0.35, 0.2, 0.55, 1) both;
}

.cinematic-flash {
  position: absolute;
  inset: 0;
  opacity: 0;
  background: radial-gradient(circle at 45% 55%, var(--kc-paper), var(--meteor-glow) 45%, transparent 80%);
  pointer-events: none;
}

.nanacote-cinematic[data-phase="flash"] .cinematic-flash {
  animation: cinematic-flash 350ms ease-out both;
}

/* One light pillar, the steel one: the silver, brass and rainbow pillars that
   followed the patente went with it in 1.62. */
.cinematic-stage {
  --pillar-color: var(--kc-sea-soft);
  --pillar-glow: var(--kc-night);

  position: relative;
  z-index: 2;
  display: grid;
  justify-items: center;
  gap: clamp(0.15rem, 0.8dvh, 0.45rem);
  text-align: center;
}

.cinematic-pillar {
  position: absolute;
  top: -40%;
  left: 50%;
  z-index: -1;
  width: min(70vw, 26rem);
  height: 180%;
  background:
    radial-gradient(ellipse at 50% 42%, var(--pillar-glow), transparent 62%),
    linear-gradient(90deg, transparent 30%, var(--pillar-color) 50%, transparent 70%);
  filter: blur(0.4rem);
  opacity: 0.45;
  translate: -50% 0;
  animation: cinematic-pillar 650ms ease-out both;
  pointer-events: none;
}

.cinematic-nanavio {
  height: min(52dvh, calc(68vw * 450 / 327), 34rem);
  aspect-ratio: 327 / 450;
  filter: drop-shadow(0 0 1.3rem var(--pillar-glow));
  animation: cinematic-nanavio-in 720ms cubic-bezier(0.2, 1.25, 0.4, 1) both;
}

.cinematic-nanavio .nanavio {
  height: 100%;
}

.cinematic-stars,
.cinematic-hint {
  margin: 0;
}

.cinematic-stars {
  display: flex;
  gap: 0.15rem;
  color: var(--kc-brass);
  font-size: clamp(1.1rem, 4vmin, 2rem);
  line-height: 1;
  text-shadow:
    0.06rem 0.06rem 0 var(--kc-navy-deep),
    -0.06rem -0.06rem 0 var(--kc-navy-deep),
    0.06rem -0.06rem 0 var(--kc-navy-deep),
    -0.06rem 0.06rem 0 var(--kc-navy-deep),
    0 0 0.5rem var(--kc-brass-glow);
}

.cinematic-stars span {
  animation: cinematic-star-pop 420ms cubic-bezier(0.2, 1.6, 0.4, 1) both;
}

.cinematic-stars span:nth-child(1) { animation-delay: 380ms; }
.cinematic-stars span:nth-child(2) { animation-delay: 500ms; }
.cinematic-stars span:nth-child(3) { animation-delay: 620ms; }
.cinematic-stars span:nth-child(4) { animation-delay: 740ms; }

/* Who came out and her class: the portrait art prints neither, so the nanavio
   says both while she is on screen. */
.cinematic-caption {
  display: grid;
  justify-items: center;
  gap: 0.1rem;
  margin: 0;
  padding: 0.25rem 0.8rem;
  text-align: center;
  background: linear-gradient(180deg, var(--kc-navy), var(--kc-navy-deep));
  border: 0.14rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
  box-shadow: inset 0 0 0 0.1rem var(--kc-navy-deep);
  animation: cinematic-caption-in 420ms ease-out 260ms both;
}

.cinematic-caption-name {
  color: var(--kc-paper);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-s);
  font-weight: 800;
  letter-spacing: 0.06em;
}

.cinematic-caption-meta {
  color: var(--kc-brass-light);
  font-size: var(--kc-type-2xs);
  font-weight: 800;
}

@keyframes cinematic-caption-in {
  from {
    opacity: 0;
    transform: translateY(0.4rem);
  }
}

.cinematic-hint {
  color: var(--kc-sea-soft);
  text-shadow: 0.06rem 0.08rem 0 var(--kc-navy-deep), 0 0 0.4rem var(--kc-navy-deep);
  font-size: var(--kc-type-2xs);
  font-weight: 800;
  animation: cinematic-hint 1.6s ease-in-out 900ms infinite alternate both;
}

/* The opening ends on the last nanavio rather than on a grid of every one: a
   nanacote is one character now, so that grid would have been a second screen
   showing the card already on the stage. CONTINUAR sits under it, above the
   sky and the meteor, and is what closes the overlay. */
.cinematic-continue {
  position: absolute;
  bottom: clamp(0.6rem, 4dvh, 2rem);
  left: 50%;
  z-index: 3;
  translate: -50% 0;
}

@keyframes cinematic-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes cinematic-twinkle {
  from { opacity: 0.45; }
  to { opacity: 1; }
}

@keyframes cinematic-drift {
  from { transform: translate(0, 0); }
  to { transform: translate(-90px, 45px); }
}

@keyframes cinematic-meteor {
  0% { opacity: 0; transform: translate(0, 0) rotate(-28deg) scaleX(0.3); }
  12% { opacity: 1; }
  85% { opacity: 1; }
  100% { opacity: 0; transform: translate(-118vw, 63vw) rotate(-28deg) scaleX(1); }
}

@keyframes cinematic-flash {
  0% { opacity: 0; }
  35% { opacity: 1; }
  100% { opacity: 0; }
}

@keyframes cinematic-pillar {
  from { transform: scaleY(0.1); }
  to { transform: scaleY(1); }
}

@keyframes cinematic-nanavio-in {
  0% { opacity: 0; transform: perspective(60rem) rotateY(-80deg) scale(0.4); filter: brightness(3); }
  60% { opacity: 1; transform: perspective(60rem) rotateY(8deg) scale(1.06); filter: brightness(1.4); }
  100% { opacity: 1; transform: perspective(60rem) rotateY(0) scale(1); }
}

@keyframes cinematic-star-pop {
  0% { opacity: 0; transform: scale(0) rotate(-45deg); }
  100% { opacity: 1; transform: scale(1) rotate(0); }
}

@keyframes cinematic-hint {
  from { opacity: 0.45; }
  to { opacity: 1; }
}

@keyframes nanacote-nanavio-reveal {
  from { opacity: 0; transform: rotateY(90deg) scale(0.92); }
  to { opacity: 1; transform: rotateY(0) scale(1); }
}

@media (max-height: 34rem) {
  .collection-screen {
    /* A landscape phone cannot fit 44px rows and stay scrollbar-free, so this
       is the one place the hit-target floor is relaxed. */
    --kc-control-min: var(--kc-control-compact);
    --collection-shell-gap: 0.2rem;
    padding: 0.3rem;
  }

  .collection-header {
    /* The account's column may shrink, so a long login is cut short rather
       than pushing sair da conta under the close button. */
    grid-template-columns: auto minmax(0, max-content);
    justify-content: center;
    align-items: center;
    gap: 0.6rem;
  }

  /* The balance, the active count and EVENTO run under both columns: held in
     the title's, EVENTO took a second line of a screen with none to spare. */
  .collection-header-live {
    grid-column: 1 / -1;
  }

  .collection-kicker {
    display: none;
  }

  .collection-tab,
  .panel-filter {
    padding: 0.15rem 0.55rem;
  }

  .collection-action {
    padding: 0.25rem 0.8rem;
  }

  .collection-login {
    gap: 0.25rem;
    padding: 0.6rem;
  }

  .collection-input {
    padding: 0.25rem 0.5rem;
  }
}

/* The collection on a narrow screen. Until 1.36 the only width query it had was
   the duel's, so the tabs wrapped to four rows of chrome before any content. */
@media (max-width: 34rem) {
  .collection-header {
    padding-inline: 3.4rem;
  }

  /* The balance, the active count and EVENTO sit under the title and the
     account, below the two corner buttons, so their row takes back the width
     the header keeps clear for them: held inside it, the three stood one per
     line at 320 wide. The layout suite holds the balance clear of both. */
  .collection-header-live {
    margin-inline: -3rem;
  }

  /* The stamp has no room beside four chips here, so it takes its own line. */
  .obs-updated {
    width: 100%;
  }

  .album-toolbar {
    justify-content: flex-start;
  }

  /* One column, so the admin surfaces stop squeezing three into 320px. */
  .obs-columns,
  .collection-admin,
  .collection-profile,
  .history-filters {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* One scrollable row of tabs and chips instead of two or three stacked ones. The
   active one is scrolled into view from showCollectionView and
   showCollectionObsSection. This was a width rule until the ninth tab wrapped at
   568x320 and took a whole row out of an already short dialog, so it now answers
   to either axis: a row that has to wrap is as expensive lying down as standing
   up. It lives here, after the narrow block it came out of, so there is one
   definition of it rather than two that can drift. */
@media (max-width: 34rem), (max-height: 34rem) {
  .collection-tabs,
  .obs-sections {
    flex-wrap: nowrap;
    justify-content: flex-start;
    overflow-x: auto;
    overscroll-behavior-inline: contain;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
    /* Fade the ends so an off-screen chip reads as "there is more". */
    mask-image: linear-gradient(
      90deg,
      transparent 0,
      var(--kc-navy-deep) 0.7rem,
      var(--kc-navy-deep) calc(100% - 0.7rem),
      transparent 100%
    );
  }

  .collection-tabs::-webkit-scrollbar,
  .obs-sections::-webkit-scrollbar {
    display: none;
  }

  .collection-tab,
  .panel-filter {
    flex: 0 0 auto;
    scroll-snap-align: start;
  }
}

/* Naval skin: a daytime naval base inspired by Kantai Collection. Parchment
   panels, navy plates, brass trims, signal red, hexagons, and mincho titles. */
.collection-screen {
  color: var(--kc-ink);
  font-family: var(--kc-font-ui);
  background:
    radial-gradient(circle at 12px 12px, var(--kc-hex) 0 6px, transparent 7px) 0 0 / 24px 21px,
    radial-gradient(circle at 12px 12px, var(--kc-hex) 0 6px, transparent 7px) 12px 10.5px / 24px 21px,
    linear-gradient(180deg, var(--kc-sea-soft), var(--kc-sea) 70%, var(--kc-steel));
}

/* Where the way out sits in the stack, beside the button it is about: the
   loading screen and the tutorial scrim both have to stay under it, and the
   token exists so that ceiling is visible in the scale that caps them. The
   value is the 50 of the site-wide .dialog-close-form rule every Marin dialog
   shares, restated here rather than moved out of it. */
.collection-screen .dialog-close-form {
  z-index: var(--kc-z-close);
}

/* The language switch is the close button's mirror image in the other corner:
   the header already keeps both sides clear for one control each, and it sits
   at the same height in the stack, so the loading screen, the tutorial scrim
   and the nanacote opening treat the two alike. Over the scrim it stands
   disabled, since the tour runs in the language it started in. */
.collection-lang {
  position: absolute;
  top: max(0.55rem, env(safe-area-inset-top));
  left: max(0.55rem, env(safe-area-inset-left));
  z-index: var(--kc-z-close);
  margin: 0;
}

.collection-lang-button {
  display: grid;
  width: var(--kc-control-min);
  min-height: var(--kc-control-min);
  padding: 0;
  place-items: center;
  color: var(--kc-brass-light);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-s);
  font-weight: 800;
  letter-spacing: 0.04em;
  background: var(--kc-navy);
  border: 0.1rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
  box-shadow: 0.14rem 0.18rem 0 var(--kc-navy-deep), inset 0 0 0 0.12rem var(--kc-navy-deep);
  cursor: pointer;
}

.collection-lang-button:not(:disabled):hover {
  color: var(--kc-paper);
}

.collection-lang-button:focus-visible {
  outline: 0.18rem solid var(--kc-signal);
  outline-offset: 0.14rem;
}

/* The language menu's list (1.118) hangs under its button, in its colours:
   one column of options at the control floor, each named in its own language,
   and two columns on a short screen, where eight rows would run past the
   bottom of a landscape phone. The ring is the light signal, since the plain
   one reads at about 1.3:1 on the navy. */
.collection-lang-list {
  position: absolute;
  top: calc(100% + 0.35rem);
  left: 0;
  display: grid;
  gap: 0.2rem;
  min-width: max-content;
  margin: 0;
  padding: 0.3rem;
  list-style: none;
  background: var(--kc-navy);
  border: 0.1rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
  box-shadow: 0.14rem 0.18rem 0 var(--kc-navy-deep), inset 0 0 0 0.12rem var(--kc-navy-deep);
}

.collection-lang-option {
  display: flex;
  width: 100%;
  min-height: var(--kc-control-min);
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0 0.7rem;
  color: var(--kc-paper);
  font-family: var(--kc-font-ui);
  font-size: var(--kc-type-s);
  font-weight: 700;
  line-height: 1.2;
  text-align: start;
  white-space: nowrap;
  background: transparent;
  border: 0;
  border-radius: var(--kc-radius-flat);
  cursor: pointer;
}

.collection-lang-option:hover {
  color: var(--kc-brass-light);
  background: var(--kc-navy-deep);
}

.collection-lang-option:focus-visible {
  outline: var(--kc-focus-width) solid var(--kc-signal-light);
  outline-offset: calc(var(--kc-focus-width) * -1);
}

.collection-lang-option[aria-current="true"] {
  color: var(--kc-brass-light);
}

.collection-lang-option[aria-current="true"]::after {
  content: "✓" / "";
  font-family: var(--kc-font-ui);
}

@media (max-height: 34rem) {
  .collection-lang-list {
    grid-template-columns: repeat(2, max-content);
  }
}

.collection-dialog.is-opening-nanacote .collection-lang,
.collection-dialog.is-event-notice .collection-lang {
  display: none;
}

/* The collection's music (1.112, 1.113): one plate in the language switch's
   colours holding the ♪ that turns it on and off and its volume. The login form
   has it in the switch's corner, which the form leaves empty, and the signed-in
   header has the same plate at the end of its live row. */
.collection-music-plate {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  padding-inline-end: 0.55rem;
  background: var(--kc-navy);
  border: 0.1rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
  box-shadow: 0.14rem 0.18rem 0 var(--kc-navy-deep), inset 0 0 0 0.12rem var(--kc-navy-deep);
}

.collection-login-music {
  position: absolute;
  top: max(0.55rem, env(safe-area-inset-top));
  left: max(0.55rem, env(safe-area-inset-left));
  z-index: var(--kc-z-close);
}

.collection-music-toggle {
  position: relative;
  display: grid;
  width: var(--kc-control-min);
  min-height: var(--kc-control-min);
  padding: 0;
  place-items: center;
  color: var(--kc-brass-light);
  font-family: var(--kc-font-ui);
  font-size: 1.35rem;
  line-height: 1;
  background: transparent;
  border: 0;
  border-radius: var(--kc-radius);
  cursor: pointer;
}

.collection-music-toggle:hover {
  color: var(--kc-paper);
}

/* Off, the note is struck through in signal red rather than only dimmed, so
   the two states differ in shape as well as in colour. */
.collection-music-toggle[aria-pressed="false"]::after {
  position: absolute;
  width: 1.6rem;
  height: 0.16rem;
  background: var(--kc-signal-light);
  border-radius: var(--kc-radius-pill);
  transform: rotate(-45deg);
  content: "";
}

.collection-music-volume {
  width: clamp(3.5rem, 14vmin, 6.5rem);
  /* The track is thin, but the control keeps the hit-target floor of the plate. */
  min-height: var(--kc-control-min);
  margin: 0;
  accent-color: var(--kc-brass);
  cursor: pointer;
}

/* The ring stands on the plate's navy, where --kc-signal reads at about 1.3:1,
   so it takes the light signal the night plates use for text. */
.collection-music-toggle:focus-visible,
.collection-music-volume:focus-visible {
  outline: var(--kc-focus-width) solid var(--kc-signal-light);
  outline-offset: -0.1rem;
}

/* Below 34rem wide or tall the plate is the ♪ alone. On the login form the
   header keeps one control's room at each corner and its centred title fills
   the rest: with the volume beside it, the plate covered "Nana". On a short
   landscape screen the form reaches up beside the corner (see the login form's
   own rule). In the signed-in header the live row is already full at those
   sizes. A phone has volume keys of its own. This comes after the rules above,
   or it would lose to them on source order. */
@media (max-width: 34rem), (max-height: 34rem) {
  .collection-music-plate {
    padding-inline-end: 0;
  }

  .collection-music-volume {
    display: none;
  }
}

.collection-screen .dialog-close,
.nanavio-viewer .dialog-close {
  width: var(--kc-control-min);
  min-height: var(--kc-control-min);
  color: var(--kc-brass-light);
  background: var(--kc-navy);
  border-color: var(--kc-brass);
  border-radius: var(--kc-radius);
  box-shadow: 0.14rem 0.18rem 0 var(--kc-navy-deep), inset 0 0 0 0.12rem var(--kc-navy-deep);
}

.collection-header {
  position: relative;
  margin:
    calc(-1 * max(0.55rem, env(safe-area-inset-top)))
    calc(-1 * max(0.55rem, env(safe-area-inset-right)))
    0
    calc(-1 * max(0.55rem, env(safe-area-inset-left)));
  padding-top: max(0.55rem, env(safe-area-inset-top));
  padding-bottom: 0.4rem;
  background:
    radial-gradient(circle at 12px 12px, var(--kc-hex-light) 0 6px, transparent 7px) 0 0 / 24px 21px,
    linear-gradient(180deg, var(--kc-navy), var(--kc-navy-deep));
  border-bottom: 0.14rem solid var(--kc-brass);
  box-shadow: 0 0.22rem 0 var(--kc-navy-deep), 0 0.3rem 0 var(--kc-brass-dark);
}

.collection-kicker {
  color: var(--kc-brass-light);
  font-family: var(--kc-font-mincho);
  font-weight: 700;
  letter-spacing: 0.24em;
}

/* On a narrow screen the eyebrow goes: at the 16px floor the collection's text
   keeps since 1.86, "nanaversário · 04 de outubro" no longer fits one line
   beside the close button even with its tracking given back, and a second row
   of header is taken straight out of the panel under it. The date is the
   countdown's and the certificate's to say. This sits straight after the rule
   it overrides on purpose: it used to live in the collection's earlier narrow
   block, where the rule above won on source order and did nothing at all. */
@media (max-width: 34rem) {
  .collection-kicker {
    display: none;
  }
}

.collection-title {
  color: var(--kc-paper);
  font-family: var(--kc-font-mincho);
  font-weight: 800;
  letter-spacing: 0.04em;
  text-shadow: 0.08rem 0.1rem 0 var(--kc-brass-dark), 0 0 0.9rem var(--kc-brass-glow);
}

/* The anchors live in the markup, aria-hidden, so they survive browsers without
   CSS content alt-text and stay out of the dialog's accessible name. */
.collection-title-mark {
  color: var(--kc-brass);
  font-size: 0.6em;
  vertical-align: 0.18em;
}

.collection-title-mark:first-child {
  margin-inline-end: 0.4em;
}

.collection-title-mark:last-child {
  margin-inline-start: 0.4em;
}

.collection-account,
.collection-online,
.collection-header-steel {
  color: var(--kc-paper-dark);
}

.collection-link-button:focus-visible,
.collection-input:focus-visible,
.album-nanavio-button:focus-visible,
.album-slot > .nanavio[tabindex]:focus-visible,
.manual-topic:focus-visible,
.nanacote-nanavio-back:focus-visible {
  outline: 0.18rem solid var(--kc-signal);
  outline-offset: 0.14rem;
}

.collection-tab,
.panel-filter {
  color: var(--kc-paper);
  font-family: var(--kc-font-mincho);
  font-weight: 700;
  letter-spacing: 0.08em;
  background: linear-gradient(180deg, var(--kc-navy), var(--kc-navy-deep));
  border: 0;
  border-radius: 0;
  box-shadow: inset 0 0 0 0.12rem var(--kc-brass), inset 0 0 0 0.22rem var(--kc-navy-deep);
  clip-path: polygon(0.55rem 0, 100% 0, 100% calc(100% - 0.55rem), calc(100% - 0.55rem) 100%, 0 100%, 0 0.55rem);
}

.collection-tab[aria-pressed="true"],
.panel-filter[aria-pressed="true"] {
  color: var(--kc-navy-deep);
  background: linear-gradient(180deg, var(--kc-brass-light), var(--kc-brass));
  box-shadow: inset 0 0 0 0.12rem var(--kc-brass-dark);
  transform: none;
}

/* Navigating between sections and filtering inside one are different jobs, so
   they stop looking identical: the tabs keep the chamfered navy plate, while
   filters become flat paper chips. */
.panel-filter {
  font-size: var(--kc-type-3xs);
  letter-spacing: 0.04em;
  border-radius: var(--kc-radius-flat);
  clip-path: none;
}

.panel-filter:not([aria-pressed="true"]) {
  color: var(--kc-navy-deep);
  background: var(--kc-paper-dark);
  box-shadow: inset 0 0 0 0.1rem var(--kc-brass-dark);
}

.panel-filter[aria-pressed="true"] {
  box-shadow: inset 0 0 0 0.1rem var(--kc-navy-deep);
}

/* The active tab also flies a brass flag along its bottom edge, so the current
   section is readable without relying on fill alone. */
.collection-tab[aria-current="page"] {
  box-shadow:
    inset 0 0 0 0.12rem var(--kc-brass-dark),
    inset 0 -0.3rem 0 var(--kc-signal);
}

/* The keyboard's place, after every state above: each of them draws with the
   same box-shadow at the same weight, and written before them the ring lost to
   all three, so a keyboard walking the chips or landing on the current tab saw
   nothing move. It is a box-shadow and not an outline because the tabs'
   chamfer is a clip-path, which clips an outline away. The current tab keeps
   its flag inside the ring. */
.collection-tab:focus-visible,
.panel-filter:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 0.2rem var(--kc-signal), inset 0 0 0 0.3rem var(--kc-paper);
}

.collection-tab[aria-current="page"]:focus-visible {
  box-shadow:
    inset 0 0 0 0.2rem var(--kc-signal),
    inset 0 0 0 0.3rem var(--kc-paper),
    inset 0 -0.6rem 0 var(--kc-signal);
}

.collection-panel,
.collection-login {
  background:
    radial-gradient(circle at 12px 12px, var(--kc-hex) 0 5px, transparent 6px) 0 0 / 24px 21px,
    var(--kc-paper);
  border: 0.14rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
  box-shadow:
    inset 0 0 0 0.2rem var(--kc-paper),
    inset 0 0 0 0.28rem var(--kc-brass-dark),
    0.2rem 0.26rem 0 var(--kc-shadow);
}

/* The login art carries its own logo on its right half, so the form stands on
   the left in landscape and under the logo in portrait. The url is behind
   `is-login`, which keeps the image off the network until the form shows. */
.collection-dialog.is-login .collection-screen {
  background:
    url("assets/images/nana-collection/login.png") 72% center / cover no-repeat,
    linear-gradient(180deg, var(--kc-sea-soft), var(--kc-sea) 70%, var(--kc-steel));
}

.collection-password {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.35rem;
}

.collection-password-reveal {
  font-size: var(--kc-type-xs);
}

.collection-dialog.is-login .collection-login {
  width: min(100%, 26rem, 45vw);
  justify-self: start;
  margin-inline-start: clamp(0rem, 5vw, 5rem);
}

@media (orientation: portrait) {
  /* A narrow screen crops the art hard, and 72% would cut the logo short. */
  .collection-dialog.is-login .collection-screen {
    background-position: 78% center, 0 0;
  }

  .collection-dialog.is-login .collection-login {
    width: min(100%, 26rem);
    align-self: end;
    justify-self: center;
    margin-inline-start: 0;
  }
}

/* On a short landscape screen the form fills nearly the whole height and comes
   up level with the music's ♪ in the corner, so it stands to the right of it
   instead: a clearance across rather than down holds whatever height the fonts
   give the form, which in CI's were a few pixels more than here. After the
   rules above, or it would lose to them on source order. */
@media (max-height: 34rem) and (orientation: landscape) {
  .collection-dialog.is-login .collection-login {
    margin-inline-start: calc(var(--kc-control-min) + 1rem);
  }
}

.collection-subtitle,
.panel-heading {
  color: var(--kc-navy-deep);
  font-family: var(--kc-font-mincho);
  font-weight: 800;
  letter-spacing: 0.08em;
}

.collection-subtitle::before {
  margin-inline-end: 0.35em;
  /* The UI stack, not mincho: this diamond is the only glyph on the site that
     lives in a numbered Japanese subset, and asking mincho for it costs a
     17KB download for one decorative mark. */
  font-family: var(--kc-font-ui);
  color: var(--kc-brass-dark);
  content: "◆";
}

.collection-action {
  color: var(--kc-paper);
  font-family: var(--kc-font-mincho);
  font-weight: 800;
  letter-spacing: 0.08em;
  background: linear-gradient(180deg, var(--kc-steel), var(--kc-navy) 55%, var(--kc-navy-deep));
  border: 0.14rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
  box-shadow: 0.16rem 0.2rem 0 var(--kc-navy-deep), inset 0 0.1rem 0 var(--kc-paper-glow);
}

.collection-action:focus-visible {
  outline: 0.18rem solid var(--kc-signal);
  outline-offset: 0.16rem;
}

.collection-action-secondary {
  color: var(--kc-navy-deep);
  background: linear-gradient(180deg, var(--kc-paper), var(--kc-paper-dark));
  border-color: var(--kc-brass-dark);
}

.collection-welcome {
  color: var(--kc-navy-deep);
  background: linear-gradient(180deg, var(--kc-brass-light), var(--kc-brass));
  border-color: var(--kc-brass-dark);
  box-shadow: 0.16rem 0.2rem 0 var(--kc-navy-deep), 0 0 0.9rem var(--kc-brass-glow);
}

/* One disabled treatment for every collection control. The three that existed
   used 0.5, 0.45 and 0.55 opacity, and tabs, filters and link buttons had none
   at all, so a dead control could look identical to a live one. */
.collection-action:disabled,
.collection-link-button:disabled,
.collection-tab:disabled,
.panel-filter:disabled,
.duel-chip:disabled,
.lobby-invite:disabled,
.collection-lang-button:disabled {
  opacity: 0.5;
  filter: grayscale(0.5);
  cursor: not-allowed;
}

/* Hover lifts, press settles. Both are palette-free, so no new colours. */
.collection-action:not(:disabled):hover,
.collection-link-button:not(:disabled):hover,
.collection-tab:not(:disabled):hover,
.panel-filter:not(:disabled):hover,
.duel-chip:not(:disabled):hover,
.picker-card:not(:disabled):hover,
.duel-slot-button:not(:disabled):hover,
.lobby-chat-delete:not(:disabled):hover {
  filter: brightness(1.08);
}

.collection-action:not(:disabled):active,
.collection-link-button:not(:disabled):active,
.collection-tab:not(:disabled):active,
.panel-filter:not(:disabled):active,
.duel-chip:not(:disabled):active {
  transform: translateY(0.08rem);
}

.collection-label {
  color: var(--kc-navy);
  font-family: var(--kc-font-mincho);
  font-weight: 700;
}

/* One line answers every action in the collection, so it says which kind of
   answer it is: a signal-red edge for a refusal, a brass one for anything
   else. Every non-empty line wears the paper plate: a plain note used to sit
   bare on the bottom of the sea gradient, navy on steel at about 3:1, which
   is below what small text needs — and the login art only made it worse. */
.collection-status:not(:empty) {
  justify-self: center;
  max-width: min(100%, 34rem);
  padding: 0.3rem 0.7rem;
  background: var(--kc-paper);
  border-inline-start: var(--kc-select-width) solid var(--kc-brass-dark);
  border-radius: var(--kc-radius-flat);
  box-shadow: 0.1rem 0.12rem 0 var(--kc-navy-deep);
}

/* Written after the plate and at its weight, so the refusal's red edge wins
   by source order rather than by a longer selector. */
.collection-status[data-tone="error"] {
  color: var(--kc-signal);
  border-inline-start-color: var(--kc-signal);
}

/* PIRACA I.A, the guide of the first visit. He is staff of the naval base, so
   his portrait is a navy plate with a brass frame and he speaks from a
   parchment balloon with the dark inner line the rest of the dialog uses. He
   belongs to the collection alone: no other page loads him. */
.collection-tutorial {
  /* Shared by the bust and the balloon's tail, which points at his face. */
  --portrait-width: clamp(2.6rem, 10vmin, 5rem);
  --balloon-tail: clamp(0.8rem, 2.6vmin, 1.2rem);
  display: grid;
  /* Capped so on a wide screen he speaks from a balloon, not a banner. */
  grid-template-columns: auto minmax(0, 46rem);
  justify-content: start;
  align-items: end;
  /* Exactly the tail's width, so the whole tail shows and its tip meets him. */
  gap: var(--balloon-tail);
}

/* The delivered art is dropped in over this placeholder, which is a naval
   officer's bust: cap band, head, shoulders. Nothing is requested until the
   tutorial runs, and a picture that is missing or fails leaves the silhouette
   rather than an empty plate. */
.tutorial-portrait {
  position: relative;
  width: var(--portrait-width);
  aspect-ratio: 3 / 4;
  background:
    radial-gradient(ellipse 62% 40% at 50% 104%, var(--kc-sea) 0 99%, transparent 100%),
    radial-gradient(circle at 50% 44%, var(--kc-sea) 0 21%, transparent 22%),
    linear-gradient(180deg, transparent 0 24%, var(--kc-brass) 24% 29%, transparent 29%),
    linear-gradient(160deg, var(--kc-navy), var(--kc-navy-deep));
  border: 0.12rem solid var(--kc-brass);
  box-shadow:
    inset 0 0 0 0.1rem var(--kc-navy-deep),
    0.16rem 0.2rem 0 var(--kc-shadow);
  clip-path: polygon(0.45rem 0, 100% 0, 100% calc(100% - 0.45rem), calc(100% - 0.45rem) 100%, 0 100%, 0 0.45rem);
}

.tutorial-portrait.has-art {
  background-image: none;
}

.tutorial-portrait-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: bottom;
}

.tutorial-balloon {
  --balloon-border: 0.14rem;
  position: relative;
  display: grid;
  grid-template-areas:
    "speaker volume"
    "line advance";
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: 0.2rem clamp(0.3rem, 1.4vmin, 0.8rem);
  padding: clamp(0.4rem, 1.6vmin, 0.75rem) clamp(0.7rem, 2.4vmin, 1.1rem);
  background: var(--kc-paper);
  border: var(--balloon-border) solid var(--kc-brass);
  border-radius: clamp(0.8rem, 3vmin, 1.4rem);
  box-shadow: inset 0 0 0 0.1rem var(--kc-navy-deep);
  /* A drop shadow rather than a box shadow, so the tail casts one too and the
     balloon reads as one cut-out shape. */
  filter: drop-shadow(0.2rem 0.24rem 0 var(--kc-shadow));
}

/* The tail points back at the portrait, so the balloon reads as his voice and
   not as one more panel. It is two stacked wedges: the brass one is the
   outline, and the paper one on top runs over the balloon's own border where
   they meet, so the border opens into the tail instead of cutting it off. Both
   reach into the gap beside the portrait, which is inside the row either way. */
.tutorial-balloon::before,
.tutorial-balloon::after {
  position: absolute;
  inset-block-end: calc(var(--portrait-width) * 0.45);
  width: var(--balloon-tail);
  height: calc(var(--balloon-tail) * 1.25);
  clip-path: polygon(100% 0, 100% 62%, 0 100%);
  content: "";
}

.tutorial-balloon::before {
  inset-inline-start: calc(-1 * var(--balloon-tail));
  background: var(--kc-brass);
}

.tutorial-balloon::after {
  inset-inline-start: calc(-1 * var(--balloon-tail) + var(--balloon-border) * 2.2);
  margin-block-end: calc(var(--balloon-border) * 1.3);
  width: calc(var(--balloon-tail) + var(--balloon-border) * 0.4);
  height: calc(var(--balloon-tail) * 1.25 - var(--balloon-border) * 3.4);
  background: var(--kc-paper);
}

.tutorial-speaker {
  grid-area: speaker;
  justify-self: start;
  margin: 0;
  padding: 0.08rem 0.5rem;
  color: var(--kc-paper);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-3xs);
  font-weight: 700;
  letter-spacing: 0.12em;
  background: linear-gradient(180deg, var(--kc-navy), var(--kc-navy-deep));
  border: 0.1rem solid var(--kc-brass);
  border-radius: var(--kc-radius-flat);
}

/* The typewriter fills this box a character at a time, so it holds two lines of
   room from the start and the balloon never grows under the reader's hand. */
.tutorial-line {
  grid-area: line;
  min-height: 2.7em;
  margin: 0;
  color: var(--kc-ink);
  font-family: var(--kc-font-ui);
  font-size: var(--kc-type-xs);
  line-height: 1.35;
}

.tutorial-advance {
  grid-area: advance;
  align-self: end;
}

/* On a landscape phone he gives back everything he can: a smaller bust and a
   single line of room. This sits here for the same reason the width block
   below does — the collection's own `max-height: 34rem` block is 370 lines
   above the rules these override, and with no extra specificity from the media
   query it simply lost to them, leaving the full-size bust and a two-line box
   on exactly the screen that had no room for either. */
@media (max-height: 34rem) {
  .collection-tutorial {
    --portrait-width: clamp(2.2rem, 12vmin, 3.2rem);
  }

  .tutorial-line {
    min-height: 1.4em;
  }

  /* His button joins the nameplate's row, so what he says runs the whole
     width of the balloon: beside the button, at the 16px floor, it took three
     lines on a landscape phone where two are all the room there is. */
  .tutorial-balloon {
    grid-template-areas:
      "speaker advance volume"
      "line line line";
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
  }

  .tutorial-advance {
    align-self: center;
    justify-self: end;
    padding-inline: 0.6rem;
    letter-spacing: 0.02em;
    white-space: nowrap;
  }
}

/* His button shares the bottom row with what he is saying, and that row's
   second track is sized to the button: `COMEÇAR TUTORIAL` is 197px of a 320px
   screen, which left the text 34px wide — four characters a line, eleven lines,
   a balloon 240px tall, and 120px for the panel he is describing. At this width
   the button takes a row of its own and the text keeps the whole balloon, which
   also stops the balloon resizing itself every time the label changes.

   This block belongs here, after the rule it overrides: a media query carries
   no extra specificity, so the collection's other `max-width: 34rem` block —
   which sits 400 lines earlier — would simply lose to the base rule. */
@media (max-width: 34rem) {
  .tutorial-balloon {
    grid-template-areas:
      "speaker volume"
      "line line"
      "advance advance";
  }

  /* At the 16px floor his text takes a line more than it did, so the balloon
     gives back its own padding and leading instead, and the volume slider
     narrows so the nameplate keeps its row (below the slider's own rule). */
  .tutorial-balloon {
    row-gap: 0.1rem;
    padding: 0.3rem 0.55rem;
  }

  .tutorial-line {
    line-height: 1.25;
  }

  /* At the 16px floor his nameplate and his button wrapped into two lines
     each, which cost the panel over him two rows; the tracking is what gives. */
  .tutorial-speaker {
    letter-spacing: 0.02em;
    white-space: nowrap;
  }

  .tutorial-advance {
    justify-self: stretch;
    letter-spacing: 0.02em;
    white-space: nowrap;
  }
}

/* His voice rides the nameplate's row, which until now carried only the plate,
   so the control costs the difference between the two heights and not a row of
   its own — the balloon is already tight on a landscape phone. */
/* His volume and the way out of the tour share the nameplate's row; the way
   out hides during the first tour, which is required since 1.104. */
.tutorial-tools {
  display: flex;
  grid-area: volume;
  align-items: center;
  justify-self: end;
  gap: 0.3rem;
}

.tutorial-volume-control {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  min-height: var(--kc-control-min);
  color: var(--kc-navy);
  cursor: pointer;
}

/* The mark rather than a word: at 320px there is no room for a label beside the
   name plate, and `♪` reads at every width. It is decorative, so it is sized to
   the plate it sits beside and the control keeps its own `aria-label`. */
.tutorial-volume-mark {
  font-family: var(--kc-font-ui);
  font-size: var(--kc-type-2xs);
  line-height: 1;
}

.tutorial-volume {
  width: clamp(3.5rem, 14vmin, 6.5rem);
  /* The track is thin but the control keeps the hit-target floor of its row. */
  min-height: var(--kc-control-min);
  margin: 0;
  accent-color: var(--kc-brass);
  cursor: pointer;
}

/* A narrow or short balloon gives the nameplate and the button their row
   back; after the rule above, or it would lose to it on source order. */
@media (max-width: 34rem), (max-height: 34rem) {
  .tutorial-volume {
    width: 2.6rem;
  }
}

.tutorial-volume:focus-visible {
  outline: var(--kc-focus-width) solid var(--kc-signal);
  outline-offset: 0.15rem;
}

/* The one tab the tutorial is waiting on. The brass plate and its glow are the
   emphasis; the beacon only breathes over them, so reduced motion, which stops
   the animation, still leaves the tab plainly marked. */
.collection-tab[data-tutorial="target"] {
  color: var(--kc-navy-deep);
  background: linear-gradient(180deg, var(--kc-brass-light), var(--kc-brass));
  box-shadow:
    inset 0 0 0 0.12rem var(--kc-navy-deep),
    inset 0 0 0 0.22rem var(--kc-brass-light),
    0 0 0.5rem var(--kc-brass-glow);
  animation: tutorial-beacon 1.3s ease-in-out infinite alternate;
}

/* The keyboard goes to that tab, and the beacon animates the very box-shadow
   the focus ring is drawn with, which no rule can beat: the ring stops the
   breathing while it shows. */
.collection-tab[data-tutorial="target"]:focus-visible {
  box-shadow:
    inset 0 0 0 0.2rem var(--kc-signal),
    inset 0 0 0 0.3rem var(--kc-paper),
    0 0 0.5rem var(--kc-brass-glow);
  animation: none;
}

/* While PIRACA I.A holds the dialog, a navy scrim covers the whole screen and
   only his balloon with its portrait and the one tab he is pointing at stand
   above it. On a replay from the manual the close button already sits higher,
   at its own z-index, so that way out is never dimmed; the first tour hides it
   (`is-tutorial-required`). The scrim sits above the invite banner, which is
   inert under him anyway. */
.collection-dialog.is-tutorial .collection-screen::before {
  position: absolute;
  inset: 0;
  z-index: var(--kc-z-tutorial);
  background: var(--kc-scrim);
  pointer-events: none;
  content: "";
  animation: tutorial-scrim-in 0.3s ease-out;
}

.collection-dialog.is-tutorial .collection-tutorial,
.collection-dialog.is-tutorial .collection-tab[data-tutorial="target"] {
  position: relative;
  z-index: calc(var(--kc-z-tutorial) + 1);
}

/* On a phone the header gives its row to him while he speaks: at the 16px floor
   his balloon needs every line of it, the header is inert and dimmed under the
   scrim anyway, and the close button lives outside it, so a replay keeps its
   way out.
   The panel he is describing keeps more room than he takes. It answered to
   height alone until the owner's revision of his lines in 1.88, whose longest
   line took his balloon past the panel on a portrait phone too. */
@media (max-height: 34rem), (max-width: 34rem) {
  .collection-dialog.is-tutorial .collection-header {
    display: none;
  }

  /* With the header gone the tab bar is the top row, under the language switch
     and the close button in its corners, which painted over the first tab he
     asks for. The bar keeps clear of both, and so does a tab scrolled into view. */
  .collection-dialog.is-tutorial .collection-tabs {
    padding-inline: calc(var(--kc-control-min) + 0.35rem);
    scroll-padding-inline: calc(var(--kc-control-min) + 0.35rem);
    /* The bar's faded ends are a mask, and a mask makes the bar a stacking
       context: the tab he points at kept its z-index inside it while the whole
       bar was painted under his scrim, so the one tab he names was as dim as
       the rest. Every tab but that one is under the scrim anyway. */
    mask-image: none;
  }

  /* The first tour hides the close button, so the bar's end keeps no room
     for it; only the language switch at its start still needs clearing. */
  .collection-dialog.is-tutorial-required .collection-tabs {
    padding-inline-end: 0;
    scroll-padding-inline-end: 0;
  }
}

/* The first login's pretend loading: the base readied before the visitor
   boards, a navy night with the brass bar filling under one line of work at a
   time. It covers the whole screen below the close button, which keeps its own
   higher layer, so the way out is never behind it. */
.collection-boot {
  position: absolute;
  inset: 0;
  z-index: var(--kc-z-loading);
  display: grid;
  place-items: center;
  padding: 1rem;
  background:
    radial-gradient(circle at 12px 12px, var(--kc-hex-light) 0 6px, transparent 7px) 0 0 / 24px 21px,
    radial-gradient(ellipse at 50% 42%, var(--kc-navy), var(--kc-navy-deep) 72%);
  animation: tutorial-scrim-in 0.3s ease-out;
}

/* Focus only lands on either by script — on the loading screen to take the
   keyboard off the login form, and on the screen itself to hold it afterwards
   until the panel is ready — so neither draws a ring around the whole dialog. */
.collection-boot:focus,
/* The screen is a scripted focus target while the loading screen runs, never a
   Tab stop, so its ring would only ever flash around a whole screen nobody
   navigated to. Scoped to that: made reachable by Tab, the tabindex stops being
   -1, this stops matching, and the ring comes back rather than being suppressed
   by a rule whose reason has quietly expired. */
.collection-screen[tabindex="-1"]:focus {
  outline: none;
}

.collection-boot-panel {
  display: grid;
  justify-items: center;
  gap: clamp(0.35rem, 1.8vmin, 0.85rem);
  width: min(100%, 28rem);
  text-align: center;
}

.collection-boot-anchor {
  margin: 0;
  color: var(--kc-brass);
  font-family: var(--kc-font-ui);
  font-size: clamp(2rem, 9vmin, 3.6rem);
  line-height: 1;
  animation: collection-boot-sway 2.4s ease-in-out infinite alternate;
}

.collection-boot-title {
  margin: 0;
  color: var(--kc-brass-light);
  font-family: var(--kc-font-mincho);
  font-size: clamp(1rem, 3.6vmin, 1.5rem);
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* Two lines of room from the start, so a long line never shifts the bar. */
.collection-boot-line {
  min-height: 2.7em;
  margin: 0;
  color: var(--kc-paper);
  font-family: var(--kc-font-ui);
  font-size: var(--kc-type-s);
  line-height: 1.35;
}

.collection-boot-bar {
  width: 100%;
  height: clamp(0.8rem, 2.4vmin, 1.1rem);
  overflow: hidden;
  background: var(--kc-navy-deep);
  border: 0.14rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
  box-shadow: inset 0 0 0 0.1rem var(--kc-navy-deep);
}

/* The fill grows by transform alone, from its left edge. */
.collection-boot-fill {
  display: block;
  height: 100%;
  background:
    repeating-linear-gradient(135deg, var(--kc-hex-light) 0 0.4rem, transparent 0.4rem 0.8rem),
    linear-gradient(180deg, var(--kc-brass-light), var(--kc-brass));
  transform: scaleX(var(--boot-progress, 0));
  transform-origin: left center;
  transition: transform 0.35s ease-out;
}

.collection-boot-percent {
  margin: 0;
  color: var(--kc-brass-light);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-s);
  font-weight: 800;
  letter-spacing: 0.08em;
}

.collection-boot-skip {
  margin: 0;
  color: var(--kc-paper);
  font-family: var(--kc-font-ui);
  font-size: var(--kc-type-3xs);
  opacity: 0.8;
}

@keyframes collection-boot-sway {
  from {
    transform: rotate(-6deg);
  }
  to {
    transform: rotate(6deg);
  }
}

/* The Vinicius Pirasoft opening (1.120): every time the collection opens, the
   company logo on its own white over the whole screen, fading in over its
   sting and the white fading off the collection, the way a console game opens
   on its publisher's. It is one native button, so a tap, Enter or Space skips
   it. Both fades are animations rather than transitions, since the screen
   comes out of display: none and a transition never starts from there. Neither
   fills backwards: under reduced motion the blanket block gives each a 0.01ms
   delay, and the logo stands at its own opacity through it rather than at the
   fade's first keyframe, which Chromium can hold for a frame or more while it
   starts the animation. So the logo is simply there and then simply gone, over
   the same time. */
.collection-splash {
  position: absolute;
  inset: 0;
  z-index: var(--kc-z-splash);
  display: grid;
  place-items: center;
  margin: 0;
  padding: 1rem;
  border: 0;
  border-radius: 0;
  background: var(--kc-splash-paper);
  box-shadow: none;
  cursor: default;
  -webkit-tap-highlight-color: transparent;
}

/* Focus only lands on it by script, as the opening takes the keyboard, so a
   ring around the whole screen would say nothing. */
.collection-splash:focus {
  outline: none;
}

.collection-splash.is-leaving {
  animation: collection-splash-leave 0.5s ease-in forwards;
  pointer-events: none;
}

.collection-splash-logo {
  display: block;
  width: min(84vw, 80dvh, 36rem);
  height: auto;
  aspect-ratio: 1;
  opacity: 0;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

.collection-splash.is-shown .collection-splash-logo {
  opacity: 1;
  animation: collection-splash-logo-in 0.6s ease-out;
}

/* While the logo holds the screen, nothing of the collection shows through or
   above it: the corner controls go with the rest. */
.collection-dialog.is-splash .dialog-close-form,
.collection-dialog.is-splash .collection-lang,
.collection-dialog.is-splash .collection-login-music {
  display: none;
}

@keyframes collection-splash-logo-in {
  from {
    opacity: 0;
    transform: scale(0.94);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes collection-splash-leave {
  to {
    opacity: 0;
  }
}

@keyframes tutorial-scrim-in {
  from {
    opacity: 0;
  }
}

@keyframes tutorial-beacon {
  from {
    box-shadow:
      inset 0 0 0 0.12rem var(--kc-navy-deep),
      inset 0 0 0 0.22rem var(--kc-brass-light),
      0 0 0.3rem var(--kc-brass-glow);
  }
  to {
    box-shadow:
      inset 0 0 0 0.12rem var(--kc-navy-deep),
      inset 0 0 0 0.22rem var(--kc-brass-light),
      0 0 1.1rem var(--kc-brass-glow);
  }
}

.collection-nanacote-status {
  color: var(--kc-navy);
}

.collection-field-error {
  margin: 0;
  color: var(--kc-signal);
  font-size: var(--kc-type-xs);
  font-weight: 800;
}

.nanavio-caption {
  font-family: var(--kc-font-mincho);
}

.collection-invite p {
  font-family: var(--kc-font-mincho);
}

.nanavio-viewer-name {
  font-family: var(--kc-font-mincho);
  letter-spacing: 0.06em;
}

/* Naval duel: preparation board and battle replay inside the DUELOS tab. */
.duel-room {
  display: grid;
  min-height: 0;
  grid-template-rows: auto minmax(0, 1fr);
  gap: clamp(0.3rem, 1.2vmin, 0.6rem);
}

/* The DUELAR button keeps its width beside a long login. */
.lobby-player .lobby-invite {
  flex: 0 0 auto;
}

.duel-header {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 0.6rem;
  align-items: center;
  justify-content: center;
}

.duel-header .collection-subtitle,
.duel-clock,
.duel-narration {
  margin: 0;
}

.duel-header .collection-subtitle {
  font-family: var(--kc-font-mincho);
}

.duel-clock {
  min-width: 3.2em;
  padding: 0.05rem 0.5rem;
  color: var(--kc-paper);
  font-variant-numeric: tabular-nums;
  font-weight: 900;
  text-align: center;
  background: var(--kc-navy-deep);
  border: 0.1rem solid var(--kc-brass);
  border-radius: var(--kc-radius-flat);
}

.duel-clock:empty {
  display: none;
}

/* The last ten seconds before the deadline fights with the saved plan. */
.duel-clock[data-urgent="true"] {
  background: var(--kc-signal);
  animation: duel-clock-urgent 1s ease-in-out infinite alternate;
}

@keyframes duel-clock-urgent {
  to { box-shadow: 0 0 0.6rem var(--kc-signal); }
}

.duel-prep {
  display: grid;
  min-height: 0;
  /* One track per child, each placed by hand below: hint, the solo screen's
     difficulty chips (a duel has none, and the row collapses), the line, the
     picker that takes the slack and scrolls, the type ring under it, and the
     actions. An implicit track here would take the 1fr away from the picker.

     The picker's floor is what keeps a fleet pickable: the rest of the rows are
     `auto` and do not shrink, so on a 320px-tall screen a bare `minmax(0, 1fr)`
     collapsed it to a 10px sliver of card. With a floor the panel stops fitting
     instead, which is what the scrolling below is for — and scrolling is how
     `TRAVAR FROTA` stays reachable there at all: it used to fall past the
     bottom edge, invisible, because an ancestor's `overflow: hidden` clipped it
     without the dialog or the document ever reporting an overflow. */
  grid-template-rows: auto auto auto minmax(calc(clamp(4.5rem, 16dvh, 9rem) + 6.5rem), 1fr) auto auto;
  gap: clamp(0.25rem, 1vmin, 0.5rem);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.duel-prep > .fleet-hint {
  grid-row: 1;
}

.duel-prep > .duel-chips {
  grid-row: 2;
}

/* The duel has no chips, so the sea it was accepted on stands in their row. */
.duel-prep > .duel-sea {
  grid-row: 2;
}

.duel-prep > .duel-board {
  grid-row: 3;
}

/* On a phone the picker comes straight under the line and the ring after it:
   the ring between them put the two a drag joins a whole screen apart. */
.duel-prep > .type-ring {
  grid-row: 5;
}

.duel-prep > .fleet-roster {
  grid-row: 4;
}

/* The picker with its two chip rows over it: which class to show, and which
   number to put first. The picker keeps the slack and scrolls; the chip rows
   scroll sideways when they do not fit, like the tab bar on a narrow screen,
   rather than wrapping into rows the picker needs. */
.fleet-roster {
  display: grid;
  min-width: 0;
  min-height: 0;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto minmax(0, 1fr);
  gap: 0.25rem;
}

.fleet-roster > .fleet-roster-chips {
  flex-wrap: nowrap;
  justify-content: flex-start;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  padding-block-end: 0.15rem;
}

.fleet-roster > .fleet-roster-chips .duel-chip,
.fleet-roster > .fleet-roster-chips .duel-chips-legend {
  flex: none;
  white-space: nowrap;
}

/* A landscape phone gives the preparation a hand's height: the two chip rows
   share one, side by side, each still scrolling on its own, so the picker
   under them is not pushed out of reach of the line it is dragged into. */
@media (max-height: 34rem) {
  .fleet-roster {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
  }

  .fleet-roster > .duel-picker {
    grid-column: 1 / -1;
  }
}

.duel-prep > .action-row {
  grid-row: 6;
}

/* The one button the whole screen leads to stays in sight while the rest
   scrolls under it: on a phone the difficulty chips and the line take the
   whole height, and ATACAR (or TRAVAR FROTA) sat below the fold
   where a first-time player never scrolled to find it. */
.duel-prep > .action-row {
  position: sticky;
  bottom: 0;
  z-index: 1;
  padding: 0.3rem 0;
  background: var(--kc-paper);
  border-top: 0.08rem solid var(--kc-brass-dark);
}

/* Where there is width beside it, the column of the line stands on the left
   and the nanavios to drag into it on the right, the way the fleet list and
   the ship list sit side by side in the naval game the collection is drawn
   from; the ring stands under the picker, and the hint, the chips and the
   actions run across both. A portrait phone has no width to spare and keeps
   the picker under the line and the ring under the picker. */
@media (min-width: 48rem), (orientation: landscape) and (min-width: 34rem) {
  /* The line takes the left column whole; the picker and, under it, the ring
     take the right, where there is width for the ring's legend beside its
     drawing. Under the line, in a column 22rem wide, the ring stood a screen
     tall and ran under the stuck action row. */
  .duel-prep {
    grid-template-columns: minmax(12rem, min(22rem, 50%)) minmax(0, 1fr);
    grid-template-rows: auto auto minmax(calc(clamp(4.5rem, 16dvh, 9rem) + 6.5rem), 1fr) auto auto;
    column-gap: clamp(0.5rem, 2vw, 1.25rem);
  }

  .duel-prep > .fleet-hint,
  .duel-prep > .duel-chips,
  .duel-prep > .duel-sea,
  .duel-prep > .action-row {
    grid-column: 1 / -1;
  }

  .duel-prep > .duel-board {
    grid-row: 3 / 5;
    grid-column: 1;
    width: 100%;
  }

  .duel-prep > .type-ring {
    grid-row: 4;
    grid-column: 2;
    justify-self: start;
  }

  /* The drawing at its smallest leaves a tablet's right column the 11rem the
     legend needs to stand beside it. */
  .duel-prep > .type-ring .type-ring-art {
    width: 9rem;
  }

  .duel-prep > .fleet-roster {
    grid-row: 3;
    grid-column: 2;
  }

  .fleet-roster > .duel-picker {
    justify-content: start;
  }

  .duel-prep > .action-row {
    grid-row: 5;
  }
}

/* The advantage ring, drawn: the five classes around a pentagon in the order
   of the cycle, a solid brass arrow from each to the next (×1,5) and a dashed
   one to the one after (×1,25). It is read, never pressed, so its legend is
   floored at the type scale instead of the 44px control height. The classes
   standing in the line light up in brass, one rule per class, off the list the
   script writes on the figure. */
/* The box hugs the drawing and its legend (1.94): stretched across its grid
   area it stood as wide as the picker on a desktop, 950px of paper around a
   160px drawing, with the legend's key of the codes run into one long row
   under it. The legend stands beside the drawing wherever 11rem is left for
   it, growing to 22rem where there is more, and drops under the drawing
   where there is not; it never shrinks below its own words, so it is never
   the strip too narrow for "contratorpedeiro" it was when it stood beside the
   drawing and shrank. */
.type-ring {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-self: center;
  width: fit-content;
  max-width: 100%;
  margin: 0;
  padding: 0.3rem 0.5rem;
  gap: 0.3rem 0.6rem;
  align-items: center;
  justify-content: center;
  color: var(--kc-navy-deep);
  background: var(--kc-paper-dark);
  border: 0.1rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius-flat);
}

.type-ring-art {
  flex: none;
  width: clamp(9rem, 20dvh, 11rem);
  height: auto;
}

.type-ring-arrow line {
  stroke: var(--kc-brass-dark);
  stroke-width: 3;
  stroke-linecap: round;
}

.type-ring-arrow polygon {
  fill: var(--kc-brass-dark);
}

.type-ring-arrow.is-slight line {
  stroke: var(--kc-steel);
  stroke-width: 1.8;
  stroke-dasharray: 5 4;
}

.type-ring-arrow.is-slight polygon {
  fill: var(--kc-steel);
}

/* Each class names itself in the collection's naval note under the mouse:
   the code is all the drawing prints. */
.type-ring-node {
  cursor: help;
}

.type-ring-node circle {
  fill: var(--kc-navy);
  stroke: var(--kc-brass-dark);
  stroke-width: 2;
}

/* The codes are drawn in the SVG's own units: 22 of 176 across a drawing at
   least 9rem wide renders them at 18px or more, and across the narrow
   block's 8rem at 16px, the floor. */
.type-ring-node text {
  fill: var(--kc-paper);
  font-family: var(--kc-font-ui);
  font-size: 22px;
  font-weight: 900;
  text-anchor: middle;
  dominant-baseline: central;
}

.type-ring[data-in-line~="cv"] .type-ring-node[data-type="cv"] circle,
.type-ring[data-in-line~="bb"] .type-ring-node[data-type="bb"] circle,
.type-ring[data-in-line~="ca"] .type-ring-node[data-type="ca"] circle,
.type-ring[data-in-line~="dd"] .type-ring-node[data-type="dd"] circle,
.type-ring[data-in-line~="ss"] .type-ring-node[data-type="ss"] circle {
  fill: var(--kc-brass);
  stroke: var(--kc-navy-deep);
}

.type-ring[data-in-line~="cv"] .type-ring-node[data-type="cv"] text,
.type-ring[data-in-line~="bb"] .type-ring-node[data-type="bb"] text,
.type-ring[data-in-line~="ca"] .type-ring-node[data-type="ca"] text,
.type-ring[data-in-line~="dd"] .type-ring-node[data-type="dd"] text,
.type-ring[data-in-line~="ss"] .type-ring-node[data-type="ss"] text {
  fill: var(--kc-navy-deep);
}

.type-ring-legend {
  display: flex;
  flex: 1 1 11rem;
  flex-wrap: wrap;
  justify-content: flex-start;
  align-self: center;
  min-width: min-content;
  max-width: 22rem;
  gap: 0.15rem 0.9rem;
  font-size: var(--kc-type-3xs);
  font-weight: 700;
  line-height: 1.3;
  text-align: start;
}

.type-ring-note {
  flex: 1 1 100%;
}

.type-ring-key {
  display: flex;
  gap: 0.35rem;
  align-items: center;
  white-space: nowrap;
}

/* The two arrows of the drawing, in small, beside what each means. */
.type-ring-swatch {
  flex: none;
  width: 1.4rem;
  height: 0;
  border-top: 0.2rem solid var(--kc-brass-dark);
}

.type-ring-swatch.is-slight {
  border-top: 0.12rem dashed var(--kc-steel);
}

/* A brass node of the ring is a class standing in the line: the legend's third
   key, in the lit node's own colours, says so. */
.type-ring-dot {
  flex: none;
  box-sizing: border-box;
  width: 0.9em;
  height: 0.9em;
  border: 0.12rem solid var(--kc-navy-deep);
  border-radius: 50%;
  background: var(--kc-brass);
}

/* The key of the codes, one class a line, a step lighter than the legend: navy
   rather than steel, which reads at 3.8:1 on the ring's darker paper. */
.type-ring-codes {
  display: grid;
  flex: 1 1 100%;
  justify-items: start;
  justify-content: start;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), max-content));
  column-gap: 1rem;
  color: var(--kc-navy);
  text-align: start;
}

/* The frota is one line, and its first place is the vanguard. The line stands
   as a column of banners, first on top, like the fleet list of the naval game
   the collection is drawn from: the ship the enemy fires at first is the one
   at the top, and the fire works its way down. The script builds the places in
   that order, so Tab walks the line the way it is read. */
/* The line keeps a margin inside it as wide as what a place draws outside its
   own edge — the brass ring of the place a drop would land on or of one chosen
   from the keyboard, and the focus outline — since the preparation scrolls and
   clips whatever runs past it: against its left edge the ring was cut off. */
.duel-board {
  display: grid;
  align-content: start;
  margin: 0 auto;
  padding: max(var(--kc-select-width), calc(var(--kc-focus-width) + 0.14rem));
  width: min(100%, 24rem);
  gap: 0.25rem;
  list-style: none;
}

.duel-slot {
  display: grid;
  min-width: 0;
}

/* A place is a banner: its number, the ship's face in a square frame, and her
   name over her class and level. An empty place keeps the number and a dashed
   edge, since it is somewhere to drop a ship. */
.duel-slot-button {
  --banner-art: clamp(2.2rem, 6.5dvh, 3.2rem);
  display: grid;
  width: 100%;
  min-width: 0;
  min-height: var(--kc-control-min);
  padding: 0.15rem 0.4rem 0.15rem 0.2rem;
  grid-template-columns: 1.7rem minmax(0, 1fr);
  align-items: center;
  gap: 0.3rem;
  color: var(--kc-steel);
  font: inherit;
  font-size: var(--kc-type-2xs);
  font-weight: 900;
  text-align: start;
  background: var(--kc-sea-soft);
  border: 0.12rem dashed var(--kc-steel);
  border-radius: var(--kc-radius);
  cursor: pointer;
  overflow: hidden;
}

.duel-slot-button:not([data-empty="true"]) {
  color: var(--kc-navy-deep);
  background: var(--kc-paper);
  border-style: solid;
}

/* The UI face and not mincho: the ordinal's º is a latin-1 mark mincho draws
   as a full-size o on the baseline, which read as "1o". */
.duel-slot-number {
  color: var(--kc-navy-deep);
  font-family: var(--kc-font-ui);
  font-size: var(--kc-type-xs);
  font-weight: 900;
  text-align: center;
}

.duel-slot-empty {
  font-size: var(--kc-type-3xs);
  font-weight: 700;
  font-style: italic;
}

/* A ship lying on her side: her face in a square frame, cut from the top of
   her card, and beside it her name over her class and level. The board and
   both replays share it. */
.ship-banner {
  display: grid;
  min-width: 0;
  grid-template-columns: var(--banner-art) minmax(0, 1fr);
  align-items: center;
  gap: 0.4rem;
}

.ship-banner-art {
  display: block;
  width: var(--banner-art);
  aspect-ratio: 1;
  overflow: hidden;
  background: var(--kc-sea-soft);
  border: 0.08rem solid var(--kc-navy);
  border-radius: var(--kc-radius);
}

/* The card inside is the whole card, top-aligned, so the frame shows her face;
   the banner already prints her name, so the card's own caption stays out. */
.ship-banner-art .nanavio {
  height: auto;
  aspect-ratio: 327 / 450;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

.ship-banner-art .nanavio-caption,
.ship-banner-art .nanavio-fallback {
  display: none;
}

/* An enemy names nobody and has no art: her frame carries her class code. */
.ship-banner-art.is-enemy {
  display: grid;
  place-items: center;
  color: var(--kc-brass-light);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-xs);
  font-weight: 800;
  background: var(--kc-navy);
}

.ship-banner-text {
  display: grid;
  min-width: 0;
  line-height: 1.15;
}

.ship-banner-name,
.ship-banner-meta {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ship-banner-name {
  color: var(--kc-navy-deep);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-xs);
  font-weight: 800;
}

.ship-banner-meta {
  color: var(--kc-steel);
  font-size: var(--kc-type-3xs);
  font-weight: 700;
}

/* A locked line takes no more moves, and a place keeps its look while it waits. */
.duel-slot-button:disabled {
  cursor: default;
}

/* The line is built by dragging: a card that can be picked up says so under
   the mouse, the one being carried fades where it was, and the place a drop
   would land on lights up. A finger resting on a card for the drag's hold must
   not select its text or open the browser's own menu. */
.duel-picker .picker-card,
.duel-slot-button {
  -webkit-touch-callout: none;
  user-select: none;
}

.duel-picker .picker-card img,
.duel-slot-button img {
  -webkit-user-drag: none;
}

.duel-picker .picker-card:not(:disabled),
.duel-slot-button:not(:disabled):not([data-empty="true"]) {
  cursor: grab;
}

.collection-dialog.is-dragging-fleet {
  cursor: grabbing;
  user-select: none;
}

/* An empty place is somewhere to drop, not something to pick up. */
.duel-slot-button[data-empty="true"] {
  cursor: default;
}

.duel-picker .picker-card[data-dragging="true"],
.duel-slot-button[data-dragging="true"] {
  opacity: 0.4;
}

/* A character already standing in the line is dimmed in the list rather than
   ringed (the owner's request in 1.86), so who is in the frota reads at a
   glance. She stays there, since dragging her again still moves her to another
   place and Enter from the keyboard takes her out. The picture inside is what
   fades, not the button: faded whole, it took the focus outline drawn on the
   button down to about 1.9:1, on the one card the keyboard has just used. */
.duel-picker .picker-card[aria-pressed="true"] {
  box-shadow: none;
}

.duel-picker .picker-card[aria-pressed="true"] .nanavio {
  opacity: 0.4;
  filter: grayscale(0.8) brightness(0.7);
}

.duel-slot-button[data-drop="true"] {
  border-style: solid;
  border-color: var(--kc-brass);
  box-shadow:
    0 0 0 var(--kc-select-width) var(--kc-brass),
    0 0 0.9rem var(--kc-brass-glow);
}

/* The card following the pointer. It is laid out once at the top left and
   moved on transform alone, and it takes no pointer so the place under it can
   be found. Carried out of the line it fades: dropped there, the ship leaves. */
.fleet-drag-ghost {
  position: fixed;
  top: 0;
  left: 0;
  z-index: var(--kc-z-tooltip);
  pointer-events: none;
  cursor: grabbing;
  border-radius: var(--kc-radius-nanavio);
  box-shadow: 0.3rem 0.4rem 0 var(--kc-shadow);
}

.fleet-drag-ghost > .nanavio {
  width: 100%;
  height: 100%;
}

/* A banner carried off the line keeps its own look over the page. */
.fleet-drag-ghost > .ship-banner {
  --banner-art: clamp(2.2rem, 6.5dvh, 3.2rem);
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  padding: 0.15rem 0.4rem;
  background: var(--kc-paper);
  border: 0.12rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
}

.fleet-drag-ghost[data-remove="true"] {
  opacity: 0.45;
}

.duel-slot-button[aria-pressed="true"] {
  box-shadow:
    0 0 0 var(--kc-select-width) var(--kc-brass),
    0 0 0.7rem var(--kc-brass-glow);
}


/* The picker's cards keep one width and the row is centred, like the board
   above it; stretched to fill auto-fill columns, a lone nanavio sat at the left
   edge under a centred board on a wide screen. auto-fit rather than auto-fill,
   since only collapsed empty tracks let the row centre. */
.duel-picker {
  grid-template-columns: repeat(auto-fit, clamp(3.6rem, 9vmin, 5.4rem));
  justify-content: center;
}

.duel-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  justify-content: center;
}

/* The row's name, before its chips: read, never pressed. */
.duel-chips-legend {
  align-self: center;
  color: var(--kc-navy-deep);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-3xs);
  font-weight: 800;
  letter-spacing: 0.04em;
}

.duel-chip {
  display: inline-flex;
  min-height: var(--kc-control-min);
  padding: 0.2rem 0.6rem;
  align-items: center;
  justify-content: center;
  color: var(--kc-navy-deep);
  font: inherit;
  font-size: var(--kc-type-xs);
  font-weight: 900;
  background: var(--kc-paper);
  border: 0.1rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius-pill);
  cursor: pointer;
}

.duel-chip[aria-pressed="true"] {
  color: var(--kc-paper);
  background: var(--kc-navy);
  border-color: var(--kc-brass);
}

/* The BATALHA risk band under a difficulty's pay: the line's power against
   what the step sends, one pill per chip in the band's colour. */
.duel-chip:has(.duel-chip-badge) {
  flex-direction: column;
  gap: 0.15rem;
}

/* It may wrap: two by two on a phone a chip is about 8rem wide, which
   "!! muito difícil" at the 16px floor is not. */
.duel-chip-badge {
  max-width: 100%;
  padding: 0 0.45rem;
  color: var(--kc-navy-deep);
  background: var(--kc-sea-soft);
  border: 0.08rem solid var(--kc-steel);
  border-radius: var(--kc-radius);
}

.duel-chip-badge[data-risk="easy"] {
  background: var(--kc-brass-light);
  border-color: var(--kc-brass-dark);
}

/* Signal on paper is 5.35:1; on the darker paper it was 4.25, under what 16px
   text needs. The red edge is what sets it apart from a paper chip. */
.duel-chip-badge[data-risk="risky"] {
  color: var(--kc-signal);
  background: var(--kc-paper);
  border-color: var(--kc-signal);
}

.duel-chip-badge[data-risk="hard"] {
  color: var(--kc-paper);
  background: var(--kc-signal);
  border-color: var(--kc-signal);
}

/* The numbers behind the badges, under the chips, across the whole row. */
.duel-chips-power {
  flex-basis: 100%;
  margin: 0;
  color: var(--kc-navy-deep);
  font-size: var(--kc-type-xs);
  font-weight: 800;
  text-align: center;
}

/* The sea a replay is fought on, beside its buttons. The narration opens on
   it aloud, so the label is for the eye alone. */
.arena-sea {
  color: var(--kc-paper);
  font-family: var(--kc-font-ui);
  font-size: var(--kc-type-3xs);
  font-weight: 800;
}

/* The sea as a card (1.117), under the difficulty chips and over the duel's
   line: its picture large on the left, its name and when it turns, what it does
   to which classes — a brass ▲ for a help, a red ▼ for a harm — and the seas of
   the next hours as chips. A paper plate in the theme's brass, its left edge in
   the sea's own colour; every word stays ink on paper. */
.sea-card {
  --sea-accent: var(--kc-sea);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: 0.6rem;
  row-gap: 0.15rem;
  align-items: center;
  box-sizing: border-box;
  width: 100%;
  max-width: 44rem;
  margin-inline: auto;
  padding: 0.35rem 0.7rem 0.35rem 0.5rem;
  color: var(--kc-ink);
  font-family: var(--kc-font-ui);
  font-size: var(--kc-type-xs);
  font-weight: 700;
  text-align: start;
  background: var(--kc-paper);
  border: 0.1rem solid var(--kc-brass-dark);
  border-inline-start: 0.4rem solid var(--sea-accent);
  border-radius: var(--kc-radius);
}

.sea-card[data-sea="neblina"] {
  --sea-accent: var(--kc-silver);
}

.sea-card[data-sea="mar-agitado"] {
  --sea-accent: var(--kc-steel);
}

.sea-card[data-sea="ceu-limpo"] {
  --sea-accent: var(--kc-brass);
}

.sea-card[data-sea="ventania"] {
  --sea-accent: var(--kc-sea-soft);
}

.sea-card[data-sea="noite"] {
  --sea-accent: var(--kc-navy);
}

.sea-card-mark {
  grid-row: 1 / span 3;
  align-self: start;
  font-family: var(--kc-font-ui);
  font-size: 2.2rem;
  line-height: 1.1;
}

.sea-card-head {
  display: flex;
  flex-wrap: wrap;
  gap: 0 0.6rem;
  align-items: baseline;
  margin: 0;
}

.sea-card-name {
  color: var(--kc-navy-deep);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-s);
  font-weight: 800;
}

.sea-card-turn {
  color: var(--kc-navy);
  font-weight: 600;
}

.sea-card-effects {
  display: flex;
  flex-wrap: wrap;
  gap: 0.05rem 0.9rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.sea-card-effects li::before {
  content: "▲" / "";
  margin-inline-end: 0.3rem;
  color: var(--kc-brass-dark);
}

.sea-card-effects li[data-effect="down"]::before {
  content: "▼" / "";
  color: var(--kc-signal);
}

.sea-card-effects li[data-effect="none"]::before {
  content: none;
}

.sea-card-next {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 0.4rem;
  align-items: center;
}

.sea-card-next-label {
  color: var(--kc-navy);
  font-weight: 600;
}

.sea-card-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.sea-card-chip {
  display: inline-flex;
  gap: 0.25rem;
  align-items: center;
  padding: 0 0.5rem;
  color: var(--kc-navy-deep);
  background: var(--kc-sea-soft);
  border: 0.08rem solid var(--kc-steel);
  border-radius: var(--kc-radius-pill);
}

.sea-card-chip-mark {
  font-family: var(--kc-font-ui);
}

.duel-arena {
  position: relative;
  /* The arena is the stacking context the sea's layer (z-index -1) stands
     in: over the arena's own navy and under everything else. Isolating the
     lines instead trapped every damage plate inside them, under the tracers
     and planes the arena paints after the lines. */
  isolation: isolate;
  display: grid;
  min-height: 0;
  grid-template-rows: minmax(0, 1fr) auto auto;
  gap: clamp(0.25rem, 1vmin, 0.5rem);
  padding: 0.3rem;
  background: linear-gradient(180deg, var(--kc-navy-deep), var(--kc-navy));
  border: 0.12rem solid var(--kc-brass);
  border-radius: var(--kc-radius-nanavio);
}

/* The two frotas stand side by side as two columns of banners, yours on the
   left and the enemy's on the right, each with its vanguard on top like the
   board: the shots cross the arena from one column to the other, and the one
   hit first is the one at the top. Each column stands at its own edge of the
   arena, with a wide gap kept between them even on a phone: pressed together
   in the middle, the owner read the two frotas as one. */
.duel-lines {
  position: relative;
  display: grid;
  min-height: 0;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.5rem, 8vw, 6rem);
}

/* The sea a replay is fought on, painted behind the two frotas (1.117): the
   arena carries data-sea and this layer, the first child of the lines, wears
   it. It sits under every banner (paper, opaque) and under the shots, which
   the arena paints after the lines, in the arena's own stacking context, and
   it stays inside the lines' box, away
   from the narration and the buttons, so no word changes its ground. Only
   transform and opacity move, over a still picture: under reduced motion the
   global block stops the motion and leaves the picture. */
.arena-weather {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  border-radius: var(--kc-radius);
  /* The weather fades out at the foot of the lines rather than stopping on a
     hard edge above the narration. */
  mask-image: linear-gradient(180deg, var(--kc-navy-deep) 78%, transparent);
}

.arena-weather::before,
.arena-weather::after {
  content: "";
  position: absolute;
  inset: 0;
}

.duel-arena:not([data-sea]) .arena-weather {
  display: none;
}

/* Calmaria: a still sea, light along the bottom, and one slow gleam. */
.duel-arena[data-sea="calmaria"] .arena-weather {
  background: linear-gradient(180deg, transparent 55%, var(--kc-sea-glow));
  opacity: 0.45;
}

.duel-arena[data-sea="calmaria"] .arena-weather::before {
  inset: 0 auto 0 0;
  width: 30%;
  background: linear-gradient(90deg, transparent, var(--kc-paper-glow), transparent);
  opacity: 0.35;
  animation: sea-gleam 14s linear infinite;
}

/* Neblina: two banks of fog drifting across at different speeds. */
.duel-arena[data-sea="neblina"] .arena-weather::before,
.duel-arena[data-sea="neblina"] .arena-weather::after {
  inset: 0 auto 0 0;
  width: 200%;
  background:
    radial-gradient(ellipse 18% 38% at 12% 30%, var(--kc-paper-glow), transparent),
    radial-gradient(ellipse 22% 30% at 38% 62%, var(--kc-paper-glow), transparent),
    radial-gradient(ellipse 18% 38% at 62% 30%, var(--kc-paper-glow), transparent),
    radial-gradient(ellipse 22% 30% at 88% 62%, var(--kc-paper-glow), transparent);
  opacity: 0.4;
  animation: sea-drift 40s linear infinite;
}

.duel-arena[data-sea="neblina"] .arena-weather::after {
  opacity: 0.28;
  transform: translateX(-25%);
  animation-duration: 26s;
  animation-direction: reverse;
}

/* Mar agitado: two rows of crests along the bottom, rising and falling out of
   step. */
.duel-arena[data-sea="mar-agitado"] .arena-weather::before,
.duel-arena[data-sea="mar-agitado"] .arena-weather::after {
  inset: auto 0 14%;
  height: 40%;
  background: radial-gradient(circle at 50% 100%, transparent 0.6rem, var(--kc-sea-glow) 0.65rem 0.8rem, transparent 0.85rem) 0 100% / 2rem 1rem repeat-x,
    linear-gradient(180deg, transparent, var(--kc-sea-glow) 75%, transparent);
  opacity: 0.5;
  animation: sea-swell 1.8s ease-in-out infinite alternate;
}

.duel-arena[data-sea="mar-agitado"] .arena-weather::after {
  bottom: 34%;
  background: radial-gradient(circle at 50% 100%, transparent 0.6rem, var(--kc-sea-glow) 0.65rem 0.8rem, transparent 0.85rem) 1rem 100% / 2rem 1rem repeat-x;
  opacity: 0.35;
  animation-duration: 2.3s;
}

/* Céu limpo: the sun glowing over the middle of the arena, and its rays. */
.duel-arena[data-sea="ceu-limpo"] .arena-weather::before {
  background: radial-gradient(circle at 50% 0%, var(--kc-brass-glow), transparent 55%);
  opacity: 0.7;
  animation: sea-glow 6s ease-in-out infinite alternate;
}

.duel-arena[data-sea="ceu-limpo"] .arena-weather::after {
  inset: -60% -10% auto;
  height: 160%;
  /* The layer reaches 60% of its height above the lines, so the sun at the
     top of the lines is 37.5% down it: the rays fan out of that point, the
     mask fades them around it and they sway about it. */
  background: repeating-conic-gradient(from 90deg at 50% 37.5%, var(--kc-brass-glow) 0deg 1.5deg, transparent 1.5deg 12deg);
  opacity: 0.16;
  mask-image: radial-gradient(circle at 50% 37.5%, var(--kc-navy-deep), transparent 45%);
  transform-origin: 50% 37.5%;
  animation: sea-rays 18s ease-in-out infinite alternate;
}

/* Ventania: thin streaks of wind running across, a slow layer and a fast one.
   Each layer is a tile of streaks 12rem wide, one tile wider than the lines,
   and runs exactly one tile, so the loop meets itself without a jump. */
.duel-arena[data-sea="ventania"] .arena-weather::before,
.duel-arena[data-sea="ventania"] .arena-weather::after {
  inset: 0 -12rem 0 0;
  background:
    radial-gradient(ellipse 3rem 0.08rem at 2rem 0.9rem, var(--kc-sea-glow), transparent),
    radial-gradient(ellipse 4rem 0.08rem at 8rem 2.6rem, var(--kc-sea-glow), transparent),
    radial-gradient(ellipse 2.6rem 0.07rem at 5rem 4.3rem, var(--kc-sea-glow), transparent);
  background-size: 12rem 5.2rem;
  opacity: 0.55;
  animation: sea-gust 2.6s linear infinite;
}

.duel-arena[data-sea="ventania"] .arena-weather::after {
  background:
    radial-gradient(ellipse 3.4rem 0.06rem at 9rem 1.6rem, var(--kc-paper-glow), transparent),
    radial-gradient(ellipse 2.2rem 0.06rem at 3rem 3.4rem, var(--kc-paper-glow), transparent);
  background-size: 12rem 4.4rem;
  opacity: 0.35;
  animation-duration: 1.7s;
}

/* Noite: the field goes dark, the stars come out and a brass crescent hangs
   over the middle. */
.duel-arena[data-sea="noite"] .arena-weather {
  background: radial-gradient(circle at 50% 1.8rem, var(--kc-brass-glow), transparent 3.2rem), var(--kc-night);
}

.duel-arena[data-sea="noite"] .arena-weather::before {
  background:
    radial-gradient(0.22rem 0.22rem at 6% 14%, var(--kc-paper) 50%, transparent 60%),
    radial-gradient(0.16rem 0.16rem at 14% 58%, var(--kc-paper) 50%, transparent 60%),
    radial-gradient(0.2rem 0.2rem at 21% 30%, var(--kc-paper) 50%, transparent 60%),
    radial-gradient(0.16rem 0.16rem at 29% 82%, var(--kc-paper) 50%, transparent 60%),
    radial-gradient(0.24rem 0.24rem at 35% 8%, var(--kc-paper) 50%, transparent 60%),
    radial-gradient(0.16rem 0.16rem at 41% 48%, var(--kc-paper) 50%, transparent 60%),
    radial-gradient(0.2rem 0.2rem at 47% 70%, var(--kc-paper) 50%, transparent 60%),
    radial-gradient(0.18rem 0.18rem at 53% 22%, var(--kc-paper) 50%, transparent 60%),
    radial-gradient(0.16rem 0.16rem at 59% 88%, var(--kc-paper) 50%, transparent 60%),
    radial-gradient(0.22rem 0.22rem at 64% 40%, var(--kc-paper) 50%, transparent 60%),
    radial-gradient(0.16rem 0.16rem at 70% 12%, var(--kc-paper) 50%, transparent 60%),
    radial-gradient(0.2rem 0.2rem at 76% 64%, var(--kc-paper) 50%, transparent 60%),
    radial-gradient(0.18rem 0.18rem at 83% 32%, var(--kc-paper) 50%, transparent 60%),
    radial-gradient(0.16rem 0.16rem at 88% 78%, var(--kc-paper) 50%, transparent 60%),
    radial-gradient(0.22rem 0.22rem at 93% 10%, var(--kc-paper) 50%, transparent 60%),
    radial-gradient(0.16rem 0.16rem at 97% 50%, var(--kc-paper) 50%, transparent 60%);
  opacity: 0.85;
  animation: sea-twinkle 3.5s ease-in-out infinite alternate;
}

/* The crescent is the brass disk with a disk cut out of it by its mask, so the
   glow around it, on the layer underneath, stays whole. */
.duel-arena[data-sea="noite"] .arena-weather::after {
  background: radial-gradient(circle at 50% 1.8rem, var(--kc-brass-light) 0 1.1rem, transparent 1.15rem);
  mask-image: radial-gradient(circle at calc(50% + 0.55rem) calc(1.8rem - 0.35rem), transparent 0 1rem, var(--kc-navy-deep) 1.05rem);
}

@keyframes sea-gleam {
  from {
    transform: translateX(-100%);
  }

  to {
    transform: translateX(340%);
  }
}

@keyframes sea-drift {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(-50%);
  }
}

@keyframes sea-swell {
  from {
    transform: translateY(0);
  }

  to {
    transform: translateY(-0.45rem);
  }
}

@keyframes sea-glow {
  from {
    opacity: 0.5;
  }

  to {
    opacity: 0.85;
  }
}

@keyframes sea-rays {
  from {
    transform: rotate(-4deg);
  }

  to {
    transform: rotate(4deg);
  }
}

@keyframes sea-gust {
  from {
    transform: translateX(-12rem);
  }

  to {
    transform: translateX(0);
  }
}

@keyframes sea-twinkle {
  from {
    opacity: 0.55;
  }

  to {
    opacity: 0.95;
  }
}

/* Five places whatever the frota brings, so the two columns line up place by
   place and a lone ship stands at the top. The column is sized by the arena
   rather than by its banners: each place takes a fifth of the height there is,
   up to a comfortable banner, and the banner reads the column's own height to
   decide how much of itself to show (the container queries below). */
.duel-fleet {
  --fleet-gap: clamp(0.15rem, 0.8dvh, 0.3rem);
  --unit-height: min(4.6rem, (100cqh - 4 * var(--fleet-gap)) / 5);
  container: fleet / size;
  display: grid;
  min-width: 0;
  min-height: 0;
  margin: 0;
  padding: 0;
  width: min(100%, 22rem);
  grid-template-rows: repeat(5, minmax(0, 4.6rem));
  gap: var(--fleet-gap);
  align-content: start;
  list-style: none;
}

.duel-fleet-me {
  justify-self: start;
}

.duel-fleet-them {
  justify-self: end;
}

/* A unit is a banner on a paper plate, with her health bar and her reload bar
   under her name, beside her face. The banner itself lends its two parts to
   the unit's grid, so the bars line up under the text. */
.duel-unit {
  position: relative;
  display: grid;
  min-width: 0;
  min-height: 0;
  padding: 0.15rem 0.35rem 0.15rem 0.15rem;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto auto auto;
  column-gap: 0.4rem;
  row-gap: 0.1rem;
  align-content: center;
  background: var(--kc-paper);
  border: 0.08rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius);
}

.duel-unit > .ship-banner {
  display: contents;
}

/* The face is as tall as the place leaves it, inside the plate's padding and
   border (0.3rem and 0.16rem). */
.duel-unit .ship-banner-art {
  width: calc(var(--unit-height) - 0.46rem);
  grid-column: 1;
  grid-row: 1 / 4;
  align-self: center;
}

.duel-unit .ship-banner-text {
  grid-column: 2;
  grid-row: 1;
}

.duel-unit > .duel-health {
  grid-column: 2;
  grid-row: 2;
}

.duel-unit > .duel-reload {
  grid-column: 2;
  grid-row: 3;
}

.duel-unit .ship-banner-art,
.duel-unit .ship-banner-text,
.duel-unit > .duel-health {
  transition: opacity 300ms ease, filter 300ms ease;
}

/* A column under about 24.5rem cannot give each banner room for a class line, so
   she shows her name and her bars; the class and level are on the board she
   was placed from. */
@container fleet (max-height: 24.5rem) {
  .duel-unit .ship-banner-meta {
    display: none;
  }
}

/* A fifth of a column this short holds her name and a thin bar, not the
   number over it: the bar says how much is left, and the narration says every
   shot. */
@container fleet (max-height: 17rem) {
  .duel-unit {
    padding-block: 0.1rem;
    row-gap: 0.05rem;
  }

  .duel-unit > .duel-health {
    height: 0.55rem;
  }

  .duel-unit .duel-health-label {
    display: none;
  }
}

/* A landscape phone leaves the column about 7rem, a fifth of which holds one
   line of text: the banner lies flat on one row, her name beside her health
   bar, with the reload bar under the bar. */
@container fleet (max-height: 13.5rem) {
  .duel-unit {
    padding-block: 0.05rem;
    grid-template-columns: auto minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto auto;
    column-gap: 0.3rem;
    row-gap: 0.05rem;
  }

  .duel-unit .ship-banner-art {
    width: calc(var(--unit-height) - 0.26rem);
    grid-row: 1 / 3;
  }

  .duel-unit .ship-banner-text {
    grid-row: 1 / 3;
    align-self: center;
  }

  .duel-unit > .duel-health {
    grid-column: 3;
    grid-row: 1;
  }

  .duel-unit > .duel-reload {
    grid-column: 3;
    grid-row: 2;
    height: 0.2rem;
  }

  /* One row has no room for the number over the bar: the bar alone says how
     much is left, and the narration says every shot. */
  .duel-unit > .duel-health {
    height: 0.6rem;
  }

  .duel-unit .duel-health-label {
    display: none;
  }
}

/* Tall enough for its number at the 16px floor: at 0.7rem the number was cut
   through the middle. */
.duel-health {
  position: relative;
  display: block;
  height: 1.25rem;
  overflow: hidden;
  background: var(--kc-navy-deep);
  border: 0.08rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius-pill);
}

.duel-health-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--health, 100%);
  background: linear-gradient(90deg, var(--kc-brass-dark), var(--kc-brass-light));
  transition: width 300ms ease-out var(--impact-delay, 0ms);
}

.duel-health-label {
  position: relative;
  display: block;
  color: var(--kc-paper);
  font-size: var(--kc-type-3xs);
  font-weight: 900;
  line-height: calc(1.25rem - 0.16rem);
  text-align: center;
  text-shadow: 0 0 0.2rem var(--kc-navy-deep), 0 0 0.1rem var(--kc-navy-deep);
}

/* The muzzle flash is its own layer, faded in and out, rather than a filter on
   the unit: animating brightness() and drop-shadow() repaints the nanavio art and
   its alpha mask every frame, once per shot, all through a replay. */
.duel-unit::after {
  position: absolute;
  inset: 0;
  border-radius: var(--kc-radius);
  background: radial-gradient(circle at 50% 45%, var(--kc-brass-glow), transparent 70%);
  opacity: 0;
  pointer-events: none;
  content: "";
}

.duel-unit.is-firing::after {
  animation: duel-fire 320ms ease-out;
}

/* The shake, the dodge, the number and the health bar wait for the shot to
   arrive (--impact-delay, written by the script per shot), since a trail or a
   plane crossing the arena takes a moment now. */
.duel-unit.is-hit {
  animation: duel-hit 320ms ease-out var(--impact-delay, 0ms);
}

/* The number a shot took off, rising out of the card it hit. The narration
   says the same thing, so it is aria-hidden decoration; the ring mark sizes and
   colours it, and it moves on transform and opacity alone. */
/* The planes of one flight stack their numbers up the card (--stack). */
.duel-damage {
  margin-top: calc(var(--stack, 0) * -1.6rem);
  position: absolute;
  top: 30%;
  left: 50%;
  z-index: 1;
  padding: 0.1rem 0.4rem;
  border: 0.1rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
  background: var(--kc-navy-deep);
  color: var(--kc-paper);
  font-family: var(--kc-font-mincho);
  font-size: clamp(1.05rem, 2.4vmin, 1.3rem);
  font-weight: 800;
  line-height: 1.1;
  white-space: nowrap;
  box-shadow: 0 0 0.5rem var(--kc-navy-deep);
  translate: -50% 0;
  pointer-events: none;
  animation: duel-damage 600ms ease-out var(--impact-delay, 0ms) both;
}

.duel-damage[data-mark="super"] {
  border-color: var(--kc-brass-light);
  background: var(--kc-signal);
  font-size: clamp(1.1rem, 2.9vmin, 1.55rem);
}

.duel-damage[data-mark="pouco"],
.duel-damage[data-mark="quase"] {
  border-color: var(--kc-sea);
  background: var(--kc-steel);
  font-size: 1rem;
}

.duel-damage[data-kind="miss"] {
  border-color: var(--kc-sea);
  color: var(--kc-sea-soft);
  font-size: 1rem;
  font-style: italic;
}

.duel-damage[data-kind="repair"] {
  top: 62%;
  border-color: var(--kc-brass-light);
  color: var(--kc-brass-light);
  font-size: 1rem;
}

@keyframes duel-damage {
  0% { opacity: 0; transform: translateY(0.3rem) scale(0.7); }
  15% { opacity: 1; transform: translateY(0) scale(1.15); }
  80% { opacity: 1; transform: translateY(-0.8rem) scale(1); }
  100% { opacity: 0; transform: translateY(-1.1rem) scale(1); }
}

/* A shot's trail from the card that fired to the card it hit, one line per
   arena laid from the two cards' centres by the script. */
.duel-tracer {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--tracer-length, 0);
  height: 0.25rem;
  border-radius: var(--kc-radius-pill);
  background: linear-gradient(90deg, transparent, var(--kc-brass-light) 60%, var(--kc-paper));
  box-shadow: 0 0 0.4rem var(--kc-brass-glow);
  opacity: 0;
  transform: translate(var(--tracer-x, 0), var(--tracer-y, 0)) rotate(var(--tracer-angle, 0)) scaleX(0);
  transform-origin: 0 50%;
  pointer-events: none;
}

.duel-tracer.is-flying {
  animation: duel-tracer var(--tracer-ms, 800ms) ease-out;
}

/* The reload bar under a card: empty the moment she fires, full again when her
   weapons are ready, over the reload the event carries. It is drawn by
   transform alone, like the tracer, and the narration never needs it. */
.duel-reload {
  display: block;
  height: 0.3rem;
  overflow: hidden;
  background: var(--kc-navy-deep);
  border-radius: var(--kc-radius-pill);
}

.duel-reload-fill {
  display: block;
  height: 100%;
  background: var(--kc-brass-light);
  transform-origin: 0 50%;
}

.duel-reload.is-reloading .duel-reload-fill {
  animation: duel-reload var(--reload-ms, 1000ms) linear both;
}

/* One ship attacks at a time: while it is her turn (`is-turn` on the arena,
   set by the script for as long as her shot or her planes take to land) the
   battle's clock stops, so every reload bar holds where it is, and she keeps a
   brass edge until the clock runs again. Drawn inside her edge, since the
   columns clip anything outside it. The pause names both classes of the rule
   above: that rule's `animation` shorthand resets the play state to running,
   so a less specific selector loses the cascade and the bars never stop. */
.is-turn .duel-reload.is-reloading .duel-reload-fill {
  animation-play-state: paused;
}

.duel-unit.is-acting {
  border-color: var(--kc-brass-light);
  box-shadow: inset 0 0 0 0.12rem var(--kc-brass-light);
}

.duel-unit.is-sunk .duel-reload {
  visibility: hidden;
}

@keyframes duel-reload {
  from {
    transform: scaleX(0);
  }

  to {
    transform: scaleX(1);
  }
}

/* One of a carrier's planes, drawn in CSS: a fuselage along the flight, two
   wings across it and a tail, flown from her card to its target along the
   same vector a tracer takes. A flight is several of them at once, so each is
   its own node and removes itself when it lands. */
.duel-aircraft {
  position: absolute;
  top: 0;
  left: 0;
  width: 1.8rem;
  height: 1.8rem;
  margin: -0.9rem 0 0 -0.9rem;
  /* Brass on the arena's navy: a plane in the theme's own navy was the one
     colour it could not be seen in. */
  background:
    linear-gradient(var(--kc-brass-light), var(--kc-brass-light)) 50% 50% / 100% 22% no-repeat,
    linear-gradient(var(--kc-brass-light), var(--kc-brass-light)) 58% 50% / 20% 100% no-repeat,
    linear-gradient(var(--kc-brass-light), var(--kc-brass-light)) 6% 50% / 12% 50% no-repeat;
  opacity: 0;
  transform: translate(var(--tracer-x, 0), var(--tracer-y, 0)) rotate(var(--tracer-angle, 0));
  pointer-events: none;
  animation: duel-aircraft var(--aircraft-ms, 1600ms) ease-in-out both;
}

.duel-aircraft[data-weapon="torpedeiro"] {
  background:
    linear-gradient(var(--kc-signal), var(--kc-signal)) 50% 50% / 100% 22% no-repeat,
    linear-gradient(var(--kc-brass-light), var(--kc-brass-light)) 58% 50% / 20% 100% no-repeat,
    linear-gradient(var(--kc-brass-light), var(--kc-brass-light)) 6% 50% / 12% 50% no-repeat;
}

@keyframes duel-aircraft {
  0% {
    opacity: 1;
    transform: translate(var(--tracer-x, 0), var(--tracer-y, 0)) rotate(var(--tracer-angle, 0)) translateX(0);
  }

  85% {
    opacity: 1;
  }

  100% {
    opacity: 0;
    transform: translate(var(--tracer-x, 0), var(--tracer-y, 0)) rotate(var(--tracer-angle, 0)) translateX(var(--tracer-length, 0));
  }
}

.duel-tracer[data-missed] {
  background: linear-gradient(90deg, transparent, var(--kc-sea) 70%, transparent);
}

@keyframes duel-tracer {
  0% {
    opacity: 1;
    transform: translate(var(--tracer-x, 0), var(--tracer-y, 0)) rotate(var(--tracer-angle, 0)) scaleX(0);
  }
  60% {
    opacity: 1;
    transform: translate(var(--tracer-x, 0), var(--tracer-y, 0)) rotate(var(--tracer-angle, 0)) scaleX(1);
  }
  100% {
    opacity: 0;
    transform: translate(var(--tracer-x, 0), var(--tracer-y, 0)) rotate(var(--tracer-angle, 0)) scaleX(1);
  }
}

/* A won solo battle marks every card that went up a level. The narration says
   the same thing aloud, so the seal is decoration and stays out of the way of
   the health bar under the card. */
/* In the banner's top-right corner, clear of her name. */
.battle-level-up {
  position: absolute;
  top: 0.15rem;
  right: 0.3rem;
  padding: 0.05rem 0.4rem;
  border: 0.1rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius);
  background: var(--kc-brass-light);
  color: var(--kc-navy-deep);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-3xs);
  font-weight: 800;
  line-height: 1.2;
  white-space: nowrap;
  box-shadow: 0 0 0.6rem var(--kc-brass-glow);
  pointer-events: none;
  animation: battle-level-up 700ms ease-out both;
}

/* A narrow column leaves her name a few letters: there the seal goes over her
   face instead, at its foot, where it hides nothing the narration does not say. */
@container fleet (max-width: 16rem) {
  .battle-level-up {
    top: auto;
    right: auto;
    bottom: 0.15rem;
    left: 0.15rem;
  }
}

@keyframes battle-level-up {
  0% { opacity: 0; scale: 0.6; }
  60% { opacity: 1; scale: 1.15; }
  100% { opacity: 1; scale: 1; }
}

/* A shot the evasion dropped: the unit slips aside instead of being shaken, so
   a miss reads as a miss without the narration having to be read. */
.duel-unit.is-dodged {
  animation: duel-dodge 320ms ease-out var(--impact-delay, 0ms);
}

/* A sunk ship greys out her banner and bar, not the whole unit: the number of
   the shot that sank her rises out of it and has to stay readable. The banner
   lends its parts to the unit's grid, so they are greyed one by one. */
.duel-unit.is-sunk .ship-banner-art,
.duel-unit.is-sunk .ship-banner-text,
.duel-unit.is-sunk > .duel-health {
  opacity: 0.35;
  filter: grayscale(1);
}

/* The line sits on its own navy plate above the shot tracer, so a trail that
   crosses the middle of the arena passes behind the words rather than through
   them. The plate hugs the text and goes away while there is none. */
.duel-narration {
  position: relative;
  z-index: 1;
  justify-self: center;
  max-width: 100%;
  min-height: 1.4em;
  padding: 0.15rem 0.6rem;
  border-radius: var(--kc-radius);
  background: var(--kc-navy-deep);
  color: var(--kc-paper);
  font-family: var(--kc-font-mincho);
  font-size: clamp(1rem, 2.2vmin, 1.1rem);
  text-align: center;
}

.duel-narration:empty {
  background: transparent;
}

/* On a phone the narration keeps to two lines and scrolls inside itself, and
   the sea's label goes, the narration having said it: the outcome of a battle
   runs five lines at the 16px floor, and taken whole it pushed both frotas out
   of an arena that had room for one banner each. */
@media (max-width: 34rem), (max-height: 34rem) {
  .duel-narration {
    max-height: calc(2 * 1.3em + 0.3rem);
    overflow-y: auto;
    overscroll-behavior: contain;
    line-height: 1.3;
  }

  .duel-arena .arena-sea {
    display: none;
  }
}

/* A landscape phone has width to spare and no height: the two columns take
   the arena's whole height on the left, and the narration and its button
   stand beside them, where under them they left the frotas a sliver. */
@media (max-height: 34rem) and (min-width: 34rem) {
  .duel-arena {
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    grid-template-rows: minmax(0, 1fr) auto;
  }

  .duel-arena > .duel-lines {
    grid-column: 1;
    grid-row: 1 / 3;
  }

  .duel-arena > .duel-narration {
    grid-column: 2;
    grid-row: 1;
    align-self: center;
    max-height: 100%;
  }

  .duel-arena > .action-row {
    grid-column: 2;
    grid-row: 2;
  }
}

/* Small screens: drop the big avatars and tighten the preparation so the
   nanavio picker and the lock button stay in view. */
@media (max-width: 34rem), (max-height: 34rem) {
  .duel-header .duel-avatars {
    display: none;
  }

  .duel-header .collection-subtitle {
    font-size: var(--kc-type-3xs);
  }

  .duel-slot-button {
    --banner-art: 1.9rem;
    padding-block: 0.1rem;
  }

  .duel-chip {
    padding: 0.1rem 0.45rem;
    font-size: var(--kc-type-3xs);
  }

  .duel-prep {
    gap: 0.2rem;
  }
}

/* Where the panel is shortest or narrowest the reverse rule and the key of the
   codes cost lines the line and the picker need, so only the drawing and the
   two arrows stay; each class is spelled out on the banners and in the
   tooltip either way. */
@media (max-height: 34rem), (max-width: 34rem) {
  .type-ring-note,
  .type-ring-codes {
    display: none;
  }

  /* The two arrows one under the other, so what is left of the legend is
     narrow enough to stand beside the drawing, which is drawn at 8rem here:
     the codes come out at 22/176 of it, 16px, the text floor. */
  .type-ring-legend {
    flex: 0 1 auto;
    flex-direction: column;
    align-items: flex-start;
  }

  .type-ring {
    padding: 0.25rem;
    gap: 0.3rem;
  }

  .type-ring-swatch {
    width: 1rem;
  }

  .type-ring-art,
  .duel-prep > .type-ring .type-ring-art {
    width: 8rem;
  }
}


/* The four difficulty chips wrapped one per row on a phone and took four rows of
   the preparation before the board; two by two they take two, with the legend
   across the top. Width only: a short landscape phone has the width for one row. */
@media (max-width: 34rem) {
  #battle-difficulties {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  #battle-difficulties .duel-chips-legend,
  #battle-difficulties .duel-chips-power {
    grid-column: 1 / -1;
    justify-self: center;
  }

  /* Two by two a chip runs to four or five lines with its seal, where a pill's
     ends turn it into an oval. */
  #battle-difficulties .duel-chip {
    padding-inline: 0.3rem;
    text-align: center;
    border-radius: var(--kc-radius);
  }
}

/* On a phone the sea's picture stands beside its name alone and the effects
   and the next hours take the card's whole width under them, where beside the
   picture the chips stood one a line. */
@media (max-width: 34rem) {
  .sea-card-mark {
    grid-row: 1;
    font-size: 1.7rem;
  }

  .sea-card-effects,
  .sea-card-next {
    grid-column: 1 / -1;
  }
}

/* A short landscape phone has the width and not the height: the seal stands
   beside the pay rather than under it, so a seal costs the row no line. */
@media (max-height: 34rem) {
  .duel-chip:has(.duel-chip-badge) {
    flex-flow: row wrap;
    column-gap: 0.4rem;
  }
}

.duel-arena .collection-link-button {
  color: var(--kc-paper);
  background: transparent;
  border-color: var(--kc-brass);
}

@keyframes duel-fire {
  0%, 100% { opacity: 0; }
  40% { opacity: 1; }
}

@keyframes duel-hit {
  0%, 100% { translate: 0 0; }
  25% { translate: -0.25rem 0; }
  50% { translate: 0.25rem 0; }
  75% { translate: -0.15rem 0; }
}

@keyframes duel-dodge {
  0%, 100% { translate: 0 0; opacity: 1; }
  40% { translate: 0 -0.3rem; opacity: 0.55; }
}

/* Lobby chat: everyone with the collection open, with their nanavio picture. */
.collection-chat {
  display: grid;
  min-height: 0;
  /* The four rows are placed explicitly: the banned-word editor is hidden most
     of the time, and auto-placement would hand the log's 1fr to another row. */
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  /* One explicit column: the emoji grid shares the log's cell, and the log,
     placed by row alone, would otherwise be pushed into a second column made
     for it beside the grid. */
  grid-template-columns: minmax(0, 1fr);
  gap: 0.4rem;
}

.collection-chat.is-editing-words {
  /* While the editor is open it shares the flexible space with the log and
     scrolls inside its own row, so a short landscape screen never pushes the
     save and send buttons out of the dialog. */
  grid-template-rows: auto minmax(0, 1fr) minmax(0, 1fr) auto;
}

.collection-chat .collection-subtitle {
  margin: 0;
  font-family: var(--kc-font-mincho);
}

.lobby-chat-header {
  grid-row: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  align-items: center;
  justify-content: space-between;
}

.lobby-banned {
  grid-row: 2;
  display: grid;
  align-content: start;
  min-height: 0;
  gap: 0.35rem;
  padding: 0.5rem;
  /* The editor scrolls inside its own row when the dialog is short, and the
     buttons stick to its bottom edge, so saving is always one tap away. */
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--kc-sea-soft);
  border: 0.12rem solid var(--kc-navy-deep);
  border-radius: var(--kc-radius);
}

.lobby-banned-input {
  min-height: var(--kc-control-min);
  max-height: 9rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  resize: vertical;
  font-family: var(--kc-font-ui);
  line-height: 1.4;
}

.lobby-banned-hint {
  margin: 0;
  color: var(--kc-steel);
  font-size: var(--kc-type-2xs);
}

.lobby-banned-actions {
  position: sticky;
  bottom: -0.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  padding: 0.35rem 0 0.5rem;
  margin-bottom: -0.5rem;
  background: var(--kc-sea-soft);
}

.lobby-chat-log {
  grid-row: 3;
  grid-column: 1;
  display: grid;
  min-height: 0;
  margin: 0;
  padding: 0.3rem;
  gap: 0.35rem;
  align-content: start;
  overflow-y: auto;
  overscroll-behavior: contain;
  list-style: none;
  background: var(--kc-sea-soft);
  border: 0.12rem solid var(--kc-navy-deep);
  border-radius: var(--kc-radius);
}

.lobby-chat-message {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "avatar author time"
    "avatar text text";
  column-gap: 0.45rem;
  align-items: center;
  padding: 0.3rem 0.5rem;
  color: var(--kc-navy-deep);
  font-size: var(--kc-type-s);
  background: var(--kc-paper);
  border: 0.08rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
}

.lobby-chat-message.is-mine {
  background: var(--kc-paper-dark);
}

/* Steel reads at 3.8:1 on the darker paper of the visitor's own message. */
.lobby-chat-message.is-mine .lobby-chat-time {
  color: var(--kc-navy);
}

.lobby-chat-message .collection-avatar {
  grid-area: avatar;
  align-self: start;
}

.lobby-chat-author {
  grid-area: author;
  min-width: 0;
  overflow-wrap: anywhere;
}

.lobby-chat-time {
  grid-area: time;
  color: var(--kc-steel);
  /* The type scale's floor, not a share of the message's own size: 0.85em of
     the smallest message landed under 0.7rem. */
  font-size: var(--kc-type-3xs);
  font-variant-numeric: tabular-nums;
}

.lobby-chat-text {
  grid-area: text;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* `/nana`: Nana's clip in place of the text, never wider than the message, at
   the proportion its width and height attributes give it. The message's words
   stay for assistive technology in a visually hidden span, held inside the
   message here rather than placed against whatever positioned box is further
   out. */
.lobby-chat-text.is-sticker {
  position: relative;
  line-height: 0;
}

.lobby-chat-sticker {
  display: block;
  width: min(100%, 14rem);
  height: auto;
  margin-top: 0.2rem;
  background: var(--kc-navy-deep);
  border: 0.08rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
}

/* The emoji grid stands in the log's own cell, over its end: placed there it
   can never be taller than the log, which is all the room a short landscape
   screen has, and it scrolls inside itself when that is not enough. */
.lobby-chat-emoji {
  grid-row: 3;
  grid-column: 1;
  align-self: end;
  justify-self: end;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--kc-control-min), 1fr));
  gap: 0.2rem;
  width: min(calc(100% - 0.6rem), 24rem);
  max-height: calc(100% - 0.6rem);
  min-height: 0;
  margin: 0.3rem;
  padding: 0.35rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--kc-paper);
  border: 0.12rem solid var(--kc-brass-dark);
  border-radius: var(--kc-radius);
  box-shadow: 0.16rem 0.2rem 0 var(--kc-navy-deep);
}

.lobby-chat-emoji-pick {
  min-width: var(--kc-control-min);
  min-height: var(--kc-control-min);
  padding: 0;
  font-family: var(--kc-font-ui);
  font-size: 1.4rem;
  line-height: 1;
  background: transparent;
  border: 0.08rem solid transparent;
  border-radius: var(--kc-radius);
  cursor: pointer;
}

.lobby-chat-emoji-pick:hover {
  background: var(--kc-paper-dark);
  border-color: var(--kc-brass);
}

.lobby-chat-emoji-pick:focus-visible {
  outline: 0.18rem solid var(--kc-signal);
  outline-offset: -0.18rem;
}

.lobby-chat-delete {
  grid-column: 1 / -1;
  justify-self: end;
  font-size: var(--kc-type-2xs);
}

.lobby-chat-form {
  grid-row: 4;
}

/* Nanavio tooltip: a small naval note above the nanavio under the pointer or focus. */
.nanavio-tooltip {
  position: fixed;
  top: var(--tooltip-y, 0);
  left: var(--tooltip-x, 0);
  z-index: var(--kc-z-tooltip);
  display: grid;
  max-width: min(16rem, calc(100vw - 1rem));
  padding: 0.35rem 0.6rem;
  gap: 0.05rem;
  color: var(--kc-paper);
  font-family: var(--kc-font-ui);
  font-size: var(--kc-type-s);
  line-height: 1.25;
  text-align: center;
  pointer-events: none;
  background: var(--kc-navy-deep);
  border: 0.1rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
  box-shadow: 0 0.3rem 0.9rem var(--kc-shadow);
  transform: translate(-50%, -100%);
  animation: nanavio-tooltip-in 120ms ease-out;
}

.nanavio-tooltip[data-placement="below"] {
  transform: translate(-50%, 0);
}

.nanavio-tooltip::after {
  position: absolute;
  top: 100%;
  left: 50%;
  width: 0;
  height: 0;
  content: "";
  border: 0.35rem solid transparent;
  border-top-color: var(--kc-brass);
  transform: translateX(-50%);
}

.nanavio-tooltip[data-placement="below"]::after {
  top: auto;
  bottom: 100%;
  border-top-color: transparent;
  border-bottom-color: var(--kc-brass);
}

.nanavio-tooltip-name {
  font-family: var(--kc-font-mincho);
  font-size: 1.05em;
  overflow-wrap: anywhere;
}

/* The class under the name, and nothing else on the note outside the
   preparation of a battle. */
.nanavio-tooltip-type {
  color: var(--kc-paper-dark);
  font-size: var(--kc-type-3xs);
}

/* On the preparation the note is wider and carries both rungs and the six
   numbers she fights with now, two to a row. */
.nanavio-tooltip[data-detail="true"] {
  max-width: min(20rem, calc(100vw - 1rem));
}

.nanavio-tooltip-detail {
  display: grid;
  margin-top: 0.2rem;
  padding-top: 0.25rem;
  gap: 0.2rem;
  font-size: var(--kc-type-3xs);
  text-align: start;
  border-top: 0.08rem solid var(--kc-brass-dark);
}

.nanavio-tooltip-rungs {
  color: var(--kc-brass-light);
  font-weight: 800;
  text-align: center;
}

.nanavio-tooltip-stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.1rem 0.6rem;
}

.nanavio-tooltip-stat {
  display: flex;
  min-width: 0;
  gap: 0.3rem;
  justify-content: space-between;
}

.nanavio-tooltip-stat > span {
  color: var(--kc-paper-dark);
}

/* Speed carries its reload beside it, so it takes the whole last row. */
.nanavio-tooltip-stat[data-stat="speed"] {
  grid-column: 1 / -1;
  order: 1;
}

@keyframes nanavio-tooltip-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* The steel mark before the header's balance, the gift, and the purchase
   price: an emoji rather than a picture, decorative for assistive technology
   where the alt-text form is understood (the first declaration is the fallback
   for a browser that is not). */
.collection-header-steel-amount::before,
.collection-welcome::before,
#buy-nanacotes-submit::before {
  margin-right: 0.35em;
  font-family: var(--kc-font-ui);
  font-weight: 400;
  content: "🔩";
  content: "🔩" / "";
}

/* No way out of an opening: the close button returns with CONTINUAR. */
.collection-dialog.is-opening-nanacote .dialog-close-form {
  display: none;
}

/* Nor of PIRACA I.A's first tour, required since 1.104: the close button
   returns with his last line. A replay from the manual keeps it. */
.collection-dialog.is-tutorial-required .dialog-close-form {
  display: none;
}

/* These three rules were pasted twice, and the second copy quietly won on
   letter-spacing and dropped the inset highlight. Collapsed to the values that
   were actually rendering. */
.collection-screen .certificate-action {
  min-height: var(--kc-control-min);
  color: var(--kc-paper);
  font-family: var(--kc-font-mincho);
  font-size: var(--kc-type-xs);
  font-weight: 800;
  letter-spacing: 0.06em;
  background: linear-gradient(180deg, var(--kc-steel), var(--kc-navy) 55%, var(--kc-navy-deep));
  border: 0.14rem solid var(--kc-brass);
  border-radius: var(--kc-radius);
  box-shadow: 0.16rem 0.2rem 0 var(--kc-navy-deep);
}

.collection-screen .certificate-action:focus-visible {
  outline-color: var(--kc-signal);
}

/* The label grows with the quantity and the language, and at 320 wide it does
   not fit one line (`COMPRAR POR 1200 DE AÇO`, longer in French): held on one,
   it ran past the panel, where `overflow: hidden` cut both ends off. A phone
   lets it wrap inside the panel's width, in even lines; from 34rem it stays on
   one line. */
.collection-buy .collection-action {
  max-width: 100%;
  padding-inline: 0.7rem;
  letter-spacing: 0.03em;
  text-wrap: balance;
}

@media (min-width: 34rem) {
  .collection-buy .collection-action {
    white-space: nowrap;
  }
}

.history-entry[data-category="steel"] {
  border-left-color: var(--kc-brass-dark);
}

/* The nanálbum's note, above the slots: a failed load with a way to try
   again, or an empty collection with the way to its first ship. */
.album-note {
  grid-row: 2;
  display: grid;
  gap: 0.35rem;
  justify-self: center;
  justify-items: center;
  max-width: min(100%, 32rem);
  margin-bottom: clamp(0.3rem, 1.2vmin, 0.6rem);
  padding: 0.4rem 0.7rem;
  color: var(--kc-navy-deep);
  font-size: var(--kc-type-2xs);
  font-weight: 700;
  line-height: 1.35;
  text-align: center;
  background: var(--kc-paper);
  border: 0.1rem solid var(--kc-brass);
  border-radius: var(--kc-radius-flat);
  box-shadow: 0.1rem 0.12rem 0 var(--kc-navy-deep);
}

.album-note p {
  margin: 0;
}

.album-progress[data-tone="error"] {
  color: var(--kc-signal);
}


/* Fullscreen paginated patch notes. */
.patch-notes-screen {
  position: relative;
  display: grid;
  width: 100%;
  height: 100%;
  min-height: 0;
  padding: max(0.65rem, env(safe-area-inset-top)) max(0.65rem, env(safe-area-inset-right)) max(0.65rem, env(safe-area-inset-bottom)) max(0.65rem, env(safe-area-inset-left));
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: clamp(0.45rem, 1.6dvh, 0.9rem);
  overflow: hidden;
  color: var(--navy-deep);
  background: linear-gradient(145deg, var(--shirt), var(--hair-pink-soft) 60%, var(--skirt-blue-soft));
}

.patch-notes-header {
  text-align: center;
}

.patch-notes-kicker,
.patch-notes-title {
  margin: 0;
}

.patch-notes-kicker {
  color: var(--hair-pink-dark);
  font-size: clamp(0.55rem, 1.5vmin, 0.75rem);
  font-weight: 900;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.patch-notes-title {
  margin-top: 0.12rem;
  font-size: clamp(1.7rem, 5vmin, 3.2rem);
  line-height: 1;
}

.release-list {
  display: grid;
  min-height: 0;
  grid-template-rows: repeat(3, minmax(0, 1fr));
  gap: clamp(0.35rem, 1.2dvh, 0.75rem);
  width: min(100%, 58rem);
  margin: 0 auto;
  padding: 0;
  list-style: none;
}

.release-entry {
  display: grid;
  min-height: 0;
  padding: clamp(0.45rem, 1.5vmin, 0.8rem);
  align-content: center;
  overflow: hidden;
  background: var(--white);
  border: 0.1rem solid var(--navy-deep);
  border-radius: 0.7rem;
  box-shadow: 0.12rem 0.15rem 0 var(--navy-deep);
}

.release-entry h3,
.release-entry p {
  margin: 0;
}

.release-entry h3 {
  color: var(--hair-pink-dark);
  font-size: clamp(0.68rem, 1.8vmin, 0.9rem);
}

.release-entry p {
  margin-top: 0.22rem;
  font-size: clamp(0.55rem, 1.5vmin, 0.76rem);
  font-weight: 700;
  line-height: 1.3;
}

.release-entry:first-child {
  background: var(--hair-pink-soft);
  border-color: var(--hair-pink-dark);
}

.release-pagination {
  display: grid;
  grid-template-columns: minmax(6rem, 10rem) auto minmax(6rem, 10rem);
  gap: clamp(0.5rem, 2vw, 1rem);
  align-items: center;
  justify-content: center;
}

.release-page-button {
  min-height: 2.5rem;
  padding: 0.45rem 0.75rem;
  color: var(--shirt);
  font-size: clamp(0.58rem, 1.6vmin, 0.75rem);
  background: var(--navy);
}

.release-page-button:disabled {
  opacity: 0.42;
  cursor: not-allowed;
}

.release-page-status {
  min-width: 3rem;
  font-size: clamp(0.62rem, 1.7vmin, 0.82rem);
  font-weight: 1000;
  text-align: center;
}

@keyframes audit-wobble {
  0%, 100% { transform: translateX(0) rotate(0); }
  25% { transform: translateX(-0.08em) rotate(-2deg); }
  75% { transform: translateX(0.08em) rotate(2deg); }
}

@keyframes audit-pulse {
  0%, 100% { box-shadow: 0.25rem 0.28rem 0 var(--navy-deep); }
  50% { box-shadow: 0.25rem 0.28rem 0 var(--navy-deep), 0 0 0 0.34rem var(--hair-pink); }
}

@keyframes audit-paper {
  from { opacity: 0; translate: 0 -0.65rem; scale: 0.7; }
  to { opacity: 1; translate: 0 0; scale: 1; }
}

@keyframes audit-rays {
  to { rotate: 1turn; }
}

@keyframes audit-spotlight {
  0% { scale: 1; }
  55% { scale: 1.13; }
  100% { scale: 1.04; }
}

@keyframes audit-final {
  0% { transform: rotate(-2deg) scale(1); }
  55% { transform: rotate(1deg) scale(1.16); }
  100% { transform: rotate(-2deg) scale(1.04); }
}

@keyframes float {
  0%, 100% { transform: rotate(-2deg) translateY(0); }
  50% { transform: rotate(-1deg) translateY(-0.25rem); }
}

@keyframes twinkle {
  0%, 100% { opacity: 0.45; scale: 0.85; }
  50% { opacity: 1; scale: 1.12; }
}

@keyframes jackpot-flash {
  0% { opacity: 0.95; }
  45% { opacity: 0.25; }
  100% { opacity: 0; }
}

@keyframes jackpot-rays {
  to { rotate: 1turn; }
}

@keyframes jackpot-confetti {
  from { translate: 0 -10dvh; rotate: 0; }
  to { translate: 8vw 118dvh; rotate: var(--confetti-turn); }
}

@keyframes card-loading-pulse {
  from { opacity: 0.62; filter: saturate(0.7); }
  to { opacity: 1; filter: saturate(1.15); }
}

@keyframes jackpot-card-reveal {
  0% { opacity: 0; transform: translateY(38%) rotate(-7deg) scale(0.28); }
  68% { opacity: 1; transform: translateY(-3%) rotate(1.5deg) scale(1.07); }
  100% { opacity: 1; transform: translateY(0) rotate(0) scale(1); }
}

@keyframes jackpot-card-repeat {
  0%, 100% { transform: translateY(0) scale(1); }
  48% { transform: translateY(-6%) rotate(1deg) scale(1.09); }
}

@keyframes achievement-card-float {
  from { transform: translateY(0) rotate(-0.7deg); }
  to { transform: translateY(-2.2%) rotate(0.7deg); }
}

@keyframes achievement-card-reveal {
  0% { opacity: 0; transform: translateY(55%) scale(0.35); }
  70% { opacity: 1; transform: translateY(-4%) scale(1.07); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes achievement-card-repeat {
  0%, 100% { transform: translateY(0) scale(1); filter: brightness(1); }
  45% { transform: translateY(-5%) scale(1.1); filter: brightness(1.28); }
}

@keyframes gojo-pose-in {
  from { opacity: 0.35; translate: -50% 0.35rem; }
  to { opacity: 1; translate: -50% 0; }
}

@media (max-width: 76rem) {
  .marin-gif-left { left: 0.3rem; }
  .marin-gif-right { right: 0.3rem; }
}

@media (max-width: 48rem) and (min-height: 34.01rem) {
  .achievement-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-template-rows: repeat(2, minmax(0, 1fr));
  }

  .achievement-slot:nth-child(4) { grid-column: 1 / 2; }
  .achievement-slot:nth-child(5) { grid-column: 3 / 4; }
}

@media (max-width: 34rem) {
  .page {
    padding: 0.35rem;
  }

  .card {
    width: 100%;
    padding: clamp(0.58rem, 1.8dvh, 0.85rem) 0.48rem;
    gap: clamp(0.32rem, 1.15dvh, 0.6rem);
    border-width: 0.18rem;
    box-shadow: 0.28rem 0.3rem 0 var(--navy-deep);
  }

  .sparkle {
    display: none;
  }

  .main-primary,
  .main-details {
    gap: clamp(0.24rem, 0.8dvh, 0.45rem);
  }

  h1 {
    max-width: 15ch;
    font-size: clamp(1.55rem, 8.5vw, 2.45rem);
  }

  .question-heading[data-name-size="long"] {
    max-width: 19ch;
    font-size: clamp(1.25rem, 6.8vw, 2rem);
  }

  .answer {
    font-size: clamp(3.5rem, 20vw, 5.6rem);
  }

  .rich-button,
  .achievements-button,
  .coffee-oracle-button,
  .certificate-button,
  .collection-button {
    min-height: 2.35rem;
    padding: 0.45rem 0.65rem;
  }

  .dramatic-no-status {
    font-size: clamp(0.52rem, 2.7vw, 0.68rem);
  }

  .coffee-oracle-panel {
    padding: 0.38rem 0.48rem;
  }

  .counter {
    gap: 0.18rem;
  }

  .counter-part {
    padding: 0.32rem 0.04rem;
    border-radius: 0.42rem;
  }

  .counter-value {
    font-size: clamp(0.92rem, 6vw, 1.35rem);
  }

  .counter-unit {
    font-size: clamp(0.4rem, 2.2vw, 0.54rem);
    letter-spacing: 0;
  }

  .marin-decorations {
    display: none;
  }

  .casino-marquee {
    top: max(0.45rem, env(safe-area-inset-top));
    right: 3.4rem;
    left: 4.6rem;
    min-height: 2.75rem;
    padding: 0.3rem 0.4rem;
  }

  .casino-title {
    font-size: clamp(1rem, 6vw, 1.45rem);
  }

  .casino-token-hud {
    top: max(0.45rem, env(safe-area-inset-top));
    left: 0.35rem;
    min-width: 3.9rem;
    padding: 0.35rem;
  }

  .casino-control-deck {
    right: 6.35rem;
    bottom: max(0.35rem, env(safe-area-inset-bottom));
    left: 0.35rem;
    gap: 0.22rem;
    padding: 0.32rem;
  }

  .casino-audio-controls {
    top: calc(max(0.45rem, env(safe-area-inset-top)) + 3.15rem);
    right: 0.35rem;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.24rem;
    width: min(9.5rem, calc(100vw - 0.7rem));
    padding: 0.26rem 0.35rem;
  }

  .casino-rules {
    right: 0.35rem;
    bottom: max(0.35rem, env(safe-area-inset-bottom));
    width: 5.65rem;
    padding: 0.35rem;
    gap: 0.2rem;
    font-size: clamp(0.42rem, 2.3vw, 0.56rem);
  }

  .casino-reel-fallback {
    top: 30%;
    right: 8%;
    left: 8%;
  }

  .classroom-screen {
    padding: 0.35rem;
    gap: 0.3rem;
  }

  .classroom-header {
    padding: 0.22rem 2.7rem;
  }

  .classroom-title {
    font-size: clamp(1.15rem, 6vw, 1.55rem);
  }

  .gojo-character {
    bottom: 0;
    max-width: 82vw;
    height: min(112%, 33rem);
  }

  .classroom-dialogue-box {
    padding: 0.48rem;
    border-radius: 0.55rem;
    box-shadow: 0.16rem 0.19rem 0 var(--navy-deep);
  }

  .classroom-answer,
  .classroom-continue {
    min-height: 2.35rem;
    padding: 0.34rem 0.45rem;
  }

  .achievement-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: repeat(3, minmax(0, 1fr));
    gap: 0.26rem;
    padding: 0.35rem;
  }

  .achievement-slot:nth-child(5) {
    grid-column: 1 / -1;
    width: calc(50% - 0.13rem);
    justify-self: center;
  }

  .achievement-slot:nth-child(4) {
    grid-column: auto;
  }

  .achievement-name {
    font-size: clamp(0.46rem, 2.4vw, 0.6rem);
  }

  .release-pagination {
    grid-template-columns: minmax(5.4rem, 7rem) auto minmax(5.4rem, 7rem);
    gap: 0.35rem;
  }

  .release-page-button {
    min-height: 2.35rem;
    padding: 0.38rem 0.4rem;
  }

  .certificate-actions {
    gap: 0.4rem;
  }

  .certificate-action {
    min-width: min(8.2rem, 43vw);
    padding: 0.42rem 0.5rem;
  }
}

@media (max-height: 34rem) and (min-width: 34.01rem) {
  .page {
    padding: 0.32rem;
  }

  .card {
    width: min(100%, 70rem);
    grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
    gap: clamp(0.55rem, 2vw, 1.2rem);
    padding: 0.5rem clamp(0.65rem, 2.4vw, 1.4rem);
  }

  .main-primary,
  .main-details {
    gap: clamp(0.16rem, 0.75dvh, 0.35rem);
  }

  .eyebrow {
    font-size: clamp(0.48rem, 1.5dvh, 0.6rem);
  }

  h1 {
    max-width: 16ch;
    font-size: clamp(1.45rem, 7dvh, 2.25rem);
  }

  .question-heading[data-name-size="long"] {
    max-width: 20ch;
    font-size: clamp(1.15rem, 5.5dvh, 1.7rem);
  }

  .answer {
    font-size: clamp(3.2rem, 22dvh, 5rem);
  }

  .rich-button,
  .achievements-button,
  .coffee-oracle-button,
  .certificate-button,
  .collection-button {
    min-height: 2.2rem;
    padding: 0.35rem 0.6rem;
    font-size: 0.62rem;
  }

  .main-actions {
    gap: 0.26rem;
  }

  .dramatic-no-status {
    min-height: 1.15em;
    font-size: clamp(0.44rem, 1.8dvh, 0.56rem);
  }

  .coffee-oracle-panel {
    padding: 0.26rem 0.38rem;
    gap: 0.08rem;
  }

  .coffee-oracle-heading {
    font-size: clamp(0.42rem, 1.5dvh, 0.52rem);
  }

  .coffee-oracle-message {
    font-size: clamp(0.56rem, 2.05dvh, 0.68rem);
    line-height: 1.15;
  }

  .counter-part {
    padding: 0.32rem 0.06rem;
  }

  .counter-value {
    font-size: clamp(1rem, 6dvh, 1.5rem);
  }

  .counter-unit,
  .credit,
  .donation,
  .version-label {
    font-size: clamp(0.42rem, 1.8dvh, 0.56rem);
  }

  .since {
    font-size: clamp(0.48rem, 2dvh, 0.62rem);
  }

  .marin-gif-frame {
    width: clamp(3rem, 7dvh, 4.2rem);
  }

  .casino-marquee {
    top: 0.35rem;
    right: 6rem;
    left: 7rem;
    min-height: 2.55rem;
    padding: 0.22rem 0.5rem;
  }

  .casino-title {
    font-size: clamp(1rem, 5dvh, 1.4rem);
  }

  .casino-token-hud {
    top: 0.35rem;
    left: 0.35rem;
    min-width: 4rem;
    padding: 0.28rem 0.4rem;
  }

  .casino-token-balance {
    font-size: clamp(1.1rem, 6dvh, 1.7rem);
  }

  .casino-control-deck {
    right: 6.35rem;
    bottom: 0.3rem;
    left: 6.5rem;
    padding: 0.28rem;
  }

  .casino-rules {
    right: 0.35rem;
    bottom: 0.3rem;
    width: 5.55rem;
    padding: 0.28rem;
    gap: 0.14rem;
  }

  .casino-audio-controls {
    top: 3.4rem;
    right: 0.35rem;
    width: clamp(11rem, 34vw, 14rem);
    padding: 0.2rem 0.3rem;
  }

  .casino-result {
    min-height: 0;
    padding: 0.25rem;
    font-size: clamp(0.5rem, 2.2dvh, 0.66rem);
  }

  .casino-rules,
  .casino-music-toggle,
  .casino-volume-control {
    font-size: clamp(0.4rem, 1.7dvh, 0.53rem);
  }

  .classroom-screen {
    padding: 0.3rem;
    gap: 0.24rem;
  }

  .classroom-header {
    padding: 0.16rem 2.8rem;
  }

  .classroom-progress {
    font-size: clamp(0.42rem, 1.7dvh, 0.56rem);
  }

  .classroom-title {
    font-size: clamp(1rem, 5.5dvh, 1.35rem);
  }

  .gojo-character {
    bottom: -55%;
    max-width: 52vw;
    height: min(155%, 23rem);
  }

  .classroom-dialogue-box {
    padding: 0.36rem 0.48rem;
    gap: 0.2rem;
  }

  .classroom-speaker {
    padding: 0.12rem 0.45rem;
    font-size: clamp(0.58rem, 2.25dvh, 0.73rem);
  }

  .classroom-dialogue-text {
    min-height: 2.45em;
    font-size: clamp(0.68rem, 2.75dvh, 0.85rem);
    line-height: 1.24;
  }

  .classroom-answer,
  .classroom-continue {
    min-height: 2rem;
    padding: 0.24rem 0.45rem;
    font-size: clamp(0.6rem, 2.4dvh, 0.75rem);
  }

  .achievement-grid {
    grid-template-columns: repeat(5, minmax(0, 1fr));
    grid-template-rows: minmax(0, 1fr);
    gap: 0.3rem;
    padding: 0.35rem;
  }

  .achievement-slot:nth-child(n) {
    grid-column: auto;
    width: auto;
  }

  .achievements-screen {
    gap: 0.25rem;
    padding: 0.35rem;
  }

  .achievements-title {
    font-size: clamp(1.35rem, 7dvh, 2rem);
  }

  .achievements-progress,
  .achievement-name {
    font-size: clamp(0.42rem, 2dvh, 0.58rem);
  }

  .patch-notes-screen {
    gap: 0.28rem;
    padding: 0.35rem;
  }

  .patch-notes-title {
    font-size: clamp(1.25rem, 7dvh, 1.8rem);
  }

  .patch-notes-kicker,
  .release-entry h3,
  .release-entry p {
    font-size: clamp(0.43rem, 1.9dvh, 0.58rem);
  }

  .release-list {
    gap: 0.25rem;
  }

  .release-entry {
    padding: 0.3rem 0.45rem;
  }

  .release-page-button {
    min-height: 2.1rem;
  }

  .certificate-screen {
    padding: 0.3rem;
    gap: 0.2rem;
  }

  .certificate-header p {
    font-size: clamp(0.4rem, 1.6dvh, 0.52rem);
  }

  .certificate-header h2 {
    font-size: clamp(0.82rem, 4.2dvh, 1.15rem);
  }

  .certificate-action {
    min-width: 8.5rem;
    min-height: 2rem;
    padding: 0.28rem 0.55rem;
    font-size: clamp(0.5rem, 1.9dvh, 0.62rem);
  }

  .certificate-status {
    font-size: clamp(0.4rem, 1.6dvh, 0.5rem);
  }

  .jackpot-copy {
    height: 94dvh;
  }

  .jackpot-title {
    font-size: clamp(1.6rem, 10dvh, 2.5rem);
  }

  .jackpot-card {
    width: min(45vw, calc(40dvh * 0.75), 10rem);
  }

  .jackpot-prize {
    font-size: clamp(0.48rem, 2.5dvh, 0.68rem);
  }

  .jackpot-continue {
    min-height: 2.3rem;
    padding: 0.38rem 0.8rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    /* The delay counts as motion: a `both`-filled animation holds its from
       state for the whole of it, so the opening's caption sat invisible for
       260ms in front of the one visitor who asked for none. */
    animation-delay: 0.01ms !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0.01ms !important;
  }

  .jackpot-flash,
  .jackpot-rays,
  .jackpot-confetti {
    display: none;
  }

  .dramatic-paper,
  .verdict::after {
    display: none;
  }
}
