Initial commit: OBS scene collection, profile, landing overlay
Tracked: scene JSON (Untitled + pre-migration backups), landing/ overlay (HTML + static-hum.wav), profile encoder/basic settings, rtmp-services metadata, modules list, ini files, CLAUDE.md. Ignored via .gitignore: stream keys (**/service.json), obs-websocket password (plugin_config/obs-websocket/), logs/, profiler_data/, .sentinel/, OBS-managed *.bak and *.json.v1 sidecars.
This commit is contained in:
274
landing/index.html
Normal file
274
landing/index.html
Normal file
@@ -0,0 +1,274 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>OPHI-118 / STAND BY</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #07080d;
|
||||
--ink: #e8e8e0;
|
||||
--hud: #4fd2ff;
|
||||
--hud-dim: #2a7fa6;
|
||||
--accent: #e63a2e;
|
||||
--warn: #ffd000;
|
||||
--mono: 'DejaVu Sans Mono', 'Liberation Mono', 'Consolas', monospace;
|
||||
--display: 'DejaVu Sans', 'Liberation Sans', 'Helvetica', 'Arial', sans-serif;
|
||||
}
|
||||
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
html, body {
|
||||
width: 100%; height: 100%;
|
||||
background: var(--bg);
|
||||
color: var(--ink);
|
||||
font-family: var(--display);
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
body {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto auto;
|
||||
padding: 40px 72px;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
/* ───────── HUD top strip ───────── */
|
||||
.hud {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-family: var(--mono);
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: var(--hud);
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.hud .group { display: flex; gap: 28px; align-items: center; }
|
||||
.hud .dim { color: var(--hud-dim); }
|
||||
.hud .led {
|
||||
display: inline-block;
|
||||
width: 11px; height: 11px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
box-shadow: 0 0 10px var(--accent);
|
||||
margin-right: 12px;
|
||||
vertical-align: middle;
|
||||
animation: rec-blink 1.6s ease-in-out infinite;
|
||||
}
|
||||
@keyframes rec-blink {
|
||||
0%, 55% { opacity: 1; }
|
||||
65%, 100% { opacity: 0.2; }
|
||||
}
|
||||
|
||||
/* ───────── Centered mark ───────── */
|
||||
.stage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 36px;
|
||||
}
|
||||
.mark {
|
||||
width: 360px; height: 360px;
|
||||
filter: drop-shadow(0 0 22px rgba(58, 209, 255, 0.35));
|
||||
animation: mark-breathe 6s ease-in-out infinite;
|
||||
}
|
||||
@keyframes mark-breathe {
|
||||
0%, 100% { filter: drop-shadow(0 0 18px rgba(58, 209, 255, 0.30)); }
|
||||
50% { filter: drop-shadow(0 0 30px rgba(58, 209, 255, 0.50)); }
|
||||
}
|
||||
.bars {
|
||||
display: flex;
|
||||
width: 720px;
|
||||
height: 14px;
|
||||
opacity: 0.32;
|
||||
}
|
||||
.bars i { flex: 1; display: block; }
|
||||
.bars i:nth-child(1) { background: #c7c7c7; }
|
||||
.bars i:nth-child(2) { background: #c7c700; }
|
||||
.bars i:nth-child(3) { background: #00c7c7; }
|
||||
.bars i:nth-child(4) { background: #00c700; }
|
||||
.bars i:nth-child(5) { background: #c700c7; }
|
||||
.bars i:nth-child(6) { background: #c70000; }
|
||||
.bars i:nth-child(7) { background: #0000c7; }
|
||||
|
||||
/* ───────── Standby block ───────── */
|
||||
.standby {
|
||||
text-align: center;
|
||||
}
|
||||
.standby .big {
|
||||
font-size: 172px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.20em;
|
||||
line-height: 1;
|
||||
text-shadow: 0 0 18px rgba(79, 210, 255, 0.18);
|
||||
animation: standby-pulse 3s ease-in-out infinite;
|
||||
}
|
||||
.standby .sub {
|
||||
margin-top: 30px;
|
||||
font-family: var(--mono);
|
||||
font-size: 26px;
|
||||
letter-spacing: 0.45em;
|
||||
color: var(--hud-dim);
|
||||
}
|
||||
@keyframes standby-pulse {
|
||||
0%, 100% { opacity: 0.90; }
|
||||
50% { opacity: 1.00; }
|
||||
}
|
||||
|
||||
/* ───────── Bottom HUD ───────── */
|
||||
.foot {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-family: var(--mono);
|
||||
font-size: 19px;
|
||||
color: var(--hud-dim);
|
||||
letter-spacing: 0.12em;
|
||||
}
|
||||
.foot .warn { color: var(--warn); }
|
||||
|
||||
/* ───────── Overlays ───────── */
|
||||
#static {
|
||||
position: absolute; inset: 0;
|
||||
width: 100%; height: 100%;
|
||||
pointer-events: none;
|
||||
opacity: 0.045;
|
||||
mix-blend-mode: screen;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
.scanlines {
|
||||
position: absolute; inset: 0;
|
||||
pointer-events: none;
|
||||
background: repeating-linear-gradient(
|
||||
to bottom,
|
||||
rgba(0,0,0,0) 0px,
|
||||
rgba(0,0,0,0) 2px,
|
||||
rgba(0,0,0,0.20) 3px,
|
||||
rgba(0,0,0,0.20) 4px
|
||||
);
|
||||
opacity: 0.5;
|
||||
mix-blend-mode: multiply;
|
||||
}
|
||||
.vignette {
|
||||
position: absolute; inset: 0;
|
||||
pointer-events: none;
|
||||
background: radial-gradient(
|
||||
ellipse at center,
|
||||
rgba(0,0,0,0) 45%,
|
||||
rgba(0,0,0,0.60) 100%
|
||||
);
|
||||
}
|
||||
.flicker {
|
||||
position: absolute; inset: 0;
|
||||
pointer-events: none;
|
||||
background: rgba(0,0,0,0);
|
||||
animation: flicker 11s ease-in-out infinite;
|
||||
}
|
||||
@keyframes flicker {
|
||||
0%, 100% { background: rgba(0,0,0,0); }
|
||||
18% { background: rgba(0,0,0,0); }
|
||||
18.3% { background: rgba(0,0,0,0.06); }
|
||||
18.5% { background: rgba(0,0,0,0); }
|
||||
47% { background: rgba(0,0,0,0); }
|
||||
47.3% { background: rgba(0,0,0,0.08); }
|
||||
47.5% { background: rgba(0,0,0,0); }
|
||||
78% { background: rgba(0,0,0,0); }
|
||||
78.4% { background: rgba(0,0,0,0.05); }
|
||||
78.7% { background: rgba(0,0,0,0); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="hud">
|
||||
<div class="group">
|
||||
<span><span class="led"></span>TRANSMISSION</span>
|
||||
<span>OPHI-118</span>
|
||||
</div>
|
||||
<div class="group">
|
||||
<span class="dim">SIGNAL</span>
|
||||
<span id="signal">▮▮▮▯▯</span>
|
||||
</div>
|
||||
<div class="group">
|
||||
<span class="dim">UTC</span>
|
||||
<span id="clock">--:--:--</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="stage">
|
||||
<svg class="mark" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="100" cy="100" r="92" fill="none" stroke="#3ad1ff" stroke-width="1.2" opacity="0.35"/>
|
||||
<circle cx="100" cy="100" r="80" fill="none" stroke="#3ad1ff" stroke-width="2.5"/>
|
||||
<g stroke="#3ad1ff" stroke-width="2" opacity="0.55">
|
||||
<line x1="100" y1="8" x2="100" y2="22"/>
|
||||
<line x1="100" y1="178" x2="100" y2="192"/>
|
||||
<line x1="8" y1="100" x2="22" y2="100"/>
|
||||
<line x1="178" y1="100" x2="192" y2="100"/>
|
||||
</g>
|
||||
<polygon points="100,52 148,140 52,140"
|
||||
fill="none" stroke="#e63a2e" stroke-width="3.5" stroke-linejoin="miter"/>
|
||||
<circle cx="100" cy="116" r="4" fill="#ffd000"/>
|
||||
</svg>
|
||||
<div class="bars"><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div>
|
||||
</main>
|
||||
|
||||
<section class="standby">
|
||||
<div class="big">PLEASE STAND BY</div>
|
||||
<div class="sub">— DO NOT ADJUST YOUR RECEIVER —</div>
|
||||
</section>
|
||||
|
||||
<footer class="foot">
|
||||
<span>CH 118.0 MHz</span>
|
||||
<span>NTSC · 525i · MONO</span>
|
||||
<span class="warn">▲ AUDIO ACTIVE</span>
|
||||
</footer>
|
||||
|
||||
<canvas id="static"></canvas>
|
||||
<div class="scanlines"></div>
|
||||
<div class="vignette"></div>
|
||||
<div class="flicker"></div>
|
||||
|
||||
<script>
|
||||
// Live UTC clock
|
||||
const clockEl = document.getElementById('clock');
|
||||
const pad = n => String(n).padStart(2, '0');
|
||||
const tickClock = () => {
|
||||
const d = new Date();
|
||||
clockEl.textContent = `${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}`;
|
||||
};
|
||||
tickClock();
|
||||
setInterval(tickClock, 1000);
|
||||
|
||||
// Signal bar fluctuation — 3 bars typical, occasional drop or spike
|
||||
const sigEl = document.getElementById('signal');
|
||||
const FILL = '▮', EMPTY = '▯';
|
||||
const drawSig = n => FILL.repeat(n) + EMPTY.repeat(5 - n);
|
||||
setInterval(() => {
|
||||
const r = Math.random();
|
||||
const n = r < 0.06 ? 2 : r < 0.70 ? 3 : r < 0.95 ? 4 : 5;
|
||||
sigEl.textContent = drawSig(n);
|
||||
}, 1500);
|
||||
|
||||
// Animated CRT static (small buffer, scaled by browser)
|
||||
const cv = document.getElementById('static');
|
||||
const ctx = cv.getContext('2d');
|
||||
const W = 320, H = 180;
|
||||
cv.width = W; cv.height = H;
|
||||
const img = ctx.createImageData(W, H);
|
||||
const drawNoise = () => {
|
||||
const d = img.data;
|
||||
for (let i = 0; i < d.length; i += 4) {
|
||||
const v = (Math.random() * 255) | 0;
|
||||
d[i] = d[i+1] = d[i+2] = v;
|
||||
d[i+3] = 255;
|
||||
}
|
||||
ctx.putImageData(img, 0, 0);
|
||||
};
|
||||
drawNoise();
|
||||
setInterval(drawNoise, 1000 / 12);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
landing/static-hum.wav
Normal file
BIN
landing/static-hum.wav
Normal file
Binary file not shown.
Reference in New Issue
Block a user