/* touch.css - on-screen controls and mobile HUD for Doodle BATTLES.
 *
 * Loaded unconditionally but inert: everything here is gated on the
 * html.tactil class that touch.js adds only on a touch device, so a desktop
 * visitor never sees a control layer or pays for the styles.
 *
 * Stacking order (deliberate):
 *   canvas  z 0
 *   #touch  z 1   <- control zones and buttons
 *   #hud    z 2   <- HUD text, menus, scoreboard
 *   #girar  z 3   <- rotate prompt
 * #hud is pointer-events:none, so taps fall through it to the zones beneath,
 * while the menu and scoreboard still sit on top and receive their own taps.
 */

/* ---------------------------------------------------------------- layer ---- */

#touch { display: none; }
#girar { display: none; }

html.tactil #touch {
  display: block;
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  color: var(--ink);
  font-family: var(--font-body);
}
/* --u is the layout unit: about 1% of the visible viewport height, set from
   visualViewport in touch.js and clamped there. It lives on <html> so the HUD
   rules below, which are not descendants of #touch, inherit the same value.
   The 4px default matches the clamp midpoint for a desktop-sized window and
   only shows for the frame before touch.js first measures. */
html.tactil { --u: 4px; }

/* Default fitted scale for the frame before applyScale() first runs. It is on
   the layer rather than on .tb so the buttons inherit whatever applyScale()
   decides; see the note on .tb. */
html.tactil #touch { --k: 1; }
html.tactil #touch.oculto { display: none; }
html.tactil #hud { z-index: 2; }
html.tactil #c { touch-action: none; }

/* No pull-to-refresh or rubber-banding, but the menu must still scroll. */
html.tactil, html.tactil body { overscroll-behavior: none; }
html.tactil .screen { touch-action: pan-y; -webkit-overflow-scrolling: touch; }

/* ----------------------------------------------------------------- zones --- */

.tz-izq, .tz-der { position: absolute; top: 0; height: 100%; pointer-events: auto; z-index: 1; }
/* Split leaves a thin dead strip in the middle so a thumb sliding across the
   screen cannot swap from walking to aiming mid-gesture. */
.tz-izq { left: 0; width: 46%; }
.tz-der { right: 0; width: 54%; }

/* ------------------------------------------------------------- move stick --- */

/* Geometry is shared with touch.js: STICK_D, STICK_TRAVEL, STICK_HOME_X and
   STICK_HOME_Y there must match 30, 0.4, 24 and 24 here. The knob is 20% of the
   ring (inset 40%), which leaves exactly 0.4 of the diameter as travel, so a
   full push parks the knob on the rim instead of sliding out of it. The negative
   margin is a half-diameter, so left and top address the ring's centre and the
   JS can treat them as the origin.

   Always painted, not shown on touch. PUBG and Free Fire both leave the stick
   visible and it is the reason this one is visible too: a ring you can see
   before you touch is a target a thumb can be trained to land on, and the
   muscle memory that buys is the whole reason those layouts feel good. The idle
   state is dimmer so it does not compete with the crosshair. */
.tpalanca {
  position: absolute;
  left: 0; top: 0;
  /* border-box on purpose. The -15u margin is meant to centre a 30u ring on the
     stick origin, and that only holds if the declared width is the width that
     gets measured. Content-box added the border on the outside, so the ring
     painted at 31u and sat half a border-width off its own origin: invisible to
     a screenshot, but it meant the home position and the drawn ring disagreed
     by a fraction of a unit at every size. */
  box-sizing: border-box;
  width: calc(var(--u) * 30);
  height: calc(var(--u) * 30);
  margin: calc(var(--u) * -15) 0 0 calc(var(--u) * -15);
  border: calc(var(--u) * .5) solid rgba(26, 48, 192, .38);
  border-radius: 50%;
  background: rgba(246, 243, 230, .2);
  opacity: .72;
  pointer-events: none;
  transition: opacity .1s ease, background .1s ease;
  z-index: 3;
}
.tpalanca.on {
  opacity: 1;
  background: rgba(246, 243, 230, .34);
  border-color: rgba(26, 48, 192, .5);
}
/* Inner knob, offset by the JS transform. */
.tpalanca::after {
  content: '';
  position: absolute;
  inset: 40%;
  border-radius: 50%;
  background: rgba(26, 48, 192, .38);
  border: calc(var(--u) * .3) solid rgba(251, 249, 241, .8);
}

