diff --git a/CLAUDE.md b/CLAUDE.md index 5ee9853..273301c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,7 +7,7 @@ Content language: Russian ## Tech Stack - **Next.js 16** (App Router, TypeScript, Turbopack) -- **Tailwind CSS v4** (dark mode only, gold/black theme) +- **Tailwind CSS v4** (dual theme: dark default + light, gold accent) - **lucide-react** for icons - **better-sqlite3** for SQLite database - **Fonts**: Inter (body) + Oswald (headings) via `next/font` @@ -111,8 +111,9 @@ src/ ## Brand / Styling - **Accent**: gold (`#c9a96e` / `hsl(37, 42%, 61%)`) -- **Background**: `#050505` – `#0a0a0a` (dark only) -- **Surface**: `#171717` dark cards +- **Dark theme** (default): background `#050505`–`#0a0a0a`, surface `#171717`, text `neutral-100` +- **Light theme**: background `white`/`neutral-50`, surface `white`, text `neutral-900` +- Theme toggle via `ThemeToggle` component, `.dark` class on ``, stored in `localStorage` - Logo: transparent PNG heart with gold glow, uses `unoptimized` ## Content Data diff --git a/src/app/admin/_components/ArrayEditor.tsx b/src/app/admin/_components/ArrayEditor.tsx index 4eec143..2de7e79 100644 --- a/src/app/admin/_components/ArrayEditor.tsx +++ b/src/app/admin/_components/ArrayEditor.tsx @@ -190,15 +190,15 @@ export function ArrayEditor({
{ itemRefs.current[i] = el; }} - className={`rounded-lg border bg-neutral-900/50 mb-3 hover:border-white/25 hover:bg-neutral-800/50 focus-within:border-gold/50 focus-within:bg-neutral-800 transition-all ${ - newItemIndex === i || droppedIndex === i ? "border-gold/40 ring-1 ring-gold/20" : "border-white/10" + className={`rounded-lg border bg-neutral-100/80 mb-3 hover:border-neutral-300 dark:hover:border-white/25 hover:bg-neutral-200/50 focus-within:border-gold/50 focus-within:bg-neutral-200 transition-all dark:bg-neutral-900/50 dark:hover:bg-neutral-800/50 dark:focus-within:bg-neutral-800 ${ + newItemIndex === i || droppedIndex === i ? "border-gold/40 ring-1 ring-gold/20" : "border-neutral-200 dark:border-white/10" } ${isHidden ? "hidden" : ""}`} > {inline ? ( /* Inline: grip + content + delete on one row */
handleGripMouseDown(e, i)} aria-label="Перетащить для сортировки" role="button" @@ -222,7 +222,7 @@ export function ArrayEditor({
handleGripMouseDown(e, i)} aria-label="Перетащить для сортировки" role="button" @@ -236,7 +236,7 @@ export function ArrayEditor({ aria-expanded={!isCollapsed} className="flex items-center gap-2 flex-1 min-w-0 text-left cursor-pointer group" > - {title} + {title} {getItemBadge?.(item, i)} @@ -304,13 +304,11 @@ export function ArrayEditor({
{ itemRefs.current[i] = el; }} - className={`rounded-lg border bg-neutral-900/50 mb-3 transition-colors ${ - "border-white/10" - }`} + className="rounded-lg border border-neutral-200 bg-neutral-100/80 mb-3 transition-colors dark:border-white/10 dark:bg-neutral-900/50" > {inline ? (
-
handleGripMouseDown(e, i)} aria-label="Перетащить для сортировки" role="button">
@@ -327,7 +325,7 @@ export function ArrayEditor({
handleGripMouseDown(e, i)} aria-label="Перетащить для сортировки" role="button" @@ -336,7 +334,7 @@ export function ArrayEditor({
{collapsible && ( @@ -384,14 +382,14 @@ export function ArrayEditor({
{(label || (collapsible && items.length > 1)) && (
- {label ?

{label}

:
} + {label ?

{label}

:
} {collapsible && items.length > 1 && (() => { const allCollapsed = collapsed.size >= items.length; return ( -

Подтвердить запись

-

{bookingName}

+

Подтвердить запись

+

{bookingName}

- +
- +
- +
- +
- + setComment(e.target.value)} placeholder="Первое занятие, пробный" - className="w-full rounded-lg border border-white/[0.08] bg-white/[0.04] px-3 py-2 text-sm text-white placeholder-neutral-500 outline-none focus:border-gold/40 disabled:opacity-30 disabled:cursor-not-allowed" + className="w-full rounded-lg border border-neutral-200 bg-neutral-100 px-3 py-2 text-sm text-neutral-900 placeholder-neutral-400 outline-none focus:border-gold/40 disabled:opacity-30 disabled:cursor-not-allowed dark:border-white/[0.08] dark:bg-white/[0.04] dark:text-white dark:placeholder-neutral-500" />
@@ -318,8 +318,8 @@ function GroupBookingsTab({ filter, onDataChange }: { filter: BookingFilter; onD onConfirm={(id) => setConfirmingId(id)} renderExtra={(b) => ( <> - {b.groupInfo && {b.groupInfo}} - {b.confirmedHall && {b.confirmedHall}} + {b.groupInfo && {b.groupInfo}} + {b.confirmedHall && {b.confirmedHall}} {(b.confirmedGroup || b.confirmedDate) && ( +
@@ -306,15 +306,15 @@ function ClassCell({
onEdit(cls.id)} >
- {cls.style} + {cls.style} {cls.startTime}–{cls.endTime}
{cls.trainer}
@@ -468,7 +468,7 @@ function ScheduleGrid({ } return ( -
+

Расписание

{halls.length === 0 ? ( @@ -484,7 +484,7 @@ function ScheduleGrid({ className={`rounded-lg px-3 py-1.5 text-xs font-medium transition-all ${ selectedHall === hall ? "bg-gold/20 text-gold border border-gold/40" - : "bg-neutral-800 text-neutral-400 border border-white/10 hover:text-white" + : "bg-neutral-100 text-neutral-500 border border-neutral-200 hover:text-neutral-900 dark:bg-neutral-800 dark:text-neutral-400 dark:border-white/10 dark:hover:text-white" }`} > {hall} @@ -502,8 +502,8 @@ function ScheduleGrid({ {timeSlots.map((time) => { const cls = hallClasses[time]; return ( -
- {time} +
+ {time}
{cls ? ( { setCreatingTime(time); setEditingClassId(null); }} - className="w-full rounded-lg border border-dashed border-white/5 p-2 text-neutral-600 hover:text-gold hover:border-gold/20 transition-colors" + className="w-full rounded-lg border border-dashed border-neutral-200 p-2 text-neutral-400 hover:text-gold hover:border-gold/20 transition-colors dark:border-white/5 dark:text-neutral-600" > @@ -545,12 +545,12 @@ function ScheduleGrid({ {confirmAction && (
setConfirmAction(null)}>
-
e.stopPropagation()}> -

{confirmAction.message}

+
e.stopPropagation()}> +

{confirmAction.message}

diff --git a/src/app/globals.css b/src/app/globals.css index d321cfe..f357d6e 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -103,6 +103,10 @@ input[type="number"] { html { scrollbar-width: thin; + scrollbar-color: rgba(160, 160, 160, 0.5) #f5f5f5; +} + +html.dark { scrollbar-color: rgba(201, 169, 110, 0.3) var(--color-surface-dark); } @@ -111,14 +115,26 @@ html { } ::-webkit-scrollbar-track { + background: #f5f5f5; +} + +html.dark ::-webkit-scrollbar-track { background: var(--color-surface-dark); } ::-webkit-scrollbar-thumb { - background: rgba(201, 169, 110, 0.3); + background: rgba(160, 160, 160, 0.5); border-radius: 4px; } +html.dark ::-webkit-scrollbar-thumb { + background: rgba(201, 169, 110, 0.3); +} + ::-webkit-scrollbar-thumb:hover { + background: rgba(120, 120, 120, 0.6); +} + +html.dark ::-webkit-scrollbar-thumb:hover { background: rgba(201, 169, 110, 0.5); } diff --git a/src/app/styles/animations.css b/src/app/styles/animations.css index ae7d421..f672a33 100644 --- a/src/app/styles/animations.css +++ b/src/app/styles/animations.css @@ -136,12 +136,12 @@ .gradient-text { background: linear-gradient( 135deg, - #8a6f3e 0%, - #c9a96e 20%, - #8a6f3e 40%, - #c9a96e 60%, - #6b5530 80%, - #8a6f3e 100% + #c9a96e 0%, + #e2c97e 20%, + #d4b87a 40%, + #e2c97e 60%, + #c9a96e 80%, + #d4b87a 100% ); background-size: 200% 200%; -webkit-background-clip: text; @@ -150,7 +150,12 @@ animation: gradient-shift 6s ease-in-out infinite; } -/* Light mode gradient text */ +/* Auto-switch gradient text for light mode */ +html:not(.dark) .gradient-text { + background-image: linear-gradient(135deg, #a08050 0%, #c9a96e 25%, #8a6f3e 50%, #c9a96e 75%, #a08050 100%); +} + +/* Explicit light mode gradient text class (legacy) */ .gradient-text-light { background: linear-gradient(135deg, #171717 0%, #c9a96e 50%, #171717 100%); background-size: 200% 200%; @@ -172,7 +177,7 @@ inset: 0; border-radius: inherit; padding: 1px; - background: linear-gradient(135deg, rgba(201, 169, 110, 0.3), transparent 40%, transparent 60%, rgba(201, 169, 110, 0.15)); + background: linear-gradient(135deg, rgba(201, 169, 110, 0.5), transparent 40%, transparent 60%, rgba(201, 169, 110, 0.3)); mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); mask-composite: exclude; pointer-events: none; @@ -180,8 +185,16 @@ opacity: 0.5; } +:is(.dark) .animated-border::before { + background: linear-gradient(135deg, rgba(201, 169, 110, 0.3), transparent 40%, transparent 60%, rgba(201, 169, 110, 0.15)); +} + .animated-border:hover::before { opacity: 1; + background: linear-gradient(135deg, rgba(201, 169, 110, 0.7), transparent 40%, transparent 60%, rgba(201, 169, 110, 0.5)); +} + +:is(.dark) .animated-border:hover::before { background: linear-gradient(135deg, rgba(201, 169, 110, 0.6), transparent 40%, transparent 60%, rgba(201, 169, 110, 0.4)); } @@ -192,10 +205,14 @@ } .glow-hover:hover { - box-shadow: 0 0 30px rgba(201, 169, 110, 0.1), 0 0 60px rgba(201, 169, 110, 0.05); + box-shadow: 0 0 20px rgba(201, 169, 110, 0.25), 0 0 50px rgba(201, 169, 110, 0.12), 0 4px 16px rgba(0, 0, 0, 0.06); transform: translateY(-4px); } +:is(.dark) .glow-hover:hover { + box-shadow: 0 0 30px rgba(201, 169, 110, 0.1), 0 0 60px rgba(201, 169, 110, 0.05); +} + /* ===== Scroll Reveal ===== */ .reveal { @@ -345,6 +362,10 @@ .section-divider { height: 1px; + background: linear-gradient(90deg, transparent, rgba(201, 169, 110, 0.4), transparent); +} + +:is(.dark) .section-divider { background: linear-gradient(90deg, transparent, rgba(201, 169, 110, 0.15), transparent); } diff --git a/src/app/styles/components.css b/src/app/styles/components.css index 4c0389a..23593d6 100644 --- a/src/app/styles/components.css +++ b/src/app/styles/components.css @@ -6,7 +6,8 @@ @apply hover:bg-gold-light hover:shadow-[0_0_30px_rgba(201,169,110,0.35)]; @apply dark:bg-gold dark:text-black; @apply dark:hover:bg-gold-light dark:hover:shadow-[0_0_30px_rgba(201,169,110,0.35)]; - @apply focus:outline-none focus-visible:ring-2 focus-visible:ring-gold-light focus-visible:ring-offset-2 focus-visible:ring-offset-black; + @apply active:scale-[0.98] disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none; + @apply focus:outline-none focus-visible:ring-2 focus-visible:ring-gold-light focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-black; } /* ===== Scrollbar ===== */ diff --git a/src/app/styles/theme.css b/src/app/styles/theme.css index a61fdc5..11576c9 100644 --- a/src/app/styles/theme.css +++ b/src/app/styles/theme.css @@ -6,8 +6,8 @@ } .surface-muted { - @apply bg-neutral-100; - @apply dark:bg-[var(--color-surface-deep)]; + @apply bg-neutral-100 text-neutral-900; + @apply dark:bg-[var(--color-surface-deep)] dark:text-neutral-100; } .surface-glass { @@ -16,8 +16,8 @@ } .surface-card { - @apply bg-white/80 backdrop-blur-sm; - @apply dark:bg-neutral-900 dark:backdrop-blur-sm; + @apply bg-white shadow-sm backdrop-blur-sm; + @apply dark:bg-neutral-900 dark:shadow-none dark:backdrop-blur-sm; } /* ===== Borders ===== */ @@ -73,20 +73,25 @@ transform: translateX(-50%); width: min(600px, 100%); height: 400px; - background: radial-gradient(ellipse, rgba(201, 169, 110, 0.05), transparent 70%); + background: radial-gradient(ellipse, rgba(201, 169, 110, 0.15), transparent 70%); pointer-events: none; } +:is(.dark) .section-glow::before { + background: radial-gradient(ellipse, rgba(201, 169, 110, 0.05), transparent 70%); +} + /* ===== Glass Card ===== */ .glass-card { @apply rounded-2xl border backdrop-blur-sm transition-all duration-300; - @apply border-neutral-200/80 bg-white/90; - @apply dark:border-white/[0.06] dark:bg-white/[0.04]; + @apply border-neutral-200/80 bg-white/90 shadow-sm shadow-gold/[0.04]; + @apply dark:border-white/[0.06] dark:bg-white/[0.04] dark:shadow-none; } .glass-card:hover { - @apply dark:border-gold/15 dark:bg-white/[0.06]; + @apply border-gold/30 bg-white shadow-md shadow-gold/[0.08]; + @apply dark:border-gold/15 dark:bg-white/[0.06] dark:shadow-none; } /* ===== Photo Filter ===== */ @@ -99,10 +104,43 @@ filter: saturate(0.6) sepia(0.2) brightness(0.9) contrast(1.1); } +/* ===== Modal Surface ===== */ + +.modal-surface { + @apply bg-white dark:bg-neutral-950; +} + +/* ===== Theme Input ===== */ + +.theme-input { + @apply border-neutral-300 bg-neutral-50 text-neutral-900 placeholder-neutral-400; + @apply focus:border-gold/60 focus:bg-white; + @apply dark:border-white/[0.08] dark:bg-white/[0.04] dark:text-white dark:placeholder-neutral-500; + @apply dark:focus:border-gold/40 dark:focus:bg-white/[0.06]; +} + +/* ===== Admin Surface ===== */ + +.admin-surface { + @apply bg-white text-neutral-900 dark:bg-neutral-950 dark:text-white; +} + +.admin-sidebar { + @apply bg-neutral-100 border-neutral-200 dark:bg-neutral-900 dark:border-white/10; +} + +.admin-nav-item { + @apply text-neutral-500 hover:text-neutral-900 hover:bg-neutral-200/60 dark:text-neutral-400 dark:hover:text-white dark:hover:bg-white/5; +} + /* ===== Custom Scrollbar ===== */ .styled-scrollbar { scrollbar-width: thin; + scrollbar-color: rgba(160, 160, 160, 0.5) transparent; +} + +:is(.dark) .styled-scrollbar { scrollbar-color: rgba(201, 169, 110, 0.25) transparent; } @@ -116,10 +154,18 @@ } .styled-scrollbar::-webkit-scrollbar-thumb { - background: rgba(201, 169, 110, 0.25); + background: rgba(160, 160, 160, 0.5); border-radius: 4px; } +:is(.dark) .styled-scrollbar::-webkit-scrollbar-thumb { + background: rgba(201, 169, 110, 0.25); +} + .styled-scrollbar::-webkit-scrollbar-thumb:hover { + background: rgba(120, 120, 120, 0.6); +} + +:is(.dark) .styled-scrollbar::-webkit-scrollbar-thumb:hover { background: rgba(201, 169, 110, 0.4); } diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx index 603d06b..5e45518 100644 --- a/src/components/layout/Footer.tsx +++ b/src/components/layout/Footer.tsx @@ -8,10 +8,11 @@ export function Footer() {