/* ============================================================================
   On-screen touch controls (web2/touch.js) — Task 9, relaid out in QA wave 3 to
   match the previous production build: floating stick on the left 40 %, look on
   the right 60 %, a big fire button bottom-right and three small ones in the
   corners. Task 10 appends the rest of the page styles BELOW the end marker;
   keep this section self-contained and do not interleave other rules into it.
   ========================================================================= */

#tc {
  /* Height of the engine's status bar at the bottom of the canvas. The stick zone, the look
     zone and the button cluster all stop above it so the HUD stays readable and a stray drag
     over the HUD cannot turn the view. */
  --tc-hud: 14vh;
  --tc-gap: 16px;
  /* Telegram's Bot API 8.0 fullscreen draws the client's own close/menu buttons over the top
     of the page; both insets are absent outside fullscreen and the fallbacks keep this at the
     ordinary safe area. Every top-anchored control below starts here. */
  --tc-top: calc(var(--tc-gap) + max(env(safe-area-inset-top),
                 var(--tg-safe-area-inset-top, 0px) + var(--tg-content-safe-area-inset-top, 0px)));
  --tc-line: rgba(255, 255, 255, .3);
  --tc-fill: rgba(0, 0, 0, .28);

  position: fixed;
  inset: 0;
  z-index: 4;
  pointer-events: none;   /* the container itself must never eat canvas input */
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

#tc > * {
  position: absolute;
  pointer-events: auto;
  touch-action: none;     /* no scrolling / pinch / double-tap zoom on the controls */
}

/* The two capture areas come first in the stacking order; every button overlaps one of them
   and has to win the pointer, which it does by being a later sibling at a higher z-index. */

/* Movement. The whole left 40 % is live, and the ring is drawn wherever the thumb lands
   (touch.js positions #tc-stick on pointerdown) — a *floating* stick, as in the production
   build. A fixed ring is the thing a thumb has to find by feel in the middle of a firefight,
   and on a 393 px-tall landscape phone there is no room to make it both large and reachable. */
#tc-zone {
  z-index: 0;
  left: 0;
  top: var(--tc-top);
  width: 40%;
  bottom: var(--tc-hud);
}

/* Look. The rest of the screen, by finger displacement (lib/touchmap.mjs). */
#tc-look {
  z-index: 0;
  right: 0;
  top: var(--tc-top);
  width: 60%;
  bottom: var(--tc-hud);
}

/* Positioned by JS at the point the finger landed, hence the translate: `left`/`top` are the
   centre. Purely decorative — #tc-zone owns the pointer and its capture. */
#tc-stick {
  z-index: 1;
  width: 200px;
  height: 200px;
  transform: translate(-50%, -50%);
  border: 2px solid var(--tc-line);
  border-radius: 50%;
  background: var(--tc-fill);
  pointer-events: none;
}

/* 90 px across in a 200 px ring leaves 55 px of travel, which is exactly touch.js's R — so the
   axis saturates when the knob touches the rim and the widget never lies about what it sends. */
#tc-knob {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 90px;
  height: 90px;
  transform: translate(-50%, -50%);
  border: 2px solid var(--tc-line);
  border-radius: 50%;
  background: rgba(255, 255, 255, .18);
  pointer-events: none;
}

.tc-b {
  z-index: 2;
  width: 72px;
  height: 72px;
  padding: 0;
  border: 2px solid var(--tc-line);
  border-radius: 50%;
  background: var(--tc-fill) center / 34px 34px no-repeat;
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
}

.tc-b:active { background-color: rgba(255, 255, 255, .26); }
.tc-b:focus { outline: none; }

/* Fire is the one button a thumb reaches for without looking, so it is the big one and it sits
   under the right thumb, clear of the HUD. */
.tc-b[data-b="fire"] {
  width: 110px;
  height: 110px;
  background-size: 48px 48px;
  right: calc(var(--tc-gap) + env(safe-area-inset-right));
  bottom: calc(var(--tc-hud) + env(safe-area-inset-bottom));
}

/* Use / interact: top-left, the hand icon of the production build. */
.tc-b[data-b="use"] {
  left: calc(var(--tc-gap) + env(safe-area-inset-left));
  top: var(--tc-top);
}

/* Jump and weapon-next share the top-right corner, weapon outermost. */
.tc-b[data-b="weapNext"] {
  right: calc(var(--tc-gap) + env(safe-area-inset-right));
  top: var(--tc-top);
}
.tc-b[data-b="jump"] {
  right: calc(var(--tc-gap) + env(safe-area-inset-right) + 84px);
  top: var(--tc-top);
}