/* --------------------------------------------------------------- buttons --- */

/* --x/--y are offsets in --u from the nearest safe-area inset and --d is the
   diameter. --k scales the whole cluster; it is written by applyScale() in
   touch.js and defaults to 1 for the frame before that first runs.

   The grid is a coarse lattice, and every pair of neighbours leaves 2.4u of
   daylight. That margin is what makes the 32px floors below safe: on the
   shortest realistic phone the floor adds well under a pixel per side, so a
   floored button can never grow into its neighbour. The wider margin is a
   direct consequence of moving from text to icons. Labels were what set the
   old pitch, because a word has a minimum width and the columns had to be
   spaced so no two labels could touch. A glyph has no minimum, so the pitch
   comes from touch comfort alone and every button got that difference back as
   diameter. */
/* --k is NOT defaulted here. It is written by applyScale() onto the layer
   element and must be inherited, and a declaration on .tb would shadow that
   inheritance: every button would silently ignore the fitted scale and sit at
   --k: 1, which is correct only on the widest screens. The fallback lives on
   #touch below instead, so the first frame before JS runs is still sane. */
.tb {
  position: absolute;
  /* 19 is the secondary diameter. It is declared here, not per button, because
     every combat button except FIRE takes it from this default and a number
     repeated nine times is a number that will drift. */
  --x: 2; --y: 2; --d: 19;
  right: calc(env(safe-area-inset-right, 0px) + var(--u) * var(--x) * var(--k));
  bottom: calc(env(safe-area-inset-bottom, 0px) + var(--u) * var(--y) * var(--k));
  /* --u tracks viewport height, and a landscape phone is only ~390px tall, so
     a 15u button lands near 58px before --k. 32px stays the hard floor: this is
     the size below which a thumb starts missing presses rather than aiming
     badly, and the fit calculation in touch.js is what keeps the rest above it. */
  width: max(32px, calc(var(--u) * var(--d) * var(--k)));
  height: max(32px, calc(var(--u) * var(--d) * var(--k)));
  margin: 0;
  padding: 0;
  display: grid;
  place-items: center;
  pointer-events: auto;
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--ink);
  background: rgba(251, 249, 241, .5);
  border: max(2px, calc(var(--u) * .45)) solid rgba(26, 48, 192, .55);
  border-radius: 50%;
  box-shadow: 0 calc(var(--u) * .22) 0 rgba(26, 48, 192, .2);
  transition: transform .06s ease, background .08s ease;
  overflow: hidden;
  z-index: 4;
}

/* Icons, not text. A stroke on a 24x24 grid scales with its own button, so one
   rule covers all thirteen and there is no per-button font size to keep in
   sync. currentColor means the pressed state recolours the glyph for free.

   58% of the button is deliberate. A stroked line has to clear the border to
   read at a glance, and the hand-drawn border is already a couple of pixels at
   this scale, so the glyph is kept well inside it rather than filling the
   circle and touching the rim. */
.tb .ico {
  width: 58%;
  height: 58%;
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* A tap can land a frame either side of the press state, and a glyph that
     swaps in slowly is worse than one that does not swap at all. */
  transition: opacity .06s ease;
}
/* The switch button's two arrows need the extra breathing room that the
   others do not, because the paths cross in the middle and would otherwise
   read as a single blob at small sizes. */
.tb-slot .ico { width: 52%; height: 52%; }
/* FIRE is large enough that a full 58% would look thin and off-centre inside
   its own much bigger circle, so it is allowed to fill more. */
.tb-fire .ico { width: 50%; height: 50%; }

/* There used to be a .twpn readout here, spelling out the current weapon under
   the system row. It is gone. The HUD already carries the weapon name in the
   left column, so the layer was showing the same word twice on one screen, and
   the copy on the right was the one colliding with the ammo count. The HUD owns
   every value; the right side owns every control. */
.tb.on { transform: scale(.9); background: rgba(26, 48, 192, .3); }

/* System row: pause, score, music, size and switch, in a single row above the
   combat cluster on the right.

   Placed with bottom, not top. The cluster is positioned with bottom and --y, so
   its height is only directly usable as a distance from the bottom edge; using
   top would mean converting through the layer height, which is the viewport
   height and not the nominal 100u that --u is a percentage of. Getting that
   conversion wrong put the row straight through the middle of the cluster.

   --k is pinned to 1 here on purpose. applyScale() writes the fitted combat
   scale onto the layer and this element inherits it, but the fit exists to stop
   the cluster reaching the movement stick and this row is nowhere near the
   stick. Letting it shrink is a pure side effect: on a portrait phone the fit
   lands near .64, which would quietly drag the pause button down onto the 32px
   floor. It also means the size slider would be resizing the pause button, which
   is not what anyone dragging it is asking for. */
