fix(front-end): css file reduced

This commit is contained in:
2026-03-09 19:21:18 +01:00
parent dfe109ac8d
commit aaa58346d3
5 changed files with 760 additions and 987 deletions

View File

@@ -1,70 +1,75 @@
<section class="section-card">
<header class="section-header">
<div class="header-copy">
<p class="eyebrow">Back-office media</p>
<h2>Media home</h2>
<section class="section-card ui-section-card ui-stack ui-stack--roomy">
<header class="section-header ui-section-header">
<div class="header-copy ui-section-header__copy">
<p class="eyebrow ui-eyebrow ui-eyebrow--compact">Back-office media</p>
<h2 class="ui-section-header__title">Media home</h2>
</div>
<div class="header-side">
<div class="header-stats">
<article class="stat-chip">
<div class="header-side ui-stack ui-stack--dense">
<div class="header-stats ui-inline-actions">
<article class="stat-chip ui-stat-chip">
<strong>{{ configuredSectionCount }}</strong>
<span>sezioni gestite</span>
</article>
<article class="stat-chip">
<article class="stat-chip ui-stat-chip">
<strong>{{ activeImageCount }}</strong>
<span>immagini attive</span>
</article>
</div>
<button type="button" (click)="loadHomeMedia()" [disabled]="loading">
<button
type="button"
class="ui-button"
(click)="loadHomeMedia()"
[disabled]="loading"
>
Aggiorna
</button>
</div>
</header>
<p class="status-banner status-banner-error" *ngIf="errorMessage">
<p class="status-banner ui-banner ui-banner--error" *ngIf="errorMessage">
{{ errorMessage }}
</p>
<p class="status-banner status-banner-success" *ngIf="successMessage">
<p class="status-banner ui-banner ui-banner--success" *ngIf="successMessage">
{{ successMessage }}
</p>
<div class="group-stack" *ngIf="!loading; else loadingTpl">
<section class="group-card" *ngFor="let group of sectionGroups">
<header class="group-header">
<div class="group-stack ui-stack" *ngIf="!loading; else loadingTpl">
<section class="group-card ui-subpanel ui-subpanel--warm" *ngFor="let group of sectionGroups">
<header class="group-header ui-row ui-row--between">
<h3>{{ group.title }}</h3>
</header>
<div class="sections">
<div class="sections ui-stack ui-stack--dense">
<section
class="media-panel"
class="media-panel ui-subpanel ui-subpanel--elevated"
*ngFor="
let section of getSectionsForGroup(group.id);
trackBy: trackSection
"
>
<header class="media-panel-header">
<div class="media-panel-copy">
<div class="title-row">
<header class="media-panel-header ui-row ui-row--between ui-row--wrap">
<div class="media-panel-copy ui-stack ui-stack--dense">
<div class="title-row ui-row ui-row--center ui-row--wrap">
<h4>{{ section.title }}</h4>
<span class="count-pill">
<span class="count-pill ui-pill ui-pill--soft">
{{ section.items.length }}
{{ section.items.length === 1 ? "attiva" : "attive" }}
</span>
</div>
</div>
<div class="media-panel-meta">
<span class="usage-pill"
<div class="media-panel-meta ui-row ui-row--wrap ui-row--end">
<span class="usage-pill ui-pill ui-pill--neutral"
>{{ section.usageType }} / {{ section.usageKey }}</span
>
<span class="layout-pill">
<span class="layout-pill ui-pill ui-pill--light">
Variante {{ section.preferredVariantName }}
</span>
</div>
</header>
<div class="workspace">
<div class="upload-panel">
<div class="panel-heading">
<div class="workspace ui-workspace-compact">
<div class="upload-panel ui-subpanel ui-subpanel--soft">
<div class="panel-heading ui-stack ui-stack--dense">
<h5>
{{
getFormState(section.usageKey).replacingUsageId
@@ -74,9 +79,9 @@
</h5>
</div>
<div class="form-grid">
<div class="form-field form-field--wide">
<span>File immagine</span>
<div class="form-grid ui-form-grid ui-form-grid--two">
<div class="form-field form-field--wide ui-form-field">
<span class="ui-form-caption">File immagine</span>
<input
[id]="'media-file-' + section.usageKey"
class="sr-only"
@@ -85,11 +90,13 @@
(change)="onFileSelected(section.usageKey, $event)"
/>
<label
class="file-picker"
class="file-picker ui-file-picker"
[for]="'media-file-' + section.usageKey"
>
<span class="file-picker-button">Scegli file</span>
<span class="file-picker-name">
<span class="file-picker-button ui-file-picker__button"
>Scegli file</span
>
<span class="file-picker-name ui-file-picker__name">
{{
getFormState(section.usageKey).file?.name ||
"Nessun file selezionato"
@@ -107,16 +114,16 @@
<img [src]="previewUrl" alt="" />
</div>
<div class="language-toolbar form-field--wide">
<div class="language-copy">
<div class="language-toolbar form-field--wide ui-language-toolbar">
<div class="language-copy ui-language-toolbar__copy">
<span>Testi localizzati</span>
<p>IT / EN / DE / FR obbligatorie</p>
</div>
<div class="language-toggle">
<div class="language-toggle ui-language-toolbar__toggle">
<button
*ngFor="let language of mediaLanguages"
type="button"
class="language-toggle-btn"
class="language-toggle-btn ui-language-toolbar__button"
[class.active]="
getFormState(section.usageKey).activeLanguage ===
language
@@ -134,8 +141,8 @@
</div>
</div>
<label class="form-field">
<span>
<label class="form-field ui-form-field">
<span class="ui-form-caption">
Titolo ({{
mediaLanguageLabels[
getFormState(section.usageKey).activeLanguage
@@ -143,14 +150,15 @@
}})
</span>
<input
class="ui-form-control"
type="text"
[(ngModel)]="getActiveTranslation(section.usageKey).title"
placeholder="Titolo immagine"
/>
</label>
<label class="form-field">
<span>
<label class="form-field ui-form-field">
<span class="ui-form-caption">
Alt text ({{
mediaLanguageLabels[
getFormState(section.usageKey).activeLanguage
@@ -158,15 +166,17 @@
}})
</span>
<input
class="ui-form-control"
type="text"
[(ngModel)]="getActiveTranslation(section.usageKey).altText"
placeholder="Testo alternativo"
/>
</label>
<label class="form-field">
<span>Sort order</span>
<label class="form-field ui-form-field">
<span class="ui-form-caption">Sort order</span>
<input
class="ui-form-control"
type="number"
[(ngModel)]="getFormState(section.usageKey).sortOrder"
min="0"
@@ -183,9 +193,10 @@
</label>
</div>
<div class="upload-actions">
<div class="upload-actions ui-row ui-row--wrap">
<button
type="button"
class="ui-button"
(click)="uploadForSection(section.usageKey)"
[disabled]="
getFormState(section.usageKey).saving ||
@@ -202,7 +213,7 @@
</button>
<button
type="button"
class="ghost"
class="ui-button ui-button--ghost"
(click)="prepareAdd(section.usageKey)"
[disabled]="getFormState(section.usageKey).saving"
>
@@ -210,7 +221,7 @@
</button>
<button
type="button"
class="ghost"
class="ui-button ui-button--ghost"
*ngIf="getFormState(section.usageKey).replacingUsageId"
(click)="cancelReplace(section.usageKey)"
[disabled]="getFormState(section.usageKey).saving"
@@ -220,21 +231,21 @@
</div>
</div>
<div class="list-panel">
<div class="panel-heading">
<div class="list-panel ui-subpanel ui-subpanel--soft">
<div class="panel-heading ui-stack ui-stack--dense">
<h5>Immagini attive</h5>
</div>
<div
class="media-list"
class="media-list ui-media-list"
*ngIf="section.items.length; else emptySectionState"
>
<article
class="media-item"
class="media-item ui-media-item"
*ngFor="let item of section.items; trackBy: trackItem"
>
<div class="thumb-wrap">
<div class="thumb">
<div class="thumb ui-thumb">
<img
*ngIf="item.previewUrl; else noPreviewTpl"
[src]="item.previewUrl"
@@ -243,8 +254,8 @@
</div>
</div>
<div class="media-copy">
<div class="media-copy-top">
<div class="media-copy ui-stack ui-stack--dense">
<div class="media-copy-top ui-row ui-row--between ui-row--center ui-row--wrap">
<div>
<h6>
{{
@@ -259,7 +270,9 @@
{{ item.mediaAssetId }}
</p>
</div>
<span class="primary-badge" *ngIf="item.isPrimary"
<span
class="primary-badge ui-pill ui-pill--accent"
*ngIf="item.isPrimary"
>Primaria</span
>
</div>
@@ -283,10 +296,11 @@
{{ item.createdAt | date: "short" }}
</p>
<div class="sort-editor">
<div class="sort-editor ui-row ui-row--wrap">
<label>
<span>Nuovo ordine</span>
<span class="ui-form-caption">Nuovo ordine</span>
<input
class="ui-form-control"
type="number"
[(ngModel)]="item.draftSortOrder"
min="0"
@@ -294,7 +308,7 @@
</label>
<button
type="button"
class="ghost"
class="ui-button ui-button--ghost"
(click)="saveSortOrder(item)"
[disabled]="
isUsageBusy(item.usageId) ||
@@ -305,10 +319,10 @@
</button>
</div>
<div class="item-actions">
<div class="item-actions ui-row ui-row--wrap">
<button
type="button"
class="ghost"
class="ui-button ui-button--ghost"
(click)="prepareReplace(section.usageKey, item)"
[disabled]="isUsageBusy(item.usageId)"
>
@@ -316,7 +330,7 @@
</button>
<button
type="button"
class="ghost"
class="ui-button ui-button--ghost"
(click)="setPrimary(item)"
[disabled]="isUsageBusy(item.usageId) || item.isPrimary"
>
@@ -324,7 +338,7 @@
</button>
<button
type="button"
class="ghost danger"
class="ui-button ui-button--ghost-danger"
(click)="removeFromHome(item)"
[disabled]="isUsageBusy(item.usageId)"
>
@@ -342,18 +356,18 @@
</div>
<ng-template #emptySectionState>
<p class="empty-state">
<p class="empty-state ui-empty-state">
Nessuna immagine attiva collegata a questa sezione home.
</p>
</ng-template>
<ng-template #noPreviewTpl>
<div class="thumb thumb-empty">
<div class="thumb thumb-empty ui-thumb ui-thumb--empty">
<span>Preview non disponibile</span>
</div>
</ng-template>
</section>
<ng-template #loadingTpl>
<p class="loading-state">Caricamento media home...</p>
<p class="loading-state ui-empty-state">Caricamento media home...</p>
</ng-template>

View File

@@ -1,286 +1,5 @@
.section-card {
display: grid;
gap: var(--space-5);
}
.section-header,
.media-panel-header,
.media-copy-top,
.upload-actions,
.item-actions,
.sort-editor {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: var(--space-3);
}
.section-header {
align-items: flex-start;
}
.eyebrow {
margin: 0 0 var(--space-2);
text-transform: uppercase;
letter-spacing: 0.14em;
font-size: 0.72rem;
font-weight: 700;
color: var(--color-secondary-600);
}
.header-copy h2,
.media-panel-header h4,
.panel-heading h5,
.media-copy h6,
.group-header h3 {
margin: 0;
}
.header-copy p,
.media-panel-header p,
.group-header p,
.panel-heading p,
.empty-state,
.meta {
margin: var(--space-2) 0 0;
color: var(--color-text-muted);
}
.header-side {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: var(--space-3);
}
.header-stats {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
gap: var(--space-2);
}
.header-side > button {
align-self: flex-start;
}
.stat-chip {
min-width: 128px;
padding: 0.75rem 0.9rem;
border-radius: var(--radius-md);
border: 1px solid var(--color-border);
background: linear-gradient(180deg, #fffdf5 0%, #ffffff 100%);
display: grid;
gap: 0.15rem;
}
.stat-chip strong {
font-size: 1.15rem;
line-height: 1;
}
.stat-chip span {
font-size: 0.8rem;
color: var(--color-text-muted);
}
.status-banner {
margin: 0;
padding: 0.85rem 1rem;
border-radius: var(--radius-md);
border: 1px solid transparent;
font-weight: 600;
}
.status-banner-error {
color: #8a241d;
background: #fff1f0;
border-color: #f2c3bf;
}
.status-banner-success {
color: #20613a;
background: #eef9f1;
border-color: #b7e3c4;
}
.group-stack {
display: grid;
gap: var(--space-4);
}
.group-card {
border: 1px solid var(--color-border);
border-radius: calc(var(--radius-lg) + 2px);
padding: clamp(12px, 1.8vw, 18px);
background: linear-gradient(180deg, #fcfbf8 0%, #ffffff 100%);
}
.group-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: var(--space-4);
margin-bottom: var(--space-3);
}
.sections {
display: grid;
gap: var(--space-3);
}
.media-panel {
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
background: #ffffff;
padding: var(--space-3);
display: grid;
gap: var(--space-3);
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.04);
}
.usage-pill,
.primary-badge,
.count-pill,
.layout-pill {
border-radius: 999px;
border: 1px solid var(--color-border);
padding: 6px 10px;
font-size: 0.78rem;
font-weight: 700;
line-height: 1;
}
.usage-pill {
background: var(--color-neutral-100);
color: var(--color-text-muted);
}
.layout-pill {
background: #f7f4e7;
color: var(--color-neutral-900);
}
.count-pill {
background: #f8f9fb;
color: var(--color-neutral-900);
}
.primary-badge {
background: #fff5b8;
color: var(--color-text);
border-color: #f1d65c;
}
.media-panel-copy,
.media-panel-meta,
.panel-heading {
display: grid;
gap: var(--space-1);
}
.media-panel-meta {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
align-items: center;
gap: var(--space-2);
}
.title-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--space-2);
}
.workspace {
display: grid;
grid-template-columns: minmax(280px, 340px) minmax(0, 1fr);
gap: var(--space-3);
align-items: start;
}
.upload-panel,
.list-panel {
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
background: linear-gradient(180deg, #fcfcfb 0%, #f7f7f4 100%);
padding: var(--space-3);
}
.form-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: var(--space-2);
margin-top: var(--space-2);
margin-bottom: var(--space-3);
}
.form-field {
display: grid;
gap: var(--space-1);
}
.language-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--space-2);
padding: var(--space-2);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
background: #fbfaf6;
}
.language-copy {
display: grid;
gap: 2px;
}
.language-copy span {
font-size: 0.76rem;
font-weight: 700;
color: var(--color-text);
}
.language-copy p {
margin: 0;
font-size: 0.76rem;
color: var(--color-text-muted);
}
.language-toggle {
display: inline-flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 0.35rem;
}
.language-toggle-btn {
min-width: 2.8rem;
padding: 0.4rem 0.6rem;
border: 1px solid var(--color-border);
border-radius: 999px;
background: #ffffff;
color: var(--color-text-muted);
font-size: 0.78rem;
font-weight: 700;
line-height: 1;
}
.language-toggle-btn.complete {
border-color: #c9d8c4;
}
.language-toggle-btn.incomplete {
border-color: #e8c8c2;
}
.language-toggle-btn.active {
background: #fff5b8;
border-color: var(--color-brand);
color: var(--color-text);
.form-field--wide {
grid-column: 1 / -1;
}
.sr-only {
@@ -295,83 +14,18 @@
border: 0;
}
.form-field--wide {
grid-column: 1 / -1;
}
.form-field > span,
.sort-editor span {
font-size: 0.76rem;
color: var(--color-text-muted);
font-weight: 600;
}
.file-picker {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-2);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
background: var(--color-bg-card);
cursor: pointer;
transition:
border-color 0.2s ease,
background-color 0.2s ease,
box-shadow 0.2s ease;
}
.file-picker:hover {
border-color: var(--color-brand);
background: #fffef8;
}
.file-picker-button {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 6.25rem;
padding: var(--space-2) var(--space-3);
border: 1px solid var(--color-border);
border-radius: calc(var(--radius-md) - 2px);
background: #ffffff;
font-weight: 600;
font-size: 0.95rem;
color: var(--color-text);
white-space: nowrap;
}
.file-picker-name {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 0.95rem;
color: var(--color-text-muted);
}
input {
width: 100%;
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: var(--space-2) var(--space-3);
background: var(--color-bg-card);
font: inherit;
font-size: 0.95rem;
color: var(--color-text);
}
.toggle {
position: relative;
display: inline-flex;
align-items: center;
gap: 0.45rem;
width: auto;
padding: var(--space-2) var(--space-3);
border: 1px solid var(--color-border);
border-radius: 999px;
padding: var(--space-2) var(--space-3);
background: var(--color-bg-card);
align-self: end;
justify-self: start;
width: auto;
cursor: pointer;
}
@@ -384,16 +38,16 @@ input {
.toggle-mark {
width: 1.05rem;
height: 1.05rem;
border-radius: 0.3rem;
border: 1px solid var(--color-border);
background: #ffffff;
position: relative;
flex: 0 0 auto;
border: 1px solid var(--color-border);
border-radius: 0.3rem;
background: #ffffff;
}
.toggle input:checked + .toggle-mark {
background: #b14fb8;
border-color: #b14fb8;
background: var(--color-brand);
border-color: var(--color-brand);
}
.toggle input:checked + .toggle-mark::after {
@@ -421,188 +75,36 @@ input {
background: var(--color-bg-card);
}
.preview-card img,
.thumb img {
.preview-card img {
width: 100%;
height: 100%;
display: block;
object-fit: cover;
}
.media-list {
display: grid;
gap: var(--space-2);
}
.media-item {
display: grid;
grid-template-columns: 168px minmax(0, 1fr);
gap: var(--space-2);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: 0.75rem;
background: var(--color-bg-card);
}
.thumb-wrap {
min-width: 0;
}
.thumb {
aspect-ratio: 16 / 10;
border-radius: var(--radius-md);
overflow: hidden;
background: var(--color-neutral-200);
border: 1px solid var(--color-border);
}
.thumb-empty {
display: grid;
place-items: center;
text-align: center;
color: var(--color-text-muted);
padding: var(--space-3);
}
.media-copy {
min-width: 0;
display: grid;
gap: var(--space-1);
}
.media-copy-top {
align-items: center;
}
.media-copy h5 {
font-size: 1rem;
}
.media-copy h6 {
font-size: 1rem;
}
.meta {
margin: 0;
font-size: 0.82rem;
color: var(--color-text-muted);
overflow-wrap: anywhere;
}
.sort-editor {
align-items: end;
flex-wrap: wrap;
}
.sort-editor label {
display: grid;
gap: var(--space-1);
}
.upload-actions {
justify-content: flex-start;
flex-wrap: wrap;
align-items: flex-start;
gap: var(--space-2);
}
.upload-actions button {
min-width: 0;
}
button {
border: 0;
border-radius: var(--radius-md);
background: var(--color-brand);
color: var(--color-neutral-900);
padding: var(--space-2) var(--space-4);
font-weight: 600;
line-height: 1.2;
cursor: pointer;
transition:
background-color 0.2s ease,
opacity 0.2s ease,
border-color 0.2s ease;
}
button:hover:not(:disabled) {
background: var(--color-brand-hover);
}
button:disabled {
opacity: 0.65;
cursor: default;
}
button.ghost {
background: var(--color-bg-card);
color: var(--color-text);
border: 1px solid var(--color-border);
}
button.ghost:hover:not(:disabled) {
background: #fff8cc;
border-color: var(--color-brand);
}
button.ghost.danger:hover:not(:disabled) {
background: #fff0f0;
border-color: #d9534f;
}
.loading-state {
margin: 0;
color: var(--color-text-muted);
}
@media (max-width: 1200px) {
.workspace {
grid-template-columns: 1fr;
}
min-width: min(100%, 180px);
}
@media (max-width: 720px) {
.form-grid,
.media-item {
grid-template-columns: 1fr;
}
.language-toolbar {
flex-direction: column;
align-items: stretch;
}
.language-toggle {
justify-content: flex-start;
}
.file-picker {
flex-direction: column;
align-items: stretch;
}
.file-picker-button {
width: 100%;
}
.section-header,
.header-side,
.header-stats,
.group-header,
.media-panel-header,
.media-copy-top,
.upload-actions,
.item-actions,
.sort-editor {
flex-direction: column;
align-items: stretch;
}
.usage-pill,
.primary-badge,
.count-pill,
.layout-pill {
width: fit-content;
}
.media-panel-meta {
justify-content: flex-start;
}
.sort-editor {
align-items: stretch;
}
}

View File

@@ -1,16 +1,19 @@
<main class="home-page">
<section class="hero">
<div class="container hero-grid">
<div class="container hero-grid ui-content-grid ui-content-grid--spacious">
<div class="hero-copy">
<p class="eyebrow">{{ "HOME.HERO_EYEBROW" | translate }}</p>
<h1 class="hero-title" [innerHTML]="'HOME.HERO_TITLE' | translate"></h1>
<p class="hero-lead">
<p class="eyebrow ui-eyebrow">{{ "HOME.HERO_EYEBROW" | translate }}</p>
<h1
class="hero-title ui-hero-display"
[innerHTML]="'HOME.HERO_TITLE' | translate"
></h1>
<p class="hero-lead ui-copy-lead">
{{ "HOME.HERO_LEAD" | translate }}
</p>
<p class="hero-subtitle">
<p class="hero-subtitle ui-copy-subtitle">
{{ "HOME.HERO_SUBTITLE" | translate }}
</p>
<div class="hero-actions">
<div class="hero-actions ui-inline-actions ui-inline-actions--wide">
<app-button variant="primary" routerLink="/calculator/basic">{{
"HOME.BTN_CALCULATE" | translate
}}</app-button>
@@ -25,12 +28,14 @@
</div>
</section>
<section class="section capabilities">
<section class="section capabilities ui-section-block">
<div class="capabilities-bg"></div>
<div class="container">
<div class="section-head">
<h2 class="section-title">{{ "HOME.SEC_CAP_TITLE" | translate }}</h2>
<p class="section-subtitle">
<div class="section-head ui-section-head">
<h2 class="section-title ui-section-display-title">
{{ "HOME.SEC_CAP_TITLE" | translate }}
</h2>
<p class="section-subtitle ui-section-display-subtitle">
{{ "HOME.SEC_CAP_SUBTITLE" | translate }}
</p>
</div>
@@ -38,7 +43,7 @@
<app-card
*ngFor="let card of capabilityCards(); trackBy: trackCapability"
>
<div class="card-image-placeholder">
<div class="card-image-placeholder ui-bleed-media">
@if (card.image; as image) {
<picture>
@if (image.source.avifUrl) {
@@ -55,23 +60,27 @@
/>
</picture>
} @else {
<div class="card-image-fallback">
<div class="card-image-fallback ui-bleed-media__fallback">
<span>{{ card.titleKey | translate }}</span>
</div>
}
</div>
<h3>{{ card.titleKey | translate }}</h3>
<p class="text-muted">{{ card.textKey | translate }}</p>
<p class="text-muted ui-text-muted">{{ card.textKey | translate }}</p>
</app-card>
</div>
</div>
</section>
<section class="section calculator">
<div class="container calculator-grid">
<section class="section calculator ui-section-block">
<div
class="container calculator-grid ui-content-grid ui-content-grid--start ui-content-grid--split"
>
<div class="calculator-copy">
<h2 class="section-title">{{ "HOME.SEC_CALC_TITLE" | translate }}</h2>
<p class="section-subtitle">
<h2 class="section-title ui-section-display-title">
{{ "HOME.SEC_CALC_TITLE" | translate }}
</h2>
<p class="section-subtitle ui-section-display-subtitle">
{{ "HOME.SEC_CALC_SUBTITLE" | translate }}
</p>
<ul class="calculator-list">
@@ -79,23 +88,25 @@
</ul>
</div>
<app-card class="quote-card">
<div class="quote-header">
<div class="quote-header ui-row ui-row--between">
<div>
<p class="quote-eyebrow">
<p class="quote-eyebrow ui-eyebrow ui-eyebrow--compact">
{{ "HOME.CARD_CALC_EYEBROW" | translate }}
</p>
<h3 class="quote-title">
{{ "HOME.CARD_CALC_TITLE" | translate }}
</h3>
</div>
<span class="quote-tag">{{ "HOME.CARD_CALC_TAG" | translate }}</span>
<span class="quote-tag ui-pill ui-pill--accent">{{
"HOME.CARD_CALC_TAG" | translate
}}</span>
</div>
<ul class="quote-steps">
<ul class="quote-steps ui-feature-list ui-feature-list--bullets">
<li>{{ "HOME.CARD_CALC_STEP_1" | translate }}</li>
<li>{{ "HOME.CARD_CALC_STEP_2" | translate }}</li>
<li>{{ "HOME.CARD_CALC_STEP_3" | translate }}</li>
</ul>
<div class="quote-actions">
<div class="quote-actions ui-actions ui-actions--stack">
<app-button
variant="primary"
[fullWidth]="true"
@@ -113,10 +124,12 @@
</div>
</section>
<section class="section shop">
<div class="container split">
<section class="section shop ui-section-block">
<div class="container split ui-content-grid ui-content-grid--start ui-content-grid--split">
<div class="shop-copy">
<h2 class="section-title">{{ "HOME.SEC_SHOP_TITLE" | translate }}</h2>
<h2 class="section-title ui-section-display-title">
{{ "HOME.SEC_SHOP_TITLE" | translate }}
</h2>
<p>
{{ "HOME.SEC_SHOP_TEXT" | translate }}
</p>
@@ -125,7 +138,7 @@
<li>{{ "HOME.SEC_SHOP_LIST_2" | translate }}</li>
<li>{{ "HOME.SEC_SHOP_LIST_3" | translate }}</li>
</ul>
<div class="shop-actions">
<div class="shop-actions ui-inline-actions">
<app-button variant="primary" routerLink="/shop">{{
"HOME.BTN_DISCOVER" | translate
}}</app-button>
@@ -135,12 +148,12 @@
</div>
</div>
<div
class="shop-gallery"
class="shop-gallery ui-gallery-strip"
tabindex="0"
[attr.aria-label]="'HOME.SHOP_GALLERY_ARIA' | translate"
>
<figure
class="shop-gallery-item"
class="shop-gallery-item ui-gallery-card"
*ngFor="let image of shopGalleryImages(); trackBy: trackMediaAsset"
>
<picture>
@@ -164,28 +177,36 @@
<div class="shop-cards">
<app-card>
<h3>{{ "HOME.CARD_SHOP_1_TITLE" | translate }}</h3>
<p class="text-muted">{{ "HOME.CARD_SHOP_1_TEXT" | translate }}</p>
<p class="text-muted ui-text-muted">
{{ "HOME.CARD_SHOP_1_TEXT" | translate }}
</p>
</app-card>
<app-card>
<h3>{{ "HOME.CARD_SHOP_2_TITLE" | translate }}</h3>
<p class="text-muted">{{ "HOME.CARD_SHOP_2_TEXT" | translate }}</p>
<p class="text-muted ui-text-muted">
{{ "HOME.CARD_SHOP_2_TEXT" | translate }}
</p>
</app-card>
<app-card>
<h3>{{ "HOME.CARD_SHOP_3_TITLE" | translate }}</h3>
<p class="text-muted">{{ "HOME.CARD_SHOP_3_TEXT" | translate }}</p>
<p class="text-muted ui-text-muted">
{{ "HOME.CARD_SHOP_3_TEXT" | translate }}
</p>
</app-card>
</div>
</div>
</section>
<section class="section about">
<div class="container about-grid">
<section class="section about ui-section-block">
<div class="container about-grid ui-content-grid ui-content-grid--start ui-content-grid--split">
<div class="about-copy">
<h2 class="section-title">{{ "HOME.SEC_ABOUT_TITLE" | translate }}</h2>
<h2 class="section-title ui-section-display-title">
{{ "HOME.SEC_ABOUT_TITLE" | translate }}
</h2>
<p>
{{ "HOME.SEC_ABOUT_TEXT" | translate }}
</p>
<div class="about-actions">
<div class="about-actions ui-inline-actions">
<app-button variant="primary" routerLink="/about">{{
"HOME.SEC_ABOUT_TITLE" | translate
}}</app-button>
@@ -195,7 +216,7 @@
</div>
</div>
<div class="about-media">
<div class="about-feature-image">
<div class="about-feature-image ui-media-frame ui-media-frame--wide">
@if (currentFounderImage(); as image) {
<picture>
@if (image.source.avifUrl) {
@@ -218,7 +239,7 @@
@if (founderImages().length > 1) {
<button
type="button"
class="founder-nav founder-nav-prev"
class="founder-nav founder-nav-prev ui-nav-button"
(click)="prevFounderImage()"
[attr.aria-label]="'HOME.FOUNDER_PREV_ARIA' | translate"
>
@@ -226,7 +247,7 @@
</button>
<button
type="button"
class="founder-nav founder-nav-next"
class="founder-nav founder-nav-next ui-nav-button"
(click)="nextFounderImage()"
[attr.aria-label]="'HOME.FOUNDER_NEXT_ARIA' | translate"
>

View File

@@ -11,7 +11,7 @@
padding: 6rem 0 5rem;
overflow: hidden;
background: var(--home-bg);
// Enhanced Grid Pattern
&::after {
content: "";
position: absolute;
@@ -24,7 +24,6 @@
}
}
// Keep the accent blob
.hero::before {
content: "";
position: absolute;
@@ -42,337 +41,70 @@
animation: floatGlow 12s ease-in-out infinite;
}
.hero-grid {
display: grid;
gap: var(--space-12);
align-items: center;
position: relative;
z-index: 1;
}
.hero-copy {
animation: fadeUp 0.8s ease both;
}
.hero-panel {
animation: fadeUp 0.8s ease 0.15s both;
}
.eyebrow {
text-transform: uppercase;
letter-spacing: 0.12em;
font-size: 0.75rem;
color: var(--color-secondary-600);
margin-bottom: var(--space-3);
font-weight: 600;
}
.hero-title {
font-size: clamp(2.5rem, 2.4vw + 1.8rem, 4rem);
font-weight: 700;
line-height: 1.05;
letter-spacing: -0.02em;
margin-bottom: var(--space-4);
}
.hero-lead {
font-size: 1.35rem;
font-weight: 500;
color: var(--color-neutral-900);
margin-bottom: var(--space-3);
max-width: 600px;
}
.hero-subtitle {
font-size: 1.1rem;
color: var(--color-text-muted);
max-width: 560px;
line-height: 1.6;
}
.hero-actions {
display: flex;
gap: var(--space-4);
flex-wrap: wrap;
margin: var(--space-6) 0 var(--space-4);
}
.hero-badges {
display: flex;
flex-wrap: wrap;
gap: var(--space-2);
}
.hero-badges span {
display: inline-flex;
padding: 0.35rem 0.75rem;
border-radius: 999px;
background: var(--color-neutral-100);
color: var(--color-neutral-900);
font-size: 0.85rem;
font-weight: 600;
border: 1px solid var(--color-border);
}
.quote-card {
display: block;
}
.focus-card {
display: grid;
gap: var(--space-4);
}
.focus-list {
list-style: none;
padding: 0;
margin: 0;
display: grid;
gap: var(--space-2);
color: var(--color-text-muted);
}
.focus-list li::before {
content: "";
color: var(--color-brand);
margin-right: var(--space-2);
}
.focus-list li {
display: flex;
align-items: baseline;
gap: var(--space-2);
}
.quote-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: var(--space-4);
margin-bottom: var(--space-4);
}
.quote-eyebrow {
text-transform: uppercase;
font-size: 0.7rem;
letter-spacing: 0.12em;
color: var(--color-secondary-600);
margin: 0 0 var(--space-2);
}
.quote-title {
margin: 0;
font-size: 1.35rem;
}
.quote-tag {
background: var(--color-neutral-100);
border: 1px solid var(--color-border);
border-radius: 999px;
padding: 0.35rem 0.75rem;
font-size: 0.8rem;
font-weight: 600;
color: var(--color-brand-600);
background: var(--color-brand-50);
border-color: var(--color-brand-200);
}
.quote-steps {
list-style: none;
padding: 0;
margin: 0 0 var(--space-5);
display: grid;
gap: var(--space-2);
}
.quote-steps li {
position: relative;
padding-left: 1.5rem;
color: var(--color-text-muted);
}
.quote-steps li::before {
content: "";
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--color-brand);
position: absolute;
left: 0.25rem;
top: 0.5rem;
}
.quote-meta {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: var(--space-4);
margin-bottom: var(--space-5);
}
.meta-label {
display: block;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-secondary-600);
margin-bottom: var(--space-1);
}
.meta-value {
font-weight: 600;
}
.quote-actions {
display: grid;
gap: var(--space-3);
}
.section.capabilities {
.capabilities {
position: relative;
border-bottom: 1px solid var(--color-border);
padding-top: 4.5rem;
}
.capabilities-bg {
display: none;
}
.section {
padding: 5.5rem 0;
position: relative;
}
.section-head {
margin-bottom: var(--space-8);
}
.section-title {
font-size: clamp(2rem, 1.8vw + 1.2rem, 2.8rem);
margin-bottom: var(--space-3);
}
.section-subtitle {
color: var(--color-text-muted);
max-width: 620px;
}
.text-muted {
color: var(--color-text-muted);
}
.calculator {
position: relative;
border-bottom: 1px solid var(--color-border);
}
.calculator-grid {
display: grid;
gap: var(--space-10);
align-items: start;
position: relative;
z-index: 1;
}
.calculator-list {
padding-left: var(--space-4);
color: var(--color-text-muted);
margin: var(--space-6) 0 0;
}
.cap-cards {
display: grid;
gap: var(--space-4);
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.card-image-placeholder {
width: 100%;
height: 160px;
background: #f5f5f5;
margin: -1.5rem -1.5rem 1.5rem -1.5rem; /* Negative margins to bleed to edge */
width: calc(100% + 3rem);
border-top-left-radius: var(--radius-lg);
border-top-right-radius: var(--radius-lg);
border-bottom: 1px solid var(--color-neutral-300);
display: flex;
align-items: center;
justify-content: center;
color: var(--color-neutral-400);
overflow: hidden;
}
.card-image-placeholder img {
width: 100%;
height: 100%;
.quote-card {
display: block;
object-fit: cover;
}
.card-image-placeholder picture {
width: 100%;
height: 100%;
display: block;
}
.card-image-fallback {
width: 100%;
height: 100%;
display: grid;
place-items: end start;
padding: var(--space-4);
background:
linear-gradient(135deg, rgba(239, 196, 61, 0.22), rgba(255, 255, 255, 0)),
linear-gradient(180deg, #f8f5eb 0%, #efede6 100%);
}
.card-image-fallback span {
display: inline-flex;
align-items: center;
min-height: 2rem;
padding: 0.35rem 0.7rem;
border-radius: 999px;
background: rgba(255, 255, 255, 0.88);
border: 1px solid rgba(17, 24, 39, 0.08);
color: var(--color-neutral-900);
font-size: 0.78rem;
font-weight: 700;
}
.shop {
background: var(--home-bg);
position: relative;
}
.shop .split {
align-items: start;
}
.shop-copy {
max-width: 760px;
}
.split {
display: grid;
gap: var(--space-10);
align-items: center;
position: relative;
z-index: 1;
}
.shop-list {
padding-left: var(--space-4);
color: var(--color-text-muted);
margin-bottom: var(--space-6);
}
.shop-actions {
display: flex;
flex-wrap: wrap;
gap: var(--space-3);
}
.shop-gallery {
display: flex;
gap: var(--space-4);
overflow-x: auto;
scroll-snap-type: x mandatory;
padding-bottom: var(--space-2);
scrollbar-width: thin;
width: min(100%, 440px);
justify-self: end;
aspect-ratio: 16 / 11;
}
.shop-gallery-item {
flex: 0 0 100%;
margin: 0;
border-radius: var(--radius-lg);
overflow: hidden;
border: 1px solid var(--color-border);
background: var(--color-neutral-100);
box-shadow: var(--shadow-sm);
scroll-snap-align: start;
aspect-ratio: 16 / 10;
}
.shop-gallery-item img {
width: 100%;
height: 100%;
display: block;
object-fit: cover;
}
.shop-gallery-item picture,
.about-feature-image picture {
width: 100%;
height: 100%;
display: block;
}
.shop-cards {
display: grid;
gap: var(--space-4);
@@ -393,34 +125,13 @@
border-top: 1px solid var(--color-border);
position: relative;
}
.about-actions {
display: flex;
flex-wrap: wrap;
gap: var(--space-3);
}
.about-grid {
display: grid;
gap: var(--space-10);
align-items: center;
}
.about-media {
position: relative;
display: flex;
justify-content: flex-end;
}
.about-feature-image {
width: 100%;
max-width: 620px;
aspect-ratio: 16 / 10;
border-radius: var(--radius-lg);
background: var(--color-neutral-100);
border: 1px solid var(--color-border);
position: relative;
overflow: hidden;
contain: layout paint;
}
.about-feature-photo {
position: absolute;
inset: 0;
@@ -430,109 +141,52 @@
object-fit: cover;
}
.founder-nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 2.25rem;
height: 2.25rem;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.6);
background: rgba(17, 24, 39, 0.45);
color: #fff;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
line-height: 1;
cursor: pointer;
z-index: 1;
transition: background-color 0.2s ease;
}
.founder-nav:hover {
background: rgba(17, 24, 39, 0.7);
}
.founder-nav-prev {
left: 0.75rem;
}
.founder-nav-next {
right: 0.75rem;
}
.founder-nav:focus-visible {
outline: 2px solid var(--color-brand);
outline-offset: 2px;
}
.media-tile p {
margin: 0;
color: var(--color-text-muted);
font-size: 0.9rem;
}
.about-note {
padding: var(--space-5);
}
@media (min-width: 960px) {
.hero-grid {
grid-template-columns: 1.1fr 0.9fr;
}
.calculator-grid {
grid-template-columns: 1.1fr 0.9fr;
}
.calculator-grid {
grid-template-columns: 1.1fr 0.9fr;
}
.split {
grid-template-columns: 1.1fr 0.9fr;
}
.shop-copy {
grid-column: 1;
}
.shop-gallery {
grid-column: 2;
}
.shop-cards {
grid-column: 1 / -1;
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.about-grid {
grid-template-columns: 1.1fr 0.9fr;
}
}
@media (max-width: 640px) {
.hero-actions {
flex-direction: column;
align-items: stretch;
}
.quote-meta {
grid-template-columns: 1fr;
}
.shop-gallery {
width: 100%;
max-width: none;
justify-self: stretch;
}
.shop-gallery-item {
aspect-ratio: 16 / 11;
}
.shop-cards {
grid-template-columns: 1fr;
}
.about-media {
justify-content: flex-start;
}
.about-feature-image {
max-width: min(100%, 360px);
aspect-ratio: 16 / 11;
}
.founder-nav {
width: 2rem;
height: 2rem;
font-size: 1.25rem;
}
}
@keyframes fadeUp {
@@ -540,26 +194,29 @@
opacity: 0;
transform: translateY(18px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes floatGlow {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(20px);
}
}
@media (prefers-reduced-motion: reduce) {
.hero-copy,
.hero-panel {
.hero-copy {
animation: none;
}
.hero::before {
animation: none;
}

View File

@@ -19,6 +19,41 @@
font-size: 1.05rem;
}
.ui-stack {
display: grid;
gap: var(--space-4);
}
.ui-stack--dense {
gap: var(--space-2);
}
.ui-stack--roomy {
gap: var(--space-5);
}
.ui-row {
display: flex;
align-items: flex-start;
gap: var(--space-3);
}
.ui-row--between {
justify-content: space-between;
}
.ui-row--center {
align-items: center;
}
.ui-row--wrap {
flex-wrap: wrap;
}
.ui-row--end {
justify-content: flex-end;
}
.ui-two-column-layout {
--ui-sidebar-width: 400px;
display: grid;
@@ -82,6 +117,244 @@
font-size: 0.9rem;
}
.ui-eyebrow {
margin: 0 0 var(--space-3);
text-transform: uppercase;
letter-spacing: 0.12em;
font-size: 0.75rem;
font-weight: 600;
color: var(--color-secondary-600);
}
.ui-eyebrow--compact {
margin-bottom: var(--space-2);
font-size: 0.7rem;
}
.ui-hero-display {
margin: 0 0 var(--space-4);
font-size: clamp(2.5rem, 2.4vw + 1.8rem, 4rem);
font-weight: 700;
line-height: 1.05;
letter-spacing: -0.02em;
}
.ui-copy-lead {
margin: 0 0 var(--space-3);
max-width: 600px;
font-size: 1.35rem;
font-weight: 500;
color: var(--color-neutral-900);
}
.ui-copy-subtitle {
margin: 0;
max-width: 560px;
font-size: 1.1rem;
line-height: 1.6;
color: var(--color-text-muted);
}
.ui-inline-actions {
display: flex;
flex-wrap: wrap;
gap: var(--space-3);
}
.ui-inline-actions--wide {
gap: var(--space-4);
}
.ui-section-block {
position: relative;
padding: 5.5rem 0;
}
.ui-section-head {
margin-bottom: var(--space-8);
}
.ui-section-display-title {
margin: 0 0 var(--space-3);
font-size: clamp(2rem, 1.8vw + 1.2rem, 2.8rem);
}
.ui-section-display-subtitle {
margin: 0;
max-width: 620px;
color: var(--color-text-muted);
}
.ui-text-muted {
color: var(--color-text-muted);
}
.ui-content-grid {
display: grid;
gap: var(--space-10);
align-items: center;
position: relative;
z-index: 1;
}
.ui-content-grid--spacious {
gap: var(--space-12);
}
.ui-content-grid--start {
align-items: start;
}
.ui-feature-list {
list-style: none;
padding: 0;
margin: 0;
display: grid;
gap: var(--space-2);
}
.ui-feature-list--bullets li {
position: relative;
padding-left: 1.5rem;
color: var(--color-text-muted);
}
.ui-feature-list--bullets li::before {
content: "";
position: absolute;
left: 0.25rem;
top: 0.5rem;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--color-brand);
}
.ui-bleed-media {
width: calc(100% + 3rem);
height: 160px;
margin: -1.5rem -1.5rem 1.5rem;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
border-top-left-radius: var(--radius-lg);
border-top-right-radius: var(--radius-lg);
border-bottom: 1px solid var(--color-neutral-300);
background: #f5f5f5;
color: var(--color-neutral-400);
}
.ui-bleed-media img,
.ui-bleed-media picture {
width: 100%;
height: 100%;
display: block;
}
.ui-bleed-media img {
object-fit: cover;
}
.ui-bleed-media__fallback {
width: 100%;
height: 100%;
display: grid;
place-items: end start;
padding: var(--space-4);
background:
linear-gradient(135deg, rgba(239, 196, 61, 0.22), rgba(255, 255, 255, 0)),
linear-gradient(180deg, #f8f5eb 0%, #efede6 100%);
}
.ui-bleed-media__fallback span {
display: inline-flex;
align-items: center;
min-height: 2rem;
padding: 0.35rem 0.7rem;
border-radius: 999px;
background: rgba(255, 255, 255, 0.88);
border: 1px solid rgba(17, 24, 39, 0.08);
color: var(--color-neutral-900);
font-size: 0.78rem;
font-weight: 700;
}
.ui-gallery-strip {
display: flex;
gap: var(--space-4);
overflow-x: auto;
scroll-snap-type: x mandatory;
padding-bottom: var(--space-2);
scrollbar-width: thin;
}
.ui-gallery-card {
flex: 0 0 100%;
margin: 0;
overflow: hidden;
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
background: var(--color-surface-muted);
box-shadow: var(--shadow-sm);
scroll-snap-align: start;
}
.ui-gallery-card picture,
.ui-gallery-card img {
width: 100%;
height: 100%;
display: block;
}
.ui-gallery-card img {
object-fit: cover;
}
.ui-media-frame {
position: relative;
overflow: hidden;
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
background: var(--color-surface-muted);
}
.ui-media-frame--wide {
width: 100%;
max-width: 620px;
aspect-ratio: 16 / 10;
contain: layout paint;
}
.ui-nav-button {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 2.25rem;
height: 2.25rem;
border: 1px solid rgba(255, 255, 255, 0.6);
border-radius: 999px;
background: rgba(17, 24, 39, 0.45);
color: #fff;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
line-height: 1;
cursor: pointer;
z-index: 1;
transition: background-color 0.2s ease;
}
.ui-nav-button:hover {
background: rgba(17, 24, 39, 0.7);
}
.ui-nav-button:focus-visible {
outline: 2px solid var(--color-brand);
outline-offset: 2px;
}
.ui-form-group {
display: flex;
flex-direction: column;
@@ -151,6 +424,26 @@ select.ui-form-control {
grid-template-columns: 1.5fr 2fr 1fr;
}
.ui-form-grid {
display: grid;
gap: var(--space-2);
}
.ui-form-grid--two {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.ui-form-field {
display: grid;
gap: var(--space-1);
}
.ui-form-caption {
font-size: 0.76rem;
font-weight: 600;
color: var(--color-text-muted);
}
.ui-field-stack {
display: flex;
flex-direction: column;
@@ -229,6 +522,111 @@ select.ui-form-control {
background: var(--color-surface-muted);
}
.ui-file-picker {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-2);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
background: var(--color-bg-card);
cursor: pointer;
transition:
border-color 0.2s ease,
background-color 0.2s ease,
box-shadow 0.2s ease;
}
.ui-file-picker:hover {
border-color: var(--color-brand);
background: #fffef8;
}
.ui-file-picker__button {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 6.25rem;
padding: var(--space-2) var(--space-3);
border: 1px solid var(--color-border);
border-radius: calc(var(--radius-md) - 2px);
background: #ffffff;
font-size: 0.95rem;
font-weight: 600;
color: var(--color-text);
white-space: nowrap;
}
.ui-file-picker__name {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 0.95rem;
color: var(--color-text-muted);
}
.ui-language-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--space-2);
padding: var(--space-2);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
background: #fbfaf6;
}
.ui-language-toolbar__copy {
display: grid;
gap: 2px;
}
.ui-language-toolbar__copy span {
font-size: 0.76rem;
font-weight: 700;
color: var(--color-text);
}
.ui-language-toolbar__copy p {
margin: 0;
font-size: 0.76rem;
color: var(--color-text-muted);
}
.ui-language-toolbar__toggle {
display: inline-flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 0.35rem;
}
.ui-language-toolbar__button {
min-width: 2.8rem;
padding: 0.4rem 0.6rem;
border: 1px solid var(--color-border);
border-radius: 999px;
background: #ffffff;
color: var(--color-text-muted);
font-size: 0.78rem;
font-weight: 700;
line-height: 1;
}
.ui-language-toolbar__button.complete {
border-color: #c9d8c4;
}
.ui-language-toolbar__button.incomplete {
border-color: #e8c8c2;
}
.ui-language-toolbar__button.active {
background: #fff5b8;
border-color: var(--color-brand);
color: var(--color-text);
}
.ui-choice-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
@@ -329,6 +727,17 @@ select.ui-form-control {
background: #dc2626;
}
.ui-button--ghost-danger {
background: var(--color-bg-card);
border: 1px solid var(--color-border);
color: var(--color-text);
}
.ui-button--ghost-danger:hover:not(:disabled) {
background: #fff0f0;
border-color: #d9534f;
}
.ui-pill,
.ui-status-chip {
display: inline-flex;
@@ -344,6 +753,95 @@ select.ui-form-control {
line-height: 1;
}
.ui-pill--neutral {
background: var(--color-surface-muted);
color: var(--color-text-muted);
}
.ui-pill--light {
background: #f7f4e7;
color: var(--color-neutral-900);
}
.ui-pill--accent {
background: #fff5b8;
color: var(--color-text);
border-color: #f1d65c;
}
.ui-pill--soft {
background: #f8f9fb;
color: var(--color-neutral-900);
}
.ui-stat-chip {
min-width: 128px;
padding: 0.75rem 0.9rem;
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
background: linear-gradient(180deg, #fffdf5 0%, #ffffff 100%);
display: grid;
gap: 0.15rem;
}
.ui-stat-chip strong {
font-size: 1.15rem;
line-height: 1;
}
.ui-stat-chip span {
font-size: 0.8rem;
color: var(--color-text-muted);
}
.ui-banner {
margin: 0;
padding: 0.85rem 1rem;
border: 1px solid transparent;
border-radius: var(--radius-md);
font-weight: 600;
}
.ui-banner--error {
color: #8a241d;
background: #fff1f0;
border-color: #f2c3bf;
}
.ui-banner--success {
color: #20613a;
background: #eef9f1;
border-color: #b7e3c4;
}
.ui-subpanel {
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
background: var(--color-bg-card);
padding: var(--space-3);
}
.ui-subpanel--soft {
background: linear-gradient(180deg, #fcfcfb 0%, #f7f7f4 100%);
}
.ui-subpanel--warm {
border-radius: calc(var(--radius-lg) + 2px);
padding: clamp(12px, 1.8vw, 18px);
background: linear-gradient(180deg, #fcfbf8 0%, #ffffff 100%);
}
.ui-subpanel--elevated {
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.04);
}
.ui-workspace-compact {
display: grid;
grid-template-columns: minmax(280px, 340px) minmax(0, 1fr);
gap: var(--space-3);
align-items: start;
}
.ui-status-chip--neutral {
background: var(--color-surface-muted);
}
@@ -473,6 +971,49 @@ select.ui-form-control {
overflow-wrap: anywhere;
}
.ui-media-list {
display: grid;
gap: var(--space-2);
}
.ui-media-item {
display: grid;
grid-template-columns: 168px minmax(0, 1fr);
gap: var(--space-2);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: 0.75rem;
background: var(--color-bg-card);
}
.ui-thumb {
aspect-ratio: 16 / 10;
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
overflow: hidden;
background: var(--color-neutral-200);
}
.ui-thumb img {
width: 100%;
height: 100%;
display: block;
object-fit: cover;
}
.ui-thumb--empty {
display: grid;
place-items: center;
text-align: center;
color: var(--color-text-muted);
padding: var(--space-3);
}
.ui-empty-state {
margin: 0;
color: var(--color-text-muted);
}
@media (min-width: 768px) {
.ui-form-row {
flex-direction: row;
@@ -483,16 +1024,25 @@ select.ui-form-control {
}
}
@media (min-width: 960px) {
.ui-content-grid--split {
grid-template-columns: 1.1fr 0.9fr;
}
}
@media (max-width: 1024px) {
.ui-two-column-layout,
.ui-split-workspace {
.ui-split-workspace,
.ui-workspace-compact {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.ui-form-row--three,
.ui-choice-grid {
.ui-choice-grid,
.ui-form-grid--two,
.ui-media-item {
grid-template-columns: 1fr;
}
}
@@ -506,4 +1056,33 @@ select.ui-form-control {
.ui-page-hero {
padding: var(--space-6) 0;
}
.ui-inline-actions {
flex-direction: column;
align-items: stretch;
}
.ui-language-toolbar {
flex-direction: column;
align-items: stretch;
}
.ui-language-toolbar__toggle {
justify-content: flex-start;
}
.ui-file-picker {
flex-direction: column;
align-items: stretch;
}
.ui-file-picker__button {
width: 100%;
}
.ui-nav-button {
width: 2rem;
height: 2rem;
font-size: 1.25rem;
}
}