/* ═══ HELHEIMRS — shell layout: header, bottom nav, footer, overlays ═══ */

/* ── Header ── */
.hdr { position: sticky; top: 0; z-index: var(--z-nav); background: linear-gradient(180deg, rgba(10,10,11,.97), rgba(10,10,11,.88)); border-bottom: 1px solid var(--line); }
:root[data-theme="light"] .hdr { background: linear-gradient(180deg, rgba(230,232,226,.97), rgba(230,232,226,.9)); }
.hdr__in { display: flex; align-items: center; gap: var(--s-5); min-height: 68px; }
.hdr__logo { display: flex; align-items: center; gap: var(--s-3); color: var(--fg-strong); text-decoration: none; flex: none; }
.hdr__logo:hover { text-decoration: none; }
.hdr__logo svg { height: 42px; width: auto; }
.hdr__logo .wordmark { height: 16px; width: auto; }
.hdr__nav { display: flex; gap: var(--s-1); flex: 1; }
.hdr__nav a { position: relative; padding: var(--s-2) var(--s-3); color: var(--fg-muted); font-weight: 600; font-size: var(--fs-sm); border-radius: var(--r-1); transition: color var(--t-ui); white-space: nowrap; }
.hdr__nav a:hover { color: var(--fg-strong); text-decoration: none; }
.hdr__nav a[aria-current="page"] { color: var(--accent-text); }
.hdr__nav a[aria-current="page"]::after { content: ""; position: absolute; inset-inline: var(--s-3); bottom: 0; height: 2px; background: var(--emerald); }
.hdr__tools { display: flex; align-items: center; gap: var(--s-2); }
.hdr__tools .btn--icon { width: 40px; min-height: 40px; }
.hdr__tools .ico { width: 18px; height: 18px; }
.hdr .kbd-hint { display: none; }
@media (min-width: 1100px) { .hdr .kbd-hint { display: inline-flex; gap: 4px; align-items: center; color: var(--fg-faint); font-size: var(--fs-xs); } }
@media (max-width: 900px) { .hdr__nav { display: none; } .hdr__in { min-height: 60px; justify-content: space-between; gap: var(--s-3); } }
@media (max-width: 600px) { .hdr__logo .wordmark { display: none; } .hdr__tools .btn--primary.btn--sm { padding: 0 var(--s-3); } }
.hdr__user { display: flex; align-items: center; gap: var(--s-2); }

/* ── Bottom nav (mobile) ── */
.bnav { display: none; position: fixed; inset-inline: 0; bottom: 0; z-index: var(--z-nav); background: linear-gradient(180deg, rgba(26,27,29,.92), rgba(10,10,11,.98)); border-top: 1px solid var(--line-strong); padding-bottom: env(safe-area-inset-bottom); }
:root[data-theme="light"] .bnav { background: linear-gradient(180deg, rgba(241,242,238,.95), rgba(230,232,226,.98)); }
.bnav__in { display: grid; grid-template-columns: repeat(5, 1fr); align-items: end; height: 64px; }
.bnav a, .bnav button { display: grid; justify-items: center; gap: 2px; padding: 8px 4px; color: var(--fg-muted); font-size: var(--fs-2xs); font-weight: 600; text-decoration: none; background: none; border: 0; }
.bnav a[aria-current="page"] { color: var(--accent-text); }
.bnav .ico { width: 22px; height: 22px; }
.bnav__d20 { position: relative; transform: translateY(-16px); filter: drop-shadow(0 0 10px rgba(47,211,154,.65)) drop-shadow(0 0 28px rgba(47,211,154,.3)); }
.bnav__d20 span { display: grid; place-items: center; width: 60px; height: 60px; clip-path: var(--clip-hex); background: var(--emerald-grad); color: var(--accent-fg); font-family: var(--font-display); font-weight: 900; font-size: 22px; animation: breathe 3.4s ease-in-out infinite; }
.bnav__d20 .ico { width: 30px; height: 30px; }
@media (max-width: 900px) { .bnav { display: block; } body { padding-bottom: 84px; } }

