/* © 2026 MEK.txt. All rights reserved. Part of the MEK design system; not licensed for reuse. */
/* MEK design system · part: HUD
   The game kit's heads-up display: stat rows (an icon, a label, a cell meter, the numbers), hearts, vials, an orb, a minimap and a score that counts
   up a step a frame. Opt-in: link it after mek.css, with js/hud.js:
     mekHud.stat(document.querySelector("#hp"), 9);      // to 9: the numbers flash, red on a loss, green on a gain
     mekHud.score(document.querySelector("#score"), 120); // counts up from what it shows
     mekHud.lives(document.querySelector("#lives"), 2.5);  // a heart and a half lost: they blink, the row shakes
     mekHud.vials(document.querySelector("#vials"), 2.5);  // drains a row a frame, the row shakes
     mekHud.orb(document.querySelector("#hp-orb"), 40);    // drains a row a frame, the orb shakes
     mekHud.map(document.querySelector("#map"), { at, x: 12, y: 8, facing: "ne", light: 6 }); // a minimap round the player
   Needs the core meter (progress and meter). In the mek.parts layer, so a project's CSS wins. */

@layer mek.parts {

/* ---------- Dials ---------- */
/* on every themed area too, so a box in another theme works these out from its own colours */
:root, [data-theme] {
  --hud-bar: 240px;                    /* a stat's meter, unless the row is given more room (.hud-stat.wide); .sm: 120px */
  --hud-heart: var(--bad);             /* a life: whole and half hearts */
  --hud-heart-empty: var(--text-muted);/* a life lost: the empty heart, 700 */
  --hud-heart-gain: var(--good);       /* a heart won back flashes this */
  --hud-vial: var(--bad);              /* a vial's liquid: its surface and lit column; the body and shadow are it darkened */
  --hud-vial-gain: var(--good);        /* a vial filling flashes this */
  --hud-vial-glass: var(--text-soft);  /* the glass, lit side (top left): 800 */
  --hud-vial-shade: var(--text-muted); /* the glass, shaded side (bottom right): 700 */
  --hud-vial-hollow: var(--surface);   /* inside an empty vial: 200 */
  --hud-vial-glint: var(--line);       /* the glint on empty glass, and the empty vial's floor: 400 */
  --hud-orb: var(--bad);               /* an orb's liquid: its surface and lit pixels; the body and shadows are it darkened */
  --hud-orb-gain: var(--good);         /* an orb filling flashes this */
  --hud-orb-rim: var(--text);          /* the glass ring, lit: 900 */
  --hud-orb-rim-shade: var(--text-soft);/* the ring in shade (bottom right): 800 */
  --hud-orb-rim-glint: var(--white);   /* the glint on the ring (top left) */
  --hud-orb-hollow: var(--surface);    /* inside the glass, above the liquid: 200 */
  --hud-orb-shine: var(--text-strong); /* the shine over the liquid: 1000 */
  --hud-orb-shine-core: var(--white);  /* its brightest pixels */
  --hud-orb-glass-shine: var(--500);   /* the shine where the orb has drained, on empty glass: 500 */
  --hud-map-floor: var(--line);        /* a minimap's ground, and any kind without its own colour: 400 */
  --hud-map-wall: var(--text-muted);   /* walls and rock: 700 */
  --hud-map-water: var(--500-accent);  /* water */
  --hud-map-grass: var(--fog-tone-2);  /* grass and trees: green toward the ground, so a quest mark stands out */
  --hud-map-memory: var(--fog-far);    /* a place seen before, out of sight now: half its pixels, as the fog's far silhouette */
  --hud-map-me: var(--text-strong);    /* the player's marker: 1000 */
  --hud-map-quest: var(--accent);      /* marks: where to go */
  --hud-map-danger: var(--bad);        /* what to keep away from */
  --hud-map-loot: var(--warn);         /* what to pick up */
  --hud-map-glint: var(--hud-orb-shine-core); /* four pixels of shine on the glass */
  --hud-feed-rows: 4;                  /* a pickup feed's rows: its height never changes */
  --hud-feed-gain: var(--good);        /* "+3": something got */
  --hud-feed-loss: var(--bad);         /* "-1": something spent or lost */
  --hud-feed-time: 2400ms;             /* how long a line stays before it dissolves */
  --hud-compass: 320px;                /* a compass strip's width (.sm: 160px) */
  --hud-compass-span: 180;             /* the degrees it shows across */
  --hud-compass-tick: var(--text-muted);/* its ticks: 700 */
  --hud-compass-letter: var(--text);   /* N E S W: 900; N in --hud-compass-north */
  --hud-compass-north: var(--bad);     /* N, so it's found at a glance */
  --hud-compass-me: var(--text-strong);/* the line at its middle, the way you face: 1000 */
}
/* Light is the one light theme: there 1000 is near black, so the shine is white, and the glint near black. */
:root:not([data-theme]), [data-theme="light"] { --hud-orb-shine: var(--white); --hud-orb-rim-glint: var(--text-strong); }

/* ---------- HUD ----------
   <div class="hud"> lines things up: 16px apart, wrapping. */
.hud { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); }

/* ---------- Stat ----------
   <div class="hud-stat"><svg class="icon">…stat-hp</svg><span class="hud-label">HP</span>
     <meter class="meter" min="0" max="20" low="6" high="12" optimum="20" value="18"></meter><span class="hud-num"></span></div>
   Twice the size by default, every pixel two: the 15px stat icon (stat-hp, -magic, -attack, -defence, -speed) drawn 30×30, the label in Mono 24 (700, 3
   letters' room), the meter --hud-bar wide, less where there isn't room (.wide: all the room) with 8px cells 2px apart, 16px tall, the numbers in
   Mono 24 (1000), "18/20", as wide as the largest, so a change never moves anything. .sm: the 1× size (Mono 12, the
   icon 15×15, the core meter's 4px cells, a 120px bar). An HP meter with low/high
   turns good, warn and bad on its own. A loss (.hit): the numbers flash inverse in --bad, two blinks, and the row
   shakes a pixel or two; a gain (.heal): the numbers flash inverse in --good. Neither under reduced motion. */