/* Icons: inline SVG data URIs, no external assets. */
.tc-b[data-b="fire"] {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2'%3E%3Ccircle cx='12' cy='12' r='7'/%3E%3Ccircle cx='12' cy='12' r='1.6' fill='%23fff' stroke='none'/%3E%3Cpath d='M12 1v4M12 19v4M1 12h4M19 12h4'/%3E%3C/svg%3E");
}
.tc-b[data-b="use"] {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linejoin='round'%3E%3Cpath d='M8 12V4a2 2 0 0 1 4 0v7m0-2a2 2 0 0 1 4 0v2m0-1a2 2 0 0 1 4 0v5a6 6 0 0 1-6 6h-2a6 6 0 0 1-6-6v-4a2 2 0 0 1 4 0'/%3E%3C/svg%3E");
}
/* A running figure, as in the production build's jump button. */
.tc-b[data-b="jump"] {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='14' cy='4' r='2'/%3E%3Cpath d='M12.5 21l1.5-5-3-2.5 1-5.5-3.5 2L7 12'/%3E%3Cpath d='M11 8.5l4 2 1.5 3.5 3 1.5'/%3E%3Cpath d='M14 16l2.5 5'/%3E%3C/svg%3E");
}
/* A gun, as in the production build's weapon-switch button. */
.tc-b[data-b="weapNext"] {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 8h18v4h-6l-2 4H8l-1-4H2z'/%3E%3Cpath d='M9 16v3'/%3E%3Cpath d='M20 8V6'/%3E%3C/svg%3E");
}

/* Landscape phones are short — an iPhone is 393 px tall in landscape, and 14 vh of that is the
   HUD. Shrink the buttons (never below the 64 px touch-target floor) and tighten the gap; the
   stick is left alone, because its 200/90 geometry is what touch.js's R = 55 assumes. */
@media (max-height: 480px) {
  #tc { --tc-gap: 10px; }
  .tc-b { width: 64px; height: 64px; background-size: 30px 30px; }
  .tc-b[data-b="fire"] { width: 96px; height: 96px; background-size: 42px 42px; }
  .tc-b[data-b="jump"] { right: calc(var(--tc-gap) + env(safe-area-inset-right) + 74px); }
}

/* Outside gameplay the controls go away completely (touch.js polls Module._WebPlayState()).
   `hidden` already means `display:none` through the global [hidden] rule, which takes the whole
   subtree out of hit-testing; pointer-events is restated only so that a future rule making #tc
   visible-but-inert cannot accidentally leave it eating taps meant for the menu. */
#tc[hidden], #tc-stick[hidden] { pointer-events: none; }

/* The menu gesture layer (touch.js + lib/menugest.mjs), live only while the engine is NOT in
   play: EDGE's menus are keyboard-driven and, on the web build, mouse buttons need a pointer
   lock a phone never has, so a tap has to become a synthetic Enter. It covers the canvas both
   to catch the gesture and to keep the same tap from reaching SDL's own listeners. At #tc's
   z-index, so every page overlay (loader, rotate nag, crash card — z 5..7) still sits above it
   and keeps its own taps. */
#tc-menu {
  position: fixed;
  inset: 0;
  z-index: 4;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

/* The one control that never hides: Escape opens the menu in a level, steps back out of a
   submenu, and is the "no" of a y/n dialog whose "yes" is a long press. Hiding it with the rest
   would leave a player who is already in the menu with no way back.
   Top-*centre* since wave 3: the wave-2 corner was the same corner as the ?diag=1 readout, and
   is now the use button's, while the top-right belongs to jump and weapon switch. */
#tc-esc {
  position: fixed;
  z-index: 4;
  top: calc(6px + max(env(safe-area-inset-top),
            var(--tg-safe-area-inset-top, 0px) + var(--tg-content-safe-area-inset-top, 0px)));
  left: 50%;
  transform: translateX(-50%);
  /* Wave 4: halved (72x64 -> 36x32, 24 px -> 12 px glyph) on the owner's «уменьшить на 50%».
     The *rendered* box is what shrank; the press target did not, and must not — ≡ is a
     two-meaning control (tap = Escape, hold = the automap) and the only way back out of a menu,
     so a missed press is the most expensive one on the screen. The ::before below puts the
     touch area back to 52x48, which keeps it over the 36 px floor on both axes. */
  width: 36px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--tc-line, rgba(255, 255, 255, .3));
  border-radius: 6px;
  background: rgba(21, 21, 21, .45);
  color: #fff;
  font: 12px/1 system-ui, sans-serif;
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
}

