/* ⭐CASCADE LAYERS (#2920, 2026-09-22): this file is the `nerm` layer. The
   order is declared once, below, and repeated inline by build-web.js so
   the world page fixes it before any stylesheet loads. Inside a layer,
   load order and specificity still decide between THIS file's rules;
   between files, the layer decides, always. */
@layer base, module, skin, nerm, corners;
@layer nerm {
/* ══════════════════════════════════════════════════════════════════════
   nerm-guide.css — the narrator's own surface (#2385).

   THIS DOES NOT STYLE `.wt-nerm`, and that is the point. The parked build
   borrowed world-target's panel and spent most of its CSS fighting the
   consequences: the skin styles that panel in three places, the foot copy
   is scoped under an ID (`#wt-foot .wt-line`) which beats any class-only
   override, and `#wt-foot` is `overflow: hidden` at a fixed height so a
   growing box cannot live there at all. A narrator that has to move to an
   arbitrary element inherits none of that usefully, so it draws its own.
   `.wt-nerm` keeps its ambient quips, untouched.

   ⛔DO NOT REACH FOR `var(--fx)` IN THIS FILE. It is the letterbox gutter
   and it is 0 under full bleed, which has silently collapsed three world
   surfaces to nothing (#2507, #2521). Every position here comes from
   getBoundingClientRect against the real viewport, in JS.

   The look is the drawn chrome (#1953): the parchment scrap is the surface,
   bitmap type, dark ink, one hard shadow. No gradients, no rounded cards,
   no serif.
   ══════════════════════════════════════════════════════════════════════ */

#ng {
  position: fixed;
  inset: 0;
  z-index: 3000;               /* over the doors and their windows */
  /* THE OVERLAY NEVER EATS A CLICK. A step that says "open the store" has
     to let the player open the store, so the layer is inert and only the
     bubble takes input. This is also what keeps a stuck tutorial from
     locking someone out of their own game. */
  pointer-events: none;
}

#ng[hidden] { display: none; }

/* NOT OVER A WINDOW (#3069 S11, C7). While a window is open under the HUD
   pass a HOME bubble steps out of the way (nerm-guide.js sets `ng-sheet`
   on a `ws:sheet` with a door and clears it when the window shuts); an
   anchored step keeps pointing. `display: none`, so it cannot swallow a
   tap on the window under it either. The HUD pass off: no rule. */
body.hud-v2 #ng.ng-home.ng-sheet { display: none; }

/* ── the spotlight ───────────────────────────────────────────────────────
   One element with an enormous spread, so the "hole" is the element's own
   box and the dimming is its shadow. Cheaper and far less fragile than four
   panels around a gap, which have to be kept in sync on every resize. */
.ng-hole {
  position: fixed;
  box-shadow: 0 0 0 9999px rgba(8, 6, 12, .58);
  outline: 2px solid rgba(255, 233, 168, .85);
  outline-offset: 0;
  pointer-events: none;
  transition: left .18s ease, top .18s ease, width .18s ease, height .18s ease;
}
.ng-hole[hidden] { display: none; }

/* ── the bubble ──────────────────────────────────────────────────────── */
.ng-bubble {
  position: fixed;
  /* HOME, and it is the default rather than a special case: an anchored
     step overwrites left/top inline and clears them again when it goes
     home, so the resting position lives here in one place. */
  left: 12px;
  bottom: 76px;
  width: 320px;
  max-width: calc(100vw - 24px);
  box-sizing: border-box;

  display: flex;
  gap: 10px;
  align-items: flex-start;

  /* the parchment scrap IS the surface, as `.wt-nerm` has it */
  background: url('/assets/ui/paper.png?v=2026-08-29a') 0 0 / 100% 100% no-repeat;
  image-rendering: pixelated;
  padding: 10px 12px;
  box-shadow: 0 3px 0 rgba(0, 0, 0, .5);

  pointer-events: auto;
}

/* ANCHORED. `top` is written inline by the placer, so `bottom` has to stop
   applying or the box is stretched between the two. */
/* HOME MOVES OVER FOR A LEFT WINDOW (2026-09-12). A left-docked sheet now
   runs to the foot, and the shell publishes its measured right edge on
   <body> as `--ws-left-edge` while one is open (class `ws-left-open`). No
   `--fx` here, as the header says: the edge is viewport-absolute already.
   Only the home position moves; an anchored step writes its own inline
   left/top and is unaffected. */
body.ws-left-open #ng.ng-home .ng-bubble {
  left: calc(var(--ws-left-edge, 0px) + 16px);
}

#ng.ng-at .ng-bubble {
  bottom: auto;
  width: 268px;
}

.ng-face {
  width: 48px;
  height: 48px;
  flex: 0 0 auto;
  background: url('/assets/ui/nerm.png?v=2026-08-29a') 0 0 / 100% 100% no-repeat;
  image-rendering: pixelated;
}
#ng.ng-at .ng-face { width: 36px; height: 36px; }