.hud-stat { display: grid; grid-template-columns: auto auto minmax(0, var(--hud-bar)) auto; justify-content: start;
  align-items: center; gap: 12px;   /* fills its place, the columns packed at the start: the bar gives up room first, no column ever gets the spare */
  font: var(--text-lg)/24px var(--font-mono); -webkit-font-smoothing: none;
  --meter-cell: 8px; --meter-gap: 2px; --meter-h: 16px; }
.hud-stat > .icon { width: 30px; height: 30px; vertical-align: 0; }
.hud-stat.sm { --hud-bar: 120px; gap: 6px; font: var(--text-crisp)/1 var(--font-mono);
  --meter-cell: 4px; --meter-gap: 1px; --meter-h: 8px; }
.hud-stat.sm > .icon { width: 15px; height: 15px; }
.hud-stat.wide { grid-template-columns: auto auto minmax(0, 1fr) auto; }
.hud-stat > .icon { color: var(--text-soft); }
.hud-label { min-width: 3ch; color: var(--text-muted); }   /* HP, MAG, ATK: three letters at most, so stacked bars start together */
.hud-num { justify-self: end; padding: 0 4px; text-align: right; color: var(--text-strong); }   /* js/hud.js sets its min-width to the widest it can say */
.hud-stat.hit { animation: mek-shake var(--dur-bar) var(--ease-frames-4); }
.hud-stat.hit > .hud-num { animation: mek-hud-flash var(--dur-beat) steps(1, end) 2; --hud-flash: var(--bad); }
.hud-stat.heal > .hud-num { animation: mek-hud-flash var(--dur-beat) steps(1, end) 2; --hud-flash: var(--good); }
@keyframes mek-hud-flash { 0% { background: var(--hud-flash); color: var(--bg); } 50% { background: none; color: var(--text-strong); } }

/* ---------- Lives ----------
   <span class="hud-lives" data-lives="3.5" data-max="5"></span>: a heart for each life, whole, half or empty (halves
   round to the nearest), data-max hearts always, so losing one never moves anything. Twice the size by default, every
   pixel two: the 9×8 heart cut out of the 15px icon, drawn 18×16, 4px apart; .sm is the 1× size (9×8, 2px apart).
   Whole and half in --hud-heart, empty in --hud-heart-empty. js/hud.js draws the hearts and the label ("3.5 of 5
   lives"; data-label names them). mekHud.lives(el, value): a loss blinks the hearts that changed twice and shakes the
   row; a gain flashes them in --hud-heart-gain. Neither under reduced motion. The hearts are masks of
   assets/icons/heart*.svg, here as data: URLs (a linked mask vanishes off its own origin): redraw one, copy it here. */
