Author SHA1 Message Date
Joe_Peach 78b2afb747 create picture page 2026-09-27 18:18:02 +03:00
Joe_Peach 9ec5e852c6 Merge pull request 'Feat/main page' (#2) from feat/main-page into dev
Reviewed-on: #2
2026-09-25 16:56:27 +00:00
Joe_Peach 6ef1e381e4 Merge pull request 'Feat/api logic' (#1) from feat/api-logic into dev
Reviewed-on: #1
2026-09-25 16:54:22 +00:00
Joe_Peach 2048c17eee made main page 2026-09-25 12:33:07 +03:00
Joe_Peach b98b531162 fixed colors and added assets 2026-09-25 12:32:46 +03:00
Joe_Peach 6770eba5b6 create navigation card-button 2026-09-25 12:32:15 +03:00
13 changed files with 981 additions and 40 deletions

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 KiB

@@ -0,0 +1,100 @@
.card {
position: relative;
display: grid;
min-height: 220px;
gap: var(--space-4);
align-content: start;
padding: var(--space-8);
overflow: hidden;
text-align: left;
color: var(--color-text);
cursor: pointer;
background:
linear-gradient(135deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0)),
var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
transition:
border-color var(--duration-fast) var(--ease-standard),
box-shadow var(--duration-fast) var(--ease-standard),
transform var(--duration-fast) var(--ease-standard);
}
.card::before {
position: absolute;
inset: auto -28px -58px auto;
width: 180px;
height: 180px;
content: "";
background: radial-gradient(circle, var(--card-accent), transparent 66%);
opacity: 0.24;
}
.card:hover {
border-color: var(--card-accent);
box-shadow: var(--shadow-lg);
transform: translateY(-4px);
}
.card:focus-visible {
outline: 3px solid var(--card-focus);
outline-offset: 3px;
}
.cyan {
--card-accent: var(--color-primary);
--card-focus: var(--color-primary-soft);
}
.violet {
--card-accent: var(--color-accent);
--card-focus: rgba(143, 84, 255, 0.24);
}
.kicker {
width: fit-content;
padding: var(--space-2) var(--space-3);
font-size: 12px;
font-weight: 800;
line-height: 1;
color: var(--color-black);
text-transform: uppercase;
letter-spacing: 0;
background: var(--card-accent);
border-radius: var(--radius-sm);
}
.title {
max-width: 12ch;
font-size: clamp(28px, 5vw, 48px);
font-weight: 900;
line-height: 1;
}
.description {
max-width: 34ch;
color: var(--color-text-muted);
}
.action {
display: inline-flex;
width: fit-content;
align-items: center;
padding-block: var(--space-2);
margin-top: auto;
font-weight: 800;
color: var(--card-accent);
}
.action::after {
margin-left: var(--space-2);
content: "->";
}
@media (max-width: 640px) {
.card {
min-height: 190px;
padding: var(--space-6);
}
}
@@ -0,0 +1,31 @@
import styles from "./GeneratorCard.module.css";
interface GeneratorCardProps{
title: string;
description: string;
label: string;
accent: "cyan" | "violet";
onClick: () => void;
};
export default function GeneratorCard({
title,
description,
label,
accent,
onClick,
}: GeneratorCardProps) {
return (
<button
className={`${styles.card} ${styles[accent]}`}
type="button"
onClick={onClick}
aria-label={label}
>
<span className={styles.kicker}>{label}</span>
<span className={styles.title}>{title}</span>
<span className={styles.description}>{description}</span>
<span className={styles.action}>Открыть</span>
</button>
);
}
+1
View File
@@ -2,6 +2,7 @@ export interface Request{
id: number; id: number;
text: string; text: string;
files: File[]; files: File[];
ratio: string;
} }
export interface Response{ export interface Response{
+1 -1
View File
@@ -1,4 +1,4 @@
.layout { .layout {
min-height: 100vh; min-height: 100vh;
padding-block: var(--space-8); /*padding-block: var(--space-8);*/
} }
+1 -2
View File
@@ -1,5 +1,4 @@
import { Navigate, Outlet } from "react-router-dom"; import { Outlet } from "react-router-dom";
import { useAppSelector } from "../../store/hooks";
import styles from "./Layout.module.css"; import styles from "./Layout.module.css";
export default function Layout() { export default function Layout() {
+38 -5
View File
@@ -1,15 +1,48 @@
.page { .page {
display: grid; display: grid;
gap: var(--space-12);
min-height: 100vh;
padding: var(--space-10);
align-content: center;
background: var(--color-bg);
}
.hero {
display: grid;
justify-items: center;
gap: var(--space-4); gap: var(--space-4);
text-align: center;
}
.logo {
width: min(540px, 86vw);
height: auto;
object-fit: contain;
filter: drop-shadow(0 24px 56px rgba(53, 216, 255, 0.2));
} }
.title { .title {
margin: 0; margin: 0;
font-size: 32px; font-size: clamp(40px, 8vw, 88px);
line-height: 1.15; font-weight: 900;
line-height: 0.95;
color: var(--color-text);
text-shadow: 0 0 32px rgba(53, 216, 255, 0.28);
} }
.text { .cards {
margin: 0; display: grid;
color: var(--color-text-muted); grid-template-columns: repeat(2, minmax(0, 1fr));
gap: var(--space-6);
}
@media (max-width: 760px) {
.page {
gap: var(--space-8);
padding-block: var(--space-8);
}
.cards {
grid-template-columns: 1fr;
}
} }
+32 -5
View File
@@ -1,15 +1,42 @@
import styles from "./Main.module.css";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import GeneratorCard from "../../components/GeneratorCard/GeneratorCard";
import logo from "../../assets/common/HITGENERATOR-Logo.png";
import styles from "./Main.module.css";
export default function Main() { export default function Main() {
const navigate = useNavigate(); const navigate = useNavigate();
return ( return (
<main className={styles.page}> <main className={styles.page}>
<h1 className={styles.title}>Главная</h1> <section className={styles.hero} aria-labelledby="main-title">
<p className={styles.text}>Базовая страница приложения готова.</p> <img
<button onClick={() => navigate('/picture')}>Go to Picture</button> className={styles.logo}
<button onClick={() => navigate('/video')}>Go to Video</button> src={logo}
alt="ShitGenerator"
width="1080"
height="608"
/>
<h1 className={styles.title} id="main-title">
ShitGenerator
</h1>
</section>
<section className={styles.cards} aria-label="Выбор генератора">
<GeneratorCard
title="Генерация видео"
description="Собирай видеоролики из идеи, промпта и нужного визуального направления."
label="Видео"
accent="cyan"
onClick={() => navigate("/video")}
/>
<GeneratorCard
title="Генерация картинок"
description="Создавай изображения под задачу, стиль и настроение за пару кликов."
label="Картинки"
accent="violet"
onClick={() => navigate("/picture")}
/>
</section>
</main> </main>
); );
} }
+540
View File
@@ -0,0 +1,540 @@
.page {
display: grid;
min-height: 100vh;
padding: var(--space-6) var(--space-10) var(--space-10);
color: var(--color-text);
background: var(--color-bg);
}
.toolbar {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 52px;
padding-bottom: var(--space-4);
border-bottom: 1px solid var(--color-border);
}
.brand {
display: flex;
align-items: center;
min-width: 0;
gap: var(--space-3);
}
.backButton,
.removeFileButton {
display: inline-grid;
flex: 0 0 auto;
place-items: center;
width: 36px;
height: 36px;
padding: 0;
color: var(--color-text);
cursor: pointer;
background: transparent;
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
transition:
color var(--duration-fast) var(--ease-standard),
border-color var(--duration-fast) var(--ease-standard),
background-color var(--duration-fast) var(--ease-standard);
}
.backButton {
font-size: 22px;
line-height: 1;
}
.backButton:hover,
.removeFileButton:hover {
color: var(--color-black);
background: var(--color-primary);
border-color: var(--color-primary);
}
.backButton:focus-visible,
.removeFileButton:focus-visible,
.uploadButton:focus-within,
.segmentedControl button:focus-visible,
.styleChoices button:focus-visible,
.generateButton:focus-visible {
outline: 3px solid var(--color-primary-soft);
outline-offset: 3px;
}
.logo {
width: 150px;
max-height: 38px;
object-fit: contain;
object-position: left center;
}
.mode,
.eyebrow,
.labelRow,
.previewHeader,
.previewFooter {
font-size: 12px;
font-weight: 800;
line-height: 1.2;
letter-spacing: 0;
text-transform: uppercase;
}
.mode,
.eyebrow {
color: var(--color-primary);
}
.workspace {
display: grid;
grid-template-columns: minmax(300px, 0.86fr) minmax(380px, 1.14fr);
gap: var(--space-6);
width: min(100%, 1120px);
margin: auto;
padding-block: var(--space-8);
}
.controls,
.previewPanel {
min-width: 0;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
}
.controls {
display: flex;
flex-direction: column;
gap: var(--space-6);
padding: var(--space-8);
}
.heading {
display: grid;
gap: var(--space-2);
}
.eyebrow {
margin: 0;
}
.heading h1 {
margin: 0;
font-size: clamp(30px, 4vw, 44px);
font-weight: 900;
line-height: 1;
}
.field {
display: grid;
min-width: 0;
gap: var(--space-3);
padding: 0;
margin: 0;
border: 0;
font-weight: 800;
}
.field legend {
padding: 0;
font-size: 14px;
}
.labelRow {
display: flex;
justify-content: space-between;
gap: var(--space-3);
color: var(--color-text-muted);
}
.labelRow span:first-child {
color: var(--color-text);
}
.field textarea {
width: 100%;
min-height: 132px;
padding: var(--space-4);
resize: vertical;
color: var(--color-text);
font: inherit;
line-height: 1.45;
background: rgba(3, 6, 16, 0.42);
border: 1px solid rgba(216, 227, 251, 0.16);
border-radius: var(--radius-sm);
outline: 0;
transition: border-color var(--duration-fast) var(--ease-standard);
}
.field textarea::placeholder {
color: var(--color-gray-500);
}
.field textarea:focus {
border-color: var(--color-primary);
}
.uploadButton,
.fileSelected {
display: flex;
align-items: center;
min-height: 48px;
padding: var(--space-2) var(--space-3);
background: rgba(3, 6, 16, 0.42);
border: 1px dashed rgba(216, 227, 251, 0.22);
border-radius: var(--radius-sm);
}
.uploadButton {
justify-content: center;
font-size: 14px;
font-weight: 800;
color: var(--color-primary);
cursor: pointer;
transition:
color var(--duration-fast) var(--ease-standard),
border-color var(--duration-fast) var(--ease-standard),
background-color var(--duration-fast) var(--ease-standard);
}
.uploadButton:hover {
color: var(--color-black);
background: var(--color-primary);
border-color: var(--color-primary);
}
.uploadButton input {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
clip-path: inset(50%);
white-space: nowrap;
}
.fileSelected {
justify-content: space-between;
gap: var(--space-3);
border-style: solid;
border-color: rgba(143, 84, 255, 0.5);
}
.fileName {
overflow: hidden;
font-size: 14px;
text-overflow: ellipsis;
white-space: nowrap;
}
.removeFileButton {
width: 30px;
height: 30px;
font-size: 20px;
}
.segmentedControl,
.styleChoices {
display: grid;
gap: var(--space-2);
}
.segmentedControl,
.styleChoices {
grid-template-columns: repeat(3, 1fr);
}
.segmentedControl button,
.styleChoices button {
min-width: 0;
padding: var(--space-3);
font: inherit;
font-size: 14px;
font-weight: 800;
color: var(--color-text-muted);
cursor: pointer;
background: rgba(3, 6, 16, 0.42);
border: 1px solid rgba(216, 227, 251, 0.16);
border-radius: var(--radius-sm);
transition:
color var(--duration-fast) var(--ease-standard),
border-color var(--duration-fast) var(--ease-standard),
background-color var(--duration-fast) var(--ease-standard);
}
.segmentedControl button:hover,
.styleChoices button:hover {
color: var(--color-text);
border-color: var(--color-accent);
}
.segmentedControl .selected,
.styleChoices .styleSelected {
color: var(--color-text);
background: var(--color-primary-soft);
border-color: var(--color-primary);
}
.styleChoices button {
display: flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
}
.swatch {
width: 14px;
height: 14px;
border-radius: 50%;
}
.synthesis {
background: var(--color-primary);
box-shadow: 10px 0 0 var(--color-accent);
}
.neon {
background: var(--color-accent);
box-shadow: 10px 0 0 var(--color-accent-warm);
}
.cinema {
background: var(--color-accent-gold);
box-shadow: 10px 0 0 var(--color-accent-warm);
}
.error {
padding: var(--space-3);
margin: 0;
font-size: 14px;
color: var(--color-rose-200);
background: rgba(190, 18, 60, 0.16);
border: 1px solid rgba(254, 205, 211, 0.36);
border-radius: var(--radius-sm);
}
.generateButton {
min-height: 52px;
padding: var(--space-3) var(--space-5);
margin-top: auto;
font: inherit;
font-weight: 900;
color: var(--color-black);
cursor: pointer;
background: var(--color-primary);
border: 1px solid var(--color-primary);
border-radius: var(--radius-sm);
transition:
background-color var(--duration-fast) var(--ease-standard),
border-color var(--duration-fast) var(--ease-standard),
transform var(--duration-fast) var(--ease-standard);
}
.generateButton:hover:not(:disabled) {
background: var(--color-primary-hover);
border-color: var(--color-primary-hover);
transform: translateY(-1px);
}
.generateButton:disabled {
cursor: wait;
opacity: 0.66;
}
.previewPanel {
display: grid;
grid-template-rows: auto minmax(0, 1fr) auto;
overflow: hidden;
}
.previewHeader,
.previewFooter {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 56px;
padding-inline: var(--space-6);
}
.previewHeader {
border-bottom: 1px solid var(--color-border);
}
.previewFooter {
color: var(--color-text-muted);
border-top: 1px solid var(--color-border);
}
.statusDot {
width: 9px;
height: 9px;
background: var(--color-gray-500);
border-radius: 50%;
}
.statusDot[data-active="true"] {
background: var(--color-primary);
box-shadow: 0 0 0 5px var(--color-primary-soft);
}
.previewArea {
display: grid;
min-height: 460px;
padding: var(--space-6);
overflow: auto;
place-items: center;
background:
linear-gradient(rgba(53, 216, 255, 0.04) 1px, transparent 1px),
linear-gradient(90deg, rgba(53, 216, 255, 0.04) 1px, transparent 1px),
rgba(3, 6, 16, 0.3);
background-size: 24px 24px;
}
.previewCanvas {
position: relative;
display: grid;
width: min(100%, 560px);
overflow: hidden;
place-items: center;
background: #0a1430;
border: 1px solid rgba(53, 216, 255, 0.34);
border-radius: var(--radius-sm);
box-shadow: var(--shadow-lg);
}
.square {
aspect-ratio: 1;
}
.portrait {
width: min(80%, 430px);
aspect-ratio: 4 / 5;
}
.wide {
aspect-ratio: 16 / 9;
}
.referenceImage {
width: 100%;
height: 100%;
object-fit: cover;
}
.emptyState {
display: grid;
justify-items: center;
gap: var(--space-4);
font-size: 13px;
font-weight: 800;
color: var(--color-text-muted);
letter-spacing: 0;
text-transform: uppercase;
}
.emptyMark {
position: relative;
width: 74px;
height: 74px;
border: 2px solid var(--color-primary);
border-radius: var(--radius-sm);
transform: rotate(8deg);
}
.emptyMark::before,
.emptyMark::after {
position: absolute;
content: "";
}
.emptyMark::before {
inset: 12px;
border: 2px solid var(--color-accent);
border-radius: 2px;
}
.emptyMark::after {
right: -12px;
bottom: -12px;
width: 24px;
height: 24px;
background: var(--color-accent-warm);
border: 2px solid var(--color-bg);
}
.scanLine {
position: absolute;
inset: 0 auto auto 0;
width: 100%;
height: 2px;
background: var(--color-primary);
box-shadow: 0 0 18px var(--color-primary);
animation: scan 1.8s ease-in-out infinite;
}
@keyframes scan {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(300px);
}
}
@media (max-width: 820px) {
.page {
padding: var(--space-4) var(--space-6) var(--space-6);
}
.workspace {
grid-template-columns: 1fr;
padding-block: var(--space-6);
}
.controls {
padding: var(--space-6);
}
.previewArea {
min-height: 360px;
}
}
@media (max-width: 480px) {
.page {
padding-inline: var(--space-4);
}
.logo {
width: 118px;
}
.mode {
font-size: 10px;
}
.controls {
gap: var(--space-5);
padding: var(--space-5);
}
.styleChoices button {
padding-inline: var(--space-2);
font-size: 12px;
}
.previewHeader,
.previewFooter {
padding-inline: var(--space-4);
}
.previewArea {
min-height: 300px;
padding: var(--space-4);
}
}
+202 -2
View File
@@ -1,8 +1,208 @@
import { useEffect, useRef, useState, type ChangeEvent, type FormEvent } from "react";
import { useNavigate } from "react-router-dom";
import logo from "../../assets/common/HITGENERATOR-Logo.png";
import { useAppDispatch, useAppSelector } from "../../store/hooks";
import { clearRequestError, request } from "../../store/slices/slice";
import styles from "./Picture.module.css";
type AspectRatio = "1:1" | "4:5" | "16:9";
const aspectRatios: AspectRatio[] = ["1:1", "4:5", "16:9"];
export default function Picture() { export default function Picture() {
const navigate = useNavigate();
const dispatch = useAppDispatch();
const { loading, error: requestError } = useAppSelector((state) => state.request);
const [prompt, setPrompt] = useState("");
const [reference, setReference] = useState<File[]>([]);
const [aspectRatio, setAspectRatio] = useState<AspectRatio>("1:1");
const [formError, setFormError] = useState<string | null>(null);
const [isSubmitted, setIsSubmitted] = useState(false);
useEffect(() => {
dispatch(clearRequestError());
}, [dispatch]);
function handleReferenceChange(event: ChangeEvent<HTMLInputElement>) {
const nextFile = event.currentTarget.files?.[0] ?? null;
if (nextFile && !nextFile.type.startsWith("image/")) {
setFormError("Выберите файл изображения.");
return;
}
setFormError(null);
//updateReference(nextFile);
setIsSubmitted(false);
setReference(nextFile ? [...reference, nextFile] : []);
}
function removeReference(index: number) {
//updateReference(null);
setIsSubmitted(false);
setReference(reference.filter((_, i) => i !== index));
}
async function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const text = prompt.trim();
if (!text) {
setFormError("Добавьте описание картинки.");
return;
}
setFormError(null);
setIsSubmitted(false);
await dispatch(
request({
id: Date.now(),
text,
files: reference ? reference : [],
ratio: aspectRatio,
}),
).unwrap();
setIsSubmitted(true);
}
const previewClassName = [
styles.previewCanvas,
aspectRatio === "1:1"
? styles.square
: aspectRatio === "4:5"
? styles.portrait
: styles.wide,
].join(" ");
const status = loading ? "Генерируем" : isSubmitted ? "Запрос принят" : "Ожидание";
return ( return (
<main> <div className={styles.page}>
<h1>Picture</h1> <header className={styles.toolbar}>
<div className={styles.brand}>
<button
className={styles.backButton}
type="button"
aria-label="На главную"
title="На главную"
onClick={() => navigate("/")}
>
<span aria-hidden="true">&larr;</span>
</button>
<img className={styles.logo} src={logo} alt="ShitGenerator" />
</div>
<span className={styles.mode}>PICTURE</span>
</header>
<main className={styles.workspace}>
<form className={styles.controls} onSubmit={handleSubmit}>
<div className={styles.heading}>
<p className={styles.eyebrow}>IMAGE LAB</p>
<h1>Генерация картинки</h1>
</div>
<label className={styles.field}>
<span className={styles.labelRow}>
<span>Описание</span>
<span>{prompt.length}/800</span>
</span>
<textarea
value={prompt}
maxLength={800}
placeholder="Опишите изображение"
onChange={(event) => {
setFormError(null);
setPrompt(event.target.value);
setIsSubmitted(false);
}}
/>
</label>
<section className={styles.field} aria-labelledby="reference-label">
<span className={styles.labelRow} id="reference-label">
<span>Референс</span>
<span>Необязательно</span>
</span>
{reference.length > 0 && (
reference.map((file, index) => (
<div className={styles.fileSelected} key={index}>
<span className={styles.fileName}>{file.name}</span>
<button
className={styles.removeFileButton}
type="button"
aria-label="Удалить референс"
title="Удалить референс"
onClick={() => removeReference(index)}
>
&times;
</button>
</div>
))
)}
<label className={styles.uploadButton}>
<input type="file" accept="image/*" onChange={handleReferenceChange} />
<span>Выбрать файл</span>
</label>
</section>
<fieldset className={styles.field}>
<span className={styles.labelRow}>Формат</span>
<div className={styles.segmentedControl}>
{aspectRatios.map((ratio) => (
<button
className={ratio === aspectRatio ? styles.selected : undefined}
type="button"
key={ratio}
aria-pressed={ratio === aspectRatio}
onClick={() => {
setAspectRatio(ratio);
setIsSubmitted(false);
}}
>
{ratio}
</button>
))}
</div>
</fieldset>
{(formError || requestError) && (
<p className={styles.error} role="alert">
{formError || requestError}
</p>
)}
<button className={styles.generateButton} type="submit" disabled={loading}>
{loading ? "Генерируем..." : "Сгенерировать"}
</button>
</form>
<section className={styles.previewPanel} aria-label="Область результата">
<div className={styles.previewHeader}>
<span>{status}</span>
<span className={styles.statusDot} data-active={loading || isSubmitted} aria-hidden="true" />
</div>
<div className={styles.previewArea}>
<div className={previewClassName}>
<div className={styles.emptyState}>
<span className={styles.emptyMark} aria-hidden="true" />
<span>{loading ? "Генерируем" : isSubmitted ? "Запрос принят" : "Ожидаемое изображение"}</span>
</div>
{loading && <span className={styles.scanLine} aria-hidden="true" />}
</div>
</div>
<footer className={styles.previewFooter}>
<span>{aspectRatio}</span>
</footer>
</section>
</main> </main>
</div>
); );
} }
+5 -1
View File
@@ -1,5 +1,9 @@
body { body {
background: var(--color-bg); background-image: url('../assets/common/background.jpeg');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
background-attachment: fixed;
color: var(--color-text); color: var(--color-text);
font-family: var(--font-family-base); font-family: var(--font-family-base);
line-height: 1.5; line-height: 1.5;
+30 -24
View File
@@ -1,23 +1,26 @@
:root { :root {
color-scheme: light; color-scheme: dark;
--font-family-base: --font-family-base:
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
sans-serif; sans-serif;
--color-white: #ffffff; --color-white: #ffffff;
--color-black: #0b1220; --color-black: #030610;
--color-gray-50: #f8fafc; --color-gray-50: #f6f8ff;
--color-gray-100: #f1f5f9; --color-gray-100: #ecf2ff;
--color-gray-200: #e2e8f0; --color-gray-200: #d8e3fb;
--color-gray-300: #cbd5e1; --color-gray-300: #b8c7e7;
--color-gray-500: #64748b; --color-gray-500: #7888aa;
--color-gray-600: #475569; --color-gray-600: #52617f;
--color-gray-700: #334155; --color-gray-700: #243452;
--color-gray-900: #0f172a; --color-gray-900: #07142d;
--color-teal-50: #ecfeff; --color-cyan-50: #e9fbff;
--color-teal-600: #0d9488; --color-cyan-400: #35d8ff;
--color-teal-700: #0f766e; --color-cyan-600: #0899d8;
--color-violet-500: #8f54ff;
--color-coral-500: #ff6b79;
--color-gold-400: #ffc96b;
--color-rose-50: #fff1f2; --color-rose-50: #fff1f2;
--color-rose-200: #fecdd3; --color-rose-200: #fecdd3;
--color-rose-700: #be123c; --color-rose-700: #be123c;
@@ -25,15 +28,18 @@
--color-green-200: #bbf7d0; --color-green-200: #bbf7d0;
--color-green-800: #166534; --color-green-800: #166534;
--color-bg: var(--color-gray-50); --color-bg: #050914;
--color-surface: var(--color-white); --color-surface: rgba(9, 18, 42, 0.86);
--color-surface-muted: var(--color-gray-100); --color-surface-muted: rgba(17, 32, 67, 0.78);
--color-text: var(--color-gray-900); --color-text: #f7fbff;
--color-text-muted: var(--color-gray-600); --color-text-muted: #aab8d8;
--color-border: var(--color-gray-200); --color-border: rgba(82, 208, 255, 0.22);
--color-primary: var(--color-teal-700); --color-primary: var(--color-cyan-400);
--color-primary-hover: #115e59; --color-primary-hover: var(--color-gold-400);
--color-primary-soft: rgba(13, 148, 136, 0.12); --color-primary-soft: rgba(53, 216, 255, 0.16);
--color-accent: var(--color-violet-500);
--color-accent-warm: var(--color-coral-500);
--color-accent-gold: var(--color-gold-400);
--color-danger-bg: var(--color-rose-50); --color-danger-bg: var(--color-rose-50);
--color-danger-border: var(--color-rose-200); --color-danger-border: var(--color-rose-200);
--color-danger-text: var(--color-rose-700); --color-danger-text: var(--color-rose-700);
@@ -55,8 +61,8 @@
--radius-md: 8px; --radius-md: 8px;
--radius-lg: 12px; --radius-lg: 12px;
--shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.08); --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.24);
--shadow-lg: 0 24px 80px rgba(15, 23, 42, 0.14); --shadow-lg: 0 24px 80px rgba(0, 0, 0, 0.36);
--duration-fast: 160ms; --duration-fast: 160ms;
--ease-standard: ease; --ease-standard: ease;