/* The invisible half of the button. A generated box is part of its originating element as far as
   hit testing goes — a pointer over it targets #tc-esc itself, not the pseudo-element — so the
   button's own pointerdown/pointerup handlers (web2/touch.js) fire from anywhere inside this box,
   with e.target still the button. `inset` is negative, so the area grows outwards from the 36x32
   visual and nothing moves on screen. */
#tc-esc::before {
  content: '';
  position: absolute;
  inset: -8px;
}

#tc-esc.back { font-size: 16px; line-height: 1; }   /* ← is taller than ≡ at the same size */
#tc-esc:active { background: rgba(255, 255, 255, .22); }
#tc-esc:focus { outline: none; }

/* ===================== end on-screen touch controls ====================== */

/* ============================================================================
   Page shell (web2/index.html + shell.js) — Task 10.
   ========================================================================= */

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: #151515;
  color: #ddd;
  font: 16px/1.4 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

/* The id MUST be "canvas" and it MUST have a CSS size — see index.html. */
#canvas {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  display: block;
  background: #000;
  outline: none;
}

/* Overlays. #tc (the touch controls above) sits at z-index 4, so everything the player
   has to get past before playing is above it — and so is #death, which has to sit over the
   touch layer, the ≡ button and the canvas while the player is dead (index.html). */
#tap, #loader, #death, #hint, #rotate, #crash {
  position: fixed;
  inset: 0;
  z-index: 5;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  padding: max(16px, env(safe-area-inset-top)) max(16px, env(safe-area-inset-right))
           max(16px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
  background: #151515;
  text-align: center;
}

/* #tap, #loader and #death get the AGL/Unity-style cover instead of the flat #hint/#rotate/#crash
   background: a dark base with a soft green glow top-centre and bottom-centre, done as two
   radial-gradient ellipses so no extra image is needed. Sized so the glows read the same shape
   in portrait (phone) and landscape (desktop, 1500x900) — about 60% of viewport width across
   and 35% of viewport height tall. */
#tap, #loader, #death {
  background:
    radial-gradient(ellipse 60vw 45vh at 50% -10%, rgba(52, 199, 40, .85), rgba(52, 199, 40, 0) 55%),
    radial-gradient(ellipse 60vw 45vh at 50% 110%, rgba(52, 199, 40, .85), rgba(52, 199, 40, 0) 55%),
    #262624;
}

[hidden] { display: none !important; }

.agl {
  width: min(220px, 56vw);
  height: auto;
}

.logo {
  font: 900 clamp(28px, 9vw, 56px)/1 Impact, "Arial Black", system-ui, sans-serif;
  letter-spacing: .06em;
  color: #b00;
  text-shadow: 0 0 24px rgba(187, 0, 0, .45);
}

.cta { margin: 0; color: #9a9a9a; }

#tap .cta { color: #cfcfcf; }

/* Loader: a determinate bar — fetchWithCache reports content-length progress. */
#track {
  width: min(320px, 70vw);
  height: 6px;
  border-radius: 3px;
  background: rgba(255, 255, 255, .12);
  overflow: hidden;
}

#bar {
  width: 0;
  height: 100%;
  background: #3ddc3a;
  box-shadow: 0 0 12px rgba(61, 220, 58, .6);
  transition: width .15s linear;
}

#status { margin: 0; color: #9a9a9a; font-size: 14px; }

/* The byte counter under the bar and, on a first launch, the reason it is worth waiting for.
   Both belong to #status rather than standing on their own, so the loader's 18px flex gap is
   pulled back: bar, phase, numbers, promise read as one block.
   `tabular-nums` because every digit changes several times a second and a proportional font makes
   the whole line twitch; `min-height` because the line appears mid-download and the loader must
   not jump when it does. */
#detail {
  margin: -12px 0 0;
  min-height: 1.2em;
  color: #7d7d7d;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

/* Reserved the same way as #detail, and hidden with `visibility` rather than the `hidden`
   attribute for exactly that reason: `[hidden]` above is `display: none`, which takes the box away
   with the text, and the whole loader — logo, bar, status — would jump up the screen at the moment
   the line is raised mid-download. shell.js toggles `.on`. */
#once {
  margin: -10px 0 0;
  min-height: 1.2em;
  max-width: 24em;
  color: #6a6a6a;
  font-size: 12px;
  visibility: hidden;
}

#once.on { visibility: visible; }

/* Cards (#hint, #crash) share one look. */
.card {
  max-width: 420px;
  padding: 22px 26px;
  border: 1px solid rgba(255, 255, 255, .12);
  border-radius: 10px;
  background: #1d1d1d;
  text-align: left;
}

