feat: refresh frontend visual design

This commit is contained in:
2026-05-05 10:26:03 +02:00
parent c3a345d60b
commit 3957987b07
8 changed files with 760 additions and 247 deletions

View File

@@ -9,66 +9,103 @@ import { API_BASE_URL } from '../services/api-config.token';
standalone: true,
imports: [RouterLink, MatButtonModule, MatCardModule],
template: `
<section class="hero">
<section class="hero page">
<div class="hero-copy">
<p class="eyebrow">AzioneLab Theatre Company</p>
<h1>Public website and booking UI foundations.</h1>
<h1>Small-stage evenings, thoughtful performances, simple reservations.</h1>
<p class="supporting">
This Angular shell is wired for the existing Django APIs and ready for the next booking-focused iterations.
AzioneLab brings contemporary theatre into intimate venues. This public frontend is shaped around clear show discovery, lightweight booking, and a calm arrival experience at the door.
</p>
<div class="hero-actions">
<a mat-flat-button color="primary" routerLink="/shows">Browse shows</a>
<a mat-stroked-button routerLink="/check-in">Check-in area</a>
<a mat-flat-button color="primary" routerLink="/shows">Browse upcoming shows</a>
<a mat-stroked-button routerLink="/check-in">Staff check-in</a>
</div>
</div>
<div class="hero-panel">
<mat-card>
<mat-card-title>Frontend wiring</mat-card-title>
<div class="hero-stage" aria-hidden="true">
<div class="curtain"></div>
<div class="stage-glow"></div>
<div class="stage-copy">
<span>Tonight at AzioneLab</span>
<strong>Doors open 30 minutes before the performance</strong>
</div>
</div>
</section>
<section class="overview page">
<div class="section-heading">
<div>
<p class="eyebrow">At a glance</p>
<h2>Built for a small company, not a sprawling ticketing empire</h2>
</div>
<p class="supporting">The public experience stays simple: browse a show, reserve seats, confirm by email, arrive with a QR code.</p>
</div>
<div class="feature-grid">
<mat-card class="feature-card">
<mat-card-title>Find the right performance</mat-card-title>
<mat-card-content>
<p><strong>API base URL</strong></p>
<code>{{ apiBaseUrl }}</code>
<p class="panel-note">Placeholders are in place for public content, booking, and staff check-in flows.</p>
<p>Show listings and detail pages keep venue, schedule, and availability visible without noise.</p>
</mat-card-content>
</mat-card>
<mat-card class="feature-card">
<mat-card-title>Confirm by email</mat-card-title>
<mat-card-content>
<p>Reservations stay pending until the audience member confirms, which keeps capacity trustworthy and easy to manage.</p>
</mat-card-content>
</mat-card>
<mat-card class="feature-card">
<mat-card-title>Check in quickly</mat-card-title>
<mat-card-content>
<p>Front-of-house staff can preview a token, validate it server-side, and record entry in one compact flow.</p>
</mat-card-content>
</mat-card>
</div>
</section>
<section class="journey-grid page">
<div class="journey-copy">
<p class="eyebrow">Audience journey</p>
<h2>From interest to entrance in a few quiet steps</h2>
<ol>
<li>Browse the public programme and open a show page.</li>
<li>Reserve seats for a performance and confirm by email.</li>
<li>Keep the QR code ready on your phone or on paper for entry.</li>
</ol>
</div>
<mat-card class="meta-card">
<mat-card-title>Runtime wiring</mat-card-title>
<mat-card-content>
<p class="meta-label">API base URL</p>
<code>{{ apiBaseUrl }}</code>
<p class="meta-note">The frontend remains aligned with the existing Django API surface without changing backend contracts.</p>
</mat-card-content>
</mat-card>
</section>
`,
styles: [`
.hero {
display: grid;
grid-template-columns: minmax(0, 1.4fr) minmax(280px, 0.9fr);
grid-template-columns: minmax(0, 1.3fr) minmax(280px, 0.9fr);
gap: 28px;
align-items: stretch;
max-width: 1180px;
margin: 0 auto;
padding: 12px 0 24px;
}
.hero-copy {
padding: 36px 0;
}
.eyebrow {
margin: 0 0 12px;
color: var(--azionelab-accent);
text-transform: uppercase;
font-size: 0.78rem;
font-weight: 700;
padding: 34px 0 20px;
}
h1 {
margin: 0;
max-width: 10ch;
font-size: clamp(2.5rem, 5vw, 4.75rem);
line-height: 0.95;
max-width: 11ch;
font-size: 3.85rem;
}
.supporting {
max-width: 52ch;
color: var(--azionelab-muted);
font-size: 1.08rem;
line-height: 1.65;
margin: 20px 0 0;
font-size: 1.06rem;
}
.hero-actions {
@@ -78,32 +115,189 @@ import { API_BASE_URL } from '../services/api-config.token';
margin-top: 28px;
}
.hero-panel mat-card {
height: 100%;
border-radius: 8px;
.hero-stage {
position: relative;
overflow: hidden;
min-height: 420px;
border-radius: var(--azionelab-radius-lg);
border: 1px solid rgba(255, 250, 245, 0.16);
background:
linear-gradient(180deg, rgba(31, 18, 18, 0.1), rgba(27, 18, 14, 0.62)),
linear-gradient(135deg, #b04b40 0%, #7f251f 24%, #43261f 56%, #211b1a 100%);
box-shadow: var(--azionelab-shadow-strong);
}
.curtain {
position: absolute;
inset: 0;
background:
linear-gradient(90deg, rgba(255, 255, 255, 0.08) 0, rgba(255, 255, 255, 0) 22%, rgba(255, 255, 255, 0.08) 48%, rgba(255, 255, 255, 0) 74%, rgba(255, 255, 255, 0.08) 100%),
repeating-linear-gradient(90deg, rgba(75, 18, 14, 0.2) 0 18px, rgba(160, 60, 52, 0.08) 18px 36px);
opacity: 0.9;
}
.stage-glow {
position: absolute;
left: 50%;
bottom: -80px;
width: 340px;
height: 340px;
transform: translateX(-50%);
border-radius: 50%;
background: radial-gradient(circle, rgba(245, 214, 150, 0.9) 0, rgba(245, 214, 150, 0.22) 44%, rgba(245, 214, 150, 0) 72%);
}
.stage-copy {
position: absolute;
left: 24px;
right: 24px;
bottom: 24px;
display: grid;
gap: 8px;
padding: 18px 20px;
border-radius: 18px;
background: rgba(24, 17, 15, 0.52);
backdrop-filter: blur(8px);
color: rgba(255, 247, 239, 0.94);
border: 1px solid rgba(255, 247, 239, 0.12);
}
.stage-copy span {
text-transform: uppercase;
font-size: 0.76rem;
letter-spacing: 0.08em;
color: rgba(255, 227, 192, 0.84);
}
.stage-copy strong {
font-family: var(--azionelab-serif);
font-size: 1.4rem;
font-weight: 700;
line-height: 1.15;
}
.overview,
.journey-grid {
margin-top: 34px;
}
.section-heading {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(260px, 420px);
gap: 24px;
align-items: end;
margin-bottom: 20px;
}
.section-heading h2,
.journey-copy h2 {
margin: 0;
max-width: 18ch;
}
.feature-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 18px;
}
.feature-card,
.meta-card {
border-radius: var(--azionelab-radius-md);
border: 1px solid var(--azionelab-border);
background: var(--azionelab-surface);
background: linear-gradient(180deg, rgba(255, 253, 250, 0.98), rgba(251, 245, 236, 0.94));
box-shadow: var(--azionelab-shadow);
}
.feature-card {
min-height: 210px;
}
.feature-card mat-card-title,
.meta-card mat-card-title {
margin-bottom: 12px;
font-family: var(--azionelab-serif);
font-size: 1.18rem;
font-weight: 700;
}
.feature-card p,
.meta-card p {
margin: 0;
color: var(--azionelab-muted);
line-height: 1.6;
}
.journey-grid {
display: grid;
grid-template-columns: minmax(0, 1.2fr) minmax(280px, 0.9fr);
gap: 24px;
align-items: start;
}
.journey-copy {
padding: 10px 0;
}
ol {
display: grid;
gap: 12px;
margin: 20px 0 0;
padding-left: 22px;
color: var(--azionelab-ink-soft);
line-height: 1.65;
}
.meta-label {
margin: 0 0 10px;
font-size: 0.8rem;
font-weight: 700;
text-transform: uppercase;
color: var(--azionelab-muted);
letter-spacing: 0.08em;
}
code {
display: inline-block;
margin-top: 8px;
padding: 10px 12px;
border-radius: 8px;
background: rgba(30, 27, 24, 0.06);
border-radius: 12px;
background: rgba(34, 28, 24, 0.06);
color: var(--azionelab-ink-soft);
word-break: break-word;
}
.panel-note {
margin-top: 20px;
color: var(--azionelab-muted);
line-height: 1.5;
.meta-note {
margin: 18px 0 0;
}
@media (max-width: 900px) {
.hero {
.hero,
.section-heading,
.journey-grid,
.feature-grid {
grid-template-columns: 1fr;
}
h1 {
font-size: 2.9rem;
}
}
@media (max-width: 640px) {
h1 {
font-size: 2.25rem;
}
.hero-stage {
min-height: 320px;
}
.stage-copy {
left: 16px;
right: 16px;
bottom: 16px;
padding: 16px;
}
}
`],
changeDetection: ChangeDetectionStrategy.OnPush,