.ng-body {
  /* `1 1 0`, NEVER `1 1 auto`. A long word gives the auto basis an enormous
     intrinsic width and the text column collapses to nothing next to the
     face. world-skin.css had already written this lesson down in a scope
     this file does not inherit. */
  flex: 1 1 0;
  min-width: 0;
}

.ng-line {
  margin: 0;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 19px;
  line-height: 1.22;
  color: #2e2113;
  /* deliberately NOT clamped: a teaching line is as long as it is, and the
     two-line clamp on the ambient box is what made the parked build lift
     itself out of the foot with position:fixed */
  overflow-wrap: break-word;
}
#ng.ng-at .ng-line { font-size: 17px; }

/* ── the pointer ─────────────────────────────────────────────────────────
   A solid triangle in the parchment's own mid-tone. Positioned along the
   edge by JS so it keeps aiming at the target after the bubble has been
   clamped back into the viewport. */
.ng-arrow {
  position: absolute;
  width: 0;
  height: 0;
  pointer-events: none;
}
.ng-arrow[hidden] { display: none; }

.ng-bubble[data-side="below"] .ng-arrow {          /* bubble under target */
  top: -9px;
  margin-left: -9px;
  border-left: 9px solid transparent;
  border-right: 9px solid transparent;
  border-bottom: 9px solid #d9c9a3;
}
.ng-bubble[data-side="above"] .ng-arrow {
  bottom: -9px;
  margin-left: -9px;
  border-left: 9px solid transparent;
  border-right: 9px solid transparent;
  border-top: 9px solid #d9c9a3;
}
.ng-bubble[data-side="right"] .ng-arrow {
  left: -9px;
  margin-top: -9px;
  border-top: 9px solid transparent;
  border-bottom: 9px solid transparent;
  border-right: 9px solid #d9c9a3;
}
.ng-bubble[data-side="left"] .ng-arrow {
  right: -9px;
  margin-top: -9px;
  border-top: 9px solid transparent;
  border-bottom: 9px solid transparent;
  border-left: 9px solid #d9c9a3;
}

/* ── the step footer ─────────────────────────────────────────────────── */
.ng-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
}
.ng-foot[hidden] { display: none; }

.ng-dots {
  flex: 1 1 auto;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 13px;
  letter-spacing: 1px;
  color: #6b5b3e;
}

.ng-skip,
.ng-next {
  flex: 0 0 auto;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 16px;
  line-height: 1;
  color: #2e2113;
  background: rgba(46, 33, 19, .10);
  border: 1px solid rgba(46, 33, 19, .45);
  /* 30px tall is the map zoom pad's height, which reads right at this size;
     the chunky rail-sized plank is the mud (#2020), so it is not used. */
  min-height: 30px;
  padding: 6px 12px;
  cursor: pointer;
}
.ng-skip:hover,
.ng-next:hover { background: rgba(46, 33, 19, .18); }
.ng-skip[hidden],
.ng-next[hidden] { display: none; }

.ng-skip {
  /* quieter than `next`: it is always available and never the suggestion */
  color: #6b5b3e;
  background: transparent;
  border-color: rgba(46, 33, 19, .28);
}

/* ── phone ───────────────────────────────────────────────────────────────
   390x664 is the surface that matters (#2441 — a browser viewport is about
   100px shorter than the screen, and 844 is the number that flattered a
   previous pass into shipping something helping nobody).

   Home goes bottom-centre and full width: at 390 a 320px box pinned left
   leaves a dead margin, and the foot bar is what it has to clear. */
@media (max-width: 900px) {
  .ng-bubble {
    left: 8px;
    right: 8px;
    width: auto;
    bottom: 68px;
  }
  #ng.ng-at .ng-bubble {
    /* anchored, JS writes left/top, so the right pin has to let go */
    right: auto;
    width: min(300px, calc(100vw - 20px));
  }
  /* BESIDE OR ABOVE THE v2 ACTION BAR (#3069 S4, behind the HUD pass
     gate). That bar is one row, shorter than v1's, so a full-width home at
     68 sat on its slots and hid the CARDS door and the PLAY ARENA plank
     standing above it: every new guest's first screen (review of #3090).
     While it is up:
       - under 708 the bar spans the screen, so home stands over all three:
         --w0bar-h is the bar's measured height (published by
         world-legacy-door.js, which stands the plank 12 above it), then the
         plank's 48 and 8 of air. The fallback is that bar's height on a
         phone (72 + 26 + the 12px safe floor), for a page with no plank.
       - from 708 the bar keeps right of this corner (world-actionbar.css
         --ab-clear-l), so home is the desktop's 320 box pinned left, and
         a landscape phone's short screen keeps its top clear. */
  @media (max-width: 707px) {
    body.hud-v2:has(#w0worldbar.ab2) #ng.ng-home .ng-bubble {
      bottom: calc(var(--w0bar-h, 110px) + 68px);
    }
  }
  @media (min-width: 708px) {
    body.hud-v2:has(#w0worldbar.ab2) #ng.ng-home .ng-bubble {
      right: auto;
      width: 320px;
    }
  }
  .ng-face { width: 40px; height: 40px; }
  .ng-line { font-size: 18px; }
  /* tap targets: 44 is the floor the tap-target sweep measures against */
  .ng-skip,
  .ng-next { min-height: 44px; padding: 8px 14px; font-size: 17px; }
}

