* { margin: 0; padding: 0; box-sizing: border-box; }

/* ---- type ----
   Rounded-sans stack: the first four are genuinely rounded faces if the
   system happens to have one, then Trebuchet (humanist, softer than Arial)
   as the broadly-available fallback. No webfont, so nothing to download and
   nothing to break offline. */
:root {
  /* Stage metrics. JS overwrites these on #stage every resize; these defaults
     are what the very first paint uses, before any script has run, and they
     reproduce the old plain-viewport behaviour. */
  --sw: 100vw;
  --sh: 100vh;
  --vpw: 100vw;

  --kb-ink: #2b180d;
  --kb-font: 'Nunito', 'Varela Round', ui-rounded, 'SF Pro Rounded',
             'Segoe UI Rounded', 'Trebuchet MS', 'Segoe UI', system-ui, sans-serif;
}
html, body { width: 100%; height: 100%; overflow: hidden; background: #000;
  font-family: var(--kb-font); touch-action: none;
  -webkit-user-select: none; user-select: none; }

/* White fill, dark-brown outline. paint-order puts the stroke BEHIND the
   fill — without it -webkit-text-stroke paints over the glyph and eats it
   from the inside. Stroke is in em so it tracks the font size. */
.hpname, .pips, #timer, #banner, #title h1, #title .sub, .controls-note,
.seljoin, #result h2, .diffbtn, #rematch, #selgo, .tbtn {
  color: #fff;
  -webkit-text-stroke: 0.055em var(--kb-ink);
  paint-order: stroke fill;
  letter-spacing: 0.02em;
}

/* ---- stage: the box the game lays itself out in ----

   Normally this is just the viewport. On a portrait phone it is the viewport
   turned on its side — JS sets the px size and adds .rotated, and the whole
   game (canvas, menus, buttons) turns with it. That covers the case a media
   query cannot: a phone held sideways with rotation lock ON never reports
   landscape, so waiting for the browser to reflow waits forever.

   --sw/--sh are the stage's own width/height in px, written by JS on every
   resize. Everything below sizes against those instead of vw/vh, because
   vw/vh keep measuring the DEVICE and would stay portrait-shaped while the
   game is sideways. */
#stage { position: fixed; left: 0; top: 0; width: 100%; height: 100%;
  transform-origin: 0 0; }

/* Portrait is turned sideways HERE, in CSS, not in script — this is what makes
   sure a portrait frame is never painted at all. main.js runs on 'load', which
   waits for every image in the game, so a JS-only rotation left the phone
   showing an upright landing screen for that whole time. These values are the
   viewport-unit approximation; resize() overwrites them with exact pixels from
   visualViewport as soon as it runs. */
@media (orientation: portrait) {
  #stage {
    width: 100vh; height: 100vw;
    --sw: 100vh; --sh: 100vw;
    transform: translateX(100vw) rotate(90deg);
  }
}
/* A transformed ancestor becomes the containing block for its position:fixed
   descendants, which is exactly what we want: the fixed overlays below pin to
   the rotated stage rather than to the screen. */
#stage.rotated { transform: translateX(var(--vpw)) rotate(90deg); }

#game { position: fixed; left: 0; top: 0; display: block; }

/* ---- health bars / timer / pips ---- */
#ui { position: fixed; left: 0; top: 0; width: 100%; pointer-events: none;
  display: flex; justify-content: space-between; padding: 12px 16px; z-index: 5; }
/* Same notch story as #touch: sideways, the cutout lands on a health bar. */
@supports (padding: env(safe-area-inset-left)) {
  #ui { padding: calc(12px + env(safe-area-inset-top))
                 calc(16px + env(safe-area-inset-right)) 12px
                 calc(16px + env(safe-area-inset-left)); }
  /* Sideways, the stage's top edge is the screen's right. Same re-map as #touch. */
  #stage.rotated #ui { padding: calc(12px + env(safe-area-inset-right))
                                calc(16px + env(safe-area-inset-bottom)) 12px
                                calc(16px + env(safe-area-inset-top)); }
}
.hpwrap { width: 38%; }
.hpname { font-weight: bold; font-size: 15px; margin-bottom: 3px;
  text-shadow: 0 2px 4px rgba(0,0,0,0.5); }
#hp2wrap .hpname { text-align: right; }
.hpbar { position: relative; height: 18px; background: #3a3a3a;
  border: 2px solid #111; border-radius: 4px; overflow: hidden; }
.hpghost { position: absolute; left: 0; top: 0; height: 100%; width: 100%;
  background: #fff; transition: width 0.6s ease; }
.hpfill { position: absolute; left: 0; top: 0; height: 100%; width: 100%;
  background: linear-gradient(#9be15d, #2f9e44); transition: width 0.08s; }
#hp2wrap .hpghost, #hp2wrap .hpfill { left: auto; right: 0; }
.pips { margin-top: 4px; font-size: 16px;
  text-shadow: 0 2px 4px rgba(0,0,0,0.5); }
#pips2 { text-align: right; }
#timer { font-size: 30px; font-weight: bold; align-self: center;
  text-shadow: 0 3px 5px rgba(0,0,0,0.55); }

/* ---- banner ---- */
#banner { position: fixed; left: 0; top: 30%; width: 100%; text-align: center;
  font-size: 64px; font-weight: bold; z-index: 8;
  text-shadow: 0 6px 14px rgba(0,0,0,0.6); pointer-events: none; }
#banner.show { animation: bannerpop 1.5s ease-out forwards; }
@keyframes bannerpop {
  0% { opacity: 0; transform: scale(2.2); }
  15% { opacity: 1; transform: scale(1); }
  75% { opacity: 1; }
  100% { opacity: 0; }
}

#fps { position: fixed; right: 8px; bottom: 8px; color: #0f0; font-size: 14px;
  z-index: 9; background: rgba(0,0,0,0.5); padding: 2px 6px; }

/* ---- title / result screens ---- */
#title, #result { position: fixed; left: 0; top: 0; width: 100%; height: 100%;
  background: rgba(10, 8, 20, 0.92); z-index: 20; text-align: center;
  padding-top: calc(var(--sh) * 0.12); }
#title h1 { font-size: 76px; text-shadow: 0 6px 16px rgba(0,0,0,0.65); }
#title .sub { font-size: 20px; margin: 12px 0 20px;
  text-shadow: 0 2px 5px rgba(0,0,0,0.5); }
.controls-note { font-size: 14px; margin: 6px 0;
  -webkit-text-stroke-width: 0.045em; text-shadow: 0 2px 4px rgba(0,0,0,0.5); }
.diffbtn, #rematch { display: block; margin: 14px auto; padding: 16px 48px;
  font-size: 24px; font-weight: bold; background: #ffd23e;
  border: none; border-radius: 12px; cursor: pointer; font-family: var(--kb-font); }
.diffbtn:active, #rematch:active { transform: scale(0.95); }
/* gamepad highlight — only applied while a pad is connected */
.diffbtn.focused, #rematch.focused { outline: 4px solid #fff;
  outline-offset: 4px; box-shadow: 0 0 22px rgba(255, 210, 62, 0.9); }
.diffbtn.active { color: #ffd23e; background: var(--kb-ink);
  box-shadow: 0 0 18px rgba(255, 210, 62, 0.95); }
#result h2 { font-size: 58px; text-shadow: 0 5px 14px rgba(0,0,0,0.6); }

/* ---- landing screen ----
   Sits above everything until the player presses anything, then hands over to
   the title screen. Also doubles as the loading screen, since it is plain
   markup and paints before any asset has arrived. */
#landing { position: fixed; left: 0; top: 0; width: 100%; height: 100%;
  z-index: 30; background: #D9CFBA; cursor: pointer;
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: calc(var(--sh) * 0.03); }
#landinglogo { width: min(calc(var(--sw) * 0.62), calc(var(--sh) * 0.58));
  max-width: 680px; height: auto;
  display: block; }
/* Sprite-download progress. Only present while #landing.loading is set, so
   the prompt sits alone once the game is actually ready. */
#loadbar { width: min(260px, calc(var(--sw) * 0.44)); height: 9px;
  border: 2px solid var(--kb-ink); border-radius: 999px;
  background: rgba(43, 24, 13, 0.14); overflow: hidden; }