.tb-sys {
  --k: 1;
  top: auto;
  bottom: calc(env(safe-area-inset-bottom, 0px) + var(--u) * (var(--ch, 70) + 2.4));
}

/* Right-hand combat cluster: FIRE dead centre, the eight secondary actions
   flanking it in two columns of three.

     on screen            x runs inward from the right edge,
     left to right        so the x: 1.6 column is the right-hand one

         climb                             nade
         jump         .-----------.        dash
      .---.---.      |    FIRE    |       .---.---.
      | reload |     |   x 23.5   |       | melee  |
      '---'---'      |   y 23     |       '---'---'
                     |   d 25     |
                     '-----------'
         duck           .----.           aim
      .---.---.       '----'
      | jump  |        x 26.5
      '---'---'        y 1.6
         climb

   The obvious way to put FIRE in the middle is a 3x3 with its middle cell blown
   up, which leaves the centre column with only two free slots and the two
   flanking columns with three each: 3 + 3 + 2 = 8. That is exactly the eight
   secondary actions that remain once FIRE and switch-weapon are accounted for,
   so the cluster is a 3x3 minus its centre, not a shape that needed inventing.

   The layout is symmetric about 36u from the right edge, the midpoint of the
   1.6 to 70.4u footprint, and about 35.5u from the bottom, the midpoint of the
   1.6 to 69.4u one. FIRE's 23.5 to 48.5u span and its 23 to 48u span are both
   centred on those two lines. Dead centre is the whole request, so that is what
   this is.

   Column anchors are x = 1.6 and 51.4 for the flanking pair and 26.5 for the
   centre column, which puts its centre at 36u to match. FIRE is --d: 25 centred
   on the same line, so it sits at 23.5, not at 26.5: the two differ by half the
   3u diameter gap between them, and giving both the same --x lines up their
   left edges and leaves the whole layout 1.5u off centre. Rows are
   y = 1.6 / 26 / 50.4 for the flanking columns; the centre column skips the
   middle row because FIRE is in it and runs 1.6 and 50.4 instead.

   Secondary buttons are --d: 19, up from 15, and FIRE is --d: 25, up from 20.
   On a 390px-tall landscape phone that is 74px and 98px, against a 32px floor.
   This is as large as a symmetric 3x3 goes: a --d: 25 FIRE needs 25 plus two
   19u buttons and three 2.4u gaps either side of it to stay centred, which is
   70.8u of height, and the flanking columns need three more rows on top of that
   to stay symmetrical.

   Every neighbouring pair keeps at least 2.4u of daylight, and the two 2.9u gaps
   either side of FIRE are equal, which is what the symmetry above is checked
   on. That margin is what makes the 32px floors safe on the shortest realistic
   phone. A 19u button is more than twice the 9.2u of the text era.

   Those two numbers, 70.4 from the right edge and 69.4 from the bottom, are the
   footprint CLUSTER_W and CLUSTER_H in touch.js measure against. They are
   extents from the screen edge rather than spans between the outermost buttons,
   because the buttons are anchored inward from those edges and it is the far
   edge that has to clear the movement zone. They are duplicated across the two
   files by necessity, since one is a declaration and the other a calculation, so
   they are called out in both places and the test suite asserts both against
   the geometry actually declared here. CLUSTER_H is also published onto the
   layer as --ch, which is what the system row and the weapon readout are placed
   against, so they follow the cluster instead of drifting away from it.

   Reach is the cost here, and it is a bigger cost than centring FIRE alone was.
   The thumb pivots near the bottom-right corner, and the far column's top
   button, grapple, now sits 85u out, which is 334px on a landscape phone. PUBG
   and Free Fire both put their climb and prone controls on the LEFT for exactly
   this reason. Everything is on the right here because that was asked for, so
   if grapple proves to be the button that gets missed, moving it to the left
   column above the stick is a two-line change and it is where the genre puts it. */
.tb-fire { --x: 23.5; --y: 23; --d: 25; transform: rotate(-3deg); }
.tb-fire.on { transform: rotate(-3deg) scale(.9); }

