← back to Nineoh Guide
design-consistency pass: interior pages (episodes/cast/search + episode & cast detail) refactored onto the design system (CSS vars, cards, .se, display font) — whole app now cohesively luxe
99ec1b591408c62ac2edfcc2bd645f305904f46f · 2026-07-25 12:55:04 -0700 · Steve
Files touched
M apps/web/app/cast/[slug]/page.tsxM apps/web/app/cast/page.tsxM apps/web/app/episodes/[season]/[episode]/page.tsxM apps/web/app/episodes/page.tsxM apps/web/app/search/page.tsx
Diff
commit 99ec1b591408c62ac2edfcc2bd645f305904f46f
Author: Steve <steve@designerwallcoverings.com>
Date: Sat Jul 25 12:55:04 2026 -0700
design-consistency pass: interior pages (episodes/cast/search + episode & cast detail) refactored onto the design system (CSS vars, cards, .se, display font) — whole app now cohesively luxe
---
apps/web/app/cast/[slug]/page.tsx | 6 +--
apps/web/app/cast/page.tsx | 31 ++++++++-----
apps/web/app/episodes/[season]/[episode]/page.tsx | 10 ++---
apps/web/app/episodes/page.tsx | 55 ++++++++++++++++-------
apps/web/app/search/page.tsx | 10 ++---
5 files changed, 71 insertions(+), 41 deletions(-)
diff --git a/apps/web/app/cast/[slug]/page.tsx b/apps/web/app/cast/[slug]/page.tsx
index 3e5abe3..1720003 100644
--- a/apps/web/app/cast/[slug]/page.tsx
+++ b/apps/web/app/cast/[slug]/page.tsx
@@ -83,7 +83,7 @@ export default async function CastMember({
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
- <p style={{ fontSize: 12, color: "#999" }}>
+ <p style={{ fontSize: 12, color: "var(--muted)" }}>
<a href="/cast">← Cast</a>
</p>
<div style={{ float: "right", marginLeft: 16 }}>
@@ -91,7 +91,7 @@ export default async function CastMember({
</div>
<h1 style={{ marginBottom: 2 }}>{p.name}</h1>
{p.character_name ? (
- <p style={{ color: "#8a1c1c", marginTop: 0, fontWeight: 600 }}>
+ <p style={{ color: "var(--maroon)", marginTop: 0, fontWeight: 600 }}>
as {p.character_name}
</p>
) : null}
@@ -99,7 +99,7 @@ export default async function CastMember({
{p.biography_original ?? "An original bio for this cast member is coming soon."}
</p>
{p.bio_status === "ai-draft" ? (
- <p style={{ fontSize: 11, color: "#aaa" }}>Editorial bio draft — pending review.</p>
+ <p style={{ fontSize: 11, color: "var(--muted)" }}>Editorial bio draft — pending review.</p>
) : null}
</article>
);
diff --git a/apps/web/app/cast/page.tsx b/apps/web/app/cast/page.tsx
index 45ac271..ab2d204 100644
--- a/apps/web/app/cast/page.tsx
+++ b/apps/web/app/cast/page.tsx
@@ -50,24 +50,33 @@ export default async function Cast() {
appear only once their image licenses are cleared.
</p>
) : (
- <div style={{ display: "grid", gap: 16 }}>
+ <div
+ style={{
+ display: "grid",
+ gap: 14,
+ gridTemplateColumns: "repeat(auto-fill, minmax(300px, 1fr))",
+ }}
+ >
{cast.map((p) => (
- <div key={p.id} style={{ display: "flex", gap: 12 }}>
- <RightsImage asset={toAsset(p)} alt={p.name} width={90} height={90} />
- <div>
- <strong>
- <a href={castPath(p.name)}>{p.name}</a>
+ <a
+ key={p.id}
+ className="card"
+ href={castPath(p.name)}
+ style={{ display: "flex", gap: 12, color: "inherit" }}
+ >
+ <RightsImage asset={toAsset(p)} alt={p.name} width={82} height={104} />
+ <div style={{ minWidth: 0 }}>
+ <strong style={{ fontFamily: "var(--font-display)", fontSize: 16 }}>
+ {p.name}
</strong>
{p.character_name ? (
- <span style={{ color: "#8a1c1c", fontSize: 13 }}>
- {" "}as {p.character_name}
- </span>
+ <div className="se" style={{ fontSize: 13 }}>as {p.character_name}</div>
) : null}
- <p style={{ margin: "4px 0", color: "#555", fontSize: 13 }}>
+ <p style={{ margin: "5px 0 0", color: "var(--ink-soft)", fontSize: 12.5, lineHeight: 1.5 }}>
{p.biography_original ?? "Bio coming soon."}
</p>
</div>
- </div>
+ </a>
))}
</div>
)}
diff --git a/apps/web/app/episodes/[season]/[episode]/page.tsx b/apps/web/app/episodes/[season]/[episode]/page.tsx
index 5b2d8b9..c4774c8 100644
--- a/apps/web/app/episodes/[season]/[episode]/page.tsx
+++ b/apps/web/app/episodes/[season]/[episode]/page.tsx
@@ -67,16 +67,16 @@ export default async function EpisodePage({
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
- <p style={{ fontSize: 12, color: "#999" }}>
+ <p style={{ fontSize: 12, color: "var(--muted)" }}>
<a href="/episodes">← Episode Guide</a>
</p>
<h1 style={{ marginBottom: 4 }}>
- <span style={{ color: "#8a1c1c" }}>
+ <span style={{ color: "var(--maroon)" }}>
S{ep.season_number}E{ep.episode_number}
</span>{" "}
· {ep.title}
</h1>
- <p style={{ color: "#999", fontSize: 13, marginTop: 0 }}>
+ <p style={{ color: "var(--muted)", fontSize: 13, marginTop: 0 }}>
{ep.air_date
? `First aired ${new Date(ep.air_date).toLocaleDateString()}`
: "Air date TBD"}
@@ -88,13 +88,13 @@ export default async function EpisodePage({
<>
<p style={{ fontSize: 16 }}>{ep.summary_short_original}</p>
{ep.recap_status === "ai-draft" ? (
- <p style={{ fontSize: 11, color: "#aaa" }}>
+ <p style={{ fontSize: 11, color: "var(--muted)" }}>
Editorial recap draft — pending review.
</p>
) : null}
</>
) : (
- <p style={{ color: "#aaa" }}>An original recap for this episode is coming soon.</p>
+ <p style={{ color: "var(--muted)" }}>An original recap for this episode is coming soon.</p>
)}
</article>
);
diff --git a/apps/web/app/episodes/page.tsx b/apps/web/app/episodes/page.tsx
index 3bd5df4..00184a0 100644
--- a/apps/web/app/episodes/page.tsx
+++ b/apps/web/app/episodes/page.tsx
@@ -28,43 +28,64 @@ export default async function Episodes() {
return (
<section>
<h1>Episode Guide</h1>
- <p style={{ color: "#666", fontSize: 13 }}>
- Factual episode index. Original spoiler-safe recaps are being written by
- our editors and will appear per episode.
+ <p style={{ color: "var(--muted)", fontSize: 13 }}>
+ Factual episode index across all five seasons. Original spoiler-safe
+ recaps are written by our editors — use the spoiler guard to hide ones
+ past where you've watched.
</p>
<SpoilerSettings seasons={Math.max(5, ...seasons, 1)} />
{seasons.length === 0 && <p>No episodes indexed yet.</p>}
{seasons.map((s) => (
- <div key={s} style={{ marginTop: 20 }}>
- <h2 style={{ fontSize: 18 }}>Season {s}</h2>
- <ul style={{ listStyle: "none", padding: 0, margin: 0 }}>
- {bySeason.get(s)!.map((ep) => (
- <li
+ <div key={s} style={{ marginTop: 26 }}>
+ <h2 style={{ margin: "0 0 10px" }}>Season {s}</h2>
+ <div
+ style={{
+ background: "var(--paper-2)",
+ border: "1px solid var(--line)",
+ borderRadius: "var(--radius)",
+ boxShadow: "var(--shadow)",
+ overflow: "hidden",
+ }}
+ >
+ {bySeason.get(s)!.map((ep, i) => (
+ <div
key={ep.episode_number}
style={{
- padding: "8px 0",
- borderBottom: "1px solid #eee",
+ padding: "11px 16px",
+ borderTop: i === 0 ? "none" : "1px solid var(--line)",
display: "flex",
- gap: 10,
+ gap: 12,
+ alignItems: "baseline",
}}
>
- <span style={{ color: "#8a1c1c", fontWeight: 600, minWidth: 56 }}>
+ <span className="se" style={{ minWidth: 52, fontSize: 13 }}>
S{s}E{ep.episode_number}
</span>
<span style={{ flex: 1 }}>
- <a href={`/episodes/${s}/${ep.episode_number}`}>{ep.title}</a>
+ <a
+ href={`/episodes/${s}/${ep.episode_number}`}
+ style={{ fontFamily: "var(--font-display)", fontSize: 15.5 }}
+ >
+ {ep.title}
+ </a>
<SpoilerRecap
season={s}
episode={ep.episode_number}
text={ep.summary_short_original}
/>
</span>
- <span style={{ color: "#999", fontSize: 12 }}>
- {ep.air_date ? new Date(ep.air_date).toLocaleDateString() : ""}
+ <span style={{ color: "var(--muted)", fontSize: 12, whiteSpace: "nowrap" }}>
+ {ep.air_date
+ ? new Date(ep.air_date).toLocaleDateString(undefined, {
+ year: "numeric",
+ month: "short",
+ day: "numeric",
+ })
+ : ""}
</span>
- </li>
+ </div>
))}
- </ul>
+ </div>
</div>
))}
</section>
diff --git a/apps/web/app/search/page.tsx b/apps/web/app/search/page.tsx
index 7fc0ba6..cfaf1da 100644
--- a/apps/web/app/search/page.tsx
+++ b/apps/web/app/search/page.tsx
@@ -63,7 +63,7 @@ export default async function Search({
</form>
{query && (
- <p style={{ color: "#666", fontSize: 13 }}>
+ <p style={{ color: "var(--muted)", fontSize: 13 }}>
{total} result{total === 1 ? "" : "s"} for “{query}”.
</p>
)}
@@ -73,12 +73,12 @@ export default async function Search({
<h2 style={{ fontSize: 16 }}>Cast</h2>
<ul style={{ listStyle: "none", padding: 0 }}>
{results.cast.map((c) => (
- <li key={c.name} style={{ padding: "6px 0", borderBottom: "1px solid #eee" }}>
+ <li key={c.name} style={{ padding: "6px 0", borderBottom: "1px solid var(--line)" }}>
<a href={castPath(c.name)}>
<strong>{c.name}</strong>
</a>
{c.character_name ? (
- <span style={{ color: "#8a1c1c" }}> as {c.character_name}</span>
+ <span style={{ color: "var(--maroon)" }}> as {c.character_name}</span>
) : null}
</li>
))}
@@ -93,10 +93,10 @@ export default async function Search({
{results.eps.map((e) => (
<li
key={`${e.season_number}-${e.episode_number}`}
- style={{ padding: "6px 0", borderBottom: "1px solid #eee" }}
+ style={{ padding: "6px 0", borderBottom: "1px solid var(--line)" }}
>
<a href={`/episodes/${e.season_number}/${e.episode_number}`}>
- <span style={{ color: "#8a1c1c", fontWeight: 600 }}>
+ <span style={{ color: "var(--maroon)", fontWeight: 600 }}>
S{e.season_number}E{e.episode_number}
</span>{" "}
{e.title}
← 592af52 games: integrate Steve's 6 original self-contained 90210 gam
·
back to Nineoh Guide
·
episode detail: prev/next adjacent-episode nav (row-comparis 7a343d7 →