Discover: redesigned Browse panel + more data per rail
Browse panel (was boring native dropdowns): - Kind + Sort are now segmented pill controls; Genre + Era are horizontally scrollable, edge-faded chip rows with an accent-glow active state; primary 'Browse all →' CTA. Genre chips rebuild when kind flips. Selection lives in state.sel (no <select> reads). More data per rail (so 'Hide owned' doesn't gut a shelf): - /discover/list gains a 'pages' param (1–3): fetches that many consecutive TMDB pages and concatenates them deduped in one response. Rails request pages=2 (~40 items); trending is a fixed list so extra pages are skipped. Cleanup: removed dead .vdsc-filterbar/.vdsc-select CSS. Tests: +3 (discover routes registered; multi-page concat+dedup; trending fetched once despite pages). API suite: 22 passed.
This commit is contained in:
parent
5c9b43a175
commit
8043db0ad4
5 changed files with 183 additions and 72 deletions
|
|
@ -64,28 +64,48 @@ def register_routes(bp):
|
|||
|
||||
@bp.route("/discover/list", methods=["GET"])
|
||||
def video_discover_list():
|
||||
"""One shelf (rail) or one page of a filtered browse — see module docstring."""
|
||||
"""One shelf (rail) or one page of a filtered browse — see module docstring.
|
||||
|
||||
``pages`` (1–3, default 1) fetches that many consecutive TMDB pages and
|
||||
concatenates them (deduped) in one response — so a rail can show ~40 items
|
||||
and still look full after 'Hide owned' drops the ones you have."""
|
||||
from core.video.enrichment.engine import get_video_enrichment_engine
|
||||
eng = get_video_enrichment_engine()
|
||||
try:
|
||||
page = max(1, int(request.args.get("page", 1) or 1))
|
||||
except (TypeError, ValueError):
|
||||
page = 1
|
||||
key = (request.args.get("key") or "").strip()
|
||||
try:
|
||||
pages = min(3, max(1, int(request.args.get("pages", 1) or 1)))
|
||||
except (TypeError, ValueError):
|
||||
pages = 1
|
||||
key = (request.args.get("key") or "").strip()
|
||||
kind = request.args.get("kind", "movie")
|
||||
genre = request.args.get("genre") or None
|
||||
year = request.args.get("year") or None
|
||||
decade = request.args.get("decade") or None
|
||||
sort = request.args.get("sort") or "popularity.desc"
|
||||
|
||||
def fetch(p):
|
||||
if key == "trending":
|
||||
items = eng.trending()
|
||||
elif key:
|
||||
items = eng.discover_curated(key, page=page)
|
||||
else:
|
||||
items = eng.discover_filter(
|
||||
request.args.get("kind", "movie"),
|
||||
genre=(request.args.get("genre") or None),
|
||||
year=(request.args.get("year") or None),
|
||||
decade=(request.args.get("decade") or None),
|
||||
sort_by=(request.args.get("sort") or "popularity.desc"),
|
||||
page=page)
|
||||
return jsonify({"items": items or [], "page": page})
|
||||
return eng.trending()
|
||||
if key:
|
||||
return eng.discover_curated(key, page=p)
|
||||
return eng.discover_filter(kind, genre=genre, year=year, decade=decade,
|
||||
sort_by=sort, page=p)
|
||||
|
||||
try:
|
||||
items, seen = [], set()
|
||||
for p in range(page, page + pages):
|
||||
for it in (fetch(p) or []):
|
||||
dk = (it.get("kind"), it.get("tmdb_id"))
|
||||
if dk in seen:
|
||||
continue
|
||||
seen.add(dk)
|
||||
items.append(it)
|
||||
if key == "trending":
|
||||
break # trending is a fixed list — extra pages just repeat it
|
||||
return jsonify({"items": items, "page": page})
|
||||
except Exception:
|
||||
logger.exception("discover list failed (key=%s)", key)
|
||||
return jsonify({"items": [], "page": page})
|
||||
|
|
|
|||
|
|
@ -54,6 +54,39 @@ def test_blueprint_exposes_dashboard_route():
|
|||
assert "/api/video/episode/<int:tmdb_id>/<int:season>/<int:episode>" in rules
|
||||
assert any(r.startswith("/api/video/backdrop/") for r in rules)
|
||||
assert "/api/video/img" in rules
|
||||
assert "/api/video/discover/hero" in rules
|
||||
assert "/api/video/discover/genres" in rules
|
||||
assert "/api/video/discover/taste" in rules
|
||||
assert "/api/video/discover/list" in rules
|
||||
|
||||
|
||||
def test_discover_list_pages_concatenates_and_dedupes(tmp_path, monkeypatch):
|
||||
client, _ = _make_client(tmp_path)
|
||||
import core.video.enrichment.engine as eng_mod
|
||||
|
||||
class FakeEng:
|
||||
def discover_curated(self, key, page=1):
|
||||
# page 1 → ids 1,2 ; page 2 → ids 2,3 (overlap on 2)
|
||||
return ([{"kind": "movie", "tmdb_id": 1}, {"kind": "movie", "tmdb_id": 2}] if page == 1
|
||||
else [{"kind": "movie", "tmdb_id": 2}, {"kind": "movie", "tmdb_id": 3}])
|
||||
monkeypatch.setattr(eng_mod, "get_video_enrichment_engine", lambda: FakeEng())
|
||||
r = client.get("/api/video/discover/list?key=popular_movies&pages=2")
|
||||
assert [it["tmdb_id"] for it in r.get_json()["items"]] == [1, 2, 3] # concatenated + deduped
|
||||
|
||||
|
||||
def test_discover_list_trending_fetches_once_despite_pages(tmp_path, monkeypatch):
|
||||
client, _ = _make_client(tmp_path)
|
||||
import core.video.enrichment.engine as eng_mod
|
||||
calls = {"n": 0}
|
||||
|
||||
class FakeEng:
|
||||
def trending(self):
|
||||
calls["n"] += 1
|
||||
return [{"kind": "movie", "tmdb_id": 9}]
|
||||
monkeypatch.setattr(eng_mod, "get_video_enrichment_engine", lambda: FakeEng())
|
||||
r = client.get("/api/video/discover/list?key=trending&pages=3")
|
||||
assert calls["n"] == 1 # fixed list — not refetched per page
|
||||
assert [it["tmdb_id"] for it in r.get_json()["items"]] == [9]
|
||||
|
||||
|
||||
def test_img_proxy_rejects_non_tmdb(tmp_path):
|
||||
|
|
|
|||
|
|
@ -871,34 +871,37 @@
|
|||
<div class="vdsc-page" data-vdsc-page>
|
||||
<div class="vdsc-amb" data-vdsc-amb aria-hidden="true"></div>
|
||||
<div class="vdsc-hero hidden" data-vdsc-hero></div>
|
||||
<div class="vdsc-filterbar">
|
||||
<span class="vdsc-filter-label">Browse</span>
|
||||
<select class="vdsc-select" data-vdsc-f-kind aria-label="Type">
|
||||
<option value="movie">Movies</option>
|
||||
<option value="show">TV Shows</option>
|
||||
</select>
|
||||
<select class="vdsc-select" data-vdsc-f-genre aria-label="Genre">
|
||||
<option value="">All genres</option>
|
||||
</select>
|
||||
<select class="vdsc-select" data-vdsc-f-decade aria-label="Decade">
|
||||
<option value="">Any decade</option>
|
||||
<option value="2020">2020s</option>
|
||||
<option value="2010">2010s</option>
|
||||
<option value="2000">2000s</option>
|
||||
<option value="1990">1990s</option>
|
||||
<option value="1980">1980s</option>
|
||||
<option value="1970">1970s</option>
|
||||
</select>
|
||||
<select class="vdsc-select" data-vdsc-f-sort aria-label="Sort">
|
||||
<option value="popularity.desc">Most popular</option>
|
||||
<option value="vote_average.desc">Top rated</option>
|
||||
<option value="vote_count.desc">Most voted</option>
|
||||
</select>
|
||||
<button class="vdsc-btn" type="button" data-vdsc-apply>Browse</button>
|
||||
<label class="vdsc-toggle">
|
||||
<input type="checkbox" data-vdsc-hideowned>
|
||||
<span>Hide owned</span>
|
||||
</label>
|
||||
<div class="vdsc-browse">
|
||||
<div class="vdsc-browse-top">
|
||||
<div class="vdsc-seg" data-vdsc-seg="kind" role="group" aria-label="Type">
|
||||
<button class="vdsc-seg-btn vdsc-seg-btn--on" type="button" data-val="movie">Movies</button>
|
||||
<button class="vdsc-seg-btn" type="button" data-val="show">TV Shows</button>
|
||||
</div>
|
||||
<div class="vdsc-seg" data-vdsc-seg="sort" role="group" aria-label="Sort">
|
||||
<button class="vdsc-seg-btn vdsc-seg-btn--on" type="button" data-val="popularity.desc">Popular</button>
|
||||
<button class="vdsc-seg-btn" type="button" data-val="vote_average.desc">Top Rated</button>
|
||||
<button class="vdsc-seg-btn" type="button" data-val="vote_count.desc">Most Voted</button>
|
||||
</div>
|
||||
<label class="vdsc-toggle">
|
||||
<input type="checkbox" data-vdsc-hideowned>
|
||||
<span>Hide owned</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="vdsc-chips" data-vdsc-chipset="genre" aria-label="Genre">
|
||||
<button class="vdsc-chip vdsc-chip--on" type="button" data-val="">All genres</button>
|
||||
</div>
|
||||
<div class="vdsc-browse-bottom">
|
||||
<div class="vdsc-chips vdsc-chips--era" data-vdsc-chipset="decade" aria-label="Decade">
|
||||
<button class="vdsc-chip vdsc-chip--on" type="button" data-val="">Any era</button>
|
||||
<button class="vdsc-chip" type="button" data-val="2020">2020s</button>
|
||||
<button class="vdsc-chip" type="button" data-val="2010">2010s</button>
|
||||
<button class="vdsc-chip" type="button" data-val="2000">2000s</button>
|
||||
<button class="vdsc-chip" type="button" data-val="1990">1990s</button>
|
||||
<button class="vdsc-chip" type="button" data-val="1980">1980s</button>
|
||||
<button class="vdsc-chip" type="button" data-val="1970">1970s</button>
|
||||
</div>
|
||||
<button class="vdsc-btn vdsc-browse-go" type="button" data-vdsc-apply>Browse all →</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vdsc-shelves" data-vdsc-shelves></div>
|
||||
<div class="vdsc-grid-wrap hidden" data-vdsc-grid-wrap>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
io: null,
|
||||
hero: { items: [], idx: 0, timer: null },
|
||||
cat: { title: '', q: '', page: 1, paginates: true, busy: false },
|
||||
sel: { kind: 'movie', genre: '', decade: '', sort: 'popularity.desc' }, // Browse panel
|
||||
};
|
||||
|
||||
function $(s, r) { return (r || document).querySelector(s); }
|
||||
|
|
@ -206,7 +207,8 @@
|
|||
if (!shelf || shelf.getAttribute('data-vdsc-loaded')) return;
|
||||
shelf.setAttribute('data-vdsc-loaded', '1');
|
||||
var rail = $('[data-vdsc-rail]', shelf);
|
||||
fetch(LIST_URL + '?' + shelf.getAttribute('data-vdsc-q'), { headers: { Accept: 'application/json' } })
|
||||
// 2 pages (~40 items) so a rail still looks full after 'Hide owned'.
|
||||
fetch(LIST_URL + '?' + shelf.getAttribute('data-vdsc-q') + '&pages=2', { headers: { Accept: 'application/json' } })
|
||||
.then(function (r) { return r.ok ? r.json() : null; })
|
||||
.then(function (d) {
|
||||
var items = (d && d.items) || [];
|
||||
|
|
@ -264,14 +266,11 @@
|
|||
});
|
||||
}
|
||||
function applyFilter() {
|
||||
var kind = ($('[data-vdsc-f-kind]') || {}).value === 'show' ? 'show' : 'movie';
|
||||
var genre = ($('[data-vdsc-f-genre]') || {}).value || '';
|
||||
var decade = ($('[data-vdsc-f-decade]') || {}).value || '';
|
||||
var sort = ($('[data-vdsc-f-sort]') || {}).value || 'popularity.desc';
|
||||
var q = ['kind=' + kind, 'sort=' + encodeURIComponent(sort)];
|
||||
var bits = [kind === 'show' ? 'Shows' : 'Movies'];
|
||||
if (genre) { q.push('genre=' + genre); var gn = genreName(kind, genre); if (gn) bits.push(gn); }
|
||||
if (decade) { q.push('decade=' + decade); bits.push(decade + 's'); }
|
||||
var s = state.sel;
|
||||
var q = ['kind=' + s.kind, 'sort=' + encodeURIComponent(s.sort)];
|
||||
var bits = [s.kind === 'show' ? 'Shows' : 'Movies'];
|
||||
if (s.genre) { q.push('genre=' + s.genre); var gn = genreName(s.kind, s.genre); if (gn) bits.push(gn); }
|
||||
if (s.decade) { q.push('decade=' + s.decade); bits.push(s.decade + 's'); }
|
||||
openCategory(bits.join(' · '), q.join('&'));
|
||||
}
|
||||
function genreName(kind, id) {
|
||||
|
|
@ -279,12 +278,17 @@
|
|||
for (var i = 0; i < list.length; i++) if (String(list[i].id) === String(id)) return list[i].name;
|
||||
return '';
|
||||
}
|
||||
function rebuildGenreOptions() {
|
||||
var sel = $('[data-vdsc-f-genre]'); if (!sel) return;
|
||||
var kind = ($('[data-vdsc-f-kind]') || {}).value === 'show' ? 'show' : 'movie';
|
||||
sel.innerHTML = '<option value="">All genres</option>' + (state.genres[kind] || []).map(function (g) {
|
||||
return '<option value="' + g.id + '">' + esc(g.name) + '</option>';
|
||||
}).join('');
|
||||
function renderGenreChips() {
|
||||
var box = $('[data-vdsc-chipset="genre"]'); if (!box) return;
|
||||
box.innerHTML = '<button class="vdsc-chip vdsc-chip--on" type="button" data-val="">All genres</button>' +
|
||||
(state.genres[state.sel.kind] || []).map(function (g) {
|
||||
return '<button class="vdsc-chip" type="button" data-val="' + g.id + '">' + esc(g.name) + '</button>';
|
||||
}).join('');
|
||||
state.sel.genre = '';
|
||||
}
|
||||
function setActive(box, el, selector, onClass) {
|
||||
var all = box.querySelectorAll(selector);
|
||||
for (var i = 0; i < all.length; i++) all[i].classList.toggle(onClass, all[i] === el);
|
||||
}
|
||||
|
||||
// ── wiring ────────────────────────────────────────────────────────────────
|
||||
|
|
@ -312,6 +316,22 @@
|
|||
if (shelf) openCategory(shelf.getAttribute('data-vdsc-title'), shelf.getAttribute('data-vdsc-q'));
|
||||
return;
|
||||
}
|
||||
var seg = e.target.closest('.vdsc-seg-btn');
|
||||
if (seg) {
|
||||
var sbox = seg.closest('[data-vdsc-seg]');
|
||||
var which = sbox.getAttribute('data-vdsc-seg');
|
||||
setActive(sbox, seg, '.vdsc-seg-btn', 'vdsc-seg-btn--on');
|
||||
state.sel[which] = seg.getAttribute('data-val');
|
||||
if (which === 'kind') renderGenreChips(); // genres differ by kind
|
||||
return;
|
||||
}
|
||||
var chip = e.target.closest('.vdsc-chip');
|
||||
if (chip) {
|
||||
var cbox = chip.closest('[data-vdsc-chipset]');
|
||||
setActive(cbox, chip, '.vdsc-chip', 'vdsc-chip--on');
|
||||
state.sel[cbox.getAttribute('data-vdsc-chipset')] = chip.getAttribute('data-val');
|
||||
return;
|
||||
}
|
||||
var arrow = e.target.closest('[data-vdsc-scroll]');
|
||||
if (arrow) {
|
||||
var rail = $('[data-vdsc-rail]', arrow.closest('.vdsc-shelf'));
|
||||
|
|
@ -325,8 +345,6 @@
|
|||
var hero = $('[data-vdsc-hero]');
|
||||
if (hero) { hero.addEventListener('mouseenter', stopHeroTimer); hero.addEventListener('mouseleave', startHeroTimer); }
|
||||
|
||||
var kindSel = $('[data-vdsc-f-kind]');
|
||||
if (kindSel) kindSel.addEventListener('change', rebuildGenreOptions);
|
||||
var apply = $('[data-vdsc-apply]'); if (apply) apply.addEventListener('click', applyFilter);
|
||||
var clear = $('[data-vdsc-clear]'); if (clear) clear.addEventListener('click', closeCategory);
|
||||
var more = $('[data-vdsc-more]'); if (more) more.addEventListener('click', function () { state.cat.page++; loadGrid(false); });
|
||||
|
|
@ -343,7 +361,7 @@
|
|||
var g = res[0] || {}, t = res[1] || {};
|
||||
state.genres = { movie: g.movie || [], show: g.show || [] };
|
||||
state.taste = { movie: t.movie || [], show: t.show || [] };
|
||||
rebuildGenreOptions();
|
||||
renderGenreChips();
|
||||
renderShelves();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2198,17 +2198,7 @@ body[data-side="video"] #soulsync-toggle { display: none; }
|
|||
background: rgba(255, 255, 255, 0.32); transition: all 0.2s ease; }
|
||||
.vdsc-dot--on { background: hsl(var(--vgm-h, 230), 85%, 65%); width: 26px; border-radius: 5px; }
|
||||
|
||||
/* ── filter bar ───────────────────────────────────────────────────────────── */
|
||||
.vdsc-filterbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 0 24px;
|
||||
padding: 14px 16px; border-radius: 14px; background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.07); }
|
||||
.vdsc-filter-label { font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.06em;
|
||||
color: rgba(255, 255, 255, 0.42); margin-right: 2px; }
|
||||
.vdsc-select { padding: 10px 13px; border-radius: 10px; background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1); color: #fff; font-size: 13px; font-weight: 600;
|
||||
cursor: pointer; outline: none; transition: border-color 0.15s ease; }
|
||||
.vdsc-select:focus { border-color: rgba(var(--accent-rgb, 88 101 242), 0.6); }
|
||||
.vdsc-select option { background: #16161d; color: #fff; }
|
||||
/* ── Browse button (shared by the Browse panel + grid header) ─────────────── */
|
||||
.vdsc-btn { padding: 10px 18px; border-radius: 10px; font-size: 13px; font-weight: 800; cursor: pointer;
|
||||
border: 1px solid transparent; background: rgb(var(--accent-rgb, 88 101 242)); color: #fff;
|
||||
box-shadow: 0 8px 22px rgba(var(--accent-rgb, 88 101 242), 0.34); transition: all 0.15s ease; }
|
||||
|
|
@ -2261,7 +2251,7 @@ body[data-side="video"] #soulsync-toggle { display: none; }
|
|||
|
||||
/* ── Discover v2: ambient glow + see-all + grid head + hide-owned + polish ─── */
|
||||
.vdsc-page { position: relative; }
|
||||
.vdsc-hero, .vdsc-filterbar, .vdsc-shelves, .vdsc-grid-wrap { position: relative; z-index: 1; }
|
||||
.vdsc-hero, .vdsc-browse, .vdsc-shelves, .vdsc-grid-wrap { position: relative; z-index: 1; }
|
||||
/* a faint page-top color bleed that follows the current hero slide's hue */
|
||||
.vdsc-amb { position: absolute; top: -40px; left: 50%; transform: translateX(-50%); z-index: 0;
|
||||
width: 130%; height: 580px; pointer-events: none; opacity: 0.5; transition: opacity 0.7s ease;
|
||||
|
|
@ -2296,3 +2286,50 @@ body[data-side="video"] #soulsync-toggle { display: none; }
|
|||
.vdsc-shelf--in .vdsc-rail { animation: none; }
|
||||
.vdsc-amb { transition: none; }
|
||||
}
|
||||
|
||||
/* ── Discover Browse panel — segmented controls + genre/era chips ──────────── */
|
||||
.vdsc-browse { position: relative; z-index: 1; margin: 6px 0 28px; padding: 16px 18px 18px;
|
||||
border-radius: 16px; border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.02));
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 18px 50px -30px rgba(var(--accent-rgb, 88 101 242), 0.5); }
|
||||
.vdsc-browse-top { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
|
||||
|
||||
/* segmented control (pill of buttons with one active) */
|
||||
.vdsc-seg { display: inline-flex; padding: 3px; gap: 2px; border-radius: 11px;
|
||||
background: rgba(0, 0, 0, 0.28); border: 1px solid rgba(255, 255, 255, 0.07); }
|
||||
.vdsc-seg-btn { padding: 8px 16px; border: none; background: none; cursor: pointer; border-radius: 9px;
|
||||
font-size: 13px; font-weight: 700; color: rgba(255, 255, 255, 0.58); white-space: nowrap;
|
||||
transition: color 0.15s ease, background 0.15s ease, box-shadow 0.2s ease; }
|
||||
.vdsc-seg-btn:hover { color: rgba(255, 255, 255, 0.92); }
|
||||
.vdsc-seg-btn--on { color: #fff;
|
||||
background: linear-gradient(135deg, rgba(var(--accent-rgb, 88 101 242), 0.95), rgba(var(--accent-rgb, 88 101 242), 0.72));
|
||||
box-shadow: 0 6px 18px -6px rgba(var(--accent-rgb, 88 101 242), 0.7); }
|
||||
|
||||
/* chip rows (genres, eras) — horizontally scrollable, edge-faded */
|
||||
.vdsc-chips { display: flex; gap: 8px; overflow-x: auto; padding: 4px 0 6px; margin-top: 14px;
|
||||
scrollbar-width: thin;
|
||||
-webkit-mask-image: linear-gradient(90deg, transparent, #000 14px, #000 calc(100% - 14px), transparent);
|
||||
mask-image: linear-gradient(90deg, transparent, #000 14px, #000 calc(100% - 14px), transparent); }
|
||||
.vdsc-chips::-webkit-scrollbar { height: 6px; }
|
||||
.vdsc-chips::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.13); border-radius: 3px; }
|
||||
.vdsc-chips--era { margin-top: 0; }
|
||||
.vdsc-chip { flex: 0 0 auto; padding: 7px 14px; border-radius: 999px; cursor: pointer; white-space: nowrap;
|
||||
font-size: 12.5px; font-weight: 700; color: rgba(255, 255, 255, 0.66);
|
||||
background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease, box-shadow 0.2s ease; }
|
||||
.vdsc-chip:hover { color: #fff; background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.2); }
|
||||
.vdsc-chip--on { color: #fff; border-color: transparent;
|
||||
background: linear-gradient(135deg, rgba(var(--accent-rgb, 88 101 242), 0.95), rgba(var(--accent-rgb, 88 101 242), 0.7));
|
||||
box-shadow: 0 8px 22px -8px rgba(var(--accent-rgb, 88 101 242), 0.8); }
|
||||
|
||||
.vdsc-browse-bottom { display: flex; align-items: center; gap: 16px; margin-top: 12px; }
|
||||
.vdsc-browse-bottom .vdsc-chips { flex: 1; min-width: 0; margin-top: 0; }
|
||||
.vdsc-browse-go { flex: 0 0 auto; padding: 11px 22px;
|
||||
box-shadow: 0 10px 26px -8px rgba(var(--accent-rgb, 88 101 242), 0.7); }
|
||||
.vdsc-browse-go:hover { filter: brightness(1.08); transform: translateY(-1px); }
|
||||
.vdsc-browse .vdsc-toggle { margin-left: auto; }
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.vdsc-browse-bottom { flex-direction: column; align-items: stretch; }
|
||||
.vdsc-browse-go { width: 100%; }
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue