Files
jacob-mathison 1de9d485aa fix: BLB throne image position + mobile responsive for featured card
- objectPosition: right center so BLB figure (right side of image) is in frame
- Drop aspect-ratio constraint on featuredImage, use minHeight: 480px instead
- Add responsive style block: mobile <640px stacks to 1 col, shorter image height
- Add className hooks for mobile overrides (blb-featured-card/image/body)
2026-04-11 15:40:03 -05:00

296 lines
8.5 KiB
TypeScript

import Image from "next/image";
import SiteLayout from "@/pages/components/SiteLayout";
const s = {
page: {
maxWidth: "1100px",
margin: "0 auto",
padding: "64px 32px 96px",
} as React.CSSProperties,
hero: {
marginBottom: "56px",
textAlign: "center" as const,
} as React.CSSProperties,
eyebrow: {
display: "block",
fontSize: "0.75rem",
fontWeight: 700,
letterSpacing: "0.18em",
textTransform: "uppercase" as const,
color: "#c0392b",
marginBottom: "16px",
} as React.CSSProperties,
h1: {
fontFamily: "'Georgia', 'Times New Roman', serif",
fontSize: "clamp(2.4rem, 6vw, 4rem)",
fontWeight: 700,
color: "#e8dcc8",
lineHeight: 1.1,
marginBottom: "20px",
} as React.CSSProperties,
heroSub: {
fontSize: "1.05rem",
color: "#9e9080",
maxWidth: "520px",
margin: "0 auto",
lineHeight: 1.7,
} as React.CSSProperties,
// Featured BLB card — wider than the rest
featuredCard: {
gridColumn: "1 / -1",
display: "grid",
gridTemplateColumns: "1fr 1fr",
gap: 0,
background: "rgba(15, 8, 24, 0.82)",
border: "1px solid rgba(180,140,255,0.22)",
borderRadius: "6px",
overflow: "hidden" as const,
marginBottom: "8px",
} as React.CSSProperties,
featuredImage: {
position: "relative" as const,
minHeight: "480px",
} as React.CSSProperties,
featuredBody: {
padding: "48px 40px",
display: "flex",
flexDirection: "column" as const,
justifyContent: "center",
} as React.CSSProperties,
grid: {
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(260px, 1fr))",
gap: "20px",
marginBottom: "40px",
} as React.CSSProperties,
card: {
background: "rgba(15, 8, 24, 0.82)",
border: "1px solid rgba(180,140,255,0.18)",
borderRadius: "6px",
overflow: "hidden" as const,
} as React.CSSProperties,
cardImage: {
position: "relative" as const,
aspectRatio: "2/3",
width: "100%",
background: "#06030f",
overflow: "hidden" as const,
} as React.CSSProperties,
cardImageLandscape: {
position: "relative" as const,
aspectRatio: "3/2",
width: "100%",
background: "#06030f",
overflow: "hidden" as const,
} as React.CSSProperties,
cardImagePlaceholder: {
position: "absolute" as const,
inset: 0,
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: "4rem",
opacity: 0.1,
} as React.CSSProperties,
cardBody: {
padding: "20px 22px 24px",
} as React.CSSProperties,
cardName: {
fontFamily: "'Georgia', 'Times New Roman', serif",
fontSize: "1.2rem",
fontWeight: 700,
color: "#e8dcc8",
marginBottom: "4px",
} as React.CSSProperties,
cardRole: {
fontSize: "0.72rem",
fontWeight: 700,
letterSpacing: "0.14em",
textTransform: "uppercase" as const,
color: "#c0392b",
marginBottom: "12px",
} as React.CSSProperties,
cardDesc: {
fontSize: "0.88rem",
color: "#9e9080",
lineHeight: 1.7,
} as React.CSSProperties,
// Minions group banner
groupBanner: {
position: "relative" as const,
aspectRatio: "3/2",
width: "100%",
borderRadius: "6px",
overflow: "hidden" as const,
border: "1px solid rgba(180,140,255,0.18)",
marginBottom: "40px",
} as React.CSSProperties,
groupBannerOverlay: {
position: "absolute" as const,
bottom: 0,
left: 0,
right: 0,
padding: "28px 32px",
background: "linear-gradient(to top, rgba(6,3,15,0.92) 0%, transparent 100%)",
} as React.CSSProperties,
credit: {
textAlign: "center" as const,
fontSize: "0.8rem",
color: "#9e9080",
opacity: 0.5,
marginTop: "8px",
letterSpacing: "0.04em",
} as React.CSSProperties,
};
const SUPPORTING: Array<{
image: string | null;
imageAlt?: string;
emoji: string;
name: string;
role: string;
desc: string;
}> = [
{
image: "/character-girl-soldier.jpg",
imageAlt: "Character — ???",
emoji: "⚔️",
name: "???",
role: "Coming Soon",
desc: "A warrior who has seen the worst the Bone Kingdom has to offer — and kept fighting anyway. The sword is the easy part.",
},
{
image: "/character-soldier-unmasked.jpg",
imageAlt: "Character — ???",
emoji: "🎭",
name: "???",
role: "Coming Soon",
desc: "The mask comes off eventually. What's underneath is more dangerous.",
},
{
image: "/character-cat.jpg",
imageAlt: "Character — ???",
emoji: "🐾",
name: "???",
role: "Coming Soon",
desc: "A creature that shouldn't exist — and yet here it is, collar and all. Whatever side it's on, it has teeth.",
},
{
image: "/character-bookie.jpg",
imageAlt: "Character — ???",
emoji: "📖",
name: "???",
role: "Coming Soon",
desc: "Knowledge is power in the Bone Kingdom. This one has both — and charges accordingly. The ledger is always open.",
},
{
image: null,
emoji: "🔥",
name: "???",
role: "Coming Soon",
desc: "A figure from the oldest part of the war. Their allegiance changes everything.",
},
{
image: null,
emoji: "🌑",
name: "???",
role: "Coming Soon",
desc: "Some figures are felt before they are seen. This one you feel first.",
},
];
export default function Characters() {
return (
<SiteLayout
title="Characters"
description="Meet the cast of Bone Lord Bob — a dark American Anime. Warriors, ghosts, and things that defy naming."
keywords="Bone Lord Bob characters, BLB cast, dark fantasy, American Anime, character art, Aaron Pressey"
>
<div style={s.page}>
{/* Hero */}
<div style={s.hero}>
<span style={s.eyebrow}>Characters</span>
<h1 style={s.h1}>The Cast</h1>
<p style={s.heroSub}>
Every soul in this world has a price. Meet the ones who pay it.
</p>
</div>
{/* BLB — featured protagonist card */}
<div style={s.featuredCard}>
<div style={s.featuredImage}>
<Image
src="/blb-throne.png"
alt="Bone Lord Bob on his throne"
fill
style={{ objectFit: "cover", objectPosition: "right center" }}
sizes="50vw"
priority
/>
</div>
<div style={s.featuredBody} className="blb-featured-body">
<span style={s.eyebrow}>The Protagonist</span>
<div style={{ ...s.cardName, fontSize: "2rem", marginBottom: "12px" }}>
Bone Lord Bob
</div>
<div style={{ ...s.cardRole, marginBottom: "20px" }}>
Lord of the Bone Kingdom
</div>
<p style={{ ...s.cardDesc, fontSize: "1rem", lineHeight: 1.8 }}>
A man cursed to walk between worlds carrying the weight of the
dead and the rage of the living. Seated on a throne of skulls,
flanked by candlelight, commanding what others cannot name.
He answers to no one.
</p>
</div>
</div>
{/* Supporting characters grid */}
<div style={s.grid}>
{SUPPORTING.map((c, i) => (
<div key={i} style={s.card}>
<div style={s.cardImage}>
{c.image ? (
<Image
src={c.image}
alt={c.imageAlt || c.name}
fill
style={{ objectFit: "cover", objectPosition: "center top" }}
sizes="(max-width: 768px) 100vw, 33vw"
/>
) : (
<div style={s.cardImagePlaceholder}>{c.emoji}</div>
)}
</div>
<div style={s.cardBody}>
<div style={s.cardName}>{c.name}</div>
<div style={s.cardRole}>{c.role}</div>
<p style={s.cardDesc}>{c.desc}</p>
</div>
</div>
))}
</div>
{/* Minions group shot banner */}
<div style={s.groupBanner}>
<Image
src="/character-minions.png"
alt="The minions of the Bone Kingdom"
fill
style={{ objectFit: "cover", objectPosition: "center" }}
sizes="100vw"
/>
<div style={s.groupBannerOverlay}>
<div style={{ ...s.cardRole, marginBottom: "6px" }}>The Bone Kingdom</div>
<div style={{ ...s.cardName, fontSize: "1.5rem" }}>
The Minions
</div>
</div>
</div>
<p style={s.credit}>Character art by Aaron Pressey</p>
</div>
</SiteLayout>
);
}