5 Commits

Author SHA1 Message Date
daniel-c-harvey ceaa684c74 Merge branch 'factory-fix' into dev
Deploy DeepDrftAPI / Build, Publish & Bundle (push) Successful in 2m28s
Package install tarball / package (push) Successful in 6s
Deploy DeepDrftAPI / Deploy (push) Successful in 1m35s
2026-06-04 14:16:35 -04:00
daniel-c-harvey cd226f3ce9 fix: factory falls back to design-time dummy; remove CI dummy-file step and creds-env cp lines 2026-06-04 14:16:31 -04:00
daniel-c-harvey f4e39c96fd Merge branch 'creds-env' into dev
Deploy DeepDrftPublic / Build & Publish (push) Successful in 4m26s
Package install tarball / package (push) Successful in 6s
Deploy DeepDrftPublic / Deploy (push) Successful in 1m25s
2026-06-04 14:01:22 -04:00
daniel-c-harvey c49f28e619 fix: wire credential files into service environment/ dirs on setup and deploy 2026-06-04 14:01:18 -04:00
daniel-c-harvey b58bcd8398 Home Page Normalization 2026-06-04 14:01:03 -04:00
15 changed files with 318 additions and 320 deletions
-8
View File
@@ -45,14 +45,6 @@ jobs:
--no-build \
-o DeepDrftAPI/publish
# DeepDrftContextFactory reads environment/connections.json at design time.
# Write a parseable dummy so the factory does not throw during bundle construction.
# The bundle only needs the provider type, not a live database connection.
- name: Write dummy connections file for EF bundle
run: |
mkdir -p DeepDrftAPI/environment
echo '{"ConnectionStrings":{"DefaultConnection":"Host=localhost;Database=dummy;Username=dummy","Auth":"Host=localhost;Database=dummy;Username=dummy"}}' > DeepDrftAPI/environment/connections.json
# EF bundle: self-contained binary that applies DeepDrftContext migrations on the host
# without the .NET SDK. AuthBlocks' Identity DB is NOT covered here — it self-migrates
# via UseAuthBlocksStartupAsync() on first boot.
+15 -16
View File
@@ -1,5 +1,6 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using NetBlocks.Utilities.Environment;
namespace DeepDrftData.Data;
@@ -7,23 +8,21 @@ public class DeepDrftContextFactory : IDesignTimeDbContextFactory<DeepDrftContex
{
public DeepDrftContext CreateDbContext(string[] args)
{
// Load the real connection string from environment/connections.json — the same
// file DeepDrftPublic's Program.cs loads via CredentialTools. When EF tools run with
// --startup-project DeepDrftPublic, the working directory resolves there, so this
// relative path works without any env var configuration.
const string relPath = "environment/connections.json";
if (!File.Exists(relPath))
throw new FileNotFoundException(
$"'{relPath}' not found. Run EF commands with --startup-project DeepDrftPublic " +
$"from the solution root (current dir: {Directory.GetCurrentDirectory()}).", relPath);
var path = CredentialTools.ResolvePath("connections", "environment/connections.json");
using var doc = System.Text.Json.JsonDocument.Parse(File.ReadAllText(relPath));
var connectionString = doc.RootElement
.GetProperty("ConnectionStrings")
.GetProperty("DefaultConnection")
.GetString()
?? throw new InvalidOperationException(
"ConnectionStrings:DefaultConnection not found in environment/connections.json");
string? connectionString = null;
if (File.Exists(path))
{
using var doc = System.Text.Json.JsonDocument.Parse(File.ReadAllText(path));
connectionString = doc.RootElement
.GetProperty("ConnectionStrings")
.GetProperty("DefaultConnection")
.GetString();
}
// Fall back to a design-time dummy — the bundle only needs the provider/schema,
// not a live connection. This removes the requirement to write a dummy file in CI.
connectionString ??= "Host=localhost;Database=deepdrft-design-time;Username=dummy";
var optionsBuilder = new DbContextOptionsBuilder<DeepDrftContext>();
optionsBuilder.UseNpgsql(connectionString);
+1
View File
@@ -18,6 +18,7 @@
</PackageReference>
<!-- Npgsql 10.0.1 requires Microsoft.EntityFrameworkCore >= 10.0.4; keep in sync -->
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" />
<PackageReference Include="Cerebellum.NetBlocks" Version="10.3.30" />
<PackageReference Include="Cerebellum.BlazorBlocks.Data" Version="10.3.30" />
<PackageReference Include="Cerebellum.BlazorBlocks.Data.Postgres" Version="10.3.30" />
</ItemGroup>
@@ -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 &mdash; 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">&infin;</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;
}
+3 -74
View File
@@ -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 &mdash; 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">&infin;</div>
<div class="hero-stat-label">Drift Points</div>
</div>
</div>
</div>
<NowPlaying />
</div>
</section>
-216
View File
@@ -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;
-3
View File
@@ -5,9 +5,6 @@
# 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.
#
# Paths are derived at runtime — no hardcoded usernames or home dirs.
# APP_HOME comes from $HOME (sshd sets this for the app user).
-3
View File
@@ -5,9 +5,6 @@
# 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.
#
# Paths are derived at runtime — no hardcoded usernames or home dirs.
# APP_HOME comes from $HOME (sshd sets this for the app user).