[object Object]

← 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

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 →