@media (prefers-reduced-motion: reduce) {
  .ng-hole { transition: none; }
}

/* ── UNDER THE HUD PASS: THE WINDOWS' SLATE, NOT THE PARCHMENT ─────────
   ⭐#3069 S15 (D7). Help keeps the walkthrough (S6 ruled that), and SHOW ME
   AGAIN replayed it in the tan parchment scrap with small outlined text
   buttons, the one v1 surface left in a v2 world. Nothing new is drawn
   here: the bubble is the kit's slate panel (world-hud-v2.css `.hv-slate`,
   kitb/panel.png at the chrome pixel) and its buttons are the plank the v2
   windows draw (world-windows-v2.css: btn.png for Skip, btngold.png for the
   one thing it asks of you, Next), in the windows' own inks. The pointer
   takes the panel's outer rim. Every rule is under body.hud-v2, so the
   HUD pass off keeps the parchment. A finger keeps its 44px. */
body.hud-v2 .ng-bubble {
  background: none;
  border-style: solid;
  border-width: 0;
  border-image: url('/assets/ui/kitb/panel.png?v=2026-09-24a') 12 fill / calc(12 * var(--hv-k) * 1px) stretch;
  padding: calc(12 * var(--hv-k) * 1px);
  box-shadow: 0 calc(2 * var(--hv-k) * 1px) 0 rgba(0, 0, 0, .45);
}
body.hud-v2 .ng-line { color: var(--hv-ink-slate, #d9cdb2); }
/* the step count in the quiet ink the pass lifted to 4.5:1 on slate */
body.hud-v2 .ng-dots { color: var(--hv-mute, #aab6c6); }
body.hud-v2 .ng-bubble[data-side="below"] .ng-arrow { border-bottom-color: #232939; }
body.hud-v2 .ng-bubble[data-side="above"] .ng-arrow { border-top-color: #232939; }
body.hud-v2 .ng-bubble[data-side="right"] .ng-arrow { border-right-color: #232939; }
body.hud-v2 .ng-bubble[data-side="left"] .ng-arrow { border-left-color: #232939; }

body.hud-v2 .ng-skip,
body.hud-v2 .ng-next {
  background: none;
  border-style: solid;
  border-image-source: url('/assets/ui/kitb/btn.png?v=2026-09-24a');
  border-image-slice: 3 3 5 3 fill;
  border-image-width: calc(3 * var(--hv-k) * 1px) calc(3 * var(--hv-k) * 1px) calc(5 * var(--hv-k) * 1px) calc(3 * var(--hv-k) * 1px);
  border-width: calc(3 * var(--hv-k) * 1px) calc(3 * var(--hv-k) * 1px) calc(5 * var(--hv-k) * 1px) calc(3 * var(--hv-k) * 1px);
  border-image-repeat: stretch;
  image-rendering: pixelated;
  min-height: 34px;
  padding: 4px 12px;
  font-family: var(--hv-f-px, 'Press Start 2P', monospace);
  font-size: calc((3 * var(--hv-k) + 5) * 1px);
  letter-spacing: .04em;
  color: var(--hv-ink, #f0e2c6);
  text-shadow: 0 2px 0 rgba(0, 0, 0, .5);
}
body.hud-v2 .ng-skip:hover,
body.hud-v2 .ng-next:hover { background: none; filter: brightness(1.18); }
body.hud-v2 .ng-skip:active,
body.hud-v2 .ng-next:active { filter: brightness(.92); }
/* Next is the one thing the bubble asks of you: gold, ink on gold
   (btngold.png's face is #dda52c; #2a1c0c on it is 7.5:1) */
body.hud-v2 .ng-next {
  border-image-source: url('/assets/ui/kitb/btngold.png?v=2026-09-24a');
  color: #2a1c0c;
  text-shadow: none;
}
@media (pointer: coarse) {
  body.hud-v2 .ng-skip,
  body.hud-v2 .ng-next { min-height: 44px; }
}

/* ── ONE NERM ON SCREEN AT A TIME ────────────────────────────────────────
   `.wt-nerm` is world-target's ambient panel: its own Nerm face, its own
   idle line, and the same bottom-left corner this bubble calls home. With
   the narrator up, the served page showed TWO Nerms an inch apart, the
   lower one saying "nothing selected. i have all day." underneath the one
   teaching the game. Found by loading the page; no test owned the
   relationship between two panels that are each correct alone.

   ⭐THIS IS THE ONE RULE IN THIS FILE THAT TOUCHES A SURFACE IT DOES NOT
   OWN, so it is deliberately the weakest thing that works: visibility only,
   no layout change, added and removed by class. world-skin.css styles
   `.wt-nerm` in three places and this must not win an argument with any of
   them beyond hiding it. `!important` is here because that skin sets
   `display: flex` on the panel in a body-scoped rule, which a plain class
   cannot outrank. */
.wt-nerm.wt-nerm--hushed {
  visibility: hidden !important;
}
}
/* end of @layer nerm */