.tb-aim    { --x: 26.5; --y: 1.6; transform: rotate(2deg); }
.tb-aim.on { transform: rotate(2deg) scale(.9); }
.tb-reload { --x: 26.5; --y: 50.4; transform: rotate(-2deg); }
.tb-reload.on { transform: rotate(-2deg) scale(.9); }

.tb-melee { --x: 1.6; --y: 1.6; transform: rotate(2deg); }
.tb-melee.on { transform: rotate(2deg) scale(.9); }
.tb-dash { --x: 1.6; --y: 26; transform: rotate(-2deg); }
.tb-dash.on { transform: rotate(-2deg) scale(.9); }
.tb-nade { --x: 1.6; --y: 50.4; transform: rotate(2deg); }
.tb-nade.on { transform: rotate(2deg) scale(.9); }

.tb-duck { --x: 51.4; --y: 1.6; transform: rotate(-2deg); }
.tb-duck.on { transform: rotate(-2deg) scale(.9); }
.tb-jump { --x: 51.4; --y: 26; transform: rotate(2deg); }
.tb-jump.on { transform: rotate(2deg) scale(.9); }
.tb-grapple { --x: 51.4; --y: 50.4; transform: rotate(-2deg); }
.tb-grapple.on { transform: rotate(-2deg) scale(.9); }

/* System row, above the cluster on the right: switch-weapon, pause, score, music
   and the size control, right-aligned to the cluster's near edge. --y is ignored
   here because .tb-sys places the row off the fitted cluster height. */
.tb-slot { --x: 1.6; --d: 9.6; transform: rotate(3deg); }
.tb-slot.on { transform: rotate(3deg) scale(.9); }
.tb-pausa { --x: 13.6; --d: 9.6; }
.tb-score { --x: 25.6; --d: 9.6; }
.tb-music { --x: 37.6; --d: 9.6; }
.tb-size  { --x: 49.6; --d: 9.6; }

/* --------------------------------------------------------- size control ---- */

/* Sits under the system row, right-aligned to it, and only exists while open.
   It tracks --ch like the row above it, so the two stay locked together as the
   combat fit changes. */
.tset {
  position: absolute;
  right: calc(env(safe-area-inset-right, 0px) + var(--u) * 1.6);
  /* bottom, not top, for the same reason as .tb-sys: the cluster it has to clear
     is positioned from the bottom edge, so the offset it inherits has to be
     measured from the same edge. Placed just above the row, which is 9.6u tall
     and starts 2.4u clear of the cluster. */
  bottom: calc(env(safe-area-inset-bottom, 0px) + var(--u) * (var(--ch, 70) + 13.4));
  display: none;
  align-items: center;
  gap: calc(var(--u) * 1.4);
  width: max(168px, calc(var(--u) * 40));
  padding: calc(var(--u) * 1.6) calc(var(--u) * 2);
  background: rgba(251, 249, 241, .96);
  border: max(2px, calc(var(--u) * .45)) solid rgba(26, 48, 192, .55);
  border-radius: calc(var(--u) * 2.4);
  box-shadow: 0 calc(var(--u) * .3) calc(var(--u) * 1.4) rgba(26, 48, 192, .3);
  font-family: var(--font-display);
  color: var(--ink);
  pointer-events: auto;
  touch-action: none;
  z-index: 6;
}
.tset.open { display: flex; }
.tset-l {
  font-size: max(9px, calc(var(--u) * 2.4));
  white-space: nowrap;
  line-height: 1.1;
}
/* Native range input, restyled rather than replaced. Inheriting the platform
   control means the drag, the thumb and the keyboard all work the way the
   phone's own settings app does, which is a large part of why this is
   reachable one-handed. */
