Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f4e39c96fd | |||
| c49f28e619 | |||
| b58bcd8398 |
@@ -0,0 +1,10 @@
|
||||
<div class="hero-eyebrow fade-up">Charleston, South Carolina</div>
|
||||
<h1 class="hero-title fade-up">Deep<br /><em>Drft</em></h1>
|
||||
<p class="hero-subtitle fade-up">Electronic Music Collective</p>
|
||||
<p class="hero-desc fade-up">
|
||||
We craft immersive electronic soundscapes — live; built from synthesizers, drum machines, and raw intention.
|
||||
</p>
|
||||
<div class="hero-actions fade-up">
|
||||
<a class="btn-primary" href="/tracks">Start Streaming</a>
|
||||
<a class="btn-ghost" href="/tracks">Browse Tracks</a>
|
||||
</div>
|
||||
@@ -0,0 +1,108 @@
|
||||
/* ── Animations ── */
|
||||
@keyframes fade-up {
|
||||
from { opacity: 0; transform: translateY(24px); }
|
||||
to { opacity: 1; transform: none; }
|
||||
}
|
||||
|
||||
.hero-eyebrow {
|
||||
font-family: var(--deepdrft-font-mono);
|
||||
font-size: 0.65rem;
|
||||
letter-spacing: 0.28em;
|
||||
color: var(--deepdrft-green-accent);
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 1.8rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
.hero-eyebrow::before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 2.5rem;
|
||||
height: 1px;
|
||||
background: var(--deepdrft-green-accent);
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-family: var(--deepdrft-font-display);
|
||||
font-size: clamp(4.5rem, 8vw, 8.5rem);
|
||||
font-weight: 300;
|
||||
line-height: 0.92;
|
||||
letter-spacing: -0.02em;
|
||||
color: var(--deepdrft-navy);
|
||||
margin-bottom: 0.5rem;
|
||||
animation-delay: 0.22s;
|
||||
}
|
||||
|
||||
.hero-title em {
|
||||
font-style: italic;
|
||||
color: var(--deepdrft-green);
|
||||
}
|
||||
|
||||
.hero-subtitle {
|
||||
font-family: var(--deepdrft-font-display);
|
||||
font-size: clamp(1rem, 2vw, 1.35rem);
|
||||
font-weight: 300;
|
||||
font-style: italic;
|
||||
color: var(--deepdrft-muted);
|
||||
margin-bottom: 3rem;
|
||||
letter-spacing: 0.04em;
|
||||
animation-delay: 0.34s;
|
||||
}
|
||||
|
||||
.hero-desc {
|
||||
font-family: var(--deepdrft-font-body);
|
||||
font-size: 0.92rem;
|
||||
line-height: 1.75;
|
||||
color: var(--deepdrft-navy);
|
||||
opacity: 0.7;
|
||||
max-width: 36ch;
|
||||
margin-bottom: 3rem;
|
||||
animation-delay: 0.44s;
|
||||
}
|
||||
|
||||
.hero-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
animation-delay: 0.54s;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
font-family: var(--deepdrft-font-mono);
|
||||
font-size: 0.68rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: var(--deepdrft-white);
|
||||
background: var(--deepdrft-navy);
|
||||
border: none;
|
||||
padding: 1rem 2.2rem;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
transition: background 0.25s, transform 0.2s;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--deepdrft-green);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-ghost {
|
||||
font-family: var(--deepdrft-font-mono);
|
||||
font-size: 0.68rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: var(--deepdrft-navy);
|
||||
background: transparent;
|
||||
border: 1px solid var(--deepdrft-border);
|
||||
padding: 1rem 2.2rem;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
transition: border-color 0.25s, color 0.25s;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.btn-ghost:hover { border-color: var(--deepdrft-navy); }
|
||||
@@ -0,0 +1,11 @@
|
||||
@* Pulsing rings *@
|
||||
<div class="circle-deco"></div>
|
||||
<div class="circle-deco"></div>
|
||||
<div class="circle-deco"></div>
|
||||
|
||||
<div class="now-playing-content">
|
||||
<NowPlayingCard />
|
||||
|
||||
@* Stat row - hard-coded for now. TODO Phase 2: wire to real track count / identity model. *@
|
||||
<NowPlayingStats />
|
||||
</div>
|
||||
@@ -0,0 +1,20 @@
|
||||
.now-playing-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 3rem;
|
||||
}
|
||||
|
||||
.circle-deco {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(61, 122, 104, 0.3);
|
||||
animation: pulse-ring 4s ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.circle-deco:nth-child(1) { width: 320px; height: 320px; animation-delay: 0s; }
|
||||
.circle-deco:nth-child(2) { width: 520px; height: 520px; animation-delay: 0.8s; }
|
||||
.circle-deco:nth-child(3) { width: 720px; height: 720px; animation-delay: 1.6s; }
|
||||
@@ -0,0 +1,46 @@
|
||||
@using DeepDrftPublic.Client.Services
|
||||
<div class="now-playing">
|
||||
<div class="np-label"><span class="np-dot"></span>Now Playing</div>
|
||||
<div class="np-title">@(Player?.CurrentTrack?.TrackName ?? "Nothing playing")</div>
|
||||
<div class="np-sub">
|
||||
@(Player?.CurrentTrack != null
|
||||
? $"{Player.CurrentTrack.Artist} · {Player.CurrentTrack.Album ?? "Single"}"
|
||||
: "Select a track to begin")
|
||||
</div>
|
||||
|
||||
@if (Player?.IsLoaded == true)
|
||||
{
|
||||
<div class="waveform-bars">
|
||||
@* 20 bars - approximate the wireframe's varied animation timings *@
|
||||
<div class="waveform-bar" style="--h-lo:6px;--h-hi:28px;--dur:0.7s;height:14px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:10px;--h-hi:36px;--dur:0.9s;height:28px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:4px;--h-hi:20px;--dur:0.65s;height:10px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:12px;--h-hi:40px;--dur:1.1s;height:36px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:6px;--h-hi:26px;--dur:0.8s;height:18px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:8px;--h-hi:32px;--dur:0.75s;height:24px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:4px;--h-hi:18px;--dur:0.95s;height:8px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:14px;--h-hi:42px;--dur:1.2s;height:32px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:6px;--h-hi:22px;--dur:0.68s;height:16px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:10px;--h-hi:38px;--dur:0.88s;height:30px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:4px;--h-hi:16px;--dur:0.72s;height:6px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:8px;--h-hi:30px;--dur:1.0s;height:20px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:12px;--h-hi:36px;--dur:0.85s;height:26px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:6px;--h-hi:24px;--dur:0.9s;height:14px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:10px;--h-hi:34px;--dur:0.78s;height:22px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:4px;--h-hi:20px;--dur:1.05s;height:12px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:14px;--h-hi:44px;--dur:0.92s;height:38px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:6px;--h-hi:26px;--dur:0.7s;height:18px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:8px;--h-hi:32px;--dur:0.82s;height:22px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:4px;--h-hi:18px;--dur:1.15s;height:10px"></div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="waveform-placeholder"></div>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
@code {
|
||||
[CascadingParameter] public IStreamingPlayerService? Player { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
.now-playing {
|
||||
background: rgba(250, 250, 248, 0.06);
|
||||
border: 1px solid rgba(250, 250, 248, 0.12);
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.np-label {
|
||||
font-family: var(--deepdrft-font-mono);
|
||||
font-size: 0.6rem;
|
||||
letter-spacing: 0.25em;
|
||||
color: var(--deepdrft-green-accent);
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 0.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.np-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--deepdrft-green-accent);
|
||||
animation: blink 1.2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.np-title {
|
||||
font-family: var(--deepdrft-font-display);
|
||||
font-size: 1.5rem;
|
||||
font-weight: 400;
|
||||
color: var(--deepdrft-white);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.np-sub {
|
||||
font-family: var(--deepdrft-font-body);
|
||||
font-size: 0.75rem;
|
||||
color: rgba(250, 250, 248, 0.45);
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.waveform-bars {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
margin-top: 1.2rem;
|
||||
}
|
||||
|
||||
.waveform-bar {
|
||||
width: 3px;
|
||||
background: var(--deepdrft-green-accent);
|
||||
border-radius: 2px;
|
||||
animation: wave-dance var(--dur, 1s) ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.waveform-placeholder {
|
||||
margin-top: 1.2rem;
|
||||
height: 1px;
|
||||
background: rgba(250, 250, 248, 0.12);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="hero-stat-row">
|
||||
<div class="hero-stat">
|
||||
<div class="hero-stat-num">47+</div>
|
||||
<div class="hero-stat-label">Live Sessions</div>
|
||||
</div>
|
||||
<div class="hero-stat">
|
||||
<div class="hero-stat-num">2</div>
|
||||
<div class="hero-stat-label">Members</div>
|
||||
</div>
|
||||
<div class="hero-stat">
|
||||
<div class="hero-stat-num">∞</div>
|
||||
<div class="hero-stat-label">Drift Points</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,28 @@
|
||||
.hero-stat-row {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.hero-stat {
|
||||
flex: 1;
|
||||
background: rgba(250, 250, 248, 0.04);
|
||||
border: 1px solid rgba(250, 250, 248, 0.08);
|
||||
padding: 1.2rem;
|
||||
}
|
||||
|
||||
.hero-stat-num {
|
||||
font-family: var(--deepdrft-font-display);
|
||||
font-size: 2rem;
|
||||
font-weight: 300;
|
||||
color: var(--deepdrft-white);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.hero-stat-label {
|
||||
font-family: var(--deepdrft-font-mono);
|
||||
font-size: 0.58rem;
|
||||
letter-spacing: 0.2em;
|
||||
color: rgba(250, 250, 248, 0.4);
|
||||
text-transform: uppercase;
|
||||
margin-top: 0.4rem;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
@page "/"
|
||||
@using DeepDrftPublic.Client.Controls
|
||||
@using DeepDrftPublic.Client.Services
|
||||
|
||||
<PageTitle>Deep DRFT - Electronic Music Collective</PageTitle>
|
||||
@@ -6,83 +7,11 @@
|
||||
@* Hero - split 50/50 *@
|
||||
<section class="hero">
|
||||
<div class="hero-left">
|
||||
<div class="hero-eyebrow fade-up">Charleston, South Carolina</div>
|
||||
<h1 class="hero-title fade-up">Deep<br /><em>Drft</em></h1>
|
||||
<p class="hero-subtitle fade-up">Electronic Music Collective</p>
|
||||
<p class="hero-desc fade-up">
|
||||
We craft immersive electronic soundscapes — live, unscripted, and built from synthesizers, drum machines, and raw intention. No sets. No loops. Pure drift.
|
||||
</p>
|
||||
<div class="hero-actions fade-up">
|
||||
<a class="btn-primary" href="/tracks">Start Streaming</a>
|
||||
<a class="btn-ghost" href="/tracks">Browse Tracks</a>
|
||||
</div>
|
||||
<DeepDrftHero />
|
||||
</div>
|
||||
|
||||
<div class="hero-right">
|
||||
@* Pulsing rings *@
|
||||
<div class="circle-deco"></div>
|
||||
<div class="circle-deco"></div>
|
||||
<div class="circle-deco"></div>
|
||||
|
||||
<div class="hero-right-content">
|
||||
@* Now-Playing card *@
|
||||
<div class="now-playing">
|
||||
<div class="np-label"><span class="np-dot"></span>Now Playing</div>
|
||||
<div class="np-title">@(Player?.CurrentTrack?.TrackName ?? "Nothing playing")</div>
|
||||
<div class="np-sub">
|
||||
@(Player?.CurrentTrack != null
|
||||
? $"{Player.CurrentTrack.Artist} · {Player.CurrentTrack.Album ?? "Single"}"
|
||||
: "Select a track to begin")
|
||||
</div>
|
||||
|
||||
@if (Player?.IsLoaded == true)
|
||||
{
|
||||
<div class="waveform-bars">
|
||||
@* 20 bars - approximate the wireframe's varied animation timings *@
|
||||
<div class="waveform-bar" style="--h-lo:6px;--h-hi:28px;--dur:0.7s;height:14px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:10px;--h-hi:36px;--dur:0.9s;height:28px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:4px;--h-hi:20px;--dur:0.65s;height:10px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:12px;--h-hi:40px;--dur:1.1s;height:36px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:6px;--h-hi:26px;--dur:0.8s;height:18px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:8px;--h-hi:32px;--dur:0.75s;height:24px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:4px;--h-hi:18px;--dur:0.95s;height:8px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:14px;--h-hi:42px;--dur:1.2s;height:32px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:6px;--h-hi:22px;--dur:0.68s;height:16px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:10px;--h-hi:38px;--dur:0.88s;height:30px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:4px;--h-hi:16px;--dur:0.72s;height:6px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:8px;--h-hi:30px;--dur:1.0s;height:20px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:12px;--h-hi:36px;--dur:0.85s;height:26px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:6px;--h-hi:24px;--dur:0.9s;height:14px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:10px;--h-hi:34px;--dur:0.78s;height:22px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:4px;--h-hi:20px;--dur:1.05s;height:12px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:14px;--h-hi:44px;--dur:0.92s;height:38px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:6px;--h-hi:26px;--dur:0.7s;height:18px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:8px;--h-hi:32px;--dur:0.82s;height:22px"></div>
|
||||
<div class="waveform-bar" style="--h-lo:4px;--h-hi:18px;--dur:1.15s;height:10px"></div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="waveform-placeholder"></div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@* Stat row - hard-coded for now. TODO Phase 2: wire to real track count / identity model. *@
|
||||
<div class="hero-stat-row">
|
||||
<div class="hero-stat">
|
||||
<div class="hero-stat-num">47+</div>
|
||||
<div class="hero-stat-label">Live Sessions</div>
|
||||
</div>
|
||||
<div class="hero-stat">
|
||||
<div class="hero-stat-num">2</div>
|
||||
<div class="hero-stat-label">Members</div>
|
||||
</div>
|
||||
<div class="hero-stat">
|
||||
<div class="hero-stat-num">∞</div>
|
||||
<div class="hero-stat-label">Drift Points</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<NowPlaying />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -43,109 +43,6 @@
|
||||
background: var(--deepdrft-white);
|
||||
}
|
||||
|
||||
.hero-eyebrow {
|
||||
font-family: var(--deepdrft-font-mono);
|
||||
font-size: 0.65rem;
|
||||
letter-spacing: 0.28em;
|
||||
color: var(--deepdrft-green-accent);
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 1.8rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
.hero-eyebrow::before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 2.5rem;
|
||||
height: 1px;
|
||||
background: var(--deepdrft-green-accent);
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-family: var(--deepdrft-font-display);
|
||||
font-size: clamp(4.5rem, 8vw, 8.5rem);
|
||||
font-weight: 300;
|
||||
line-height: 0.92;
|
||||
letter-spacing: -0.02em;
|
||||
color: var(--deepdrft-navy);
|
||||
margin-bottom: 0.5rem;
|
||||
animation-delay: 0.22s;
|
||||
}
|
||||
|
||||
.hero-title em {
|
||||
font-style: italic;
|
||||
color: var(--deepdrft-green);
|
||||
}
|
||||
|
||||
.hero-subtitle {
|
||||
font-family: var(--deepdrft-font-display);
|
||||
font-size: clamp(1rem, 2vw, 1.35rem);
|
||||
font-weight: 300;
|
||||
font-style: italic;
|
||||
color: var(--deepdrft-muted);
|
||||
margin-bottom: 3rem;
|
||||
letter-spacing: 0.04em;
|
||||
animation-delay: 0.34s;
|
||||
}
|
||||
|
||||
.hero-desc {
|
||||
font-family: var(--deepdrft-font-body);
|
||||
font-size: 0.92rem;
|
||||
line-height: 1.75;
|
||||
color: var(--deepdrft-navy);
|
||||
opacity: 0.7;
|
||||
max-width: 36ch;
|
||||
margin-bottom: 3rem;
|
||||
animation-delay: 0.44s;
|
||||
}
|
||||
|
||||
.hero-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
animation-delay: 0.54s;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
font-family: var(--deepdrft-font-mono);
|
||||
font-size: 0.68rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: var(--deepdrft-white);
|
||||
background: var(--deepdrft-navy);
|
||||
border: none;
|
||||
padding: 1rem 2.2rem;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
transition: background 0.25s, transform 0.2s;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--deepdrft-green);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-ghost {
|
||||
font-family: var(--deepdrft-font-mono);
|
||||
font-size: 0.68rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: var(--deepdrft-navy);
|
||||
background: transparent;
|
||||
border: 1px solid var(--deepdrft-border);
|
||||
padding: 1rem 2.2rem;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
transition: border-color 0.25s, color 0.25s;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.btn-ghost:hover { border-color: var(--deepdrft-navy); }
|
||||
|
||||
.hero-right {
|
||||
background: var(--deepdrft-navy);
|
||||
position: relative;
|
||||
@@ -155,119 +52,6 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.circle-deco {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(61, 122, 104, 0.3);
|
||||
animation: pulse-ring 4s ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.circle-deco:nth-child(1) { width: 320px; height: 320px; animation-delay: 0s; }
|
||||
.circle-deco:nth-child(2) { width: 520px; height: 520px; animation-delay: 0.8s; }
|
||||
.circle-deco:nth-child(3) { width: 720px; height: 720px; animation-delay: 1.6s; }
|
||||
|
||||
.hero-right-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 3rem;
|
||||
}
|
||||
|
||||
.now-playing {
|
||||
background: rgba(250, 250, 248, 0.06);
|
||||
border: 1px solid rgba(250, 250, 248, 0.12);
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.np-label {
|
||||
font-family: var(--deepdrft-font-mono);
|
||||
font-size: 0.6rem;
|
||||
letter-spacing: 0.25em;
|
||||
color: var(--deepdrft-green-accent);
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 0.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.np-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--deepdrft-green-accent);
|
||||
animation: blink 1.2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.np-title {
|
||||
font-family: var(--deepdrft-font-display);
|
||||
font-size: 1.5rem;
|
||||
font-weight: 400;
|
||||
color: var(--deepdrft-white);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.np-sub {
|
||||
font-family: var(--deepdrft-font-body);
|
||||
font-size: 0.75rem;
|
||||
color: rgba(250, 250, 248, 0.45);
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.waveform-bars {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
margin-top: 1.2rem;
|
||||
}
|
||||
|
||||
.waveform-bar {
|
||||
width: 3px;
|
||||
background: var(--deepdrft-green-accent);
|
||||
border-radius: 2px;
|
||||
animation: wave-dance var(--dur, 1s) ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.waveform-placeholder {
|
||||
margin-top: 1.2rem;
|
||||
height: 1px;
|
||||
background: rgba(250, 250, 248, 0.12);
|
||||
}
|
||||
|
||||
.hero-stat-row {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.hero-stat {
|
||||
flex: 1;
|
||||
background: rgba(250, 250, 248, 0.04);
|
||||
border: 1px solid rgba(250, 250, 248, 0.08);
|
||||
padding: 1.2rem;
|
||||
}
|
||||
|
||||
.hero-stat-num {
|
||||
font-family: var(--deepdrft-font-display);
|
||||
font-size: 2rem;
|
||||
font-weight: 300;
|
||||
color: var(--deepdrft-white);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.hero-stat-label {
|
||||
font-family: var(--deepdrft-font-mono);
|
||||
font-size: 0.58rem;
|
||||
letter-spacing: 0.2em;
|
||||
color: rgba(250, 250, 248, 0.4);
|
||||
text-transform: uppercase;
|
||||
margin-top: 0.4rem;
|
||||
}
|
||||
|
||||
/* ── DIVIDER ── */
|
||||
.section-divider {
|
||||
display: flex;
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
# Expects in ${APP_HOME}/staging/:
|
||||
# deepdrft-manager.tar.gz -- published self-contained linux-x64 binary tree
|
||||
#
|
||||
# DeepDrftManager receives its API URL + API key credential via systemd LoadCredential
|
||||
# (api-manager.json -> $CREDENTIALS_DIRECTORY/api at runtime). No env file copy needed.
|
||||
# DeepDrftManager reads its API URL and API key credential from environment/api.json at startup
|
||||
# (populated by setup-step10-creds.sh). The env-file copy block below keeps it current.
|
||||
#
|
||||
# Paths are derived at runtime — no hardcoded usernames or home dirs.
|
||||
# APP_HOME comes from $HOME (sshd sets this for the app user).
|
||||
@@ -36,6 +36,18 @@ rm -f "${STAGING}/${ARCHIVE}"
|
||||
|
||||
echo "[deploy-manager] archive extracted"
|
||||
|
||||
# ── Apply environment files (host-managed, not in archive) ────────────────
|
||||
if [[ -d "${APPROOT}/environment" ]]; then
|
||||
shopt -s nullglob
|
||||
env_files=("${APPROOT}/environment/"*)
|
||||
shopt -u nullglob
|
||||
if [[ ${#env_files[@]} -gt 0 ]]; then
|
||||
mkdir -p "${APPROOT}/bin/environment"
|
||||
cp "${env_files[@]}" "${APPROOT}/bin/environment/"
|
||||
echo "[deploy-manager] environment files applied"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Enable and restart service ─────────────────────────────────────────────
|
||||
systemctl --user enable deepdrftmanager.service
|
||||
systemctl --user restart deepdrftmanager.service
|
||||
|
||||
+14
-2
@@ -5,8 +5,8 @@
|
||||
# Expects in ${APP_HOME}/staging/:
|
||||
# deepdrft-public.tar.gz -- published self-contained linux-x64 binary tree
|
||||
#
|
||||
# DeepDrftPublic receives its API URL credential via systemd LoadCredential
|
||||
# (api-public.json -> $CREDENTIALS_DIRECTORY/api at runtime). No env file copy needed.
|
||||
# DeepDrftPublic reads its API URL credential from environment/api.json at startup
|
||||
# (populated by setup-step10-creds.sh). The env-file copy block below keeps it current.
|
||||
#
|
||||
# Paths are derived at runtime — no hardcoded usernames or home dirs.
|
||||
# APP_HOME comes from $HOME (sshd sets this for the app user).
|
||||
@@ -36,6 +36,18 @@ rm -f "${STAGING}/${ARCHIVE}"
|
||||
|
||||
echo "[deploy-public] archive extracted"
|
||||
|
||||
# ── Apply environment files (host-managed, not in archive) ────────────────
|
||||
if [[ -d "${APPROOT}/environment" ]]; then
|
||||
shopt -s nullglob
|
||||
env_files=("${APPROOT}/environment/"*)
|
||||
shopt -u nullglob
|
||||
if [[ ${#env_files[@]} -gt 0 ]]; then
|
||||
mkdir -p "${APPROOT}/bin/environment"
|
||||
cp "${env_files[@]}" "${APPROOT}/bin/environment/"
|
||||
echo "[deploy-public] environment files applied"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Enable and restart service ─────────────────────────────────────────────
|
||||
systemctl --user enable deepdrftpublic.service
|
||||
systemctl --user restart deepdrftpublic.service
|
||||
|
||||
@@ -92,6 +92,7 @@ need_cred() {
|
||||
if need_cred "filedatabase"; then
|
||||
write_cred "filedatabase" \
|
||||
"{\"FileDatabaseSettings\":{\"VaultPath\":\"${APP_HOME}/api/deepdrft/vaults\"}}"
|
||||
cp "${CREDDIR}/filedatabase.json" "${APP_HOME}/api/deepdrft/environment/filedatabase.json"
|
||||
else
|
||||
echo "[setup-step10-creds] filedatabase.json already exists, skipping"
|
||||
fi
|
||||
@@ -110,6 +111,7 @@ if need_cred "apikey"; then
|
||||
unset API_KEY_INPUT
|
||||
write_cred "apikey" \
|
||||
"{\"ApiKeySettings\":{\"ApiKey\":\"$(json_escape "${API_KEY}")\"}}"
|
||||
cp "${CREDDIR}/apikey.json" "${APP_HOME}/api/deepdrft/environment/apikey.json"
|
||||
else
|
||||
echo "[setup-step10-creds] apikey.json already exists, skipping"
|
||||
# Still need the value for api-manager.json if that's also being written.
|
||||
@@ -150,6 +152,7 @@ if need_cred "connections"; then
|
||||
AUTH_CONN="Host=localhost;Database=${DB_AUTH};Username=${PG_ROLE};Password=$(json_escape "${PG_PASSWORD}")"
|
||||
write_cred "connections" \
|
||||
"{\"ConnectionStrings\":{\"DefaultConnection\":\"${META_CONN}\",\"Auth\":\"${AUTH_CONN}\"}}"
|
||||
cp "${CREDDIR}/connections.json" "${APP_HOME}/api/deepdrft/environment/connections.json"
|
||||
unset PG_PASSWORD META_CONN AUTH_CONN
|
||||
else
|
||||
echo "[setup-step10-creds] connections.json already exists, skipping"
|
||||
@@ -204,6 +207,7 @@ if need_cred "authblocks"; then
|
||||
{"AuthBlocks":{"Jwt":{"Secret":"$(json_escape "${JWT_SECRET}")","Issuer":"$(json_escape "${JWT_ISSUER}")","Audience":"$(json_escape "${JWT_AUDIENCE}")"},"Email":{"Host":"$(json_escape "${EMAIL_HOST}")","Token":"$(json_escape "${EMAIL_TOKEN}")"},"Admin":{"UserName":"$(json_escape "${ADMIN_USERNAME}")","Email":"$(json_escape "${ADMIN_EMAIL}")","Password":"$(json_escape "${ADMIN_PASSWORD}")"},"SupportEmail":"$(json_escape "${SUPPORT_EMAIL}")"}}
|
||||
JSON
|
||||
)"
|
||||
cp "${CREDDIR}/authblocks.json" "${APP_HOME}/api/deepdrft/environment/authblocks.json"
|
||||
unset JWT_SECRET JWT_ISSUER JWT_AUDIENCE EMAIL_HOST EMAIL_TOKEN
|
||||
unset ADMIN_USERNAME ADMIN_EMAIL ADMIN_PASSWORD SUPPORT_EMAIL
|
||||
else
|
||||
@@ -214,6 +218,7 @@ fi
|
||||
if need_cred "api-public"; then
|
||||
write_cred "api-public" \
|
||||
"{\"Api\":{\"ContentApiUrl\":\"http://localhost:${PORT_API:-5002}\"}}"
|
||||
cp "${CREDDIR}/api-public.json" "${APP_HOME}/public/environment/api.json"
|
||||
else
|
||||
echo "[setup-step10-creds] api-public.json already exists, skipping"
|
||||
fi
|
||||
@@ -227,6 +232,7 @@ if need_cred "api-manager"; then
|
||||
fi
|
||||
write_cred "api-manager" \
|
||||
"{\"Api\":{\"ContentApiUrl\":\"http://localhost:${PORT_API:-5002}\",\"ContentApiKey\":\"$(json_escape "${API_KEY}")\"}}"
|
||||
cp "${CREDDIR}/api-manager.json" "${APP_HOME}/manager/environment/api.json"
|
||||
unset API_KEY
|
||||
else
|
||||
echo "[setup-step10-creds] api-manager.json already exists, skipping"
|
||||
|
||||
Reference in New Issue
Block a user