From fc0e2c7af6b386f18a9c14c3075d7ca020c4fa39 Mon Sep 17 00:00:00 2001 From: 4DBug <4DBug@github.com> Date: Wed, 26 Aug 2026 21:15:42 -0500 Subject: [PATCH] push --- rest/server.js | 3 + web/css/components.css | 24 ++++ web/css/pages.css | 251 ++++++++++++++++++++++++++++++++++++++++- web/index.html | 7 +- web/js/cards.js | 143 ++++++++++++++++++++++- web/js/home.js | 29 +++-- web/js/watch.js | 18 ++- 7 files changed, 461 insertions(+), 14 deletions(-) diff --git a/rest/server.js b/rest/server.js index c8705db..eca2448 100644 --- a/rest/server.js +++ b/rest/server.js @@ -178,6 +178,8 @@ function searchResult(m) { name: m.title || m.name, year: date ? String(date).slice(0, 4) : '', poster: m.poster_path ? `https://image.tmdb.org/t/p/w342${m.poster_path}` : '', + backdrop: m.backdrop_path ? `https://image.tmdb.org/t/p/w1280${m.backdrop_path}` : '', + rating: m.vote_average ? Number(m.vote_average).toFixed(1) : '', overview: m.overview || '', }; } @@ -239,6 +241,7 @@ async function handleEpisodes(req, res, url, id, season) { episode_number: e.episode_number, name: e.name, air_date: e.air_date || '', + still: e.still_path ? `https://image.tmdb.org/t/p/w300${e.still_path}` : '', })), }); } catch (e) { diff --git a/web/css/components.css b/web/css/components.css index 4debeac..fa84507 100644 --- a/web/css/components.css +++ b/web/css/components.css @@ -230,13 +230,37 @@ select.field { .card__meta { margin-top: 2px; + display: flex; + align-items: center; + gap: 6px; + min-width: 0; font-size: var(--text-xs); color: var(--color-text-faint); +} + +.card__meta__text { + min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.card__ep { + color: var(--color-text); + font-weight: var(--font-weight-semibold); + letter-spacing: 0.03em; + white-space: nowrap; +} + +.card__ep-dot { + margin: 0 2px; + color: var(--color-text-faint); +} + +.card__dot { + color: var(--color-text-faint); +} + .card__progress { position: absolute; left: 0; diff --git a/web/css/pages.css b/web/css/pages.css index 9b176ad..3ac7fdf 100644 --- a/web/css/pages.css +++ b/web/css/pages.css @@ -2,7 +2,230 @@ PAGES — page-specific layout: home, search, watch. Widget styling lives in components.css; this file only positions page-level blocks and handles responsive behavior. -*/ + */ + +/* ---- home: hero carousel ------------------------------------------------------ */ + +.hero { + position: relative; + height: clamp(440px, 72vh, 640px); + margin: calc(-1 * var(--space-5)) 0 var(--space-6); + overflow: hidden; + background: var(--color-surface); +} + +.hero__slides, +.hero__slide { + position: absolute; + inset: 0; +} + +.hero__slide { + opacity: 0; + visibility: hidden; + transition: opacity 600ms ease, visibility 0s linear 600ms; +} + +.hero__slide.is-active { + z-index: 1; + opacity: 1; + visibility: visible; + transition: opacity 600ms ease; +} + +.hero__backdrop { + width: 100%; + height: 100%; + object-fit: cover; + transform: scale(1.04); + transition: transform 8s ease; +} + +.hero__slide.is-active .hero__backdrop { + transform: scale(1); +} + +.hero__scrim { + position: absolute; + inset: 0; + background: + linear-gradient(90deg, rgba(0, 0, 0, 0.84) 0%, rgba(0, 0, 0, 0.58) 45%, rgba(0, 0, 0, 0.22) 75%, rgba(0, 0, 0, 0.05) 100%), + linear-gradient(to top, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0.18) 45%, rgba(0, 0, 0, 0.05) 100%); +} + +.hero__fade { + position: absolute; + left: 0; + right: 0; + bottom: 0; + height: 34%; + background: linear-gradient(to top, var(--color-bg) 2%, color-mix(in srgb, var(--color-bg) 72%, transparent) 58%, transparent 100%); + pointer-events: none; +} + +.hero__inner { + position: absolute; + top: 0; + bottom: 0; + left: 50%; + transform: translateX(-50%); + z-index: 2; + width: 100%; + max-width: var(--container-max); + padding: 0 var(--container-pad); + display: flex; + align-items: flex-end; +} + +.hero__content { + max-width: 620px; + padding-bottom: clamp(72px, 12vh, 112px); +} + +.hero__kicker { + display: inline-block; + margin-bottom: var(--space-3); + padding: 4px 10px; + border: 1px solid rgba(255, 255, 255, 0.14); + border-radius: var(--radius-full); + background: rgba(255, 255, 255, 0.08); + backdrop-filter: blur(8px); + color: rgba(255, 255, 255, 0.78); + font-size: var(--text-xs); + font-weight: var(--font-weight-semibold); + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.hero__title { + font-size: clamp(2.2rem, 5vw, 3.6rem); + line-height: 1.05; + letter-spacing: -0.03em; + text-shadow: 0 2px 24px rgba(0, 0, 0, 0.4); +} + +.hero__meta { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 10px; + margin-top: var(--space-4); + color: rgba(255, 255, 255, 0.78); + font-size: var(--text-sm); + font-weight: var(--font-weight-medium); +} + +.hero__rating { + color: var(--color-star); + font-weight: var(--font-weight-semibold); +} + +.hero__dot { + color: rgba(255, 255, 255, 0.35); +} + +.hero__meta .badge { + border-color: rgba(255, 255, 255, 0.18); + background: rgba(255, 255, 255, 0.08); + color: rgba(255, 255, 255, 0.88); +} + +.hero__overview { + max-width: 58ch; + margin-top: var(--space-4); + color: rgba(255, 255, 255, 0.8); + font-size: var(--text-sm); + line-height: 1.6; + text-shadow: 0 1px 12px rgba(0, 0, 0, 0.35); + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.hero__actions { + display: flex; + flex-wrap: wrap; + gap: var(--space-3); + margin-top: var(--space-5); +} + +.hero__play { + padding: 10px 24px; + font-size: var(--text-md); +} + +.hero__play svg { + width: 18px; + height: 18px; +} + +.hero__arrow { + position: absolute; + z-index: 3; + top: 50%; + transform: translateY(-50%); + display: grid; + place-items: center; + width: 44px; + height: 44px; + border: none; + border-radius: var(--radius-full); + background: rgba(10, 10, 10, 0.42); + color: #fff; + backdrop-filter: blur(8px); + cursor: pointer; + opacity: 0; + transition: opacity 160ms ease, background 120ms ease; +} + +.hero:hover .hero__arrow, +.hero__arrow:focus-visible { + opacity: 1; +} + +.hero__arrow:hover { + background: rgba(10, 10, 10, 0.65); +} + +.hero__arrow svg { + width: 22px; + height: 22px; +} + +.hero__arrow--prev { + left: 20px; +} + +.hero__arrow--next { + right: 20px; +} + +.hero__dots { + position: absolute; + z-index: 3; + left: 50%; + transform: translateX(-50%); + bottom: var(--space-5); + display: flex; + gap: var(--space-2); +} + +.hero__dot { + width: 8px; + height: 8px; + padding: 0; + border: none; + border-radius: var(--radius-full); + background: rgba(255, 255, 255, 0.32); + cursor: pointer; + transition: width 160ms ease, background 160ms ease; +} + +.hero__dot.is-active { + width: 24px; + background: var(--color-accent); +} /* ---- search ------------------------------------------------------------------ */ @@ -249,6 +472,15 @@ object-fit: cover; } +.episode__thumb--empty { + display: grid; + place-items: center; + color: var(--color-text-faint); + font-size: var(--text-lg); + font-weight: var(--font-weight-semibold); + background: linear-gradient(135deg, var(--color-surface-2), var(--color-surface)); +} + .episode__play { position: absolute; inset: 0; @@ -428,6 +660,23 @@ --card-width: 132px; } + .hero { + height: clamp(360px, 58vh, 520px); + margin-bottom: var(--space-5); + } + + .hero__content { + padding-bottom: 64px; + } + + .hero__overview { + -webkit-line-clamp: 2; + } + + .hero__arrow { + display: none; + } + .watch-hero { flex-direction: column; align-items: flex-start; diff --git a/web/index.html b/web/index.html index 31d9957..b68617c 100644 --- a/web/index.html +++ b/web/index.html @@ -28,8 +28,11 @@ -
-
+
+
+
+
+