.card h2 {
  margin: 0 0 14px;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: #b00;
}

.card dl {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 8px 16px;
  margin: 0;
}

.card dt {
  font: 600 14px/1.4 ui-monospace, Menlo, Consolas, monospace;
  color: #fff;
  white-space: nowrap;
}

.card dd { margin: 0; color: #9a9a9a; font-size: 14px; }
.card .cta { margin-top: 18px; text-align: center; font-size: 14px; }

/* The pointer-lock line on the hint card: prose, not a binding, so it sits below the <dl>. */
.card .note {
  margin: 14px 0 0;
  color: #9a9a9a;
  font-size: 13px;
  line-height: 1.5;
}

/* GPL-3 attribution for EDGE-Classic. Small and quiet, but it has to be on screen: the engine
   is GPL-3 and the page ships its build. */
.card .gpl {
  margin: 16px 0 0;
  padding-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, .1);
  color: #6f6f6f;
  font-size: 11px;
  line-height: 1.5;
  text-align: left;
}

#crash .card { text-align: center; }
#crash .card p { margin: 0 0 16px; color: #9a9a9a; }
#crash .card .gpl { margin-bottom: 0; text-align: center; }

#crash-retry {
  padding: 10px 22px;
  border: 0;
  border-radius: 6px;
  background: #b00;
  color: #fff;
  font: 600 15px/1 inherit;
  cursor: pointer;
}

/* The death card (index.html #death, shell.js showDeath). Two big buttons a thumb cannot miss
   and a player at a desk cannot mistake: the primary is the same red as the logo, the ghost is
   outlined. 56 px tall is the touch floor with room to spare, and the width is the same
   min(360px, 84vw) on a portrait phone, a landscape phone and a desktop window, so the two
   buttons always read as one column. `font: inherit` because a <button> does not inherit the
   page font by default and would otherwise render in the UA's 13 px. */
#death button {
  box-sizing: border-box;
  width: min(360px, 84vw);
  min-height: 56px;
  margin: 0;
  padding: 10px 18px;
  border: 2px solid #b00;
  border-radius: 10px;
  background: #b00;
  color: #fff;
  font: 600 17px/1.2 inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

#death button:active { filter: brightness(1.25); }
#death button:disabled { opacity: .55; cursor: default; }

/* The price line lives inside the primary button, under its label, so the two never separate:
   «бесплатно · 1 раз на уровень» on the first death, «25 ⭐» on the second. The ad button
   (#death-ad, shown instead of or above the Stars one) carries the same two-line shape. */
#death-revive .t, #death-revive .sub, #death-ad .t, #death-ad .sub { display: block; }
#death-revive .sub, #death-ad .sub { margin-top: 2px; font-size: 13px; font-weight: 400; opacity: .85; }
#death-revive .sub:empty { display: none; }

/* When both the ad and the Stars offer are up, the ad is the primary (it costs the player
   nothing) and the Stars button drops to the outlined style — shell.js toggles .ghost on it. */
#death-ad { margin-bottom: -6px; }

#death button.ghost {
  background: transparent;
  border-color: rgba(255, 255, 255, .35);
  color: #ddd;
}

/* «Оплата не прошла» / «Не получилось…» — small, grey, and with its box reserved so the card
   does not jump when the line appears or goes away (the same trick as #detail / #once above). */
#death-note {
  margin: -6px 0 0;
  min-height: 1.3em;
  max-width: 24em;
  color: #9a9a9a;
  font-size: 13px;
  line-height: 1.3;
}

/* The card's own gap is the overlays' 18 px; the primary and the ghost sit a little closer to
   each other than to the logo above them. */
#death-restart { margin-top: -6px; }

/* A landscape phone is 393 px tall: the AGL mark and the title shrink and the gaps tighten so the
   logo, the two buttons and the note all fit above the fold without a scroll. */
@media (max-height: 480px) {
  #death { gap: 12px; }
  #death .agl { width: min(150px, 30vw); }
  #death .logo { font-size: clamp(24px, 6vh, 36px); }
  #death button { min-height: 52px; padding: 8px 18px; }
  #death-restart { margin-top: -2px; }
  #death-note { margin-top: -2px; }
}

/* #pl-dot lived here until wave 4. It marked the centre of the *canvas*, but the shot goes
   through the centre of the 3-D view — the canvas minus the status bar — so it sat low, which is
   the offset the owner measured against a standard monster. The crosshair is EDGE's own now
   (crosshair_image/_color/_size, written by shell.js into edge-classic.cfg), drawn at the true
   view centre on both platforms; the page has no crosshair element at all. */

