/* COMPONENTS — reusable widgets. Each block is self-contained and named after the thing it styles (BEM-ish: block, block__element, block--modifier). Theme values come from tokens.css; never hardcode colors/sizes here if a token exists. */ /* ---- buttons ------------------------------------------------------------- */ .btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); padding: var(--space-2) var(--space-5); border: 1px solid var(--color-border-strong); border-radius: var(--radius-full); background: var(--color-surface-2); color: var(--color-text); font-size: var(--text-sm); font-weight: var(--font-weight-medium); line-height: 1.3; cursor: pointer; user-select: none; transition: background 120ms ease, border-color 120ms ease, color 120ms ease; } .btn:hover { background: var(--color-surface-hover); border-color: var(--color-border-strong); } .btn:active { transform: translateY(1px); } .btn:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; } .btn--primary { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-accent-text); } .btn--primary:hover { background: var(--color-accent-hover); border-color: var(--color-accent-hover); } .btn--ghost { background: transparent; border-color: transparent; color: var(--color-text-dim); } .btn--ghost:hover { background: var(--color-surface); color: var(--color-text); } .btn--sm { padding: var(--space-1) var(--space-3); font-size: var(--text-xs); } .btn:disabled { opacity: 0.5; cursor: default; pointer-events: none; } /* ---- form fields --------------------------------------------------------- */ .field { width: 100%; padding: var(--space-2) var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-sm); background: var(--color-surface-2); color: var(--color-text); font: inherit; font-size: var(--text-sm); } .field::placeholder { color: var(--color-text-faint); } .field:focus { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 25%, transparent); } select.field { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%239a9a9a' stroke-width='2'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 12px center; padding-right: 32px; } .field-label { display: block; margin-bottom: var(--space-1); font-size: var(--text-xs); font-weight: var(--font-weight-medium); color: var(--color-text-dim); } /* ---- badges & tags -------------------------------------------------------- */ .badge { display: inline-flex; align-items: center; padding: 1px var(--space-2); border: 1px solid var(--color-border-strong); border-radius: var(--radius-full); font-size: var(--text-xs); font-weight: var(--font-weight-semibold); letter-spacing: 0.04em; text-transform: uppercase; color: var(--color-text-dim); white-space: nowrap; } .badge--accent { border-color: var(--color-accent); color: var(--color-accent); } .tag { display: inline-block; padding: 2px var(--space-2); border-radius: var(--radius-full); background: var(--color-surface-2); border: 1px solid var(--color-border); font-size: var(--text-xs); color: var(--color-text-dim); } /* ---- chips (filters) ------------------------------------------------------- */ .chips { display: flex; flex-wrap: wrap; gap: var(--space-2); } .chip { padding: var(--space-1) var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-full); background: transparent; color: var(--color-text-dim); font-size: var(--text-sm); font-weight: var(--font-weight-medium); cursor: pointer; transition: background 120ms ease, color 120ms ease, border-color 120ms ease; } .chip:hover { border-color: var(--color-border-strong); color: var(--color-text); } .chip--active { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-accent-text); } /* ---- toggle switch --------------------------------------------------------- */ .switch { position: relative; display: inline-flex; flex: 0 0 auto; cursor: pointer; } .switch input { position: absolute; opacity: 0; width: 0; height: 0; } .switch__track { position: relative; width: 44px; height: 24px; border-radius: var(--radius-full); background: var(--color-surface-2); border: 1px solid var(--color-border-strong); transition: background 140ms ease, border-color 140ms ease; } .switch__thumb { position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: var(--radius-full); background: var(--color-text-dim); transition: transform 140ms ease, background 140ms ease; } .switch input:checked + .switch__track { background: var(--color-accent); border-color: var(--color-accent); } .switch input:checked + .switch__track .switch__thumb { transform: translateX(20px); background: var(--color-accent-text); } .switch input:focus-visible + .switch__track { outline: 2px solid var(--color-accent); outline-offset: 2px; } /* ---- media cards (poster) --------------------------------------------------- */ .card { display: block; flex: 0 0 var(--card-width); width: var(--card-width); cursor: pointer; } .card__poster { position: relative; display: block; aspect-ratio: var(--poster-aspect); border-radius: var(--radius-md); overflow: hidden; background: var(--color-surface); border: 1px solid var(--color-border); transition: border-color 120ms ease, box-shadow 120ms ease; } .card:hover .card__poster { border-color: var(--color-accent); box-shadow: 0 0 0 1px var(--color-accent); } .card__poster img { width: 100%; height: 100%; object-fit: cover; transition: transform 220ms ease; } .card:hover .card__poster img { transform: scale(1.04); } .card__poster--empty { display: flex; align-items: center; justify-content: center; color: var(--color-text-faint); font-size: var(--text-2xl); } .card__title { margin-top: var(--space-2); font-size: var(--text-sm); font-weight: var(--font-weight-medium); line-height: 1.3; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .card__meta { margin-top: 2px; font-size: var(--text-xs); color: var(--color-text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .card__progress { position: absolute; left: 0; right: 0; bottom: 0; height: 4px; background: rgba(0, 0, 0, 0.6); } .card__progress i { display: block; height: 100%; background: var(--color-accent); } /* ---- rows (horizontal carousels) ------------------------------------------ */ .row { margin-bottom: var(--space-6); } .row__header { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-3); } .row__title { font-size: var(--text-lg); font-weight: var(--font-weight-semibold); } .row__link { font-size: var(--text-sm); color: var(--color-text-dim); } .row__link:hover { color: var(--color-text); } .row__scroll { display: flex; gap: var(--space-3); overflow-x: auto; scroll-snap-type: x proximity; padding-bottom: var(--space-2); scrollbar-width: thin; scrollbar-color: var(--color-border-strong) transparent; } .row__scroll .card { scroll-snap-align: start; } /* ---- results grid ---------------------------------------------------------- */ .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--card-width), 1fr)); gap: var(--space-4); } .grid .card { flex: none; width: auto; } /* ---- toast ----------------------------------------------------------------- */ .toast { position: fixed; left: 50%; bottom: var(--space-6); z-index: 110; transform: translateX(-50%); max-width: min(90vw, 480px); padding: var(--space-2) var(--space-5); border: 1px solid var(--color-border-strong); border-radius: var(--radius-full); background: var(--color-surface); box-shadow: var(--shadow-2); font-size: var(--text-sm); opacity: 0; pointer-events: none; transition: opacity 150ms ease, transform 150ms ease; } .toast--show { opacity: 1; } .toast--error { border-color: var(--color-danger); color: var(--color-danger); } /* ---- loading & empty states ------------------------------------------------- */ .spinner { width: 28px; height: 28px; border: 3px solid var(--color-border); border-top-color: var(--color-accent); border-radius: 50%; animation: spin 0.8s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } .spinner--center { padding: var(--space-8) 0; text-align: center; } .empty { padding: var(--space-7) 0; text-align: center; color: var(--color-text-faint); } .empty h3 { font-size: var(--text-lg); color: var(--color-text-dim); margin-bottom: var(--space-2); } /* ---- player controls --------------------------------------------------------- */ .player-wrap { --pc-progress: 0%; position: relative; aspect-ratio: var(--player-aspect); border-radius: var(--radius-md); overflow: hidden; background: #000; border: 1px solid var(--color-border); cursor: default; } .player-wrap video { width: 100%; height: 100%; display: block; background: #000; } /* top-left back button */ .player-back { position: absolute; top: var(--space-3); left: var(--space-3); z-index: 5; display: inline-flex; align-items: center; justify-content: center; width: 42px; height: 42px; border-radius: var(--radius-full); background: rgba(0, 0, 0, 0.45); color: #fff; backdrop-filter: blur(6px); opacity: 0; transform: translateY(-4px); transition: opacity 160ms ease, transform 160ms ease, background 120ms ease; } .player-back:hover { background: rgba(0, 0, 0, 0.7); } .player-back svg { width: 22px; height: 22px; } /* centered big play (shown when paused) */ .player-bigplay { position: absolute; top: 50%; left: 50%; z-index: 4; transform: translate(-50%, -50%); width: 76px; height: 76px; border: none; border-radius: var(--radius-full); background: color-mix(in srgb, var(--color-accent) 90%, transparent); color: var(--color-accent-text); display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: var(--shadow-2); opacity: 0; pointer-events: none; transition: opacity 160ms ease, transform 160ms ease; } .player-bigplay:hover { transform: translate(-50%, -50%) scale(1.05); } .player-bigplay svg { width: 34px; height: 34px; margin-left: 4px; } /* bottom controls bar */ .player-controls { position: absolute; left: 0; right: 0; bottom: 0; z-index: 6; display: flex; flex-direction: column; padding: var(--space-4) var(--space-4) var(--space-3); background: linear-gradient(to top, rgba(0, 0, 0, 0.85), rgba(0, 0, 0, 0)); opacity: 0; transform: translateY(6px); transition: opacity 160ms ease, transform 160ms ease; } .player-controls__bar { display: flex; align-items: center; gap: var(--space-2); } .player-controls__spacer { flex: 1; } .pc-btn { display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px; flex: 0 0 auto; border: none; border-radius: var(--radius-sm); background: transparent; color: #fff; cursor: pointer; transition: background 120ms ease; } .pc-btn:hover { background: rgba(255, 255, 255, 0.14); } .pc-btn:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; } .pc-btn svg { width: 20px; height: 20px; } .pc-btn--text { width: auto; padding: 0 var(--space-2); font-size: var(--text-xs); font-weight: var(--font-weight-bold); letter-spacing: 0.04em; } .pc-btn.is-on { color: var(--color-accent); } .pc-time { flex: 0 0 auto; font-size: var(--text-xs); font-variant-numeric: tabular-nums; color: rgba(255, 255, 255, 0.85); white-space: nowrap; } .pc-quality { flex: 0 0 auto; font-size: var(--text-xs); font-weight: var(--font-weight-semibold); color: var(--color-accent); white-space: nowrap; } /* range inputs (seek + volume) */ .pc-seek, .pc-vol { -webkit-appearance: none; appearance: none; height: 4px; border-radius: var(--radius-full); background: linear-gradient( to right, var(--color-accent) var(--pc-progress), rgba(255, 255, 255, 0.28) var(--pc-progress) ); cursor: pointer; } .pc-seek { width: 100%; margin-bottom: var(--space-2); } .pc-vol { width: 72px; flex: 0 0 auto; } .pc-seek::-webkit-slider-thumb, .pc-vol::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 13px; height: 13px; border-radius: 50%; background: var(--color-accent); border: none; box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.4); } .pc-seek::-moz-range-thumb, .pc-vol::-moz-range-thumb { width: 13px; height: 13px; border-radius: 50%; background: var(--color-accent); border: none; box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.4); } /* show back + controls when hovered or flagged */ .player-wrap:hover .player-controls, .player-wrap:hover .player-back, .player-wrap--controls .player-controls, .player-wrap--controls .player-back { opacity: 1; transform: none; } .player-wrap:hover .player-bigplay, .player-wrap--controls .player-bigplay { /* big play only when paused — handled by .player-wrap--paused below */ } .player-wrap--paused .player-bigplay { opacity: 1; pointer-events: auto; }