#loadfill { width: 0%; height: 100%; background: #ffd23e;
  transition: width 0.18s linear; }
#landing:not(.loading) #loadbar { display: none; }
/* The prompt stays away until the game can actually honour it — it appears
   the moment the sprites are in. The bar carries the wait on its own. */
#landing.loading #gamestart { display: none; }

/* the SVG inside is generated by KB.pixelTextSVG and re-sized on resize */
#gamestart { animation: gamestartpulse 1.15s ease-in-out infinite;
  will-change: transform, opacity; }
/* Exactly 5px of outline at any normal size. Only on a viewport too narrow
   for the word does this scale the whole thing down (outline included) to
   fit, which beats letting it run off the screen. */
#gamestart svg { display: block; max-width: calc(var(--sw) * 0.92); height: auto; }
@keyframes gamestartpulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.07); opacity: 0.68; }
}
/* a player who prefers less motion still gets the prompt, just steady */
@media (prefers-reduced-motion: reduce) {
  #gamestart { animation: none; }
}

/* ---- character select ---- */
#select { position: fixed; left: 0; top: 0; width: 100%; height: 100%;
  z-index: 20; overflow: hidden; }
/* Two full-screen gradient layers. The right one is clipped to a single
   straight diagonal — the old zigzag read as jagged and no ribbon could
   cover it. JS overwrites this clip-path with one aimed along the divider
   art's own centreline (see _layoutDivider); these values are the fallback
   for when the art fails to load. Colors are set from JS per character. */
.selbg { position: absolute; left: 0; top: 0; width: 100%; height: 100%;
  background: linear-gradient(#384345, #262B33); }
.selbg.selright { clip-path: polygon(33% 0, 100% 0, 100% 100%, 69% 100%); }

/* The divider ribbon sits above both sections and hides the seam where they
   meet. Sized and positioned entirely from JS so it tracks the viewport. */
.seldivide { position: absolute; z-index: 3; pointer-events: none;
  filter: drop-shadow(0 0 18px rgba(0,0,0,0.45)); }
/* The host is sized to the WHOLE svg viewBox and slid so the ribbon lands on
   the seam, so it overspills the viewport on purpose — #select clips it. */
.seldivide img, .seldivide svg { display: block; width: 100%; height: 100%; }
.selportrait { position: absolute; bottom: 0;
  height: calc(var(--sh) * 0.82); max-width: calc(var(--sw) * 0.46);
  object-fit: contain; z-index: 2; pointer-events: none;
  filter: drop-shadow(0 10px 24px rgba(0,0,0,0.55)); }
.selportrait.left { left: calc(var(--sw) * 0.02); }
/* Only the artwork mirrors. The name lives in its own element below, which is
   deliberately NOT flipped — that is the whole point of splitting them. */
.selportrait.right { right: calc(var(--sw) * 0.02); transform: scaleX(-1); }

/* Name lettering: sits above the portrait and one layer behind it (z-index 1
   vs 2), so the character's head overlaps the bottom of the letters. */
.seltitleart { position: absolute; top: calc(var(--sh) * 0.05);
  width: min(calc(var(--sw) * 0.28), 440px);
  height: auto; z-index: 1; pointer-events: none;
  filter: drop-shadow(0 8px 18px rgba(0,0,0,0.45)); }
.seltitleart.left { left: calc(var(--sw) * 0.05); }
.seltitleart.right { right: calc(var(--sw) * 0.05); }
/* no .selname rules: the mugshots carry their own lettering — and no heading
   or VS badge either: the portraits say who is fighting, GO says what to do */
.seldiff { position: absolute; right: calc(var(--sw) * 0.06);
  bottom: calc(var(--sh) * 0.08); z-index: 5;
  display: flex; flex-direction: column; gap: 8px; align-items: stretch; }
.seldiff .diffbtn { width: 168px; margin: 0; padding: 10px 24px;
  font-size: 20px; border-radius: 8px; }
.seljoin { position: absolute; left: 0; bottom: calc(var(--sh) * 0.025);
  width: 100%; z-index: 5;
  text-align: center; font-size: 15px; pointer-events: none;
  -webkit-text-stroke-width: 0.045em; text-shadow: 0 2px 5px rgba(0,0,0,0.6); }
/* GO: the fight no longer starts on its own, so this is the only way in. Sits
   above everything on the select screen and is revealed after the VS flash. */
#selgo { position: absolute; left: 50%; top: calc(var(--sh) * 0.05);
  transform: translateX(-50%); z-index: 7;
  padding: calc(var(--sh) * 0.028) calc(var(--sw) * 0.07);
  font-size: clamp(22px, calc(var(--sh) * 0.085), 44px); font-weight: bold;
  font-family: var(--kb-font);
  background: #3ddc6b; border: 4px solid #2b180d; border-radius: 14px;
  cursor: pointer; box-shadow: 0 6px 0 #1f7d3f, 0 10px 22px rgba(0,0,0,0.45);
  animation: gopulse 1s ease-in-out infinite; }
#selgo:active { transform: translateX(-50%) translateY(4px);
  box-shadow: 0 2px 0 #1f7d3f, 0 6px 14px rgba(0,0,0,0.45); }
#selgo.focused { outline: 4px solid #fff; outline-offset: 4px; }
@keyframes gopulse {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(1.18); }
}