.hud-lives { display: inline-flex; gap: 4px; vertical-align: middle;
  --hud-px: 2px;   /* one icon pixel */
  --heart: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 15 15' shape-rendering='crispEdges'%3E%3Cpath d='M4 4h2v1h-2zM9 4h2v1h-2zM3 5h4v1h-4zM8 5h4v1h-4zM3 6h9v1h-9zM3 7h9v1h-9zM4 8h7v1h-7zM5 9h5v1h-5zM6 10h3v1h-3zM7 11h1v1h-1z'/%3E%3C/svg%3E");
  --heart-half: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 15 15' shape-rendering='crispEdges'%3E%3Cpath d='M4 4h2v1h-2zM9 4h2v1h-2zM3 5h4v1h-4zM8 5h1v1h-1zM11 5h1v1h-1zM3 6h5v1h-5zM11 6h1v1h-1zM3 7h4v1h-4zM11 7h1v1h-1zM4 8h3v1h-3zM10 8h1v1h-1zM5 9h2v1h-2zM9 9h1v1h-1zM6 10h1v1h-1zM8 10h1v1h-1zM7 11h1v1h-1z'/%3E%3C/svg%3E");
  --heart-empty: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 15 15' shape-rendering='crispEdges'%3E%3Cpath d='M4 4h2v1h-2zM9 4h2v1h-2zM3 5h1v1h-1zM6 5h1v1h-1zM8 5h1v1h-1zM11 5h1v1h-1zM3 6h1v1h-1zM7 6h1v1h-1zM11 6h1v1h-1zM3 7h1v1h-1zM11 7h1v1h-1zM4 8h1v1h-1zM10 8h1v1h-1zM5 9h1v1h-1zM9 9h1v1h-1zM6 10h1v1h-1zM8 10h1v1h-1zM7 11h1v1h-1z'/%3E%3C/svg%3E"); }
.hud-lives.sm { gap: 2px; --hud-px: 1px; }
.hud-lives > i { display: block; width: calc(9 * var(--hud-px)); height: calc(8 * var(--hud-px)); background: var(--hud-heart);
  -webkit-mask: var(--heart) calc(-3 * var(--hud-px)) calc(-4 * var(--hud-px)) / calc(15 * var(--hud-px)) no-repeat;
  mask: var(--heart) calc(-3 * var(--hud-px)) calc(-4 * var(--hud-px)) / calc(15 * var(--hud-px)) no-repeat; }
.hud-lives > i[data-heart="half"] { -webkit-mask-image: var(--heart-half); mask-image: var(--heart-half); }
.hud-lives > i[data-heart="empty"] { -webkit-mask-image: var(--heart-empty); mask-image: var(--heart-empty); background: var(--hud-heart-empty); }
.hud-lives.hit { animation: mek-shake var(--dur-bar) var(--ease-frames-4); }
.hud-lives > i.lost { animation: mek-hud-blink var(--dur-beat) steps(1, end) 2; }
.hud-lives > i.won { animation: mek-hud-won var(--dur-beat) steps(1, end) 2; }
@keyframes mek-hud-blink { 0% { visibility: hidden; } 50% { visibility: visible; } }
@keyframes mek-hud-won { 0% { background: var(--hud-heart-gain); } 50% { background: var(--hud-heart); } }

/* ---------- Vials ----------
   <span class="hud-vials" data-value="3.5" data-max="5"></span>: a vial for each of data-max, filled from the bottom,
   7 rows inside: full, empty or anywhere between (a part fills floor(7 × part) rows, at least 1: a half is 3). Twice
   the size by default, every pixel two: the 7×10 vial drawn 14×20, 4px apart; .sm is the 1× size (7×10, 2px apart).
   Always data-max vials, so emptying one never moves anything. The liquid is --hud-vial (--bad: HP); set it on the
   row (or one vial) for anything else: style="--hud-vial: var(--accent)". Its body and shadow are it darkened (75%,
   45%), its shine lightened (40%); a faithful theme mixes nothing, so its liquid is one colour and its shine --white.
   The glass is --hud-vial-glass (lit) and --hud-vial-shade, empty inside in --hud-vial-hollow with a --hud-vial-glint.
   Drawn in CSS, one gradient a run of pixels, so every colour is a token and follows the theme.
   js/hud.js draws the vials and the label ("3.5 of 5 vials"; data-label names them). mekHud.vials(el, value): a
   loss drains a row a frame, the last vial first, and shakes the row; a gain fills a row a frame and flashes the
   liquid in --hud-vial-gain. At once under reduced motion. */
@property --lvl { syntax: "<integer>"; inherits: false; initial-value: 7; }   /* rows of liquid: 0 to 7 */
.hud-vials { display: inline-flex; gap: 4px; vertical-align: middle; --hud-px: 2px; }
.hud-vials.sm { gap: 2px; --hud-px: 1px; }
.hud-vials > i { --p: var(--hud-px); display: block; width: calc(7 * var(--p)); height: calc(10 * var(--p));
  --v-hi: var(--hud-vial);
  --v-mid: color-mix(in srgb, var(--hud-vial) calc(75% + 25% * var(--faithful)), var(--black));
  --v-dark: color-mix(in srgb, var(--hud-vial) calc(45% + 55% * var(--faithful)), var(--black));
  --v-shine: color-mix(in srgb, var(--white) calc(var(--faithful) * 100%), color-mix(in srgb, var(--hud-vial) 40%, var(--white)));
  background:
    /* liquid: the surface, a lit row */
    linear-gradient(var(--v-hi) 0 0) calc(1 * var(--p)) calc((8 - var(--lvl)) * var(--p)) / calc(5 * var(--p)) calc(min(var(--lvl), 1) * var(--p)),
    /* the shine, under the surface once there are 3 rows */
    linear-gradient(var(--v-shine) 0 0) calc(2 * var(--p)) calc((9 - var(--lvl)) * var(--p)) / calc(1 * var(--p)) calc(clamp(0, var(--lvl) - 2, 1) * var(--p)),
    /* the lit column */
    linear-gradient(var(--v-hi) 0 0) calc(2 * var(--p)) calc((9 - var(--lvl)) * var(--p)) / calc(1 * var(--p)) calc(max(var(--lvl) - 1, 0) * var(--p)),
    /* the shadow column */
    linear-gradient(var(--v-dark) 0 0) calc(5 * var(--p)) calc((9 - var(--lvl)) * var(--p)) / calc(1 * var(--p)) calc(max(var(--lvl) - 1, 0) * var(--p)),
    /* the body */
    linear-gradient(var(--v-mid) 0 0) calc(1 * var(--p)) calc((9 - var(--lvl)) * var(--p)) / calc(5 * var(--p)) calc(max(var(--lvl) - 1, 0) * var(--p)),
    /* the bottom row */
    linear-gradient(var(--v-dark) 0 0) calc(1 * var(--p)) calc(8 * var(--p)) / calc(5 * var(--p)) calc(min(var(--lvl), 1) * var(--p)),
    /* glass: the glint inside */
    linear-gradient(var(--hud-vial-glint) 0 0) calc(3 * var(--p)) calc(1 * var(--p)) / calc(2 * var(--p)) calc(2 * var(--p)),
    /* the hollow */
    linear-gradient(var(--hud-vial-hollow) 0 0) calc(1 * var(--p)) calc(1 * var(--p)) / calc(5 * var(--p)) calc(7 * var(--p)),
    /* the floor */
    linear-gradient(var(--hud-vial-glint) 0 0) calc(1 * var(--p)) calc(8 * var(--p)) / calc(5 * var(--p)) calc(1 * var(--p)),
    /* the rim and its glint */
    linear-gradient(var(--white) 0 0) calc(3 * var(--p)) 0 / calc(2 * var(--p)) calc(1 * var(--p)),
    linear-gradient(var(--hud-vial-glass) 0 0) calc(1 * var(--p)) 0 / calc(2 * var(--p)) calc(1 * var(--p)),
    linear-gradient(var(--hud-vial-glass) 0 0) calc(5 * var(--p)) 0 / calc(1 * var(--p)) calc(1 * var(--p)),
    /* the sides, lit then shaded */
    linear-gradient(var(--hud-vial-glass) 0 0) 0 calc(1 * var(--p)) / calc(1 * var(--p)) calc(5 * var(--p)),
    linear-gradient(var(--hud-vial-shade) 0 0) 0 calc(6 * var(--p)) / calc(1 * var(--p)) calc(3 * var(--p)),
    linear-gradient(var(--hud-vial-glass) 0 0) calc(6 * var(--p)) calc(1 * var(--p)) / calc(1 * var(--p)) calc(3 * var(--p)),
    linear-gradient(var(--hud-vial-shade) 0 0) calc(6 * var(--p)) calc(4 * var(--p)) / calc(1 * var(--p)) calc(5 * var(--p)),
    /* the foot */
    linear-gradient(var(--hud-vial-shade) 0 0) calc(1 * var(--p)) calc(9 * var(--p)) / calc(5 * var(--p)) calc(1 * var(--p));
  background-repeat: no-repeat;
  transition: --lvl 0s linear; }   /* js/hud.js sets the duration: a frame a row */
.hud-vials.hit { animation: mek-shake var(--dur-bar) var(--ease-frames-4); }
.hud-vials > i.won { animation: mek-hud-vial-won var(--dur-beat) steps(1, end) 2; }
@keyframes mek-hud-vial-won { 0%, 49% { --hud-vial: var(--hud-vial-gain); } 50%, 100% { --hud-vial: inherit; } }   /* a custom property flips halfway between keyframes: hold each half */

/* ---------- Orb ----------
   <span class="hud-orb" data-value="70" data-max="100" data-label="health"></span>: a glass ball of liquid, the big
   gauge in the corner (health, mana), from the user's Pixelmek sketch. 26 rows of liquid inside: a value fills its
   share of them, rounded, at least 1 and at most 25 until it's full or empty. Twice the size by default, every pixel
   two: the 32×32 orb drawn 64×64; .sm is the 1× size (32), .lg 4× (128). Its size never changes.
   The liquid is --hud-orb (--bad: health); set it on the orb for anything else: style="--hud-orb: var(--accent)".
   The surface row and the lit pixels are that colour; the body is it darkened (80%), the shadow (50%) and the
   deepest shadow (30%) more. Set --hud-orb-body, --hud-orb-lit, --hud-orb-shadow or --hud-orb-deep on the orb to
   choose one exactly. A faithful theme mixes nothing: the liquid is one colour, the shadow it dithered with the
   ground (2×2), the deepest shadow the ground with it one pixel in four.
   The glass is a ring (--hud-orb-rim, --hud-orb-rim-shade, a --hud-orb-rim-glint), empty inside in --hud-orb-hollow.
   The shine sits on the glass: over liquid it's --hud-orb-shine with a --hud-orb-shine-core; where the orb has
   drained it's --hud-orb-glass-shine, the glass catching the light. Each colour is a layer, masked to its pixels,
   so every one is a token and follows the theme; the level clips them a row at a time.
   js/hud.js draws the layers and the label ("70 of 100 health"; data-label names it, "health" by default).
   mekHud.orb(el, value): a loss drains a row a frame and shakes the orb; a gain fills a row a frame and flashes the
   liquid in --hud-orb-gain. At once under reduced motion. */
@property --hud-orb-lvl { syntax: "<integer>"; inherits: true; initial-value: 26; }   /* rows of liquid: 0 to 26 */
.hud-orb, .hud-map { --hud-px: 2px; --p: var(--hud-px); position: relative; display: inline-block; flex: none; vertical-align: middle;
  width: calc(32 * var(--p)); height: calc(32 * var(--p));
  --orb-rim-shade: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' shape-rendering='crispEdges'%3E%3Cpath d='M1 15h1v1h-1zM1 17h1v1h-1zM1 18h1v1h-1zM1 19h1v1h-1zM30 19h1v1h-1zM2 20h1v1h-1zM29 20h1v1h-1zM2 21h1v1h-1zM29 21h1v1h-1zM2 22h1v1h-1zM29 22h1v1h-1zM3 23h1v1h-1zM28 23h1v1h-1zM4 24h1v1h-1zM27 24h1v1h-1zM4 25h1v1h-1zM27 25h1v1h-1zM5 26h1v1h-1zM26 26h1v1h-1zM6 27h2v1h-2zM24 27h2v1h-2zM8 28h1v1h-1zM23 28h1v1h-1zM9 29h3v1h-3zM20 29h3v1h-3zM12 30h8v1h-8z'/%3E%3C/svg%3E");
  --orb-rim: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' shape-rendering='crispEdges'%3E%3Cpath d='M12 1h5v1h-5zM18 1h2v1h-2zM9 2h3v1h-3zM20 2h2v1h-2zM8 3h1v1h-1zM6 4h2v1h-2zM5 5h1v1h-1zM4 6h1v1h-1zM4 7h1v1h-1zM3 8h1v1h-1zM2 9h1v1h-1zM2 10h1v1h-1zM2 11h1v1h-1zM29 11h1v1h-1zM1 12h1v1h-1zM30 12h1v1h-1zM1 13h1v1h-1zM30 13h1v1h-1zM1 14h1v1h-1zM30 15h1v1h-1zM1 16h1v1h-1zM30 16h1v1h-1zM30 17h1v1h-1zM30 18h1v1h-1z'/%3E%3C/svg%3E");
  --orb-rim-glint: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' shape-rendering='crispEdges'%3E%3Cpath d='M17 1h1v1h-1zM22 2h1v1h-1zM23 3h1v1h-1zM24 4h2v1h-2zM26 5h1v1h-1zM27 6h1v1h-1zM27 7h1v1h-1zM28 8h1v1h-1zM29 9h1v1h-1zM29 10h1v1h-1zM30 14h1v1h-1z'/%3E%3C/svg%3E");
  --orb-hollow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' shape-rendering='crispEdges'%3E%3Cpath d='M12 2h8v1h-8zM9 3h14v1h-14zM8 4h16v1h-16zM6 5h20v1h-20zM5 6h22v1h-22zM5 7h22v1h-22zM4 8h24v1h-24zM3 9h26v1h-26zM3 10h26v1h-26zM3 11h26v1h-26zM2 12h28v1h-28zM2 13h28v1h-28zM2 14h28v1h-28zM2 15h28v1h-28zM2 16h28v1h-28zM2 17h28v1h-28zM2 18h28v1h-28zM2 19h28v1h-28zM3 20h26v1h-26zM3 21h26v1h-26zM3 22h26v1h-26zM4 23h24v1h-24zM5 24h22v1h-22zM5 25h22v1h-22zM6 26h20v1h-20zM8 27h16v1h-16zM9 28h14v1h-14zM12 29h8v1h-8z'/%3E%3C/svg%3E");
  --orb-liquid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' shape-rendering='crispEdges'%3E%3Cpath d='M12 3h8v1h-8zM10 4h12v1h-12zM8 5h16v1h-16zM7 6h18v1h-18zM6 7h20v1h-20zM5 8h22v1h-22zM5 9h22v1h-22zM4 10h24v1h-24zM4 11h24v1h-24zM3 12h26v1h-26zM3 13h26v1h-26zM3 14h26v1h-26zM3 15h26v1h-26zM3 16h26v1h-26zM3 17h26v1h-26zM3 18h26v1h-26zM3 19h26v1h-26zM4 20h24v1h-24zM4 21h24v1h-24zM5 22h22v1h-22zM5 23h22v1h-22zM6 24h20v1h-20zM7 25h18v1h-18zM8 26h16v1h-16zM10 27h12v1h-12zM12 28h8v1h-8z'/%3E%3C/svg%3E");
  --orb-lit: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' shape-rendering='crispEdges'%3E%3Cpath d='M12 3h3v1h-3zM16 3h3v1h-3zM11 4h1v1h-1zM13 4h1v1h-1zM15 4h1v1h-1zM17 4h1v1h-1zM19 4h1v1h-1zM21 4h1v1h-1zM8 5h1v1h-1zM10 5h1v1h-1zM12 5h1v1h-1zM14 5h10v1h-10zM9 6h1v1h-1zM11 6h1v1h-1zM13 6h1v1h-1zM15 6h3v1h-3zM19 6h3v1h-3zM23 6h1v1h-1zM6 7h1v1h-1zM8 7h1v1h-1zM10 7h1v1h-1zM12 7h14v1h-14zM7 8h1v1h-1zM9 8h1v1h-1zM11 8h1v1h-1zM13 8h7v1h-7zM21 8h1v1h-1zM23 8h3v1h-3zM6 9h1v1h-1zM8 9h1v1h-1zM10 9h15v1h-15zM26 9h1v1h-1zM7 10h1v1h-1zM9 10h1v1h-1zM11 10h1v1h-1zM13 10h13v1h-13zM27 10h1v1h-1zM8 11h1v1h-1zM10 11h1v1h-1zM12 11h15v1h-15zM7 12h1v1h-1zM9 12h1v1h-1zM11 12h1v1h-1zM13 12h1v1h-1zM15 12h9v1h-9zM25 12h1v1h-1zM27 12h1v1h-1zM8 13h1v1h-1zM10 13h1v1h-1zM12 13h1v1h-1zM14 13h11v1h-11zM26 13h1v1h-1zM28 13h1v1h-1zM9 14h1v1h-1zM11 14h1v1h-1zM13 14h1v1h-1zM15 14h1v1h-1zM17 14h5v1h-5zM23 14h1v1h-1zM25 14h1v1h-1zM27 14h1v1h-1zM8 15h1v1h-1zM10 15h1v1h-1zM12 15h3v1h-3zM16 15h7v1h-7zM24 15h1v1h-1zM26 15h1v1h-1zM28 15h1v1h-1zM11 16h1v1h-1zM13 16h1v1h-1zM15 16h1v1h-1zM17 16h1v1h-1zM19 16h1v1h-1zM21 16h1v1h-1zM23 16h1v1h-1zM27 16h1v1h-1zM10 17h1v1h-1zM12 17h1v1h-1zM14 17h1v1h-1zM16 17h1v1h-1zM18 17h1v1h-1zM20 17h1v1h-1zM22 17h1v1h-1zM24 17h1v1h-1zM26 17h1v1h-1zM28 17h1v1h-1zM13 18h1v1h-1zM15 18h1v1h-1zM17 18h1v1h-1zM19 18h1v1h-1zM21 18h1v1h-1zM23 18h1v1h-1zM25 18h1v1h-1zM12 19h1v1h-1zM14 19h1v1h-1zM16 19h1v1h-1zM18 19h1v1h-1zM20 19h1v1h-1zM22 19h1v1h-1zM24 19h1v1h-1zM26 19h1v1h-1zM15 20h1v1h-1zM19 20h1v1h-1zM18 21h1v1h-1zM20 21h1v1h-1zM22 21h1v1h-1zM24 21h1v1h-1z'/%3E%3C/svg%3E");
  --orb-shadow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' shape-rendering='crispEdges'%3E%3Cpath d='M5 10h1v1h-1zM5 11h2v1h-2zM4 12h3v1h-3zM4 13h3v1h-3zM4 14h4v1h-4zM4 15h4v1h-4zM5 16h3v1h-3zM4 17h6v1h-6zM4 18h2v1h-2zM7 18h3v1h-3zM4 19h8v1h-8zM5 20h1v1h-1zM7 20h1v1h-1zM9 20h3v1h-3zM13 20h2v1h-2zM5 21h13v1h-13zM25 21h2v1h-2zM7 22h1v1h-1zM9 22h1v1h-1zM11 22h1v1h-1zM13 22h1v1h-1zM15 22h3v1h-3zM19 22h7v1h-7zM6 23h20v1h-20zM7 24h1v1h-1zM9 24h1v1h-1zM11 24h1v1h-1zM13 24h1v1h-1zM15 24h1v1h-1zM17 24h1v1h-1zM19 24h1v1h-1zM21 24h3v1h-3zM8 25h1v1h-1zM10 25h3v1h-3zM14 25h3v1h-3zM18 25h6v1h-6zM11 26h1v1h-1zM13 26h1v1h-1zM15 26h1v1h-1zM17 26h1v1h-1zM19 26h1v1h-1zM21 26h1v1h-1zM12 27h3v1h-3zM16 27h4v1h-4z'/%3E%3C/svg%3E");
  --orb-deep: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' shape-rendering='crispEdges'%3E%3Cpath d='M28 19h1v1h-1z'/%3E%3C/svg%3E");
  --orb-shine: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' shape-rendering='crispEdges'%3E%3Cpath d='M12 3h8v1h-8zM11 4h11v1h-11zM9 5h5v1h-5zM17 5h5v1h-5zM8 6h6v1h-6zM16 6h2v1h-2zM19 6h4v1h-4zM15 7h5v1h-5zM22 7h2v1h-2zM15 8h1v1h-1zM17 8h1v1h-1zM19 8h1v1h-1zM22 8h2v1h-2zM14 9h1v1h-1zM16 9h1v1h-1zM18 9h5v1h-5zM24 9h1v1h-1zM15 10h1v1h-1zM17 10h1v1h-1zM19 10h1v1h-1zM21 10h1v1h-1zM23 10h1v1h-1zM27 10h1v1h-1zM14 11h1v1h-1zM16 11h1v1h-1zM18 11h1v1h-1zM20 11h3v1h-3zM24 11h1v1h-1zM27 11h1v1h-1zM19 12h1v1h-1zM23 12h1v1h-1zM28 12h1v1h-1zM18 13h1v1h-1zM20 13h1v1h-1zM22 13h1v1h-1zM26 13h1v1h-1zM28 13h1v1h-1zM28 14h1v1h-1zM21 15h2v1h-2zM27 15h2v1h-2zM20 16h4v1h-4zM27 16h2v1h-2zM21 17h2v1h-2zM27 17h2v1h-2zM26 18h3v1h-3zM26 19h2v1h-2zM26 20h1v1h-1zM25 21h2v1h-2zM24 22h2v1h-2z'/%3E%3C/svg%3E");
  --orb-shine-core: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' shape-rendering='crispEdges'%3E%3Cpath d='M18 6h1v1h-1zM20 7h2v1h-2zM20 8h2v1h-2zM23 9h1v1h-1z'/%3E%3C/svg%3E");
  --o-cut: calc(29 - var(--hud-orb-lvl));   /* the liquid's top row (it fills rows 3 to 28) */
  --o-body: var(--hud-orb-body, color-mix(in srgb, var(--hud-orb) calc(80% + 20% * var(--faithful)), var(--black)));
  --o-lit: var(--hud-orb-lit, var(--hud-orb));
  --o-shadow: var(--hud-orb-shadow, color-mix(in srgb, var(--hud-orb) calc(50% + 50% * var(--faithful)), var(--black)));
  --o-shadow-dots: color-mix(in srgb, var(--bg) calc(100% * var(--faithful)), transparent);
  --o-deep: var(--hud-orb-deep, color-mix(in srgb, var(--bg) calc(100% * var(--faithful)), color-mix(in srgb, var(--hud-orb) 30%, var(--black))));
  --o-deep-dots: color-mix(in srgb, var(--hud-orb) calc(100% * var(--faithful)), transparent);
  transition: --hud-orb-lvl 0s linear; }   /* js/hud.js sets the duration: a frame a row */
.hud-orb.sm { --hud-px: 1px; }
.hud-orb.lg { --hud-px: 4px; }   /* the big gauge on its own: every pixel four, 128×128, as the minimap's .lg */
:is(.hud-orb, .hud-map) > i { position: absolute; inset: 0; -webkit-mask: var(--m) 0 0 / 100% 100% no-repeat; mask: var(--m) 0 0 / 100% 100% no-repeat; }
:is(.hud-orb, .hud-map) > [data-l="hollow"] { --m: var(--orb-hollow); background: var(--hud-orb-hollow); }
:is(.hud-orb, .hud-map) > [data-l="rim-shade"] { --m: var(--orb-rim-shade); background: var(--hud-orb-rim-shade); }
:is(.hud-orb, .hud-map) > [data-l="rim"] { --m: var(--orb-rim); background: var(--hud-orb-rim); }
:is(.hud-orb, .hud-map) > [data-l="rim-glint"] { --m: var(--orb-rim-glint); background: var(--hud-orb-rim-glint); }
/* The liquid: from its top row down. */
.hud-orb > :is([data-l="liquid"], [data-l="lit"], [data-l="shadow"], [data-l="deep"], [data-l="shine"], [data-l="core"]) {
  clip-path: inset(calc(var(--o-cut) * var(--p)) 0 0 0); }
.hud-orb > [data-l="liquid"] { --m: var(--orb-liquid); background: var(--o-body); }
.hud-orb > [data-l="lit"] { --m: var(--orb-lit); background: var(--o-lit); }
.hud-orb > [data-l="shadow"] { --m: var(--orb-shadow);
  background: conic-gradient(var(--o-shadow-dots) 0 25%, transparent 0 50%, var(--o-shadow-dots) 0 75%, transparent 0) 0 0 / calc(2 * var(--p)) calc(2 * var(--p)), var(--o-shadow); }
.hud-orb > [data-l="deep"] { --m: var(--orb-deep);
  background: conic-gradient(var(--o-deep-dots) 0 25%, transparent 0) 0 0 / calc(2 * var(--p)) calc(2 * var(--p)), var(--o-deep); }
/* The surface: the top row in the lit colour, unless it's full or empty. */
.hud-orb > [data-l="surface"] { --m: var(--orb-liquid); background: var(--o-lit);
  clip-path: inset(calc(var(--o-cut) * var(--p)) 0 calc((32 - var(--o-cut) - min(1, 26 - var(--hud-orb-lvl), var(--hud-orb-lvl))) * var(--p)) 0); }
.hud-orb > [data-l="shine"] { --m: var(--orb-shine); background: var(--hud-orb-shine); }
.hud-orb > [data-l="core"] { --m: var(--orb-shine-core); background: var(--hud-orb-shine-core); }
/* The shine on drained glass: above the liquid. */
.hud-orb > :is([data-l="dry"], [data-l="dry-core"]) { --m: var(--orb-shine); background: var(--hud-orb-glass-shine);
  clip-path: inset(0 0 calc((32 - var(--o-cut)) * var(--p)) 0); }
.hud-orb > [data-l="dry-core"] { --m: var(--orb-shine-core); }
.hud-orb.hit { animation: mek-shake var(--dur-bar) var(--ease-frames-4); }
.hud-orb.won { animation: mek-hud-orb-won var(--dur-beat) steps(1, end) 2; }
@keyframes mek-hud-orb-won { 0%, 49% { --hud-orb: var(--hud-orb-gain); } 50%, 100% { --hud-orb: inherit; } }

/* ---------- Minimap ----------
   <span class="hud-map" data-label="Map"></span>: the orb's glass with the world inside, a tile a pixel, centred on
   the player, for the corner across from the health orb. Twice the size by default, every pixel two: 32×32 drawn
   64×64; .sm is 1×, .lg 4× (128×128). .square, .wide and .tall frame it in a box instead (see below). Its size never changes.
   js/hud.js draws it on a 32×32 canvas inside the glass: mekHud.map(el, { at(x, y) or grid, x, y, facing, marks,
   light, zoom }); or start it from data-x, data-y, data-facing, data-light, data-zoom, and data-sample for a sample
   valley to sketch with. A tile's kind is a name; its colour is --hud-map-<kind> (floor, wall, water, grass here; a project
   adds its own), --hud-map-floor if there's none. Toward the rim the map thins through a 4×4 Bayer dither, as glass
   does. With light (tiles), only what's in sight shows: past it two tiles of dither, then what was seen before in
   --hud-map-memory on half its pixels, then nothing. The player is a 3×3 arrow the way they face (8 ways) in
   --hud-map-me. Marks (quest, danger, loot, or any kind) are a 3×3 plus in their colour; one out of view waits at the
   rim the way it lies. mekHud.ping(el, x, y, kind): a ring grows a pixel a frame from there, twice (not under reduced
   motion). Solid colours only, so faithful themes draw it as they are; a theme change redraws it. */
.hud-map.sm { --hud-px: 1px; }
.hud-map.lg { --hud-px: 4px; }
/* .square (32×32), .wide (48×32) and .tall (32×48): a box of glass instead of the ball. A pixel of frame in the ring's
   colours (lit, shade at the bottom and right, a glint in the top left corner), its corners stepped a pixel, the map inside. */
.hud-map.wide { width: calc(48 * var(--p)); }
.hud-map.tall { height: calc(48 * var(--p)); }
.hud-map:is(.square, .wide, .tall) > i { -webkit-mask: none; mask: none; }
.hud-map:is(.square, .wide, .tall) > [data-l="rim"] {
  background: linear-gradient(var(--hud-orb-rim-glint) 0 0) calc(2 * var(--p)) 0 / var(--p) var(--p) no-repeat,
    linear-gradient(var(--hud-orb-rim-glint) 0 0) 0 calc(2 * var(--p)) / var(--p) var(--p) no-repeat,
    linear-gradient(var(--hud-orb-rim-shade) 0 0) 0 100% / 100% var(--p) no-repeat,
    linear-gradient(var(--hud-orb-rim-shade) 0 0) 100% 0 / var(--p) 100% no-repeat, var(--hud-orb-rim);
  clip-path: polygon(var(--p) 0, calc(100% - var(--p)) 0, calc(100% - var(--p)) var(--p), 100% var(--p), 100% calc(100% - var(--p)),
    calc(100% - var(--p)) calc(100% - var(--p)), calc(100% - var(--p)) 100%, var(--p) 100%, var(--p) calc(100% - var(--p)), 0 calc(100% - var(--p)), 0 var(--p), var(--p) var(--p)); }
.hud-map:is(.square, .wide, .tall) > [data-l="hollow"] { inset: var(--p); background: var(--hud-orb-hollow); }
.hud-map:is(.square, .wide, .tall) > canvas { inset: var(--p); width: calc(100% - 2 * var(--p)); height: calc(100% - 2 * var(--p)); -webkit-mask: none; mask: none; }
.hud-map > canvas { position: absolute; inset: 0; width: 100%; height: 100%; image-rendering: pixelated;
  -webkit-mask: var(--orb-hollow) 0 0 / 100% 100% no-repeat; mask: var(--orb-hollow) 0 0 / 100% 100% no-repeat; }
.hud-map > [data-l="glint"] { --m: var(--orb-shine-core); background: var(--hud-map-glint); }

/* ---------- Pickup feed ----------
   <ol class="hud-feed" aria-label="Pickups"><li data-count="3">Slime gel</li></ol>: a line for each thing got (or
   lost), newest at the bottom, --hud-feed-rows of them (4); the feed is always that tall. Mono 24 on 32px rows (.sm:
   Mono 14 on 18px), the count first ("+3" in --hud-feed-gain, "-1" in --hud-feed-loss), an icon if it has one, then
   the name. mekHud.feed(el, { item, count, icon }): a line comes in through the dither (a quarter, a half, all),
   stays --hud-feed-time and dissolves out the same way. The same thing again while its line shows adds to its count
   and starts it over; past the rows the oldest goes at once. Lines in the markup stay put: a sketch. A log, read out. */
.hud-feed { --p: 2px; --row: 32px; list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; justify-content: flex-end;
  height: calc(var(--hud-feed-rows) * var(--row)); overflow: hidden; font: var(--text-lg)/var(--row) var(--font-mono); -webkit-font-smoothing: none; color: var(--text); }
.hud-feed.sm { --p: 1px; --row: 18px; font-size: var(--text-crisp); }
.hud-feed > li { display: flex; align-items: center; gap: calc(4 * var(--p)); height: var(--row); flex: none; white-space: nowrap; }
.hud-feed > li > .hud-feed-n { min-width: 3ch; color: var(--hud-feed-gain); }
.hud-feed > li.loss > .hud-feed-n { color: var(--hud-feed-loss); }
.hud-feed > li > .icon { width: calc(15 * var(--p)); height: calc(15 * var(--p)); vertical-align: 0; color: var(--text-strong); }
.hud-feed > li.in { animation: mek-hud-in calc(2 * var(--frame)) steps(1, end) both; }
.hud-feed > li.out { animation: mek-hud-out calc(3 * var(--frame)) steps(1, end) both; }
.hud-feed > li.more > .hud-feed-n { animation: mek-hud-flash var(--dur-beat) steps(1, end) 1; --hud-flash: var(--hud-feed-gain); }
.hud-feed > li.loss.more > .hud-feed-n { --hud-flash: var(--hud-feed-loss); }
/* A dither in and out: a pixel in four, then two, then all (and back). Each held, so it steps. */
@keyframes mek-hud-in {
  0%, 49% { -webkit-mask: conic-gradient(#000 0 25%, #0000 0) 0 0 / calc(2 * var(--p)) calc(2 * var(--p)); mask: conic-gradient(#000 0 25%, #0000 0) 0 0 / calc(2 * var(--p)) calc(2 * var(--p)); }
  50%, 99% { -webkit-mask: conic-gradient(#000 0 25%, #0000 0 50%, #000 0 75%, #0000 0) 0 0 / calc(2 * var(--p)) calc(2 * var(--p)); mask: conic-gradient(#000 0 25%, #0000 0 50%, #000 0 75%, #0000 0) 0 0 / calc(2 * var(--p)) calc(2 * var(--p)); } }
@keyframes mek-hud-out {
  0%, 32% { -webkit-mask: conic-gradient(#000 0 25%, #0000 0 50%, #000 0 75%, #0000 0) 0 0 / calc(2 * var(--p)) calc(2 * var(--p)); mask: conic-gradient(#000 0 25%, #0000 0 50%, #000 0 75%, #0000 0) 0 0 / calc(2 * var(--p)) calc(2 * var(--p)); }
  33%, 65% { -webkit-mask: conic-gradient(#000 0 25%, #0000 0) 0 0 / calc(2 * var(--p)) calc(2 * var(--p)); mask: conic-gradient(#000 0 25%, #0000 0) 0 0 / calc(2 * var(--p)) calc(2 * var(--p)); }
  66%, 100% { visibility: hidden; } }

/* ---------- Compass ----------
   <div class="hud-compass" data-heading="45" data-marks="quest 80, danger -30"></div>: a strip across the top of the
   screen, --hud-compass wide, showing --hud-compass-span degrees (180) round the way you face. N E S W in Mono 24 (.sm:
   14; N in --hud-compass-north), a long tick each 45°, a short one each 15°, all moving by whole pixels; a line at the
   middle is you. Marks (quest, danger, loot, any kind: the minimap's --hud-map-<kind> colours) are a 3×3 plus under
   the ticks at their bearing; one outside the strip waits at its end as a caret pointing on. It thins to the ends
   through a checkerboard. mekHud.compass(el, { heading, marks: [{ bearing | x, y, kind }], from: { x, y } }): x, y
   with from are map tiles (north is up), turned to a bearing. Read as "Heading 45°, north-east: quest to the right". */
.hud-compass { --p: 2px; position: relative; display: block; width: var(--hud-compass); max-width: 100%; height: calc(28px + 14 * var(--p)); overflow: hidden;
  font: var(--text-lg)/28px var(--font-mono); -webkit-font-smoothing: none; color: var(--hud-compass-letter);
  -webkit-mask: linear-gradient(#000 0 0) calc(12 * var(--p)) 0 / calc(100% - 24 * var(--p)) 100% no-repeat, conic-gradient(#000 0 25%, #0000 0 50%, #000 0 75%, #0000 0) 0 0 / calc(2 * var(--p)) calc(2 * var(--p));
  mask: linear-gradient(#000 0 0) calc(12 * var(--p)) 0 / calc(100% - 24 * var(--p)) 100% no-repeat, conic-gradient(#000 0 25%, #0000 0 50%, #000 0 75%, #0000 0) 0 0 / calc(2 * var(--p)) calc(2 * var(--p)); }
.hud-compass.sm { --p: 1px; --hud-compass: 160px; height: calc(18px + 14 * var(--p)); font-size: var(--text-crisp); line-height: 18px; }
.hud-compass > * { position: absolute; left: 0; }
.hud-compass > b { top: 0; width: 24px; font-weight: inherit; text-align: center; }
.hud-compass > b.n { color: var(--hud-compass-north); }
.hud-compass > .t { bottom: calc(6 * var(--p)); width: var(--p); height: calc(2 * var(--p)); background: var(--hud-compass-tick); }
.hud-compass > .t.long { height: calc(4 * var(--p)); }
.hud-compass > .me { bottom: calc(5 * var(--p)); width: var(--p); height: calc(6 * var(--p)); background: var(--hud-compass-me); }   /* js/hud.js puts it on the middle tick's pixel */
.hud-compass > .mk { bottom: 0; width: calc(3 * var(--p)); height: calc(3 * var(--p)); background: var(--c);
  clip-path: polygon(var(--p) 0, calc(2 * var(--p)) 0, calc(2 * var(--p)) var(--p), 100% var(--p), 100% calc(2 * var(--p)), calc(2 * var(--p)) calc(2 * var(--p)), calc(2 * var(--p)) 100%, var(--p) 100%, var(--p) calc(2 * var(--p)), 0 calc(2 * var(--p)), 0 var(--p), var(--p) var(--p)); }
.hud-compass > .mk.far { bottom: calc(-1 * var(--p)); width: calc(3 * var(--p)); height: calc(5 * var(--p));   /* ▶, pointing on */
  clip-path: polygon(0 0, var(--p) 0, var(--p) var(--p), calc(2 * var(--p)) var(--p), calc(2 * var(--p)) calc(2 * var(--p)), 100% calc(2 * var(--p)), 100% calc(3 * var(--p)), calc(2 * var(--p)) calc(3 * var(--p)), calc(2 * var(--p)) calc(4 * var(--p)), var(--p) calc(4 * var(--p)), var(--p) 100%, 0 100%); }
.hud-compass > .mk.far.left { scale: -1 1; }

/* ---------- Stats ----------
   <div class="hud-stats"> stacks stat rows close: 4px apart (rows 24px tall, so one every 28px); 2px for .sm rows. */
.hud-stats { display: grid; gap: var(--space-1); }
.hud-stats:has(> .hud-stat.sm) { gap: 2px; }

/* ---------- Score ----------
   <span class="hud-score" data-digits="6">0</span>: Mono 24 (.sm: 12), crisp, 1000, padded with zeros to data-digits (6), the
   zeros in 400 so the number reads. mekHud.score(el, to) counts from what it shows to `to` in at most 12 steps, a step
   each display-rate frame of --frame (83ms): the digits roll, the width never changes. At once under reduced motion. */
.hud-score { font: var(--text-lg)/1 var(--font-mono); color: var(--text-strong); -webkit-font-smoothing: none; font-variant-numeric: tabular-nums; }
.hud-score .hud-pad { color: var(--line); }
.hud-score.sm { font-size: var(--text-crisp); }   /* the small Mono size (14) */

}
