diff --git a/flightscore/src/components/icon-card.ts b/flightscore/src/components/icon-card.ts
new file mode 100644
index 0000000..61142c5
--- /dev/null
+++ b/flightscore/src/components/icon-card.ts
@@ -0,0 +1,79 @@
+// components/icon-card.ts
+import { LitElement, html, css } from 'lit';
+import { customElement, property } from 'lit/decorators.js';
+
+@customElement('icon-card')
+export class IconCard extends LitElement {
+ static styles = css`
+ :host {
+ display: block;
+ }
+
+ .card {
+ background: var(--color-bg-nav);
+ border: 1px solid var(--color-border);
+ border-radius: 0.75rem;
+ padding: 1.75rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+ transition: border-color 0.25s ease;
+ }
+
+ .card:hover {
+ border-color: color-mix(
+ in srgb,
+ var(--color-accent) 40%,
+ transparent
+ );
+ }
+
+ .icon {
+ width: 2.25rem;
+ height: 2.25rem;
+ display: grid;
+ place-items: center;
+ border-radius: 0.5rem;
+ background: color-mix(
+ in srgb,
+ var(--color-accent) 10%,
+ transparent
+ );
+ color: var(--color-accent);
+ }
+
+ ::slotted(svg) {
+ width: 1.15rem;
+ height: 1.15rem;
+ }
+
+ h3 {
+ margin: 0;
+ font-size: 1rem;
+ font-weight: 650;
+ color: var(--color-text);
+ }
+
+ p {
+ margin: 0;
+ font-size: 0.875rem;
+ line-height: 1.6;
+ color: color-mix(in srgb, var(--color-text) 55%, transparent);
+ }
+ `;
+
+ @property() heading = '';
+ @property() description = '';
+
+ render() {
+ return html`
+
+
+
+
+
${this.heading}
+
${this.description}
+
+ `;
+ }
+}
\ No newline at end of file
diff --git a/flightscore/src/components/stat-card.ts b/flightscore/src/components/stat-card.ts
new file mode 100644
index 0000000..84dbd7f
--- /dev/null
+++ b/flightscore/src/components/stat-card.ts
@@ -0,0 +1,56 @@
+// components/stat-card.ts
+import { LitElement, html, css } from 'lit';
+import { customElement, property } from 'lit/decorators.js';
+
+@customElement('stat-card')
+export class StatCard extends LitElement {
+ static styles = css`
+ :host {
+ display: block;
+ }
+
+ .card {
+ background: var(--color-bg-nav);
+ border: 1px solid var(--color-border);
+ border-radius: 0.75rem;
+ padding: 1.5rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+ transition: border-color 0.25s ease;
+ }
+
+ .card:hover {
+ border-color: color-mix(
+ in srgb,
+ var(--color-accent) 40%,
+ transparent
+ );
+ }
+
+ .value {
+ font-size: 2rem;
+ font-weight: 800;
+ letter-spacing: -0.03em;
+ color: var(--color-accent);
+ }
+
+ .label {
+ font-size: 0.85rem;
+ color: color-mix(in srgb, var(--color-text) 55%, transparent);
+ font-weight: 500;
+ }
+ `;
+
+ @property() value = '';
+ @property() label = '';
+
+ render() {
+ return html`
+
+ ${this.value}
+ ${this.label}
+
+ `;
+ }
+}
\ No newline at end of file
diff --git a/flightscore/src/components/ui-badge.ts b/flightscore/src/components/ui-badge.ts
new file mode 100644
index 0000000..4bcbdd6
--- /dev/null
+++ b/flightscore/src/components/ui-badge.ts
@@ -0,0 +1,79 @@
+// components/ui-badge.ts
+import { LitElement, html, css } from 'lit';
+import { customElement, property } from 'lit/decorators.js';
+
+export type BadgeVariant = 'accent' | 'success' | 'warning' | 'muted' | 'error';
+
+@customElement('ui-badge')
+export class UiBadge extends LitElement {
+ static styles = css`
+ :host {
+ display: inline-flex;
+ }
+
+ .badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.4rem;
+ padding: 0.3rem 0.75rem;
+ font-size: 0.75rem;
+ font-weight: 600;
+ letter-spacing: 0.03em;
+ text-transform: uppercase;
+ border-radius: 2rem;
+ border: 1px solid transparent;
+ white-space: nowrap;
+ }
+
+ .badge.accent {
+ color: var(--color-accent);
+ background: color-mix(in srgb, var(--color-accent) 10%, transparent);
+ border-color: color-mix(
+ in srgb,
+ var(--color-accent) 25%,
+ transparent
+ );
+ }
+
+ .badge.success {
+ color: #30a46c;
+ background: color-mix(in srgb, #30a46c 10%, transparent);
+ border-color: color-mix(in srgb, #30a46c 25%, transparent);
+ }
+
+ .badge.warning {
+ color: #e79d13;
+ background: color-mix(in srgb, #e79d13 10%, transparent);
+ border-color: color-mix(in srgb, #e79d13 25%, transparent);
+ }
+
+ .badge.muted {
+ color: color-mix(in srgb, var(--color-text) 45%, transparent);
+ background: color-mix(in srgb, var(--color-text) 6%, transparent);
+ border-color: var(--color-border);
+ }
+
+ .badge.error {
+ color: #e5484d;
+ background: color-mix(in srgb, #e5484d 10%, transparent);
+ border-color: color-mix(in srgb, #e5484d 25%, transparent);
+ }
+
+
+ ::slotted(svg) {
+ width: 0.85rem;
+ height: 0.85rem;
+ flex-shrink: 0;
+ }
+ `;
+
+ @property() variant: BadgeVariant = 'accent';
+
+ render() {
+ return html`
+
+
+
+ `;
+ }
+}
\ No newline at end of file
diff --git a/flightscore/src/components/ui-button-secondary.ts b/flightscore/src/components/ui-button-secondary.ts
new file mode 100644
index 0000000..c398b41
--- /dev/null
+++ b/flightscore/src/components/ui-button-secondary.ts
@@ -0,0 +1,72 @@
+// components/ui-button-secondary.ts
+import { LitElement, html, css } from 'lit';
+import { customElement, property } from 'lit/decorators.js';
+
+@customElement('ui-button-secondary')
+export class UiButtonSecondary extends LitElement {
+ static styles = css`
+ :host {
+ display: inline-flex;
+ }
+
+ :host([full]) {
+ display: flex;
+ }
+
+ :host([full]) button {
+ width: 100%;
+ }
+
+ button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.45rem;
+ padding: 0.65rem 1.25rem;
+ font-size: 0.95rem;
+ font-weight: 600;
+ color: var(--color-text);
+ background: transparent;
+ border: 1px solid var(--color-border);
+ border-radius: 0.5rem;
+ cursor: pointer;
+ transition:
+ border-color 0.25s ease,
+ color 0.25s ease,
+ background 0.25s ease,
+ transform 0.15s ease;
+ }
+
+ button:hover:not(:disabled) {
+ border-color: var(--color-accent);
+ color: var(--color-accent);
+ background: color-mix(in srgb, var(--color-accent) 6%, transparent);
+ }
+
+ button:active:not(:disabled) {
+ transform: scale(0.98);
+ }
+
+ button:disabled {
+ opacity: 0.55;
+ cursor: not-allowed;
+ }
+
+ ::slotted(svg) {
+ width: 1rem;
+ height: 1rem;
+ flex-shrink: 0;
+ }
+ `;
+
+ @property({ type: Boolean }) disabled = false;
+ @property({ type: Boolean, reflect: true }) full = false;
+
+ render() {
+ return html`
+
+
+
+ `;
+ }
+}
\ No newline at end of file
diff --git a/flightscore/src/pages/home-page.ts b/flightscore/src/pages/home-page.ts
index afc691d..592f851 100644
--- a/flightscore/src/pages/home-page.ts
+++ b/flightscore/src/pages/home-page.ts
@@ -1,7 +1,11 @@
+// pages/home/home-page.ts (updated)
import { LitElement, html, css } from 'lit';
import { customElement } from 'lit/decorators.js';
-import '../components/ui-card';
import '../components/ui-button';
+import '../components/ui-button-secondary';
+import '../components/ui-badge';
+import '../components/stat-card';
+import '../components/icon-card';
import '../components/ui-link';
@customElement('home-page')
@@ -45,29 +49,8 @@ export class HomePage extends LitElement {
background: var(--color-border);
}
- .hero-badge {
- display: inline-flex;
- align-items: center;
- gap: 0.4rem;
- padding: 0.35rem 0.85rem;
- font-size: 0.78rem;
- font-weight: 600;
- letter-spacing: 0.03em;
- text-transform: uppercase;
- color: var(--color-accent);
- background: color-mix(in srgb, var(--color-accent) 10%, transparent);
- border: 1px solid color-mix(in srgb, var(--color-accent) 25%, transparent);
- border-radius: 2rem;
- margin-bottom: 1.5rem;
- }
-
- .hero-badge svg {
- width: 0.85rem;
- height: 0.85rem;
- }
-
.hero h1 {
- margin: 0;
+ margin: 1.5rem 0 0;
font-size: clamp(2.2rem, 5vw, 3.5rem);
font-weight: 800;
letter-spacing: -0.03em;
@@ -102,30 +85,6 @@ export class HomePage extends LitElement {
justify-content: center;
}
- .btn-secondary {
- display: inline-flex;
- align-items: center;
- gap: 0.4rem;
- padding: 0.65rem 1.25rem;
- font-size: 0.95rem;
- font-weight: 600;
- color: var(--color-text);
- background: transparent;
- border: 1px solid var(--color-border);
- border-radius: 0.5rem;
- cursor: pointer;
- transition:
- border-color 0.25s ease,
- color 0.25s ease,
- background 0.25s ease;
- }
-
- .btn-secondary:hover {
- border-color: var(--color-accent);
- color: var(--color-accent);
- background: color-mix(in srgb, var(--color-accent) 6%, transparent);
- }
-
section {
padding: 4rem 1.5rem;
max-width: 1100px;
@@ -168,84 +127,12 @@ export class HomePage extends LitElement {
gap: 1rem;
}
- .stat-card {
- background: var(--color-bg-nav);
- border: 1px solid var(--color-border);
- border-radius: 0.75rem;
- padding: 1.5rem;
- display: flex;
- flex-direction: column;
- gap: 0.25rem;
- transition: border-color 0.25s ease;
- }
-
- .stat-card:hover {
- border-color: color-mix(in srgb, var(--color-accent) 40%, transparent);
- }
-
- .stat-value {
- font-size: 2rem;
- font-weight: 800;
- letter-spacing: -0.03em;
- color: var(--color-accent);
- }
-
- .stat-label {
- font-size: 0.85rem;
- color: color-mix(in srgb, var(--color-text) 55%, transparent);
- font-weight: 500;
- }
-
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.25rem;
}
- .feature-card {
- background: var(--color-bg-nav);
- border: 1px solid var(--color-border);
- border-radius: 0.75rem;
- padding: 1.75rem;
- display: flex;
- flex-direction: column;
- gap: 0.75rem;
- transition: border-color 0.25s ease;
- }
-
- .feature-card:hover {
- border-color: color-mix(in srgb, var(--color-accent) 40%, transparent);
- }
-
- .feature-icon {
- width: 2.25rem;
- height: 2.25rem;
- display: grid;
- place-items: center;
- border-radius: 0.5rem;
- background: color-mix(in srgb, var(--color-accent) 10%, transparent);
- color: var(--color-accent);
- }
-
- .feature-icon svg {
- width: 1.15rem;
- height: 1.15rem;
- }
-
- .feature-card h3 {
- margin: 0;
- font-size: 1rem;
- font-weight: 650;
- color: var(--color-text);
- }
-
- .feature-card p {
- margin: 0;
- font-size: 0.875rem;
- line-height: 1.6;
- color: color-mix(in srgb, var(--color-text) 55%, transparent);
- }
-
.competitions-list {
display: flex;
flex-direction: column;
@@ -265,7 +152,11 @@ export class HomePage extends LitElement {
}
.comp-row:hover {
- border-color: color-mix(in srgb, var(--color-accent) 40%, transparent);
+ border-color: color-mix(
+ in srgb,
+ var(--color-accent) 40%,
+ transparent
+ );
}
.comp-date {
@@ -274,7 +165,11 @@ export class HomePage extends LitElement {
align-items: center;
min-width: 3.5rem;
padding: 0.5rem 0.65rem;
- background: color-mix(in srgb, var(--color-accent) 10%, transparent);
+ background: color-mix(
+ in srgb,
+ var(--color-accent) 10%,
+ transparent
+ );
border-radius: 0.5rem;
flex-shrink: 0;
}
@@ -322,35 +217,6 @@ export class HomePage extends LitElement {
flex-shrink: 0;
}
- .comp-badge {
- font-size: 0.72rem;
- font-weight: 600;
- letter-spacing: 0.03em;
- text-transform: uppercase;
- padding: 0.3rem 0.7rem;
- border-radius: 2rem;
- flex-shrink: 0;
- }
-
- .comp-badge.upcoming {
- color: var(--color-accent);
- background: color-mix(in srgb, var(--color-accent) 10%, transparent);
- border: 1px solid
- color-mix(in srgb, var(--color-accent) 25%, transparent);
- }
-
- .comp-badge.live {
- color: #30a46c;
- background: color-mix(in srgb, #30a46c 10%, transparent);
- border: 1px solid color-mix(in srgb, #30a46c 25%, transparent);
- }
-
- .comp-badge.ended {
- color: color-mix(in srgb, var(--color-text) 45%, transparent);
- background: color-mix(in srgb, var(--color-text) 6%, transparent);
- border: 1px solid var(--color-border);
- }
-
@media (max-width: 600px) {
.hero {
padding: 3.5rem 1.25rem 3rem;
@@ -364,10 +230,6 @@ export class HomePage extends LitElement {
flex-wrap: wrap;
gap: 0.75rem;
}
-
- .comp-badge {
- margin-left: auto;
- }
}
`;
@@ -384,29 +246,28 @@ export class HomePage extends LitElement {
render() {
return html`
-
-
+
+
Live Scoring Platform
-
+
Precision scoring for balloon competitions
- Track tasks, manage participants, and deliver real-time results
- for hot air balloon competitions worldwide.
+ Track tasks, manage participants, and deliver real-time
+ results for hot air balloon competitions worldwide.
this.navigate('/competitions')}>
Browse competitions
- this.navigate('/register')}
>
Create account
-
+
@@ -417,22 +278,13 @@ export class HomePage extends LitElement {
FlightScore powers balloon competitions across the globe.
-
- 42
- Competitions scored
-
-
- 318
- Registered pilots
-
-
- 1,240
- Tasks completed
-
-
- 12
- Countries
-
+
+
+
+
@@ -443,92 +295,68 @@ export class HomePage extends LitElement {
Everything organizers, judges, and pilots need in one place.
-
-
-
Real-Time Scoring
-
- Scores update live as judges submit results. Pilots and
- spectators always see the latest standings.
-
-
-
-
-
Task Management
-
- Define and manage competition tasks with support for all
- standard ballooning task types.
-
-
-
-
-
Pilot Profiles
-
- Every pilot gets a profile with competition history,
- rankings, and performance statistics.
-
-
-
-
-
Live Leaderboard
-
- Public leaderboards let spectators follow the action
- from anywhere in the world.
-
-
-
-
-
GPS Integration
-
- Import GPS tracks and calculate distances to targets
- automatically for precise scoring.
-
-
-
-
-
-
-
-
-
-
-
Result Export
-
- Export final standings and detailed score sheets as PDF
- or CSV for official records.
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -548,9 +376,9 @@ export class HomePage extends LitElement {
Mar
-
European Balloon Challenge 2026
+
+ European Balloon Challenge 2026
+
@@ -559,7 +387,7 @@ export class HomePage extends LitElement {
Salzburg, Austria
- Upcoming
+ Upcoming
- Live
+ Live
Feb
-
Albuquerque Winter Fiesta
+
+ Albuquerque Winter Fiesta
+
@@ -603,7 +431,7 @@ export class HomePage extends LitElement {
New Mexico, USA
- Ended
+ Ended
Jan
-
Swiss Alpine Balloon Trophy
+
+ Swiss Alpine Balloon Trophy
+
@@ -626,7 +454,7 @@ export class HomePage extends LitElement {
Chateau-d'Oex, Switzerland
- Ended
+ Canceled