feat(brand): emblem behind hero text + Powered By footer with logo

Address review: the SVG footer badge rendered too small, and the default
hero should feature the Runic Gateway emblem rather than the moon.

- Footer: drop powered-by.svg; render the emblem PNG beside a "Powered by
  Runic Gateway" label (left-justified, info text stays centered).
- Default hero: brand.hero (and the client fallbacks) now default to the
  emblem PNG. The untouched default hero centers the square emblem behind
  the text as a medallion with a symmetric legibility overlay (per-layer
  background-size so the overlay stays full-bleed). BRAND_HERO still
  overrides.
- Admin login / player shell / maintenance backgrounds center the emblem
  as a capped medallion instead of a cropped full-bleed cover.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XmHdsbnLzDMAVQkAoTQSBe
This commit is contained in:
2026-07-18 16:56:22 -05:00
parent 352ae4f256
commit 526160721a
10 changed files with 66 additions and 27 deletions

View File

@@ -1,19 +1,27 @@
// Shared hero-layout helpers used by the public portal and the admin editor.
// Neutral built-in default; the instance hero image (BRAND_HERO) overrides it at
// runtime, threaded in as `defaultImage` by the portal.
export const DEFAULT_HERO_IMAGE = '/assets/img/hero-moon.png'
// Runic Gateway default hero emblem; the instance hero image (BRAND_HERO)
// overrides it at runtime, threaded in as `defaultImage` by the portal.
export const DEFAULT_HERO_IMAGE = '/assets/img/runic-emblem.png'
// The original hand-tuned multi-gradient hero background over a given image
// (used for the untouched default so the live page stays consistent until edited).
// Default hero background: the emblem centered behind the text as a medallion,
// under a symmetric dark overlay tuned to keep centered hero copy legible.
// Two layers (overlay gradient + image) so the per-layer background-size in
// `heroBackground` can contain the square emblem while the overlay stays full-bleed.
export function heroBgStack(image) {
return (
"linear-gradient(90deg,rgba(11,15,20,0.34) 0%,rgba(11,15,20,0.5) 36%,rgba(11,15,20,0.78) 62%,rgba(11,15,20,0.66) 100%),linear-gradient(180deg,rgba(11,15,20,0.08) 0%,rgba(11,15,20,0.72) 100%),url('" +
'linear-gradient(180deg,rgba(11,15,20,0.62) 0%,rgba(11,15,20,0.48) 38%,rgba(11,15,20,0.52) 58%,rgba(11,15,20,0.86) 100%),' +
"url('" +
(image || DEFAULT_HERO_IMAGE) +
"')"
)
}
// Keep the emblem fully visible and centered, capped so it never overflows a
// narrow viewport; the overlay layer covers.
export const HERO_DEFAULT_SIZE = 'cover, min(74vh, 640px, 86vw)'
export const HERO_DEFAULT_POSITION = 'center, center'
export const HERO_BG = heroBgStack(DEFAULT_HERO_IMAGE)
// Single-stop dark overlay driven by the editor's opacity slider.
@@ -26,13 +34,20 @@ export function buildOverlay(opacity) {
export function heroBackground(layout, { isDefault = false, defaultImage } = {}) {
const bg = layout.background || {}
const fallback = defaultImage || DEFAULT_HERO_IMAGE
const backgroundImage =
isDefault && !bg.image_url
? heroBgStack(fallback)
: `${buildOverlay(layout.overlay?.opacity ?? 0.72)}, url('${bg.image_url || fallback}')`
// Untouched default: emblem contained + centered behind the text (per-layer
// size/position so the overlay stays full-bleed while the square emblem fits).
if (isDefault && !bg.image_url) {
return {
backgroundColor: 'var(--bg-deep)',
backgroundImage: heroBgStack(fallback),
backgroundPosition: HERO_DEFAULT_POSITION,
backgroundRepeat: 'no-repeat',
backgroundSize: HERO_DEFAULT_SIZE,
}
}
return {
backgroundColor: 'var(--bg-deep)',
backgroundImage,
backgroundImage: `${buildOverlay(layout.overlay?.opacity ?? 0.72)}, url('${bg.image_url || fallback}')`,
backgroundPosition: `${bg.position_x || 'left'} ${bg.position_y || 'center'}`,
backgroundRepeat: 'no-repeat',
backgroundSize: bg.size || 'cover',