Update padding in Parallax component for improved layout consistency
Some checks failed
Deploy / deploy (push) Has been cancelled
Some checks failed
Deploy / deploy (push) Has been cancelled
This commit is contained in:
BIN
data/app.db
BIN
data/app.db
Binary file not shown.
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { getSession } from "@/lib/session";
|
import { getSession } from "@/lib/session";
|
||||||
|
import { PixelCorner } from "@/components/pixel-decor";
|
||||||
import { GirisForm } from "./giris-form";
|
import { GirisForm } from "./giris-form";
|
||||||
import { GirisBaslik } from "./giris-baslik";
|
import { GirisBaslik } from "./giris-baslik";
|
||||||
|
|
||||||
@@ -27,24 +28,35 @@ export default async function GirisPage({
|
|||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen flex-col bg-slate-50 text-slate-900">
|
<div className="flex min-h-screen flex-col bg-slate-50 text-slate-900">
|
||||||
<main className="mx-auto flex w-full max-w-md flex-1 flex-col items-stretch justify-center px-4 py-16">
|
<main className="mx-auto flex w-full max-w-md flex-1 flex-col items-stretch justify-center px-4 py-16">
|
||||||
<div className="rounded-2xl border border-slate-200 bg-white p-8 shadow-sm">
|
<div className="relative">
|
||||||
<GirisBaslik funnel={funnel} />
|
<div className="relative rounded-2xl border border-slate-200 bg-white p-8 shadow-sm">
|
||||||
<p className="mt-2 text-sm text-slate-600">
|
<GirisBaslik funnel={funnel} />
|
||||||
{funnel ? (
|
<p className="mt-2 text-sm text-slate-600">
|
||||||
<>
|
{funnel ? (
|
||||||
Seçimlerin hazır — giriş yap,{" "}
|
<>
|
||||||
<span className="font-semibold">5 deneme kredinle</span> listen
|
Seçimlerin hazır — giriş yap,{" "}
|
||||||
hemen üretilsin.
|
<span className="font-semibold">5 deneme kredinle</span> listen
|
||||||
</>
|
hemen üretilsin.
|
||||||
) : (
|
</>
|
||||||
<>
|
) : (
|
||||||
Kayıt olduğunda{" "}
|
<>
|
||||||
<span className="font-semibold">5 deneme kredisi</span> hesabına
|
Kayıt olduğunda{" "}
|
||||||
tanımlanır — AI danışmana ücretsiz soru sorabilirsin.
|
<span className="font-semibold">5 deneme kredisi</span>{" "}
|
||||||
</>
|
hesabına tanımlanır — AI danışmana ücretsiz soru sorabilirsin.
|
||||||
)}
|
</>
|
||||||
</p>
|
)}
|
||||||
<GirisForm callbackURL={callbackURL} />
|
</p>
|
||||||
|
<GirisForm callbackURL={callbackURL} />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
aria-hidden
|
||||||
|
className="pointer-events-none absolute inset-0 z-10 text-primary"
|
||||||
|
>
|
||||||
|
<PixelCorner className="absolute -right-4 -top-4 size-9 [&_rect]:[fill-opacity:0.65]" />
|
||||||
|
<PixelCorner className="absolute -bottom-4 -right-4 size-9 rotate-90 [&_rect]:[fill-opacity:0.65]" />
|
||||||
|
<PixelCorner className="absolute -bottom-4 -left-4 size-9 rotate-180 [&_rect]:[fill-opacity:0.65]" />
|
||||||
|
<PixelCorner className="absolute -left-4 -top-4 size-9 -rotate-90 [&_rect]:[fill-opacity:0.65]" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-6 text-center text-xs leading-relaxed text-slate-500">
|
<p className="mt-6 text-center text-xs leading-relaxed text-slate-500">
|
||||||
Giriş yaparak verilerinin tercih önerileri üretmek amacıyla{" "}
|
Giriş yaparak verilerinin tercih önerileri üretmek amacıyla{" "}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Parallax } from "@/components/parallax";
|
import { Parallax } from "@/components/parallax";
|
||||||
import { ScrollFlow } from "@/components/scroll-flow";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Gizlilik ve KVKK — KolayTercih",
|
title: "Gizlilik ve KVKK — KolayTercih",
|
||||||
@@ -10,8 +9,7 @@ export const metadata: Metadata = {
|
|||||||
// NOT: MVP taslağıdır; yayına almadan önce hukuki gözden geçirme gerekir.
|
// NOT: MVP taslağıdır; yayına almadan önce hukuki gözden geçirme gerekir.
|
||||||
export default function GizlilikPage() {
|
export default function GizlilikPage() {
|
||||||
return (
|
return (
|
||||||
<ScrollFlow>
|
<main className="mx-auto w-full max-w-2xl px-4 py-16">
|
||||||
<main className="mx-auto w-full max-w-2xl px-4 py-16">
|
|
||||||
<Parallax fromStart strength={10}>
|
<Parallax fromStart strength={10}>
|
||||||
<h1 className="font-heading text-3xl font-bold">
|
<h1 className="font-heading text-3xl font-bold">
|
||||||
Gizlilik ve Kişisel Verilerin Korunması
|
Gizlilik ve Kişisel Verilerin Korunması
|
||||||
@@ -73,7 +71,6 @@ export default function GizlilikPage() {
|
|||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
</Parallax>
|
</Parallax>
|
||||||
</main>
|
</main>
|
||||||
</ScrollFlow>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Mail } from "lucide-react";
|
import { Mail } from "lucide-react";
|
||||||
import { Parallax } from "@/components/parallax";
|
import { Parallax } from "@/components/parallax";
|
||||||
import { ScrollFlow } from "@/components/scroll-flow";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "İletişim — KolayTercih",
|
title: "İletişim — KolayTercih",
|
||||||
@@ -9,8 +8,7 @@ export const metadata: Metadata = {
|
|||||||
|
|
||||||
export default function IletisimPage() {
|
export default function IletisimPage() {
|
||||||
return (
|
return (
|
||||||
<ScrollFlow>
|
<main className="mx-auto w-full max-w-2xl px-4 py-16">
|
||||||
<main className="mx-auto w-full max-w-2xl px-4 py-16">
|
|
||||||
<Parallax fromStart strength={10}>
|
<Parallax fromStart strength={10}>
|
||||||
<h1 className="font-heading text-3xl font-bold">İletişim</h1>
|
<h1 className="font-heading text-3xl font-bold">İletişim</h1>
|
||||||
</Parallax>
|
</Parallax>
|
||||||
@@ -30,7 +28,6 @@ export default function IletisimPage() {
|
|||||||
hesap adresini eklersen daha hızlı yardımcı olabiliriz. Kart bilgini
|
hesap adresini eklersen daha hızlı yardımcı olabiliriz. Kart bilgini
|
||||||
asla e-postayla paylaşma.
|
asla e-postayla paylaşma.
|
||||||
</p>
|
</p>
|
||||||
</main>
|
</main>
|
||||||
</ScrollFlow>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Parallax } from "@/components/parallax";
|
import { Parallax } from "@/components/parallax";
|
||||||
import { ScrollFlow } from "@/components/scroll-flow";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Kullanım ve Satış Koşulları — KolayTercih",
|
title: "Kullanım ve Satış Koşulları — KolayTercih",
|
||||||
@@ -10,8 +9,7 @@ export const metadata: Metadata = {
|
|||||||
// NOT: MVP taslağıdır; yayına almadan önce hukuki gözden geçirme gerekir.
|
// NOT: MVP taslağıdır; yayına almadan önce hukuki gözden geçirme gerekir.
|
||||||
export default function KosullarPage() {
|
export default function KosullarPage() {
|
||||||
return (
|
return (
|
||||||
<ScrollFlow>
|
<main className="mx-auto w-full max-w-2xl px-4 py-16">
|
||||||
<main className="mx-auto w-full max-w-2xl px-4 py-16">
|
|
||||||
<Parallax fromStart strength={10}>
|
<Parallax fromStart strength={10}>
|
||||||
<h1 className="font-heading text-3xl font-bold">
|
<h1 className="font-heading text-3xl font-bold">
|
||||||
Kullanım ve Satış Koşulları
|
Kullanım ve Satış Koşulları
|
||||||
@@ -80,7 +78,6 @@ export default function KosullarPage() {
|
|||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
</Parallax>
|
</Parallax>
|
||||||
</main>
|
</main>
|
||||||
</ScrollFlow>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import { ListeCekmecesi } from "@/components/manuel-liste/liste-cekmecesi";
|
|||||||
import { HeaderGate } from "@/components/header-gate";
|
import { HeaderGate } from "@/components/header-gate";
|
||||||
import { ProgressiveBlur } from "@/components/ui/skiper-ui/skiper41";
|
import { ProgressiveBlur } from "@/components/ui/skiper-ui/skiper41";
|
||||||
import { DevPanel } from "@/components/dev/dev-panel";
|
import { DevPanel } from "@/components/dev/dev-panel";
|
||||||
|
import { ScrollFlow } from "@/components/scroll-flow";
|
||||||
|
import { PagePixelLayer } from "@/components/page-pixel-layer";
|
||||||
|
import { ConsoleAsr } from "@/components/console-asr";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
|
||||||
const calSans = Cal_Sans({
|
const calSans = Cal_Sans({
|
||||||
@@ -54,6 +57,8 @@ export default function RootLayout({
|
|||||||
className={`${calSans.variable} ${clarityCity.variable} ${geistMono.variable} ${bricolage.variable} h-full antialiased`}
|
className={`${calSans.variable} ${clarityCity.variable} ${geistMono.variable} ${bricolage.variable} h-full antialiased`}
|
||||||
>
|
>
|
||||||
<body className="min-h-full flex flex-col bg-slate-50">
|
<body className="min-h-full flex flex-col bg-slate-50">
|
||||||
|
<ConsoleAsr />
|
||||||
|
<PagePixelLayer />
|
||||||
{/* Site geneli progressive blur — üst ve alt */}
|
{/* Site geneli progressive blur — üst ve alt */}
|
||||||
<div className="pointer-events-none fixed inset-x-0 top-0 z-40 h-28">
|
<div className="pointer-events-none fixed inset-x-0 top-0 z-40 h-28">
|
||||||
<ProgressiveBlur
|
<ProgressiveBlur
|
||||||
@@ -74,7 +79,9 @@ export default function RootLayout({
|
|||||||
<HeaderGate>
|
<HeaderGate>
|
||||||
<SiteHeader />
|
<SiteHeader />
|
||||||
</HeaderGate>
|
</HeaderGate>
|
||||||
{children}
|
<ScrollFlow className="flex min-h-0 flex-1 flex-col">
|
||||||
|
{children}
|
||||||
|
</ScrollFlow>
|
||||||
{/* Manuel 24'lük liste çekmecesi + "+" uçuş katmanı (navbar'dan açılır) */}
|
{/* Manuel 24'lük liste çekmecesi + "+" uçuş katmanı (navbar'dan açılır) */}
|
||||||
<ListeCekmecesi />
|
<ListeCekmecesi />
|
||||||
<Toaster position="top-center" />
|
<Toaster position="top-center" />
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import type { RaporParams } from "@/lib/rapor-havuzu";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { RaporListesi } from "@/components/rapor-listesi";
|
import { RaporListesi } from "@/components/rapor-listesi";
|
||||||
|
import { ViewportPortal } from "@/components/viewport-portal";
|
||||||
import { raporMaskele, ACIK_SATIR } from "@/lib/rapor-maske";
|
import { raporMaskele, ACIK_SATIR } from "@/lib/rapor-maske";
|
||||||
import { SohbetClient } from "./sohbet-client";
|
import { SohbetClient } from "./sohbet-client";
|
||||||
import { RevizyonKutusu } from "./revizyon-kutusu";
|
import { RevizyonKutusu } from "./revizyon-kutusu";
|
||||||
@@ -322,13 +323,15 @@ export default async function ListemPage({
|
|||||||
|
|
||||||
{/* Mobilde sohbet sağ rayla birlikte en alta düşer; yüzen kısayol
|
{/* Mobilde sohbet sağ rayla birlikte en alta düşer; yüzen kısayol
|
||||||
kullanıcıyı 24 satırı kaydırmadan danışmana ulaştırır. */}
|
kullanıcıyı 24 satırı kaydırmadan danışmana ulaştırır. */}
|
||||||
<a
|
<ViewportPortal>
|
||||||
href="#sohbet"
|
<a
|
||||||
className="fixed bottom-5 right-4 z-40 inline-flex h-11 items-center gap-2 rounded-full bg-primary px-4 text-sm font-semibold text-white shadow-lg transition-transform duration-200 active:scale-[0.97] lg:hidden print:hidden"
|
href="#sohbet"
|
||||||
>
|
className="fixed bottom-5 right-4 z-40 inline-flex h-11 items-center gap-2 rounded-full bg-primary px-4 text-sm font-semibold text-white shadow-lg transition-transform duration-200 active:scale-[0.97] lg:hidden print:hidden"
|
||||||
<MessageCircleQuestion className="size-4" aria-hidden />
|
>
|
||||||
Danışmana sor
|
<MessageCircleQuestion className="size-4" aria-hidden />
|
||||||
</a>
|
Danışmana sor
|
||||||
|
</a>
|
||||||
|
</ViewportPortal>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { HeroFocusButton } from "@/components/hero-focus-button";
|
||||||
import { HeroForm } from "@/components/hero-form";
|
import { HeroForm } from "@/components/hero-form";
|
||||||
import { Parallax } from "@/components/parallax";
|
import { Parallax } from "@/components/parallax";
|
||||||
import { ScrollFlow } from "@/components/scroll-flow";
|
|
||||||
import { Reveal } from "@/components/landing/reveal";
|
import { Reveal } from "@/components/landing/reveal";
|
||||||
import { TurkeyPixelMap } from "@/components/turkey-pixel-map";
|
import { TurkeyPixelMap } from "@/components/turkey-pixel-map";
|
||||||
import {
|
import {
|
||||||
@@ -24,7 +24,6 @@ import {
|
|||||||
} from "@/components/pixel-decor";
|
} from "@/components/pixel-decor";
|
||||||
import { URUNLER } from "@/lib/credits";
|
import { URUNLER } from "@/lib/credits";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import {
|
import {
|
||||||
Accordion,
|
Accordion,
|
||||||
AccordionContent,
|
AccordionContent,
|
||||||
@@ -166,8 +165,10 @@ function ComparisonCell({ value }: { value: boolean | string }) {
|
|||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className="bg-slate-50 text-slate-900">
|
<div
|
||||||
<ScrollFlow className="flex min-h-screen flex-col">
|
id="top"
|
||||||
|
className="flex min-h-screen flex-col bg-slate-50 text-slate-900"
|
||||||
|
>
|
||||||
<main className="flex-1">
|
<main className="flex-1">
|
||||||
{/* Hero */}
|
{/* Hero */}
|
||||||
<section className="relative isolate overflow-hidden">
|
<section className="relative isolate overflow-hidden">
|
||||||
@@ -193,7 +194,7 @@ export default function Home() {
|
|||||||
<Parallax
|
<Parallax
|
||||||
fromStart
|
fromStart
|
||||||
strength={14}
|
strength={14}
|
||||||
className="mx-auto flex min-h-[calc(100svh-5.5rem)] max-w-6xl flex-col items-center justify-center px-4 pb-44 pt-4 text-center"
|
className="mx-auto flex min-h-[calc(100svh-5.5rem)] max-w-6xl flex-col items-center justify-center px-4 py-4 text-center sm:pb-44 sm:pt-4"
|
||||||
>
|
>
|
||||||
<Badge
|
<Badge
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
@@ -201,7 +202,7 @@ export default function Home() {
|
|||||||
>
|
>
|
||||||
2026 YKS tercih dönemi açık
|
2026 YKS tercih dönemi açık
|
||||||
</Badge>
|
</Badge>
|
||||||
<h1 className="max-w-3xl font-heading text-4xl font-bold tracking-[0.015em] sm:text-6xl">
|
<h1 className="max-w-3xl font-heading text-7xl font-bold tracking-[0.015em] sm:text-6xl">
|
||||||
Dört yıllık geleceğini <br className="hidden sm:block" />
|
Dört yıllık geleceğini <br className="hidden sm:block" />
|
||||||
<span className="font-bricolage italic tracking-tighter font-bold text-primary">
|
<span className="font-bricolage italic tracking-tighter font-bold text-primary">
|
||||||
48 saatlik panikle
|
48 saatlik panikle
|
||||||
@@ -481,7 +482,7 @@ export default function Home() {
|
|||||||
value={faq.question}
|
value={faq.question}
|
||||||
className="last:border-b-0"
|
className="last:border-b-0"
|
||||||
>
|
>
|
||||||
<AccordionTrigger className="cursor-pointer text-left font-heading text-base font-semibold">
|
<AccordionTrigger className="cursor-pointer text-left font-sans text-base font-semibold">
|
||||||
{faq.question}
|
{faq.question}
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent className="text-base leading-relaxed text-slate-600">
|
<AccordionContent className="text-base leading-relaxed text-slate-600">
|
||||||
@@ -522,13 +523,7 @@ export default function Home() {
|
|||||||
Sıralamanı gir, yapay zekâ danışmanın veriyle konuşsun.
|
Sıralamanı gir, yapay zekâ danışmanın veriyle konuşsun.
|
||||||
Tercih dönemi bitmeden yerini al.
|
Tercih dönemi bitmeden yerini al.
|
||||||
</p>
|
</p>
|
||||||
<Button
|
<HeroFocusButton />
|
||||||
asChild
|
|
||||||
size="lg"
|
|
||||||
className="mt-8 h-12 cursor-pointer bg-orange-500 px-8 text-white transition-colors duration-200 hover:bg-orange-600"
|
|
||||||
>
|
|
||||||
<a href="#hero-form">Ücretsiz dene</a>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Reveal>
|
</Reveal>
|
||||||
@@ -571,7 +566,6 @@ export default function Home() {
|
|||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</ScrollFlow>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { URUNLER, DENEME_KREDISI } from "@/lib/credits";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Parallax } from "@/components/parallax";
|
import { Parallax } from "@/components/parallax";
|
||||||
import { ScrollFlow } from "@/components/scroll-flow";
|
|
||||||
import { SatinAlForm } from "./satin-al-form";
|
import { SatinAlForm } from "./satin-al-form";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
@@ -53,8 +52,7 @@ export default async function PaketPage({
|
|||||||
const odemeHatasi = hata === "token" || hata === "siparis";
|
const odemeHatasi = hata === "token" || hata === "siparis";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-slate-50 text-slate-900">
|
<div className="flex min-h-screen flex-col bg-slate-50 text-slate-900">
|
||||||
<ScrollFlow className="flex min-h-screen flex-col">
|
|
||||||
<main className="mx-auto w-full max-w-3xl flex-1 px-4 py-16">
|
<main className="mx-auto w-full max-w-3xl flex-1 px-4 py-16">
|
||||||
{odemeHatasi ? (
|
{odemeHatasi ? (
|
||||||
<div
|
<div
|
||||||
@@ -189,7 +187,6 @@ export default async function PaketPage({
|
|||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
</main>
|
</main>
|
||||||
</ScrollFlow>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { animate } from "motion";
|
|||||||
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
|
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { ViewportPortal } from "@/components/viewport-portal";
|
||||||
import type { SihirbazFacetleri } from "@/lib/db";
|
import type { SihirbazFacetleri } from "@/lib/db";
|
||||||
import {
|
import {
|
||||||
SIHIRBAZ_STORAGE_KEY,
|
SIHIRBAZ_STORAGE_KEY,
|
||||||
@@ -446,54 +447,56 @@ export function SihirbazBolumu({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Sabit alt çağrı kartı — liste/panel yokken ve kart kapatılmadıysa */}
|
{/* Sabit alt çağrı kartı — liste/panel yokken ve kart kapatılmadıysa */}
|
||||||
{monteEdildi && !panelde && !kartGizle && !acik ? (
|
<ViewportPortal>
|
||||||
<div className="pointer-events-none fixed inset-x-0 bottom-4 z-40 px-4">
|
{monteEdildi && !panelde && !kartGizle && !acik ? (
|
||||||
<div className="pointer-events-auto mx-auto flex max-w-xl items-center gap-4 rounded-2xl border border-slate-200 bg-white p-4 shadow-xl">
|
<div className="pointer-events-none fixed inset-x-0 bottom-4 z-40 px-4">
|
||||||
<div className="flex-1">
|
<div className="pointer-events-auto mx-auto flex max-w-xl items-center gap-4 rounded-2xl border border-slate-200 bg-white p-4 shadow-xl">
|
||||||
<p className="font-heading text-sm font-bold text-slate-900">
|
<div className="flex-1">
|
||||||
{girisBekliyor
|
<p className="font-heading text-sm font-bold text-slate-900">
|
||||||
? "Seçimlerin kayıtlı — AI listen hazır bekliyor"
|
{girisBekliyor
|
||||||
: "3 adımda 24 tercihlik listen"}
|
? "Seçimlerin kayıtlı — AI listen hazır bekliyor"
|
||||||
</p>
|
: "3 adımda 24 tercihlik listen"}
|
||||||
<p className="text-xs text-slate-500">
|
</p>
|
||||||
{girisBekliyor
|
<p className="text-xs text-slate-500">
|
||||||
? "Ücretsiz giriş yap, 5 deneme kredinle listen otomatik kurulsun."
|
{girisBekliyor
|
||||||
: "Sıralamana uygun bölümleri seç, yapay zekâ listeni kursun."}
|
? "Ücretsiz giriş yap, 5 deneme kredinle listen otomatik kurulsun."
|
||||||
</p>
|
: "Sıralamana uygun bölümleri seç, yapay zekâ listeni kursun."}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{girisBekliyor ? (
|
||||||
|
<Button
|
||||||
|
asChild
|
||||||
|
size="sm"
|
||||||
|
className="cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600"
|
||||||
|
>
|
||||||
|
<Link href={girisUrl}>Giriş yap</Link>
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
onClick={() => acikDegisti(true)}
|
||||||
|
className="cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600"
|
||||||
|
>
|
||||||
|
Başla
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label="Kapat"
|
||||||
|
onClick={() => {
|
||||||
|
setKartGizle(true);
|
||||||
|
try {
|
||||||
|
sessionStorage.setItem(KAPATMA_KEY, "1");
|
||||||
|
} catch {}
|
||||||
|
}}
|
||||||
|
className="cursor-pointer rounded-full p-1 text-slate-400 transition-colors duration-200 hover:bg-slate-100 hover:text-slate-600"
|
||||||
|
>
|
||||||
|
<X className="size-4" aria-hidden />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{girisBekliyor ? (
|
|
||||||
<Button
|
|
||||||
asChild
|
|
||||||
size="sm"
|
|
||||||
className="cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600"
|
|
||||||
>
|
|
||||||
<Link href={girisUrl}>Giriş yap</Link>
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
onClick={() => acikDegisti(true)}
|
|
||||||
className="cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600"
|
|
||||||
>
|
|
||||||
Başla
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
aria-label="Kapat"
|
|
||||||
onClick={() => {
|
|
||||||
setKartGizle(true);
|
|
||||||
try {
|
|
||||||
sessionStorage.setItem(KAPATMA_KEY, "1");
|
|
||||||
} catch {}
|
|
||||||
}}
|
|
||||||
className="cursor-pointer rounded-full p-1 text-slate-400 transition-colors duration-200 hover:bg-slate-100 hover:text-slate-600"
|
|
||||||
>
|
|
||||||
<X className="size-4" aria-hidden />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : null}
|
||||||
) : null}
|
</ViewportPortal>
|
||||||
|
|
||||||
{/* Üretim başladığında modalı kaldır; yeni panelin geçişini Motion
|
{/* Üretim başladığında modalı kaldır; yeni panelin geçişini Motion
|
||||||
üstlenir ve odağı panel içeriğine taşır. */}
|
üstlenir ve odağı panel içeriğine taşır. */}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { useEffect, useState } from "react";
|
|||||||
import { MessageCircleMore, X } from "lucide-react";
|
import { MessageCircleMore, X } from "lucide-react";
|
||||||
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
|
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
|
||||||
import { SohbetClient } from "@/app/listem/sohbet-client";
|
import { SohbetClient } from "@/app/listem/sohbet-client";
|
||||||
|
import { ViewportPortal } from "@/components/viewport-portal";
|
||||||
|
|
||||||
export function SohbetPopup({ kredi }: { kredi: number }) {
|
export function SohbetPopup({ kredi }: { kredi: number }) {
|
||||||
const [acik, setAcik] = useState(false);
|
const [acik, setAcik] = useState(false);
|
||||||
@@ -23,7 +24,7 @@ export function SohbetPopup({ kredi }: { kredi: number }) {
|
|||||||
}, [acik]);
|
}, [acik]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<ViewportPortal>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{acik ? (
|
{acik ? (
|
||||||
<motion.div
|
<motion.div
|
||||||
@@ -68,6 +69,6 @@ export function SohbetPopup({ kredi }: { kredi: number }) {
|
|||||||
)}
|
)}
|
||||||
{acik ? "Kapat" : "Danışmana sor"}
|
{acik ? "Kapat" : "Danışmana sor"}
|
||||||
</button>
|
</button>
|
||||||
</>
|
</ViewportPortal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
33
src/components/console-asr.tsx
Normal file
33
src/components/console-asr.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
let hasPlayed = false;
|
||||||
|
|
||||||
|
const consoleMessage = [
|
||||||
|
"%cبِسْمِ اللَّهِ الرَّحْمٰنِ الرَّحِيمِ",
|
||||||
|
"",
|
||||||
|
"وَالْعَصْرِۙ اِنَّ الْاِنْسَانَ لَف۪ي خُسْرٍۙ",
|
||||||
|
"اِلَّا الَّذ۪ينَ اٰمَنُوا وَعَمِلُوا الصَّالِحَاتِ",
|
||||||
|
"وَتَوَاصَوْا بِالْحَقِّ وَتَوَاصَوْا بِالصَّبْرِ",
|
||||||
|
"",
|
||||||
|
"Rahmân ve Rahîm olan Allah’ın adıyla.",
|
||||||
|
"",
|
||||||
|
"Asra yemîn olsun ki,",
|
||||||
|
"İnsan gerçekten ziyândadır.",
|
||||||
|
"Ancak iman edip sâlih ameller yapanlar, birbirlerine hakkı tavsiye",
|
||||||
|
"edenler ve birbirlerine sabretmeyi öğütleyenler müstesnâ!",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
export function ConsoleAsr() {
|
||||||
|
useEffect(() => {
|
||||||
|
if (hasPlayed) return;
|
||||||
|
hasPlayed = true;
|
||||||
|
|
||||||
|
window.setTimeout(() => {
|
||||||
|
console.log(consoleMessage, "font-size:16px");
|
||||||
|
}, 250);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -200,7 +200,10 @@ export function DevPanel() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-4 overflow-y-auto p-4 text-sm">
|
<div
|
||||||
|
data-lenis-prevent
|
||||||
|
className="flex flex-col gap-4 overflow-y-auto p-4 text-sm"
|
||||||
|
>
|
||||||
{/* Oturum */}
|
{/* Oturum */}
|
||||||
<section>
|
<section>
|
||||||
<p className="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
<p className="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
||||||
|
|||||||
26
src/components/hero-focus-button.tsx
Normal file
26
src/components/hero-focus-button.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
export function HeroFocusButton() {
|
||||||
|
function handleClick() {
|
||||||
|
const input = document.getElementById("siralama");
|
||||||
|
|
||||||
|
if (input instanceof HTMLInputElement) {
|
||||||
|
input.focus({ preventScroll: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
size="lg"
|
||||||
|
onClick={handleClick}
|
||||||
|
className="mt-8 h-12 cursor-pointer bg-orange-500 px-8 text-white transition-colors duration-200 hover:bg-orange-600"
|
||||||
|
>
|
||||||
|
Ücretsiz dene
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -132,7 +132,7 @@ export function HeroForm() {
|
|||||||
// flex-1 yalnızca yatay dizilimde (sm+): mobilde form flex-col olduğu
|
// flex-1 yalnızca yatay dizilimde (sm+): mobilde form flex-col olduğu
|
||||||
// için flex-1 inputun yüksekliğini eziyordu. Mobilde geniş padding
|
// için flex-1 inputun yüksekliğini eziyordu. Mobilde geniş padding
|
||||||
// + büyük yazı; sm ve üzeri eski görünüm.
|
// + büyük yazı; sm ve üzeri eski görünüm.
|
||||||
className="h-12 bg-background px-5 text-lg sm:flex-1 sm:px-2.5 sm:text-base"
|
className="h-14 bg-background px-6 py-3 text-lg sm:h-12 sm:flex-1 sm:px-2.5 sm:py-1 sm:text-base"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|||||||
@@ -206,6 +206,7 @@ function CekmeceSheet() {
|
|||||||
layout="position"
|
layout="position"
|
||||||
layoutScroll
|
layoutScroll
|
||||||
as="ol"
|
as="ol"
|
||||||
|
data-lenis-prevent
|
||||||
className="min-h-0 flex-1 space-y-2 overflow-x-hidden overflow-y-auto px-4 py-4"
|
className="min-h-0 flex-1 space-y-2 overflow-x-hidden overflow-y-auto px-4 py-4"
|
||||||
>
|
>
|
||||||
<AnimatePresence initial={false}>
|
<AnimatePresence initial={false}>
|
||||||
|
|||||||
46
src/components/page-pixel-layer.tsx
Normal file
46
src/components/page-pixel-layer.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { usePathname } from "next/navigation";
|
||||||
|
import { PixelCorner } from "@/components/pixel-decor";
|
||||||
|
|
||||||
|
const pixelClass = "size-10 [&_rect]:[fill-opacity:0.55]";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ana sayfanın kendi zengin piksel kompozisyonunu tekrar etmeden, iç
|
||||||
|
* sayfalara ortak bir piksel çerçevesi taşır. Katman içeriklerin üstünde
|
||||||
|
* kaldığı için sayfaların opak zeminlerinde de görünür; kenarlara
|
||||||
|
* yerleştiğinden etkileşimleri ve okunabilirliği engellemez.
|
||||||
|
*/
|
||||||
|
export function PagePixelLayer() {
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
if (pathname === "/") return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
aria-hidden
|
||||||
|
className="pointer-events-none fixed inset-0 z-30 overflow-hidden print:hidden"
|
||||||
|
>
|
||||||
|
<PixelCorner
|
||||||
|
className={`absolute -left-3 top-28 -rotate-90 ${pixelClass}`}
|
||||||
|
/>
|
||||||
|
<PixelCorner
|
||||||
|
className={`absolute -right-3 top-28 ${pixelClass}`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PixelCorner
|
||||||
|
className={`absolute -left-4 top-[46%] -rotate-90 text-orange-500 max-sm:hidden ${pixelClass}`}
|
||||||
|
/>
|
||||||
|
<PixelCorner
|
||||||
|
className={`absolute -right-4 top-[58%] rotate-90 max-sm:hidden ${pixelClass}`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PixelCorner
|
||||||
|
className={`absolute -bottom-3 left-6 rotate-180 ${pixelClass}`}
|
||||||
|
/>
|
||||||
|
<PixelCorner
|
||||||
|
className={`absolute -bottom-3 right-6 rotate-90 text-orange-500 ${pixelClass}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -35,24 +35,27 @@ export function ScrollFlow({
|
|||||||
const reduceMotion = useReducedMotion();
|
const reduceMotion = useReducedMotion();
|
||||||
const { scrollY } = useScroll();
|
const { scrollY } = useScroll();
|
||||||
const velocity = useVelocity(scrollY);
|
const velocity = useVelocity(scrollY);
|
||||||
// Ham hız titrek olur; yay ile yumuşatınca bükülme salınıp yerine oturur
|
|
||||||
const smoothVelocity = useSpring(velocity, {
|
// Önce hızı kesintisiz bir hedef açıya dönüştür, sonra doğrudan görünen
|
||||||
damping: 50,
|
// açıyı yumuşat. Böylece eşik/clamp kaynaklı ani hız değişimleri yaya girmez.
|
||||||
stiffness: 420,
|
const targetSkew = useTransform(velocity, (v) => {
|
||||||
mass: 0.6,
|
const RESPONSE = 1800;
|
||||||
|
return Math.tanh(v / RESPONSE) * maxSkew;
|
||||||
});
|
});
|
||||||
// Ölü bölge: yavaş/ufak kaydırmalarda bükülme tamamen 0 kalır — transform
|
const smoothSkew = useSpring(targetSkew, {
|
||||||
// hiç değişmediği için compositor'a kare başı iş çıkmaz. Eşik üstünde
|
stiffness: 240,
|
||||||
// kareyle (ease-in) yükselir, 0,01°'lik adımlara yuvarlanır ki yayın
|
damping: 26,
|
||||||
// mikro-salınımları her karede katmanı kirletmesin.
|
mass: 0.7,
|
||||||
const skewY = useTransform(smoothVelocity, (v) => {
|
restDelta: 0.005,
|
||||||
const DEADZONE = 500; // px/sn — bunun altı "normal gezinme" sayılır
|
restSpeed: 0.01,
|
||||||
const FULL = 2600; // px/sn — bu hızda maxSkew'e ulaşılır
|
});
|
||||||
const mag = Math.abs(v);
|
|
||||||
if (mag < DEADZONE) return 0;
|
// Yuvarlama görsel basamaklanma üretir. Tam hassasiyetle çiz; yalnızca açı
|
||||||
const t = Math.min(1, (mag - DEADZONE) / (FULL - DEADZONE));
|
// algılanamayacak kadar küçüldüğünde transform'u kaldırarak dev sayfa
|
||||||
const s = t * t * maxSkew * Math.sign(v);
|
// katmanının GPU belleğinde boşuna tutulmasını önle.
|
||||||
return Math.round(s * 100) / 100;
|
const transform = useTransform(smoothSkew, (v) => {
|
||||||
|
if (Math.abs(v) < 0.01) return "none";
|
||||||
|
return `skewY(${v}deg)`;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (reduceMotion) {
|
if (reduceMotion) {
|
||||||
@@ -61,12 +64,7 @@ export function ScrollFlow({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactLenis root options={{ lerp: 0.09, anchors: true }}>
|
<ReactLenis root options={{ lerp: 0.09, anchors: true }}>
|
||||||
{/* willChange: bükülme 0'a döndüğünde dev katman GPU'dan düşüp
|
<motion.div className={className} style={{ transform }}>
|
||||||
bir sonraki tekerlekte yeniden rasterize edilmesin */}
|
|
||||||
<motion.div
|
|
||||||
className={className}
|
|
||||||
style={{ skewY, willChange: "transform" }}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</ReactLenis>
|
</ReactLenis>
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ function ChatContainerRoot({
|
|||||||
return (
|
return (
|
||||||
<StickToBottom
|
<StickToBottom
|
||||||
className={cn("flex overflow-y-auto", className)}
|
className={cn("flex overflow-y-auto", className)}
|
||||||
|
data-lenis-prevent
|
||||||
resize="smooth"
|
resize="smooth"
|
||||||
initial="instant"
|
initial="instant"
|
||||||
role="log"
|
role="log"
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ function DialogContent({
|
|||||||
<DialogOverlay />
|
<DialogOverlay />
|
||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
data-slot="dialog-content"
|
data-slot="dialog-content"
|
||||||
|
data-lenis-prevent
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
||||||
className
|
className
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ function SelectContent({
|
|||||||
<SelectPrimitive.Portal>
|
<SelectPrimitive.Portal>
|
||||||
<SelectPrimitive.Content
|
<SelectPrimitive.Content
|
||||||
data-slot="select-content"
|
data-slot="select-content"
|
||||||
|
data-lenis-prevent
|
||||||
data-align-trigger={position === "item-aligned"}
|
data-align-trigger={position === "item-aligned"}
|
||||||
className={cn("relative z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", position ==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className )}
|
className={cn("relative z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", position ==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className )}
|
||||||
position={position}
|
position={position}
|
||||||
|
|||||||
23
src/components/viewport-portal.tsx
Normal file
23
src/components/viewport-portal.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { type ReactNode, useSyncExternalStore } from "react";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
|
|
||||||
|
const subscribe = () => () => {};
|
||||||
|
const getClientSnapshot = () => true;
|
||||||
|
const getServerSnapshot = () => false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Viewport'a sabit kalması gereken öğeleri, transform uygulanan sayfa
|
||||||
|
* katmanının dışına taşır. Böylece üst katmandaki scroll skew animasyonu
|
||||||
|
* `position: fixed` koordinat sistemini değiştirmez.
|
||||||
|
*/
|
||||||
|
export function ViewportPortal({ children }: { children: ReactNode }) {
|
||||||
|
const mounted = useSyncExternalStore(
|
||||||
|
subscribe,
|
||||||
|
getClientSnapshot,
|
||||||
|
getServerSnapshot,
|
||||||
|
);
|
||||||
|
|
||||||
|
return mounted ? createPortal(children, document.body) : null;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user