Files
mediaplayer/includes/layout.php
T
Ty Clifford 6486483d88 -
2026-06-16 16:31:08 -04:00

277 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* layout.php — shared HTML head, header, and footer partials
*/
function render_head(string $title = 'Media — TyClifford.com', string $extra_css = ''): void { ?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= h($title) ?></title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Share+Tech+Mono&display=swap" rel="stylesheet">
<!-- Video.js -->
<link href="https://vjs.zencdn.net/8.10.0/video-js.css" rel="stylesheet" />
<script src="https://vjs.zencdn.net/8.10.0/video.min.js"></script>
<style>
/* ── Reset & design tokens (mirrors gate.php exactly) ──────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg: #09090f;
--surface: #0f0f1a;
--surface-2: #141425;
--border: #1e1e38;
--border-2: #2a2a48;
--text: #e8e8f8;
--text-2: #9898c0;
--text-3: #58587a;
--green: #00e87a;
--blue: #00c8ff;
--purple: #b060ff;
--red: #ff3355;
--orange: #ff8800;
--yellow: #ffd040;
--pat: #ff424d;
--pat-lite: rgba(255,66,77,.10);
--pat-border:rgba(255,66,77,.32);
--pat-glow: rgba(255,66,77,.18);
--r: 6px;
--gap: 1.25rem;
--page-pad: 1rem;
}
@media (min-width:600px) { :root { --page-pad: 1.5rem; } }
@media (min-width:1000px) { :root { --page-pad: 2rem; } }
html { font-size:16px; -webkit-text-size-adjust:100%; scroll-behavior:smooth; }
body {
min-height: 100dvh;
background: var(--bg);
background-image:
radial-gradient(ellipse 70% 50% at 20% 10%, rgba(176,96,255,.05) 0%, transparent 60%),
radial-gradient(ellipse 60% 40% at 80% 80%, rgba(0,200,255,.04) 0%, transparent 60%),
radial-gradient(ellipse 50% 60% at 50% 50%, rgba(255,66,77,.03) 0%, transparent 70%);
font-family: 'Inter', system-ui, sans-serif;
color: var(--text);
display: flex;
flex-direction: column;
align-items: center;
}
body::after {
content:''; position:fixed; inset:0; pointer-events:none; z-index:999;
background: repeating-linear-gradient(0deg,transparent 0px,transparent 3px,rgba(0,0,0,.06) 3px,rgba(0,0,0,.06) 4px);
}
@keyframes rise { from{opacity:0;transform:translateY(14px) scale(.98)} to{opacity:1;transform:translateY(0) scale(1)} }
@keyframes shimmer{ from{background-position:0% 50%} to{background-position:200% 50%} }
@keyframes pulse-border { 0%,100%{box-shadow:0 0 0 0 var(--pat-glow)} 50%{box-shadow:0 0 0 6px transparent} }
@keyframes live-pulse { 0%,100%{opacity:1} 50%{opacity:.4} }
/* ── Page wrapper ── */
.page { width:100%; max-width:1100px; padding:var(--page-pad); display:flex; flex-direction:column; gap:1.5rem; animation:rise .45s cubic-bezier(.22,.68,0,1.2) both; }
/* ── Top bar ── */
.topbar { display:flex; align-items:center; justify-content:space-between; gap:1rem; flex-wrap:wrap; }
.topbar-brand { display:flex; align-items:center; gap:.75rem; }
.brand-name { font-size:1rem; font-weight:700; letter-spacing:-.01em; color:var(--text); text-decoration:none; }
.brand-sub { font-family:'Share Tech Mono',monospace; font-size:.62rem; letter-spacing:.18em; text-transform:uppercase; color:var(--text-3); }
.topbar-social { display:flex; align-items:center; gap:.5rem; flex-wrap:wrap; }
.social-pill {
display:inline-flex; align-items:center; gap:.4rem;
padding:.35rem .75rem;
background:rgba(255,255,255,.03);
border:1px solid var(--border-2);
border-radius:99px;
font-family:'Share Tech Mono',monospace; font-size:.65rem; letter-spacing:.08em;
color:var(--text-3); text-decoration:none;
transition:border-color .2s, color .2s, background .2s;
}
.social-pill:hover { color:var(--text-2); background:rgba(255,255,255,.06); }
.social-pill svg { flex-shrink:0; }
.social-pill.active { border-color:var(--purple); color:var(--purple); }
/* ── Cards ── */
.card {
background:var(--surface);
border:1px solid var(--border);
border-radius:calc(var(--r) + 2px);
padding:1.4rem 1.5rem;
display:flex; flex-direction:column; gap:.9rem;
position:relative; overflow:hidden;
}
.card::after {
content:''; position:absolute; bottom:-1px; right:-1px;
width:14px; height:14px;
border-bottom:2px solid var(--purple); border-right:2px solid var(--purple);
border-radius:0 0 calc(var(--r)+2px) 0; opacity:.5;
}
.card-title { font-size:.95rem; font-weight:700; letter-spacing:-.01em; color:var(--text); line-height:1.2; }
.card-body { font-size:.85rem; color:var(--text-2); line-height:1.7; }
.card-body strong { color:var(--text); font-weight:600; }
.card-body a { color:var(--pat); text-decoration:none; border-bottom:1px solid rgba(255,66,77,.25); transition:border-color .15s; }
.card-body a:hover { border-color:rgba(255,66,77,.6); }
/* ── Eyebrow ── */
.eyebrow { font-family:'Share Tech Mono',monospace; font-size:.62rem; letter-spacing:.2em; text-transform:uppercase; }
.eyebrow-green { color:var(--green); text-shadow:0 0 10px rgba(0,232,122,.5); }
.eyebrow-green::before { content:'> '; opacity:.5; }
.eyebrow-purple { color:var(--purple); }
.eyebrow-blue { color:var(--blue); }
/* ── Stream card / player card ── */
.stream-card {
background:var(--surface); border:1px solid var(--border);
border-radius:calc(var(--r)+2px); overflow:hidden; position:relative;
box-shadow:0 0 0 1px rgba(255,255,255,.03), 0 4px 50px rgba(0,0,0,.6);
}
.stream-card::before {
content:''; position:absolute; top:0; left:0; right:0; height:2px; z-index:2;
background:linear-gradient(90deg,var(--pat),var(--purple),var(--blue),var(--green),var(--blue),var(--purple),var(--pat));
background-size:300% 100%; animation:shimmer 5s linear infinite;
}
.stream-ratio { position:relative; width:100%; padding-top:56.25%; background:#000; }
.stream-ratio > * { position:absolute; inset:0; width:100%; height:100%; border:0; display:block; }
.stream-footer {
display:flex; align-items:center; justify-content:space-between; gap:.75rem;
padding:.75rem 1.1rem; border-top:1px solid var(--border); flex-wrap:wrap;
}
.stream-meta { font-family:'Share Tech Mono',monospace; font-size:.65rem; letter-spacing:.1em; color:var(--text-3); }
.stream-meta span { color:var(--text-2); }
/* ── Footer ── */
.page-footer {
padding:.5rem 0 1rem; display:flex; align-items:center; justify-content:center;
gap:1.5rem; flex-wrap:wrap; border-top:1px solid var(--border);
}
.footer-note { font-family:'Share Tech Mono',monospace; font-size:.63rem; color:var(--text-3); letter-spacing:.06em; text-align:center; }
.footer-note::before { content:'// '; opacity:.4; }
.footer-note a { color:var(--text-3); text-decoration:none; transition:color .15s; }
.footer-note a:hover { color:var(--text-2); }
/* ── Buttons ── */
.btn {
display:inline-flex; align-items:center; gap:.45rem;
padding:.6rem 1.1rem; border-radius:var(--r);
font-family:'Inter',sans-serif; font-size:.82rem; font-weight:600;
text-decoration:none; cursor:pointer; border:none;
transition:transform .15s, filter .15s, background .2s;
}
.btn:hover { transform:translateY(-1px); filter:brightness(1.08); }
.btn:active { transform:scale(.98); }
.btn-primary { background:var(--purple); color:#fff; box-shadow:0 4px 18px rgba(176,96,255,.25); }
.btn-secondary { background:rgba(255,255,255,.06); color:var(--text-2); border:1px solid var(--border-2); }
.btn-secondary:hover { color:var(--text); background:rgba(255,255,255,.1); }
.btn-danger { background:rgba(255,51,85,.15); color:var(--red); border:1px solid rgba(255,51,85,.3); }
.btn-danger:hover { background:rgba(255,51,85,.25); }
.btn-green { background:rgba(0,232,122,.12); color:var(--green); border:1px solid rgba(0,232,122,.3); }
.btn-green:hover { background:rgba(0,232,122,.2); }
.btn-sm { padding:.38rem .75rem; font-size:.75rem; }
/* ── Form controls ── */
.form-group { display:flex; flex-direction:column; gap:.4rem; }
.form-label { font-family:'Share Tech Mono',monospace; font-size:.65rem; letter-spacing:.12em; text-transform:uppercase; color:var(--text-3); }
.form-input, .form-select, .form-textarea {
background:var(--surface-2); border:1px solid var(--border-2); border-radius:var(--r);
color:var(--text); padding:.6rem .85rem; font-family:'Inter',sans-serif; font-size:.85rem;
transition:border-color .2s;
width:100%;
}
.form-input:focus, .form-select:focus, .form-textarea:focus {
outline:none; border-color:var(--purple);
box-shadow:0 0 0 2px rgba(176,96,255,.15);
}
.form-textarea { resize:vertical; min-height:80px; }
.form-select option { background:var(--surface-2); }
/* ── Notice/alert ── */
.notice {
padding:.75rem 1rem; border-radius:var(--r); font-size:.82rem;
border:1px solid; margin-bottom:1rem;
}
.notice-success { background:rgba(0,232,122,.08); border-color:rgba(0,232,122,.3); color:var(--green); }
.notice-error { background:rgba(255,51,85,.08); border-color:rgba(255,51,85,.3); color:var(--red); }
.notice-info { background:rgba(0,200,255,.08); border-color:rgba(0,200,255,.3); color:var(--blue); }
/* ── Pagination ── */
.pagination { display:flex; align-items:center; gap:.4rem; flex-wrap:wrap; }
.pagination a, .pagination span {
display:inline-flex; align-items:center; justify-content:center;
min-width:2rem; height:2rem; padding:0 .5rem;
border-radius:var(--r); font-family:'Share Tech Mono',monospace; font-size:.72rem;
text-decoration:none; transition:background .2s, color .2s, border-color .2s;
border:1px solid var(--border-2); color:var(--text-3);
}
.pagination a:hover { border-color:var(--purple); color:var(--purple); background:rgba(176,96,255,.08); }
.pagination .current { border-color:var(--purple); color:var(--purple); background:rgba(176,96,255,.15); }
.pagination .disabled { opacity:.3; pointer-events:none; }
<?= $extra_css ?>
</style>
</head>
<body>
<?php }
function render_topbar(string $active = ''): void {
$title = setting('site_title', 'Ty Clifford');
$sub = setting('site_sub', 'tyclifford.com / player');
?>
<header class="topbar">
<div class="topbar-brand">
<div>
<a href="index.php" class="brand-name"><?= h($title) ?></a>
<div class="brand-sub"><?= h($sub) ?></div>
</div>
</div>
<nav class="topbar-social" aria-label="Navigation">
<a class="social-pill <?= $active==='home' ? 'active' : '' ?>" href="index.php">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/><polyline points="9,22 9,12 15,12 15,22"/></svg>
Home
</a>
<a class="social-pill <?= $active==='catalogue' ? 'active' : '' ?>" href="catalogue.php">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>
Videos
</a>
</nav>
</header>
<?php }
function render_footer(): void { ?>
<footer class="page-footer" role="contentinfo">
<p class="footer-note">TyClifford.com &nbsp;·&nbsp; player</p>
<p class="footer-note"><a href="https://git.tyclifford.com/">open source</a></p>
<p class="footer-note">hobbies are my hobby</p>
<p class="footer-note"><a href="mailto:ty@tyclifford.com">ty@tyclifford.com</a></p>
</footer>
<?php }
function render_pagination(int $current, int $total, string $url_pattern): void {
if ($total <= 1) return;
echo '<div class="pagination">';
$prev = $current - 1;
$next = $current + 1;
if ($prev >= 1) {
echo '<a href="' . sprintf($url_pattern, $prev) . '"></a>';
} else {
echo '<span class="disabled"></span>';
}
$range = range(max(1, $current-2), min($total, $current+2));
if (!in_array(1, $range)) { echo '<a href="' . sprintf($url_pattern, 1) . '">1</a><span>…</span>'; }
foreach ($range as $p) {
if ($p === $current) echo '<span class="current">' . $p . '</span>';
else echo '<a href="' . sprintf($url_pattern, $p) . '">' . $p . '</a>';
}
if (!in_array($total, $range)) { echo '<span>…</span><a href="' . sprintf($url_pattern, $total) . '">' . $total . '</a>'; }
if ($next <= $total) {
echo '<a href="' . sprintf($url_pattern, $next) . '"></a>';
} else {
echo '<span class="disabled"></span>';
}
echo '</div>';
}