/* ── Footer ── */
.ftr { border-top: 1px solid var(--line); margin-top: var(--s-10); padding: var(--s-8) 0 var(--s-7); color: var(--fg-muted); font-size: var(--fs-sm); position: relative; overflow: hidden; }
.ftr__in { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: var(--s-6); }
.ftr .ftr__h { color: var(--fg-strong); font-size: var(--fs-md); font-weight: 700; margin: 0 0 var(--s-3); }
.ftr ul { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--s-half); }
.ftr a { color: var(--fg-muted); }
.ftr a:hover { color: var(--accent-text); }
.ftr__brand svg { height: 56px; width: auto; margin-bottom: var(--s-3); }
.ftr__bottom { margin-top: var(--s-7); padding-top: var(--s-4); border-top: 1px solid var(--line); display: flex; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; font-size: var(--fs-xs); color: var(--fg-faint); }
.ftr .rune-band { margin-top: var(--s-5); }
@media (max-width: 900px) { .ftr__in { grid-template-columns: 1fr 1fr; } }
@media (max-width: 520px) { .ftr__in { grid-template-columns: 1fr; } }

/* ── Dice FAB (desktop) ── */
.dice-fab { position: fixed; bottom: var(--s-6); inset-inline-start: var(--s-6); z-index: var(--z-tray); width: 64px; height: 64px; display: grid; place-items: center; background: none; border: 0; color: var(--emerald); filter: drop-shadow(0 14px 18px rgba(0,0,0,.55)) drop-shadow(0 0 12px rgba(47,211,154,.4)); cursor: pointer; transition: transform var(--t-ui) var(--ease-stone); }
.dice-fab::before { content: ""; position: absolute; inset: 2px; clip-path: var(--clip-hex); background: var(--bg-elev); z-index: -1; }
.dice-fab::after { content: ""; position: absolute; inset: 0; clip-path: var(--clip-hex); background: var(--emerald-grad); z-index: -2; opacity: .9; }
/* the d20 is the single resting beacon on desktop — nothing else pulses */
.dice-fab svg { width: 34px; height: 34px; position: relative; animation: breathe 3.4s ease-in-out infinite; }
:root[data-theme="light"] .dice-fab { color: var(--jade); filter: drop-shadow(0 12px 16px rgba(20,24,22,.25)) drop-shadow(0 0 8px rgba(47,211,154,.35)); }
.dice-fab:hover { transform: translateY(-3px) rotate(-6deg); }
@media (max-width: 900px) { .dice-fab { display: none; } }

/* ── Sound toggle ── */
.sound-btn { position: fixed; bottom: var(--s-6); inset-inline-end: var(--s-6); z-index: var(--z-tray); display: flex; align-items: center; gap: var(--s-2); padding: 6px 10px 6px 12px; background: var(--bg-elev); border: 1px solid var(--line-strong); clip-path: var(--clip-facet); --facet: 8px; color: var(--fg-muted); font-size: var(--fs-xs); font-weight: 600; cursor: pointer; }
.sound-btn[aria-pressed="true"] { color: var(--accent-text); border-color: var(--line-glow); }
.sound-btn .viz { display: flex; gap: 2px; align-items: flex-end; height: 16px; direction: ltr; }
/* pointed rune staves, not dots: at rest they hold a fixed silhouette so the
   control reads as a visualiser rather than an ellipsis */
.sound-btn .viz i { width: 3px; height: 5px; background: currentColor; clip-path: polygon(50% 0, 100% 30%, 100% 100%, 0 100%, 0 30%); transition: height 80ms linear; }
.sound-btn .viz i:nth-child(2) { height: 9px; }
.sound-btn .viz i:nth-child(3) { height: 6px; }
.sound-btn .viz i:nth-child(4) { height: 13px; }
.sound-btn .viz i:nth-child(5) { height: 7px; }
.sound-btn .viz i:nth-child(6) { height: 10px; }
.sound-btn[aria-pressed="false"] .viz { opacity: .55; }
/* On phones the label made this a wide bar that sat across page content
   (it covered the spell filter rail). Collapse to a single 44px control:
   same target size, a corner footprint a floating control is allowed to own. */
@media (max-width: 900px) {
  .sound-btn { bottom: calc(84px + var(--s-3)); inset-inline-end: var(--s-4); width: 44px; height: 44px; padding: 0; justify-content: center; background: var(--bg-elev-2); }
  .sound-btn .sound-btn__label { display: none; }
  .sound-btn .viz { height: 18px; }
}

/* ── Gatekeeper guide (corner mascot) ── */
.guide { position: fixed; bottom: calc(var(--s-6) + 76px); inset-inline-start: var(--s-6); z-index: var(--z-tray); display: flex; align-items: flex-end; gap: var(--s-3); pointer-events: none; opacity: 0; transform: translateY(12px); transition: opacity var(--t-slow) var(--ease-out), transform var(--t-slow) var(--ease-out); }
.guide.is-on { opacity: 1; transform: none; pointer-events: auto; }
.guide .keeper { width: 72px; filter: drop-shadow(0 0 12px rgba(47,211,154,.45)); }
.guide .speech { margin-bottom: 8px; }
.guide .speech button { margin-top: 6px; }
@media (max-width: 900px) { .guide { bottom: calc(84px + 60px); inset-inline-start: var(--s-4); } .guide .keeper { width: 56px; } }

/* ── Fog canvas & lantern ── */
/* width/height are required, not decorative: fog.js renders the backing store at
   half resolution (SCALE .5) and relies on the element stretching to the viewport.
   Without these the canvas takes its intrinsic half size and, in this RTL document,
   `inset: 0` parks it at the TOP-RIGHT — a hard-edged rectangle of fog over the
   corner of every page. */
.fog-canvas { position: fixed; inset: 0; width: 100%; height: 100%; z-index: 2; pointer-events: none; opacity: .85; }
:root[data-theme="light"] .fog-canvas { mix-blend-mode: multiply; opacity: .3; }
.lantern { position: fixed; left: 0; top: 0; z-index: var(--z-cursor); pointer-events: none; width: 26px; height: 26px; margin: -13px 0 0 -13px; border-radius: 50%; border: 1.5px solid rgba(47,211,154,.9); box-shadow: 0 0 10px rgba(47,211,154,.6), 0 0 60px 18px rgba(47,211,154,.12); mix-blend-mode: screen; transition: transform var(--t-fast) var(--ease-out), opacity var(--t-ui); opacity: 0; }
.lantern::after { content: ""; position: absolute; inset: 9px; border-radius: 50%; background: var(--emerald); box-shadow: 0 0 8px var(--emerald); }
body.has-lantern .lantern.is-live { opacity: 1; }
.lantern.is-hover { transform: scale(1.8); border-color: rgba(47,211,154,.5); }
.lantern.is-down { transform: scale(.7); }
/* hidden runes revealed by the lantern */
.secret-rune { position: absolute; font-family: var(--font-rune); font-size: 28px; color: var(--emerald); opacity: 0; pointer-events: none; user-select: none; transition: opacity .3s; text-shadow: 0 0 12px var(--emerald); }
.secret-msg { position: absolute; font-size: var(--fs-xs); color: var(--emerald-soft); opacity: 0; pointer-events: none; white-space: nowrap; transition: opacity .3s; }
.secret-rune.is-lit, .secret-msg.is-lit { opacity: .9; }

/* ── Portal page transition ── */
.portal-x { position: fixed; inset: 0; z-index: var(--z-portal); pointer-events: none; display: grid; place-items: center; opacity: 0; }
.portal-x.is-on { pointer-events: auto; opacity: 1; }
.portal-x__veil { position: absolute; inset: 0; background: var(--void); opacity: 0; }
.portal-x__frame { position: absolute; width: 180px; height: 220px; clip-path: var(--clip-hex); background: conic-gradient(from 180deg, var(--emerald), var(--jade), var(--emerald)); box-shadow: var(--glow-strong); transform: scale(0); }
.portal-x__frame::after { content: ""; position: absolute; inset: 3px; clip-path: var(--clip-hex); background: var(--void); }
.portal-x__rune { position: absolute; font-family: var(--font-rune); font-size: 42px; color: var(--emerald); text-shadow: 0 0 20px var(--emerald); opacity: 0; }