.selgrid { position: absolute; left: 50%; bottom: calc(var(--sh) * 0.07); z-index: 5;
  transform: translateX(-50%); display: flex; gap: 18px; }
/* Cells are portrait-ish and the art is CONTAINed, never cropped: the
   mugshots differ a lot in shape (Poh is 649x1024, Killa Hawk 1010x1024) and
   `cover` was slicing the sides off the tall one. Contain letterboxes
   instead, so every fighter shows whole, name and all. */
.selcell { position: relative; width: 124px; height: 150px; padding: 5px;
  border: 4px solid #5a6570; border-radius: 10px; overflow: hidden;
  background: rgba(0,0,0,0.45); cursor: pointer; }
.selcell img { width: 100%; height: 100%; object-fit: contain;
  object-position: center; display: block; }
/* Two cursors can sit on the grid at once, so P1 and P2 get distinct rings.
   Both on one cell = a doubled ring (outline outside, border inside). */
.selcell.p1focus { border-color: #ffd23e;
  box-shadow: 0 0 20px rgba(255, 210, 62, 0.85); transform: scale(1.06); }
.selcell.p2focus { outline: 4px solid #5cc8ff; outline-offset: 3px;
  box-shadow: 0 0 20px rgba(92, 200, 255, 0.85); transform: scale(1.06); }
.selcell.p1focus.p2focus { box-shadow: 0 0 22px rgba(255, 210, 62, 0.85),
  0 0 30px rgba(92, 200, 255, 0.8); }
.selcell.p1locked, .selcell.p2locked { transform: scale(1.12); }
.selcell.p1locked::after, .selcell.p2locked::after {
  content: ''; position: absolute; inset: 0; border-radius: 6px;
  box-shadow: inset 0 0 0 3px rgba(255,255,255,0.9); }

/* ---- touch controls ---- */
#touch { position: fixed; left: 0; bottom: 0; width: 100%; z-index: 10;
  display: flex; justify-content: space-between; align-items: flex-end;
  padding: 0 12px 14px; pointer-events: none; }
/* The viewport is `viewport-fit=cover`, so on a notched phone held sideways
   the notch eats the d-pad and the home indicator sits on the punch buttons.
   Push clear of both. Browsers without env() drop this and keep the padding
   above, which is why that declaration stays. */
@supports (padding: env(safe-area-inset-left)) {
  #touch { padding: 0 calc(12px + env(safe-area-inset-right))
                     calc(14px + env(safe-area-inset-bottom))
                     calc(12px + env(safe-area-inset-left)); }
  /* Turned sideways the insets no longer line up with the stage's own edges:
     rotating 90deg clockwise sends the stage's bottom to the screen's LEFT,
     its left to the top, and its right to the bottom. Re-map accordingly. */
  #stage.rotated #touch { padding: 0 calc(12px + env(safe-area-inset-bottom))
                                    calc(14px + env(safe-area-inset-left))
                                    calc(12px + env(safe-area-inset-top)); }
}
/* Both clusters are the same 3x3 grid with the corners left empty, which is
   what produces the cross for movement and the diamond for attacks. Sized off
   the stage height so the pads stay in proportion on a small phone without
   swallowing the fight. */