/* The delta mouse-look fallback hides the cursor — that is half of what makes it work: the
   arrow is pinned against the edge of the screen while the deltas keep coming, and seeing it
   stuck there is exactly the thing that reads as broken. !important is required, not decorative: SDL's emscripten backend keeps
   the cursor in an *inline* style on the canvas (SDL_ShowCursor writes canvas.style.cursor, and
   it was measured as `default` on the running page), which an ordinary author rule cannot
   outrank. This changes nothing the engine reads — its own button gate is SDL_ShowCursor(QUERY),
   which is SDL state, not CSS. */
body.mouselook #canvas { cursor: none !important; }

/* Diagnostics overlay (?diag=1 / #diag → lib/diag.mjs). Above everything, including the
   rotate nag, so a QA run can read it whatever the page is showing. */
#diag {
  position: fixed;
  top: max(6px, env(safe-area-inset-top), var(--tg-content-safe-area-inset-top, 0px));
  left: max(6px, env(safe-area-inset-left));
  z-index: 7;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  padding: 5px 8px;
  border-radius: 4px;
  background: rgba(0, 0, 0, .65);
  color: #0f0;
  font: 12px/1.35 ui-monospace, Menlo, Consolas, monospace;
  pointer-events: none;
}

#diag pre { margin: 0; font: inherit; white-space: pre; }

/* On a phone the top-left corner is the use button's (wave 3), so the QA readout drops below the
   row of controls rather than sitting on top of one. Still above everything at z-index 7. */
body.touch #diag { top: calc(84px + max(6px, env(safe-area-inset-top))); }

#diag button {
  pointer-events: auto;
  padding: 3px 8px;
  border: 0;
  border-radius: 3px;
  background: rgba(255, 255, 255, .15);
  color: #ddd;
  font: inherit;
  cursor: pointer;
}

/* Rotate nag: touch clients only, and only while the phone is upright. shell.js puts
   .touch / .desktop on <body>, so it can never appear on a desktop client. */
#rotate { z-index: 6; display: none; }
@media (orientation: portrait) {
  body.touch #rotate { display: flex; }
}
/* shell.js sets .landscape from innerWidth > innerHeight once Telegram's fullscreen viewport
   has actually turned. Inside a Mini App the orientation media query follows the WebView, which
   on iOS can still report portrait for a frame or two after the rotation, so the measured class
   is what finally hides the nag. Losing it (back to portrait) brings the nag back by itself. */
body.landscape #rotate { display: none !important; }

/* ========================= end page shell ================================ */

/* «Да» / «Нет» for the engine's y/n questions (touch.js, play state 5). Above the menu layer
   (z 4) and below the page cards (z 5+), centred low so the question text above stays readable. */
#tc-yesno {
  position: fixed;
  left: 50%;
  bottom: max(18px, env(safe-area-inset-bottom));
  transform: translateX(-50%);
  z-index: 4;
  display: flex;
  gap: 16px;
}
#tc-yesno button {
  min-width: 132px;
  min-height: 52px;
  border: 2px solid rgba(255, 255, 255, .35);
  border-radius: 12px;
  background: rgba(21, 21, 21, .7);
  color: #fff;
  font: 600 20px system-ui, sans-serif;
  touch-action: none;
}
#tc-yes { border-color: #3ddc3a; }
#tc-no { border-color: #b00; }
#tc-yesno button:active { background: rgba(255, 255, 255, .22); }

/* Arena mode (lib/arena.mjs). #respawn is a full-screen tap target above the canvas and the touch
   layer — a dead deathmatch player has nothing else to press — with its hint as a pill low on the
   screen, clear of the frag counter at the top. #arena-result is the same pill shape, higher up,
   over the engine's own tally screen. Neither blocks anything while hidden. */
#respawn {
  position: fixed;
  inset: 0;
  z-index: 6;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: calc(18vh + env(safe-area-inset-bottom));
  background: transparent;
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
  cursor: pointer;
}
#respawn p, #arena-result {
  margin: 0;
  padding: 10px 18px;
  border-radius: 999px;
  background: rgba(0, 0, 0, .62);
  color: #fff;
  font: 600 16px/1.2 system-ui, sans-serif;
  pointer-events: none;
}
#arena-result {
  position: fixed;
  top: calc(12vh + env(safe-area-inset-top));
  left: 50%;
  transform: translateX(-50%);
  z-index: 6;
  white-space: nowrap;
}
#respawn[hidden], #arena-result[hidden] { display: none; }