/* ── Ravens ── */
.raven-layer { position: fixed; inset: 0; z-index: var(--z-raven); pointer-events: none; overflow: hidden; }
.raven { position: absolute; left: 0; top: 0; width: 132px; height: 132px; will-change: transform; }
/* left/top pinned on purpose: the flight path in fx/ravens.js is computed in
   viewport coordinates, and an auto-offset absolute box takes its static
   position — the RIGHT edge in this RTL document — which flung the bird
   off-screen by the width of the viewport. */
.raven svg { filter: drop-shadow(0 0 12px rgba(47,211,154,.34)) drop-shadow(0 2px 6px rgba(0,0,0,.5)); }
.raven-msg { position: fixed; z-index: var(--z-raven); top: 84px; inset-inline-start: var(--s-5); width: min(360px, calc(100vw - 2 * var(--s-5))); pointer-events: auto; }
.raven-msg .toast { transform-origin: top; }
.raven-msg .toast i.rune { float: inline-end; font-size: 18px; }

/* ── Crit / fumble screen effects ── */
.crit-fx { position: fixed; inset: 0; z-index: var(--z-portal); pointer-events: none; display: grid; place-items: center; }
.crit-fx__text { font-family: var(--font-display); font-weight: 900; font-size: clamp(2.6rem, 9vw, 7rem); color: var(--emerald); text-shadow: 0 0 30px var(--emerald), 0 0 90px rgba(47,211,154,.6); animation: critText 1.6s var(--ease-out) both; }
@keyframes critText { 0% { opacity: 0; transform: scale(.4) scaleX(1.35); filter: blur(8px); } 30% { opacity: 1; transform: scale(1.05); filter: blur(0); } 80% { opacity: 1; } 100% { opacity: 0; transform: scale(1.2); } }
.crit-fx__fire { position: absolute; inset: 0; background: radial-gradient(ellipse at 50% 110%, rgba(47,211,154,.55), transparent 60%), radial-gradient(ellipse at 50% -10%, rgba(47,211,154,.35), transparent 55%); animation: critFire 1.6s ease-out both; }
@keyframes critFire { 0% { opacity: 0 } 25% { opacity: 1 } 100% { opacity: 0 } }
.crit-fx__runes { position: absolute; inset: 0; display: flex; flex-wrap: wrap; align-content: space-around; justify-content: space-around; font-family: var(--font-rune); font-size: 40px; color: var(--emerald); opacity: 0; animation: runeFlash 1.4s .1s both; text-shadow: 0 0 16px var(--emerald); }
@keyframes runeFlash { 0%,100% { opacity: 0 } 20%, 40% { opacity: .9 } 30%, 50% { opacity: .2 } }
body.is-fumble::before { content: ""; position: fixed; inset: 0; z-index: calc(var(--z-portal) - 1); pointer-events: none; backdrop-filter: grayscale(1) contrast(.9); -webkit-backdrop-filter: grayscale(1) contrast(.9); }
.fumble-crack { position: fixed; inset: 0; z-index: var(--z-portal); pointer-events: none; opacity: 0; animation: crackIn 2.2s ease-out both; }
@keyframes crackIn { 0% { opacity: 0 } 10% { opacity: 1 } 80% { opacity: 1 } 100% { opacity: 0 } }
.ghost-hand { position: fixed; bottom: 0; left: 50%; transform: translate(-50%, 100%); z-index: var(--z-portal); pointer-events: none; width: 220px; opacity: .85; animation: handUp 2.4s var(--ease-heavy) both; filter: drop-shadow(0 0 20px rgba(200,220,210,.4)); }
@keyframes handUp { 0% { transform: translate(-50%, 100%) } 35% { transform: translate(-50%, 18%) } 65% { transform: translate(-50%, 22%) } 100% { transform: translate(-50%, 100%) } }