.tset-r {
  flex: 1 1 auto;
  min-width: 0;
  height: calc(var(--u) * 2.2);
  margin: 0;
  accent-color: #1a30c0;
  background: transparent;
  touch-action: none;
}
.tset-v {
  font-size: max(9px, calc(var(--u) * 2.4));
  min-width: calc(var(--u) * 8);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

  /* ------------------------------------------------------------ portrait fit --- */

  /* Portrait no longer needs a squeeze of its own. It used to pin --k to .78
     here because the cluster was a fixed 49.7u and a phone held upright only has
     about 48u past the movement zone. That is now applyScale()'s job: it
     divides the width that is actually left by CLUSTER_W every time the
     viewport changes, which covers portrait, landscape, tablets and iOS
     browser chrome with one rule and no breakpoint to fall out of date.

     What is still worth a media query is what the fit cannot express.

     First, losing the two system niceties: score and music are optional, and on
     a narrow screen the five-button row they sit in is competing for the same
     corner. That leaves switch, pause and size on a phone held upright.

     Second, and the reason the two rules below reassign --x rather than just
     hiding buttons: .tb-sys is pinned to --k: 1, so it does not shrink with the
     fitted cluster. A portrait phone has the largest --u of any viewport we
     care about, which is the worst case for a fixed-width row, and at 59.2u the
     full row reached 260px from a 390px screen. The fit is what keeps the
     cluster out of the movement zone, and the row reaching into that zone is the
     same failure the fit exists to prevent: pausing by accident while walking.
     Narrowing the row to the three buttons that survive keeps each one at full
     size, and 33.6u clears the zone at every portrait width while staying
     right-aligned to the cluster's near edge. */
  @media (orientation: portrait) {
    html.tactil #touch .tb-music,
    html.tactil #touch .tb-score { display: none; }
    html.tactil #touch .tb-pausa { --x: 13.6; }
    html.tactil #touch .tb-size  { --x: 25.6; }
  }

  /* Very narrow landscape (a small phone turned sideways). The cluster is now
     56.8u tall, which is room on a 390px screen but not on a 320px one, so the
     least-used action goes and the symmetric layout survives without it. */
  @media (orientation: landscape) and (max-height: 360px) {
    html.tactil #touch .tb-nade { display: none; }
  }

/* ------------------------------------------------------------ compact HUD --- */

/* Desktop puts score top-left and ammo bottom-left. On touch the bottom-left is
   the movement zone and the bottom-right is the action cluster, so the whole
   read-out moves into a strip along the top, clear of both. */
html.tactil .hud-tl {
  left: calc(env(safe-area-inset-left, 0px) + var(--u) * 10);
  top: calc(env(safe-area-inset-top, 0px) + var(--u) * .4);
  font-size: calc(var(--u) * 3.4);
  transform: rotate(-1deg);
}
html.tactil .hud-bl {
  left: calc(env(safe-area-inset-left, 0px) + var(--u) * 10);
  top: calc(env(safe-area-inset-top, 0px) + var(--u) * 4);
  bottom: auto;
}
html.tactil .hud-br {
  left: calc(env(safe-area-inset-left, 0px) + var(--u) * 10);
  right: auto;
  top: calc(env(safe-area-inset-top, 0px) + var(--u) * 13.6);
  bottom: auto;
  text-align: left;
}
/* The status strip used to sit top-right, on the assumption that the only
   control up there was the pause button. The system row moved to the right and
   took that corner, so the strip joins the rest of the HUD in the left column
   instead: four stacked readouts on one side, every control on the other. */
html.tactil .hud-tr {
  right: auto;
  left: calc(env(safe-area-inset-left, 0px) + var(--u) * 10);
  top: calc(env(safe-area-inset-top, 0px) + var(--u) * 23.2);
  font-size: calc(var(--u) * 2.6);
  text-align: left;
}
html.tactil .hud-tr .left { font-size: calc(var(--u) * 2); }

html.tactil .ammo b { font-size: calc(var(--u) * 5.4); }
html.tactil .ammo span { font-size: calc(var(--u) * 2.4); }
html.tactil .reloading { font-size: calc(var(--u) * 1.8) !important; }
html.tactil .health { font-size: calc(var(--u) * 2); gap: calc(var(--u) * .7); margin-bottom: calc(var(--u) * .4); }
html.tactil .bar { width: calc(var(--u) * 22); height: calc(var(--u) * 1.5); }
html.tactil .nades { margin-left: calc(var(--u) * .8); }
html.tactil .nades i { width: calc(var(--u) * 1.1); height: calc(var(--u) * 1.3); border-width: calc(var(--u) * .2); }
html.tactil .nades i::after { left: calc(var(--u) * .25); top: calc(var(--u) * -.5); width: calc(var(--u) * .25); height: calc(var(--u) * .4); }
html.tactil .weapon { font-size: calc(var(--u) * 2.4); }
html.tactil .hint { display: none; }
/* The per-weapon slot list is a keyboard affordance; the GUN button covers it. */
html.tactil .slots { display: none; }
/* Decorative wave tally would sit under the movement thumb. */
html.tactil .tally { display: none; }

/* Tips and kill feed were placed for a mouse UI, in the middle of the new
   button cluster. */
html.tactil .tip {
  bottom: auto;
  top: calc(env(safe-area-inset-top, 0px) + var(--u) * 2.4);
  font-size: calc(var(--u) * 2.2);
  padding: 0 calc(var(--u) * 4);
}
html.tactil .killfeed {
  top: 52%;
  right: calc(env(safe-area-inset-right, 0px) + var(--u) * 2);
  font-size: calc(var(--u) * 2.1);
}
html.tactil .message { top: 20%; }
html.tactil .msg-main { font-size: calc(var(--u) * 11); }
html.tactil .msg-sub { font-size: calc(var(--u) * 2.8); }
html.tactil .combo { font-size: calc(var(--u) * 2.2); min-height: calc(var(--u) * 2.6); }
html.tactil .modifier { font-size: calc(var(--u) * 1.9); }
html.tactil .timer { font-size: calc(var(--u) * 2); }
html.tactil .pvpscore { font-size: calc(var(--u) * 1.7); }

/* Fixed 420px-wide chrome does not fit a phone. */
html.tactil .bossbar { width: min(420px, 62vw); margin-left: min(-210px, -31vw); font-size: calc(var(--u) * 2.1); top: calc(env(safe-area-inset-top, 0px) + var(--u) * 7); }
html.tactil .bar.big { width: 100%; height: calc(var(--u) * 1.7); }
html.tactil .board { min-width: 0; width: min(420px, 90vw); font-size: calc(var(--u) * 2.1); padding: calc(var(--u) * 1.2) calc(var(--u) * 2); top: 16%; }
html.tactil .board h3 { font-size: calc(var(--u) * 2.4); }
html.tactil .board .foot { font-size: calc(var(--u) * 1.5); }

/* ---------------------------------------------------------- legend swap --- */

/* Three columns do not fit; show only the one that matches the device. */
.panel .col-touch { display: none; }
html.tactil .panel .col-mk,
html.tactil .panel .col-pad { display: none; }
html.tactil .panel .col-touch { display: block; }

/* --------------------------------------------------------- rotate prompt --- */

html.tactil #girar {
  display: grid;
  position: fixed;
  inset: 0;
  z-index: 3;
  place-items: center;
  text-align: center;
  padding: calc(var(--u) * 6);
  background: rgba(26, 48, 192, .93);
  color: var(--paper-hi);
}
html.tactil #girar.oculto { display: none; }
#girar .g-icon {
  width: calc(var(--u) * 16);
  height: calc(var(--u) * 16);
  margin: 0 auto calc(var(--u) * 3);
  border: calc(var(--u) * .8) solid var(--paper-hi);
  border-radius: calc(var(--u) * 2.5);
  animation: gira 2.4s ease-in-out infinite;
}
@keyframes gira {
  0%, 40% { transform: rotate(0deg); }
  60%, 100% { transform: rotate(-90deg); }
}
#girar h2 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: calc(var(--u) * 6);
  margin: 0 0 calc(var(--u) * 1.4);
  letter-spacing: 2px;
}
#girar p { font-size: calc(var(--u) * 3); margin: 0; opacity: .9; }

/* ------------------------------------------------- iOS install nudge --- */

/* Only ever shown on iOS Safari, which cannot install a PWA from a banner. */
#ioshint {
  position: fixed;
  z-index: 4;
  left: 50%;
  bottom: calc(env(safe-area-inset-bottom, 0px) + var(--u) * 22);
  transform: translateX(-50%);
  display: none;
  align-items: center;
  gap: calc(var(--u) * 1.4);
  max-width: min(420px, 88vw);
  padding: calc(var(--u) * 1.4) calc(var(--u) * 2.4);
  font-size: calc(var(--u) * 2.1);
  line-height: 1.25;
  color: var(--ink);
  background: var(--paper-hi);
  border: calc(var(--u) * .3) solid var(--ink);
  border-radius: var(--wob-b);
  box-shadow: var(--lift);
  pointer-events: auto;
  cursor: pointer;
}
html.tactil #ioshint.mostrar { display: flex; }
#ioshint .ih-x { font-size: calc(var(--u) * 3); opacity: .6; margin-left: auto; }
#ioshint b { font-family: var(--font-display); font-weight: 400; color: var(--red); }