.pad { display: grid; pointer-events: auto;
  /* A cross is three buttons tall whatever you do, so button size is the only
     lever on how much screen the pads eat. 0.14 puts them at ~44% of stage
     height on a phone; the 44px floor is the smallest comfortable thumb
     target, the 58px cap stops them bloating on a tablet. Tune the 0.14. */
  --btn: clamp(44px, calc(var(--sh) * 0.14), 58px);
  --gap: clamp(3px, calc(var(--sh) * 0.011), 6px);
  grid-template-columns: repeat(3, var(--btn));
  grid-template-rows: repeat(3, var(--btn));
  gap: var(--gap); }
/* Analog stick. One circle instead of a three-row cross, which also gives the
   fight back a chunk of the screen the D-pad was using. */
.stick { position: relative; pointer-events: auto; touch-action: none;
  --base: clamp(116px, calc(var(--sh) * 0.40), 172px);
  width: var(--base); height: var(--base); border-radius: 50%;
  border: 2px solid rgba(255,255,255,0.9); background: rgba(255,255,255,0.13);
  -webkit-tap-highlight-color: transparent; }
.stick.on { background: rgba(255,255,255,0.2); }
.stick-knob { position: absolute; left: 50%; top: 50%;
  width: 44%; height: 44%; border-radius: 50%; pointer-events: none;
  border: 2px solid #fff; background: rgba(255,255,255,0.42);
  transform: translate(-50%, -50%);
  box-shadow: 0 2px 8px rgba(0,0,0,0.35); }
/* No transition while dragging — the knob must track the thumb exactly — but
   ease it home on release. */
.stick:not(.on) .stick-knob { transition: transform 0.12s ease-out; }

.cell-up    { grid-area: 1 / 2 / 2 / 3; }
.cell-left  { grid-area: 2 / 1 / 3 / 2; }
.cell-right { grid-area: 2 / 3 / 3 / 4; }
.cell-down  { grid-area: 3 / 2 / 4 / 3; }

.tbtn { width: var(--btn); height: var(--btn); border: 2px solid #fff;
  background: rgba(255,255,255,0.18); font-size: calc(var(--btn) * 0.34);
  display: flex; align-items: center; justify-content: center;
  padding: 0; touch-action: none; font-family: var(--kb-font);
  -webkit-tap-highlight-color: transparent; }
.actions .tbtn { border-radius: 50%; }
.tbtn.on { background: rgba(255,255,255,0.5); transform: scale(0.94); }

.tbtn svg { width: 56%; height: 56%; fill: #fff; display: block;
  filter: drop-shadow(0 1px 1.5px rgba(0,0,0,0.65)); }

/* Colour-coded: a punch, a kick, a guard — and jump on top, left plain white
   so it reads as movement rather than a fourth attack. */
.act.jump   { background: rgba(255, 255, 255, 0.22); border-color: #ffffff; }
.act.punch  { background: rgba(255, 210, 62, 0.28);  border-color: #ffd23e; }
.act.kick   { background: rgba(255, 122, 60, 0.28);  border-color: #ff7a3c; }
.act.shield { background: rgba(92, 200, 255, 0.28);  border-color: #5cc8ff; }
.act.jump.on   { background: rgba(255, 255, 255, 0.55); }
.act.punch.on  { background: rgba(255, 210, 62, 0.62); }
.act.kick.on   { background: rgba(255, 122, 60, 0.62); }
.act.shield.on { background: rgba(92, 200, 255, 0.62); }

/* Cramped-stage tweaks. Driven by a JS-set class rather than a max-width media
   query, because a media query measures the DEVICE: a sideways phone has a
   roomy 844px stage on a 390px-wide screen and would wrongly match. */
#stage.narrow .seldiff { right: calc(var(--sw) * 0.03);
  bottom: calc(var(--sh) * 0.08); gap: 6px; }
#stage.narrow .seldiff .diffbtn { width: 124px; padding: 8px 12px; font-size: 16px; }

.hidden { display: none !important; }