/* ── Ragnarök (Konami) ── */
.ragnarok { position: fixed; inset: 0; z-index: var(--z-portal); pointer-events: none; }
/* Ember is the reserved danger colour and this is the end of the world — the one
   place it gets a whole screen. The cold emerald serpent rises through it. */
.ragnarok__scene { position: absolute; inset: 0; background: url(../assets/art/forest.jpg) 50% 55%/cover no-repeat; filter: saturate(.5) brightness(.46) contrast(1.12); animation: fade 1.2s both; }
.ragnarok__scene::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(10,10,11,.74), rgba(10,10,11,.2) 42%, rgba(10,10,11,.6)); }
.ragnarok__veil { position: absolute; inset: 0; background: radial-gradient(ellipse at 50% 104%, rgba(194,58,43,.52), rgba(194,58,43,.14) 44%, transparent 74%); animation: fade 1.2s both; }
.ragnarok__serpent { position: absolute; inset: 0; width: 100%; height: 100%; filter: drop-shadow(0 0 30px rgba(47,211,154,.45)); animation: rise 1.8s var(--ease-out) both; }
.ragnarok__title { position: absolute; top: 18%; left: 0; right: 0; text-align: center; font-family: var(--font-display); font-weight: 900; font-size: clamp(2.4rem, 8vw, 6rem); color: var(--emerald); text-shadow: 0 0 30px var(--emerald); animation: critText 6s var(--ease-out) both; }

