From 52cce42cde4809be2a717d3367200443c8f682e4 Mon Sep 17 00:00:00 2001
From: Martin Dvorak
+
This deck isn't in this browser's library. It may have been deleted, or it was created in a different browser profile.
diff --git a/app/globals.css b/app/globals.css index 4efcd96..50cda71 100644 --- a/app/globals.css +++ b/app/globals.css @@ -17,17 +17,17 @@ --color-frost-900: #1d2740; --color-frost-950: #0e1526; - /* Accent: the icon's snowflake blue. ------------------------------------ */ - --color-accent-50: #eff7ff; - --color-accent-100: #dbecff; - --color-accent-200: #bcdcff; - --color-accent-300: #8fc6ff; - --color-accent-400: #59a8f7; - --color-accent-500: #3f95ea; - --color-accent-600: #2478d2; - --color-accent-700: #1d61ab; - --color-accent-800: #1c518b; - --color-accent-900: #1b4473; + /* Accent: glacier cyan to ice blue — focus rings, links, highlights. ---- */ + --color-accent-50: #ecf9ff; + --color-accent-100: #d6f1ff; + --color-accent-200: #b0e5ff; + --color-accent-300: #7dd4fc; + --color-accent-400: #38bdf8; + --color-accent-500: #0ea5e9; + --color-accent-600: #0284c7; + --color-accent-700: #0369a1; + --color-accent-800: #075985; + --color-accent-900: #0c4a6e; /* Aurora hues used only for the frosted backdrop wash. ----------------- */ --color-aurora-mint: #9fe3d0; @@ -81,104 +81,110 @@ --------------------------------------------------------------------------- */ :root { color-scheme: light; - --page: #f4f7fd; - --sidebar: #e9eff9; + --page: #eef5fd; + --sidebar: #ffffff; --surface: #ffffff; --surface-raised: #ffffff; - --surface-strong: #edf2fa; - --card-hover-bg: #f7faff; - --surface-sunken: rgb(14 21 40 / 0.05); - --hairline: #dce4f1; - --card-border: #d7e0ef; - --sidebar-divider: #d7e0ef; - --hairline-soft: rgb(14 21 40 / 0.07); + --surface-strong: rgb(255 255 255 / 0.72); /* nested glass: inputs, chips */ + --card-hover-bg: rgb(255 255 255 / 0.72); + --surface-sunken: rgb(12 32 62 / 0.06); + --hairline: #d5e4f4; + --card-border: var(--glass-border); + --sidebar-divider: rgb(255 255 255 / 0.75); + --hairline-soft: rgb(12 32 62 / 0.08); --glass-tint: rgb(255 255 255 / 0.55); - --text: #0e1526; /* 16.9:1 on #f4f7fd */ - --text-muted: #4d5c78; /* 6.3:1 */ - --text-subtle: #5a6a86; /* 5.1:1 */ - --hover: rgb(14 21 40 / 0.05); - --sidebar-top: #eef3fb; - --sidebar-bottom: #e5edf9; - --sidebar-edge: 0 1px 0 var(--sidebar-divider), 6px 0 18px -12px rgb(14 21 40 / 0.35); + --text: #0b1830; /* 16.6:1 on #eef5fd */ + --text-muted: #46586f; /* 6.6:1 on light glass */ + --text-subtle: #566a84; /* 5.0:1 on light glass */ + --hover: rgb(12 32 62 / 0.05); + --focus: #0ea5e9; + + /* Frosted ice glass — one shared recipe for every large surface. */ + --glass-bg: rgb(255 255 255 / 0.55); + --glass-border: rgb(255 255 255 / 0.8); + --glass-highlight: rgb(255 255 255 / 0.85); + --glass-shadow: 0 20px 50px -20px rgb(30 64 175 / 0.35); + --glass-solid-bg: rgb(255 255 255 / 0.8); /* question card: more opaque */ + --glass-fallback: rgb(247 251 255 / 0.97); /* no backdrop-filter */ + --nav-active-bg: rgb(255 255 255 / 0.75); + --nav-active-border: rgb(56 189 248 / 0.5); /* API-key gate: cool near-white stage with soft blue glows, tinted input. */ - --gate-base: #eef3fb; - --gate-glow-a: rgb(90 150 240 / 0.25); - --gate-glow-b: rgb(140 150 240 / 0.2); - --gate-card: #ffffff; - --gate-card-border: #dde5f2; - --gate-callout-bg: #eef3fa; - --gate-callout-border: #c9d5e8; - --field: #eef2f9; - --field-border: #dbe3f0; + --gate-card: rgb(255 255 255 / 0.62); + --gate-callout-bg: rgb(255 255 255 / 0.55); + --gate-callout-border: rgb(56 189 248 / 0.32); + --field: rgb(255 255 255 / 0.66); + --field-border: rgb(255 255 255 / 0.9); --shadow-gate: 0 28px 70px -36px rgb(14 21 40 / 0.3), 0 1px 2px rgb(14 21 40 / 0.05); /* Install sheet: light variant (dark is the default, see .dark). */ - --install-sheet: #ffffff; - --install-card: #f7f7f8; - --install-chip: #f2f2f2; - --install-border: #e0e0e0; + --install-sheet: rgb(255 255 255 / 0.6); + --install-card: rgb(255 255 255 / 0.7); + --install-chip: rgb(255 255 255 / 0.55); + --install-border: rgb(255 255 255 / 0.8); --shadow-card: 0 1px 2px rgb(14 21 40 / 0.06), 0 1px 3px rgb(14 21 40 / 0.04); --card-shadow: 0 1px 2px rgb(14 21 40 / 0.05), 0 5px 14px -6px rgb(14 21 40 / 0.12); - --shadow-card-hover: 0 12px 30px -12px rgb(14 21 40 / 0.2), 0 2px 6px rgb(14 21 40 / 0.07); + --shadow-card-hover: 0 24px 60px -24px rgb(30 64 175 / 0.45), 0 2px 6px rgb(30 64 175 / 0.12); } .dark { color-scheme: dark; - --page: #0e1424; - --sidebar: #0a0f1c; - --surface: #171f33; - --surface-raised: #1e2740; - --surface-strong: #202a42; - --card-hover-bg: #1e2740; + --page: #0a1120; + --sidebar: #0b1524; + --surface: #16233a; + --surface-raised: #1d2c47; + --surface-strong: rgb(255 255 255 / 0.08); + --card-hover-bg: rgb(255 255 255 / 0.1); --surface-sunken: rgb(255 255 255 / 0.07); --hairline: rgb(255 255 255 / 0.12); - --card-border: rgb(255 255 255 / 0.12); - --sidebar-divider: rgb(255 255 255 / 0.1); + --card-border: var(--glass-border); + --sidebar-divider: rgb(255 255 255 / 0.12); --hairline-soft: rgb(255 255 255 / 0.08); --glass-tint: rgb(255 255 255 / 0.06); - --text: #e9eefb; /* 15.4:1 on #0e1424 */ - --text-muted: #b7c3dc; /* 10.4:1 */ - --text-subtle: #94a4c2; /* 7.3:1 */ + --text: #e8f0ff; /* 16.3:1 on #0a1120 */ + --text-muted: #b3c4e0; /* 10.1:1 */ + --text-subtle: #90a4c6; /* 6.9:1 */ --hover: rgb(255 255 255 / 0.07); - --sidebar-top: #0b1120; - --sidebar-bottom: #080d18; - --sidebar-edge: 0 1px 0 var(--hairline), 6px 0 16px -14px rgb(0 0 0 / 0.75); + --focus: #38bdf8; + + /* Frosted ice glass — dark: barely-there tint, brighter rim. */ + --glass-bg: rgb(255 255 255 / 0.06); + --glass-border: rgb(255 255 255 / 0.12); + --glass-highlight: rgb(255 255 255 / 0.25); + --glass-shadow: 0 20px 50px -20px rgb(30 64 175 / 0.35); + --glass-solid-bg: rgb(255 255 255 / 0.1); + --glass-fallback: rgb(16 26 45 / 0.97); + --nav-active-bg: rgb(56 189 248 / 0.14); + --nav-active-border: rgb(56 189 248 / 0.4); /* API-key gate: navy stage with the icon's blue glows, raised input. */ - --gate-base: #0a0f1c; - --gate-glow-a: rgb(70 140 235 / 0.2); - --gate-glow-b: rgb(120 120 235 / 0.14); - --gate-card: #131a2c; - --gate-card-border: rgb(255 255 255 / 0.1); + --gate-card: rgb(255 255 255 / 0.06); --gate-callout-bg: rgb(255 255 255 / 0.06); - --gate-callout-border: rgb(255 255 255 / 0.2); - --field: #1b2438; - --field-border: rgb(255 255 255 / 0.1); + --gate-callout-border: rgb(56 189 248 / 0.3); + --field: rgb(255 255 255 / 0.07); + --field-border: rgb(255 255 255 / 0.14); --shadow-gate: 0 30px 80px -40px rgb(0 0 0 / 0.85), 0 1px 2px rgb(0 0 0 / 0.4); /* Install sheet: layered dark surfaces (default theme for the sheet). */ - --install-sheet: #1c1c1e; - --install-card: #2c2c2e; - --install-chip: #3a3a3c; - --install-border: rgb(255 255 255 / 0.08); + --install-sheet: rgb(255 255 255 / 0.06); + --install-card: rgb(255 255 255 / 0.07); + --install-chip: rgb(255 255 255 / 0.1); + --install-border: rgb(255 255 255 / 0.14); --shadow-card: 0 1px 2px rgb(0 0 0 / 0.25); --card-shadow: var(--shadow-card); - --shadow-card-hover: 0 10px 28px -16px rgb(0 0 0 / 0.55), 0 1px 2px rgb(0 0 0 / 0.3); + --shadow-card-hover: 0 24px 60px -24px rgb(30 64 175 / 0.55), 0 2px 8px rgb(0 0 0 / 0.3); } -/* Backdrop: the icon's navy base and blue wash, seen through frosted glass. */ +/* Ice scene tokens: the fixed layered background behind everything. */ :root { - --bd-sky: rgb(110 175 245 / 0.2); - --bd-lilac: rgb(145 145 240 / 0.14); - --bd-mint: rgb(110 210 230 / 0.12); - --bd-top: #f8fafe; - --bd-mid: #f2f6fd; - --bd-bottom: #ecf1fb; + --ice-base-top: #f4f9ff; + --ice-base-bottom: #e6f1fb; + --ice-blob-opacity: 0.18; + --ice-orb-opacity: 0.55; + --ice-grain-opacity: 0.05; } .dark { - --bd-sky: rgb(60 130 230 / 0.14); - --bd-lilac: rgb(110 115 235 / 0.1); - --bd-mint: rgb(70 180 215 / 0.08); - --bd-top: #111a2e; - --bd-mid: #0e1424; - --bd-bottom: #0a101d; + --ice-base-top: #070b16; + --ice-base-bottom: #0c1426; + --ice-blob-opacity: 0.4; + --ice-orb-opacity: 0.8; + --ice-grain-opacity: 0.045; } html, @@ -209,126 +215,196 @@ body { background: transparent; } -/* API-key gate stage: two big blurred blobs (top-right + bottom-left) over a - near-black (dark) or near-white (light) base. Real blurred elements read as - atmosphere; a flat radial gradient was too faint. Both blobs are decorative, - non-interactive and sit behind the card (z-index 0 vs the card's z-10). */ -.gate-bg { - background: var(--gate-base); -} -.gate-bg::before, -.gate-bg::after { - content: ""; - position: absolute; - z-index: 0; - width: 48rem; - height: 48rem; - border-radius: 9999px; - filter: blur(120px); - pointer-events: none; -} -.gate-bg::before { - top: -20rem; - right: -16rem; - background: var(--gate-glow-a); -} -.gate-bg::after { - bottom: -20rem; - left: -16rem; - background: var(--gate-glow-b); -} +/* The key gate rides on the shared ice scene (see .ice-scene). */ -.app-backdrop { +/* The fixed ice scene: navy/pale base, drifting glacier blobs, glossy ice + orbs and a whisper of grain. Purely decorative. */ +.ice-scene { position: fixed; inset: 0; - z-index: -10; + z-index: -1; overflow: hidden; pointer-events: none; - background: - radial-gradient(135vw 110vh at 4% -30%, var(--bd-sky) 0%, transparent 72%), - radial-gradient(125vw 105vh at 104% -18%, var(--bd-lilac) 0%, transparent 70%), - radial-gradient(135vw 105vh at 38% 128%, var(--bd-mint) 0%, transparent 72%), - linear-gradient(180deg, var(--bd-top) 0%, var(--bd-mid) 50%, var(--bd-bottom) 100%); + background: linear-gradient(180deg, var(--ice-base-top) 0%, var(--ice-base-bottom) 100%); } -/* Two slow, very soft blobs for a little life behind the glass. */ -.app-backdrop::before, -.app-backdrop::after { - content: ""; +.ice-blob { position: absolute; border-radius: 50%; - filter: blur(110px); - opacity: 0.22; + opacity: var(--ice-blob-opacity); + will-change: transform; + /* No filter: blur() here on purpose. A radial gradient fading to + transparent is already a soft blob, and blurring a huge layer that is + also being animated forces a re-rasterisation every frame (measured + 150ms vs 33ms per frame in a software renderer). The falloff stops below + keep the same softness. */ } -.app-backdrop::before { - width: 52vw; - height: 52vw; - min-width: 24rem; - min-height: 24rem; - top: -26vh; - left: -18vw; - background: radial-gradient(circle, var(--bd-sky), transparent 66%); - animation: float 16s ease-in-out infinite; +.ice-blob-a { + width: 52vw; height: 52vw; min-width: 24rem; min-height: 24rem; + top: -14vh; left: -10vw; + background: radial-gradient(circle, rgb(56 189 248 / 0.9) 0%, rgb(56 189 248 / 0.45) 38%, transparent 72%); + animation: ice-drift-a 44s ease-in-out infinite; } -.app-backdrop::after { - width: 46vw; - height: 46vw; - min-width: 22rem; - min-height: 22rem; - bottom: -28vh; - right: -14vw; - background: radial-gradient(circle, var(--bd-mint), transparent 66%); - animation: float 20s ease-in-out infinite reverse; +.ice-blob-b { + width: 46vw; height: 46vw; min-width: 22rem; min-height: 22rem; + top: 4vh; right: -12vw; + background: radial-gradient(circle, rgb(96 165 250 / 0.9) 0%, rgb(96 165 250 / 0.45) 38%, transparent 72%); + animation: ice-drift-b 52s ease-in-out infinite; } -.dark .app-backdrop::before, -.dark .app-backdrop::after { - opacity: 0.3; +.ice-blob-c { + width: 48vw; height: 48vw; min-width: 22rem; min-height: 22rem; + bottom: -16vh; left: 14vw; + background: radial-gradient(circle, rgb(129 140 248 / 0.9) 0%, rgb(129 140 248 / 0.42) 38%, transparent 74%); + animation: ice-drift-c 60s ease-in-out infinite; +} +.ice-blob-d { + width: 34vw; height: 34vw; min-width: 16rem; min-height: 16rem; + bottom: 2vh; right: 6vw; + background: radial-gradient(circle, rgb(56 189 248 / 0.85) 0%, rgb(56 189 248 / 0.4) 38%, transparent 74%); + animation: ice-drift-a 48s ease-in-out infinite reverse; +} + +@keyframes ice-drift-a { + 0%, 100% { transform: translate3d(0, 0, 0) scale(1); } + 50% { transform: translate3d(4vw, 3vh, 0) scale(1.08); } +} +@keyframes ice-drift-b { + 0%, 100% { transform: translate3d(0, 0, 0) scale(1.05); } + 50% { transform: translate3d(-5vw, 4vh, 0) scale(1); } +} +@keyframes ice-drift-c { + 0%, 100% { transform: translate3d(0, 0, 0) scale(1); } + 50% { transform: translate3d(3vw, -4vh, 0) scale(1.1); } +} + +/* Glossy ice orbs, placed so the glass panels blur them. */ +.ice-orb { + position: absolute; + border-radius: 50%; + opacity: var(--ice-orb-opacity); + background: radial-gradient( + circle at 30% 25%, + rgb(255 255 255 / 0.95) 0%, + rgb(191 227 255 / 0.55) 18%, + rgb(96 165 250 / 0.35) 48%, + rgb(37 99 235 / 0.45) 100% + ); + box-shadow: 0 18px 40px -18px rgb(30 64 175 / 0.55), inset 0 -8px 18px -10px rgb(30 64 175 / 0.6); +} +.ice-orb-1 { width: 190px; height: 190px; top: 14%; left: 12%; } +.ice-orb-2 { width: 84px; height: 84px; top: 58%; left: 4%; } +.ice-orb-3 { width: 220px; height: 220px; bottom: -70px; right: 10%; } +.ice-orb-4 { width: 52px; height: 52px; top: 24%; right: 28%; } +.ice-orb-5 { width: 120px; height: 120px; bottom: 16%; left: 38%; } + +/* Frosted grain over the whole scene. */ +.ice-grain { + position: absolute; + inset: 0; + opacity: var(--ice-grain-opacity); + mix-blend-mode: overlay; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E"); +} + +@media (prefers-reduced-motion: reduce) { + .ice-blob { animation: none; } +} + +/* Phones: fewer, softer blobs so the drift animation stays cheap. Unlayered + on purpose — layered rules would lose to the .ice-blob declarations above. */ +@media (max-width: 1023px) { + .ice-blob-c, + .ice-blob-d, + .ice-orb-4, + .ice-orb-5 { + display: none; + } + .ice-orb-1 { width: 120px; height: 120px; } + .ice-orb-3 { width: 150px; height: 150px; } } @layer components { -/* Glass primitives ------------------------------------------------------ */ -.glass-panel { - @apply backdrop-blur-xl backdrop-saturate-150; - background: color-mix(in oklab, var(--surface) 82%, transparent); - border: 1px solid var(--hairline); - box-shadow: var(--shadow-card); +/* Frosted ice glass ------------------------------------------------------ + One shared recipe for the large surfaces only: translucent tint, 24px + blur, a bright rim, an inner top highlight and a blue-tinted shadow. + Nested glass (chips, inputs) never adds a second blur layer. Tailwind's + backdrop utilities emit the -webkit- prefix too (hand-written + backdrop-filter gets minified to -webkit- only, which paints nothing). */ +.glass-panel, +.card, +.sidebar-surface, +.study-chrome, +.tabbar, +.glass, +.gate-card { + @apply backdrop-blur-xl backdrop-saturate-[1.4]; + background: var(--glass-bg); + box-shadow: var(--glass-shadow), inset 0 1px 0 var(--glass-highlight); } +.glass-panel { + border: 1px solid var(--glass-border); + border-radius: 1.5rem; +} + +/* Nested glass: slightly more opaque than its parent, no extra blur. */ .glass-chip { - @apply backdrop-blur-md; background: var(--surface-sunken); - border: 1px solid transparent; + border: 1px solid var(--glass-border); +} + +/* More opaque glass for the study question card, where text must stay crisp. */ +.glass-solid { + background: var(--glass-solid-bg); } .glass-chip:hover { background: var(--hover); } .glass { - /* Tailwind emits both the prefixed and standard properties; hand-written - backdrop-filter gets minified down to -webkit- only, which paints nothing. */ - @apply backdrop-blur-xl backdrop-saturate-150; - background: color-mix(in oklab, var(--surface) 70%, transparent); - border-bottom: 1px solid var(--hairline); + border-bottom: 1px solid var(--glass-border); } /* Study-feed chrome (progress header + bottom pager): the same neutral surface as the cards, with a neutral border and shadow. */ .study-chrome { - @apply backdrop-blur-xl backdrop-saturate-150; - background: color-mix(in oklab, var(--surface) 88%, transparent); - border: 1px solid var(--hairline); - box-shadow: var(--shadow-card); -} -.dark .study-chrome { - border-color: var(--hairline-soft); + border: 1px solid var(--glass-border); } .card { - border-radius: 1.125rem; /* 18px */ - @apply backdrop-blur-xl backdrop-saturate-150; - background: color-mix(in oklab, var(--surface) 78%, transparent); - border: 1px solid var(--card-border); - box-shadow: var(--card-shadow); + border-radius: 1.5rem; /* 24px */ + border: 1px solid var(--glass-border); +} + +.gate-card { + border: 1px solid var(--glass-border); + border-radius: 1.75rem; /* 28px */ +} + +/* Blur is expensive on phones: halve it, and thin out the ice scene. */ +@media (max-width: 1023px) { + .glass-panel, + .card, + .sidebar-surface, + .study-chrome, + .tabbar, + .glass, + .gate-card { + @apply backdrop-blur-lg; + } +} + +/* No backdrop-filter support: fall back to a more opaque surface. */ +@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { + .glass-panel, + .card, + .sidebar-surface, + .study-chrome, + .tabbar, + .glass, + .gate-card { + background: var(--glass-fallback); + } } /* Opt-in hover lift for interactive cards (deck cards). Pointer devices only, @@ -339,10 +415,8 @@ body { @media (hover: hover) and (pointer: fine) { .card-hover:hover { background: var(--card-hover-bg); - border-color: color-mix(in oklab, var(--hairline) 45%, var(--text) 20%); + border-color: var(--nav-active-border); box-shadow: var(--shadow-card-hover); - } - :root:not(.dark) .card-hover:hover { transform: translateY(-2px); } } @@ -386,14 +460,19 @@ body { } .segmented > button[aria-pressed="true"], .segmented > button[aria-checked="true"] { - background: var(--surface-strong); - color: var(--text); - box-shadow: 0 1px 2px rgb(13 13 13 / 0.12); + background-image: linear-gradient(135deg, #38bdf8 0%, #60a5fa 100%); + color: #06111f; + box-shadow: 0 6px 16px -8px rgb(56 189 248 / 0.7); } .dark .segmented > button[aria-pressed="true"], .dark .segmented > button[aria-checked="true"] { - background: var(--surface-raised); - color: #fff; + background-image: linear-gradient(135deg, #38bdf8 0%, #60a5fa 100%); + color: #06111f; +} + +/* Ice gradient for progress bars and selected states. */ +.grad-ice { + background-image: linear-gradient(90deg, #38bdf8 0%, #60a5fa 100%); } /* Sheet grabber --------------------------------------------------------- */ @@ -406,9 +485,7 @@ body { } .tabbar { - @apply backdrop-blur-xl; - background: color-mix(in oklab, var(--page) 88%, transparent); - border-top: 1px solid var(--hairline); + border-top: 1px solid var(--glass-border); } /* Soft tinted chip for activity types (a hint of colour, not a rainbow) -- */ @@ -424,15 +501,10 @@ body { .dark .tint-rose { background: linear-gradient(140deg, rgb(230 130 150 / 0.26), rgb(230 130 150 / 0.1)); color: #fbd5dd } /* Sidebar surface: frosted navy glass with a real edge. */ +/* Sidebar surface: one tall inset glass panel. */ .sidebar-surface { - @apply backdrop-blur-xl backdrop-saturate-150; - background: linear-gradient( - 180deg, - color-mix(in oklab, var(--sidebar-top) 84%, transparent), - color-mix(in oklab, var(--sidebar-bottom) 84%, transparent) - ); - border-right: 1px solid var(--sidebar-divider); - box-shadow: var(--sidebar-edge); + border: 1px solid var(--glass-border); + border-radius: 1.5rem; } /* Sidebar nav rows */ @@ -454,8 +526,9 @@ body { } .nav-row[aria-current="page"], .nav-row[aria-pressed="true"] { - background: var(--surface-sunken); + background: var(--nav-active-bg); color: var(--text); + box-shadow: inset 0 0 0 1px var(--nav-active-border), inset 0 1px 0 var(--glass-highlight); } /* Shared CTA language: the icon's blue, with a deeper fill in light mode. */ @@ -464,35 +537,44 @@ body { align-items: center; justify-content: center; gap: 0.5rem; - border-radius: 999px; - background: #1d61ab; + border-radius: 14px; + background-image: linear-gradient(135deg, #1d4ed8 0%, #1a5fd0 55%, #0ea5e9 100%); color: #ffffff; font-weight: 500; - box-shadow: 0 1px 2px rgb(14 21 40 / 0.22); - transition: background-color 160ms ease, transform 160ms ease, box-shadow 160ms ease; + box-shadow: 0 10px 24px -12px rgb(14 165 233 / 0.7); + transition: box-shadow 180ms ease, filter 180ms ease, transform 160ms ease; +} +.btn-primary:hover { + filter: brightness(1.07); + box-shadow: 0 14px 32px -12px rgb(56 189 248 / 0.85); } -.btn-primary:hover { background: #2478d2; } .btn-primary:active { transform: scale(0.985); } .dark .btn-primary { - background: #59a8f7; - color: #081020; - box-shadow: none; + background-image: linear-gradient(135deg, #ffffff 0%, #bfe3ff 100%); + color: #0b1220; + box-shadow: 0 10px 26px -14px rgb(56 189 248 / 0.6); +} +.dark .btn-primary:hover { + filter: brightness(1.03); + box-shadow: 0 14px 34px -14px rgb(56 189 248 / 0.95); } -.dark .btn-primary:hover { background: #7bbaff; } .btn-secondary { display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem; - border-radius: 999px; - background: transparent; + border-radius: 14px; + background: var(--surface-strong); color: var(--text); - border: 1px solid var(--hairline); + border: 1px solid var(--glass-border); font-weight: 500; transition: background-color 160ms ease, border-color 160ms ease, transform 160ms ease; } -.btn-secondary:hover { background: var(--hover); border-color: color-mix(in oklab, var(--hairline) 70%, var(--text) 12%); } +.btn-secondary:hover { + background: var(--hover); + border-color: var(--nav-active-border); +} .btn-secondary:active { transform: scale(0.985); } .btn-ghost { @@ -500,7 +582,7 @@ body { align-items: center; justify-content: center; gap: 0.5rem; - border-radius: 0.75rem; + border-radius: 14px; background: transparent; color: var(--text-muted); font-weight: 500; diff --git a/app/layout.tsx b/app/layout.tsx index 058afb7..978af14 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,5 +1,6 @@ import type { Metadata, Viewport } from "next"; +import { IceBackdrop } from "@/components/IceBackdrop"; import { InstallSheet } from "@/components/InstallSheet"; import { ServiceWorkerRegistrar } from "@/components/ServiceWorkerRegistrar"; import "./globals.css"; @@ -74,7 +75,7 @@ export default function RootLayout({ children }: { children: React.ReactNode }) - +@@ -363,12 +363,12 @@ function HelpDialog({ open, onClose }: { open: boolean; onClose: () => void }) { exit={reduceMotion ? { opacity: 0 } : { y: 24, opacity: 0 }} transition={{ type: "spring", stiffness: 340, damping: 32 }} onClick={(event) => event.stopPropagation()} - className="thin-scroll max-h-[90dvh] w-full max-w-[480px] overflow-y-auto rounded-t-[24px] border border-[var(--gate-card-border)] bg-[var(--gate-card)] p-6 pb-safe shadow-[var(--shadow-gate)] sm:rounded-[24px] sm:pb-6" + className="gate-card thin-scroll max-h-[90dvh] w-full max-w-[480px] overflow-y-auto p-6 pb-safe sm:pb-6" >
diff --git a/components/SettingsDialog.tsx b/components/SettingsDialog.tsx index df2c6e9..17365a0 100644 --- a/components/SettingsDialog.tsx +++ b/components/SettingsDialog.tsx @@ -65,10 +65,10 @@ export function SettingsDialog({ open, onClose }: { open: boolean; onClose: () =
+
Models come from .env.local on
the server; your API key stays in this browser.