/* ── Dice tray ── */
.tray-backdrop { position: fixed; inset: 0; z-index: var(--z-tray); background: rgba(0,0,0,.4); backdrop-filter: blur(3px); animation: fade var(--t-ui) both; }
.tray { --facet: 14px; max-height: calc(100vh - 110px); max-height: calc(100dvh - 110px); overflow-y: auto; overscroll-behavior: contain; position: fixed; z-index: calc(var(--z-tray) + 1); bottom: calc(var(--s-6) + 76px); inset-inline-start: var(--s-6); width: min(440px, calc(100vw - 2 * var(--s-4))); background: var(--stone-grad); border: 1px solid var(--line-strong); clip-path: var(--clip-facet); box-shadow: var(--shadow-stone), var(--glow-inset); animation: rise var(--t-slow) var(--ease-out) both; }
@media (max-width: 900px) { .tray { bottom: calc(84px + var(--s-3)); inset-inline: var(--s-3); width: auto; } }
.tray__head { display: flex; align-items: center; justify-content: space-between; padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--line); }
.tray__head :is(h2,h3) { margin: 0; font-size: var(--fs-md); }
.tray__slab { position: relative; height: 240px; background: radial-gradient(ellipse at 50% 40%, #232527, #111214 80%); border-block: 1px solid var(--line); overflow: hidden; }
.tray__slab canvas { width: 100%; height: 100%; }
.tray__slab::before { content: "ᚠᚢᚦᚨᚱᚲ ᚷᚹᚺᚾᛁᛃ ᛈᛉᛊᛏᛒᛖ ᛗᛚᛜᛞᛟ"; position: absolute; inset: 0; display: grid; place-items: center; font-family: var(--font-rune); color: rgba(255,255,255,.04); font-size: 36px; letter-spacing: .5em; text-align: center; pointer-events: none; line-height: 1.6; }
.tray__result { position: absolute; bottom: var(--s-3); inset-inline: var(--s-4); display: flex; align-items: baseline; gap: var(--s-3); font-variant-numeric: tabular-nums; }
.tray__result .total { font-family: var(--font-display); font-weight: 900; font-size: 44px; line-height: 1; color: var(--emerald); text-shadow: 0 0 16px rgba(47,211,154,.7); }
.tray__result .total.is-nat1 { color: var(--ember-soft); text-shadow: 0 0 16px rgba(194,58,43,.7); }
.tray__result .detail { color: var(--fg-muted); font-size: var(--fs-sm); direction: ltr; unicode-bidi: isolate; }
.tray__ctrl { padding: var(--s-3) var(--s-4); display: grid; gap: var(--s-3); }
.tray__dice { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--s-half); }
.tray__dice button { min-height: 40px; padding: 0; font-family: var(--font-display); font-weight: 800; font-size: var(--fs-xs); }
.tray__dice button.is-sel { color: var(--accent-text); border-color: var(--line-glow); box-shadow: var(--glow-inset); }
.tray__expr { display: flex; gap: var(--s-2); }
/* the hand: one chip per die type with its own − and +, then the flat modifier */
.tray__pool { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); min-height: 36px; }
.tray__empty { color: var(--fg-faint); font-size: var(--fs-xs); }
.tray__chip { display: inline-flex; align-items: stretch; border: 1px solid var(--line-glow); background: var(--bg-elev-2); clip-path: var(--clip-facet); --facet: 6px; }
.tray__chip b { display: flex; align-items: baseline; justify-content: center; align-self: center; gap: 3px; min-width: 3.6em; padding-inline: 6px; font-weight: 800; font-size: var(--fs-sm); color: var(--fg); font-variant-numeric: tabular-nums; }
.tray__chip b .die { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-weight: 600; color: var(--accent-text); }
.tray__chip.is-neg b { color: var(--ember-soft); }
.tray__chip--mod { border-color: var(--line-strong); }
.tray__chip--mod b { min-width: 2.8em; color: var(--fg-muted); }
.tray__chip button { min-width: 36px; min-height: 36px; border: 0; background: none; color: var(--accent-text); font-size: 18px; line-height: 1; cursor: pointer; }
.tray__chip button:hover { background: rgba(47,211,154,.1); }
.tray__chip button:focus-visible { outline: 2px solid var(--emerald); outline-offset: -2px; }
.tray__clear { margin-inline-start: auto; min-height: 36px; padding-inline: var(--s-3); border: 1px solid var(--line); background: none; color: var(--fg-muted); font: inherit; font-size: var(--fs-xs); cursor: pointer; }
.tray__clear:hover { color: var(--fg); border-color: var(--line-strong); }
.tray__read { display: grid; gap: 2px; min-width: 0; }
.tray__result .runes { display: flex; flex-wrap: wrap; align-items: center; gap: .32em; font-family: var(--font-rune); font-size: 20px; line-height: 1.1; color: var(--emerald-soft); direction: ltr; text-shadow: 0 0 10px rgba(47,211,154,.45); }
.tray__result .runes .gate { width: .78em; height: .95em; filter: drop-shadow(0 0 5px rgba(47,211,154,.5)); }
.tray__legend { font-size: var(--fs-xs); color: var(--fg-muted); }
.tray__legend summary { cursor: pointer; color: var(--accent-text); min-height: 32px; display: flex; align-items: center; }
.tray__legend p { margin: var(--s-2) 0; line-height: 1.8; }
.tray__runes { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(10, minmax(0, 1fr)); gap: 4px; }
.tray__runes li { display: grid; justify-items: center; gap: 1px; padding: 3px 0; border: 1px solid var(--line); }
.tray__runes b { font-family: var(--font-rune); font-weight: 400; font-size: 17px; color: var(--fg); }
@media (max-width: 420px) { .tray__runes { grid-template-columns: repeat(7, minmax(0, 1fr)); } }
/* dice notation must be read exactly — Cinzel sets lowercase as small caps and its
   1 is a bare stem, so "1d8" read as "ID8". A monospace keeps every digit distinct. */
.tray__expr .input { direction: ltr; text-align: left; font-family: ui-monospace, 'SF Mono', 'Cascadia Mono', Menlo, Consolas, monospace; font-weight: 600; letter-spacing: .02em; min-height: 42px; }
.tray__log { max-height: 96px; overflow: auto; font-size: var(--fs-xs); color: var(--fg-muted); display: grid; gap: 2px; }
.tray__log b { color: var(--accent-text); }
