feat(v2): 9–10 Ekim çalışma ağacı — deneyim dili 1. dalga, PWA manifest, revizyon niyet modeli, listem/tadımlık bileşen denetimi
9–10 Ekim oturumlarının commit'lenmemiş işi tek parça alındı (raporlar: docs/ekip-raporlari/2026-10-09, 2026-10-10): - Landing deneyim dili 1. dalga: sayfa geçişi (ViewTransition), piksel morf/şerit, sorun sahneleri, adım akışı, sayan sayı, mobil menü, adım göstergesi, logoya tepki; DESIGN.md §11 ve AGENTS.md kuralları, tasarım denetimine yeni kurallar - Tema: tema ikonu, D kısayolu, tema geçişi - PWA: manifest.ts ve ikonlar - Danışman: revizyon niyet modeli (OPENROUTER_NIYET_MODEL), sohbet geçmişi, soru API'si - /listem ve /sonuc tadımlığı bileşen denetimi düzeltmeleri - eslint: .venv-tts/ yok sayılır tsc, eslint ve tasarım denetimi sıfır hata. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@ import { kuralOzeti } from "@/lib/revizyon-kurali";
|
||||
import {
|
||||
anketCevabiDogrula,
|
||||
anketCevabiMetni,
|
||||
type AnketCevaplari,
|
||||
type RevizyonOzeti,
|
||||
type SohbetMeta,
|
||||
} from "@/lib/ai/sohbet-meta";
|
||||
@@ -138,6 +139,7 @@ export async function POST(request: NextRequest) {
|
||||
// üretilir; istemcinin yazdığı metin kullanılmaz.
|
||||
const sonAnket = sonMesaj?.role === "assistant" ? sonMesaj.meta?.anket : undefined;
|
||||
let kullaniciMeta: SohbetMeta | undefined;
|
||||
let anketCevaplari: AnketCevaplari | null = null;
|
||||
if (anketCevabiHam) {
|
||||
if (!sonAnket || sonAnket.id !== anketCevabiHam.anketId) {
|
||||
return Response.json({ error: "Bu form artık geçerli değil; isteğini yazarak söyleyebilirsin." }, { status: 409 });
|
||||
@@ -148,6 +150,7 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
mesaj = anketCevabiMetni(sonAnket, cevaplar);
|
||||
kullaniciMeta = { anketCevabi: { anketId: sonAnket.id, cevaplar } };
|
||||
anketCevaplari = cevaplar;
|
||||
}
|
||||
const bedelsiz = Boolean(anketCevabiHam) || onayMesajiMi(mesaj, sonAsistan);
|
||||
|
||||
@@ -294,7 +297,7 @@ export async function POST(request: NextRequest) {
|
||||
let islem: KoddanIslem | undefined;
|
||||
if (paketli && revizyonNiyetiVar(mesaj)) {
|
||||
const t0 = Date.now();
|
||||
const niyet = await revizyonNiyetiCikar({ mesaj, params: raporParams, oncekiForm: anketCevabiHam ? sonAnket : null });
|
||||
const niyet = await revizyonNiyetiCikar({ mesaj, params: raporParams, oncekiForm: anketCevabiHam ? sonAnket : null, anketCevabi: anketCevaplari });
|
||||
console.info("[soru] revizyon niyeti:", niyet.niyet, "kural" in niyet ? "kural" : "form" in niyet ? `form (eksik: ${niyet.eksik.join(", ") || "—"})` : "neden" in niyet ? niyet.neden : "", `${Date.now() - t0} ms`);
|
||||
if (niyet.niyet === "revizyon" && "kural" in niyet) {
|
||||
const kural = niyet.kural;
|
||||
|
||||
@@ -74,6 +74,11 @@
|
||||
--chart-4: oklch(0.371 0 0);
|
||||
--chart-5: oklch(0.269 0 0);
|
||||
--radius: 0.625rem;
|
||||
/* Hareket eğrileri (DESIGN.md §6); JS karşılığı src/lib/hareket.ts EGRI */
|
||||
--egri-giris: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
--egri-standart: cubic-bezier(0.22, 1, 0.36, 1);
|
||||
--egri-guclu-cikis: cubic-bezier(0.23, 1, 0.32, 1);
|
||||
--egri-yerlesme: cubic-bezier(0.32, 0.72, 0, 1);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
@@ -484,3 +489,297 @@
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tema geçişi (DESIGN.md §2.3, lib/tema-gecisi.ts): yeni tema header'daki
|
||||
logodan yumuşak bir halka gibi açılır. Eski görüntü yerinde durur; yeni
|
||||
görüntü radyal degrade maskesiyle üstünde belirir. Maskenin kenarı keskin
|
||||
değil: yarıçapın %55'ine kadar tam opak, oradan kenara doğru tüy gibi
|
||||
söner (--kt-tema-cekirdek; JS'teki CEKIRDEK ile aynı olmalı) — ışık
|
||||
yayılır gibi, sert çizgi yok. Yarıçap (--kt-tema-r) 0 → --kt-tema-son
|
||||
yumuşak kalkıp yumuşak durur; kısa ve hafif (600 ms) — asıl gösteri,
|
||||
geçiş bitince oynayan logo dönüşümüdür. Merkez ve son yarıçap JS'ten gelir.
|
||||
Hareket azaltılmışsa JS geçişi hiç başlatmaz, tema düz değişir. */
|
||||
@property --kt-tema-r {
|
||||
syntax: "<length>";
|
||||
inherits: true;
|
||||
initial-value: 0px;
|
||||
}
|
||||
.kt-tema-gecis {
|
||||
view-transition-name: root;
|
||||
--kt-tema-cekirdek: 0.55;
|
||||
}
|
||||
.kt-tema-gecis::view-transition-old(root),
|
||||
.kt-tema-gecis::view-transition-new(root) {
|
||||
animation: none;
|
||||
mix-blend-mode: normal;
|
||||
}
|
||||
.kt-tema-gecis::view-transition-old(root) {
|
||||
z-index: 1;
|
||||
}
|
||||
.kt-tema-gecis::view-transition-new(root) {
|
||||
z-index: 2;
|
||||
mask-image: radial-gradient(
|
||||
circle at var(--kt-tema-x) var(--kt-tema-y),
|
||||
#000 calc(var(--kt-tema-r) * var(--kt-tema-cekirdek)),
|
||||
transparent var(--kt-tema-r)
|
||||
);
|
||||
animation: kt-tema-halka 600ms cubic-bezier(0.4, 0, 0.2, 1) both;
|
||||
}
|
||||
@keyframes kt-tema-halka {
|
||||
from {
|
||||
--kt-tema-r: 0px;
|
||||
}
|
||||
to {
|
||||
--kt-tema-r: var(--kt-tema-son);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sayfa geçişi (DESIGN.md §6, components/sayfa-gecisi.tsx): rota değişince
|
||||
eski içerik hızla söner, yenisi hafifçe yükselerek belirir — çıkış girişten
|
||||
hızlı. Üst çubuk kendi grubunda ve animasyonsuz: içerik değişir, çubuk
|
||||
yerinde kalır (uygulama hissi, §11.4). Tema geçişinde (`.kt-tema-gecis`)
|
||||
çubuk adını bırakır ki halka onu da kaplasın. */
|
||||
.kt-ust-cubuk {
|
||||
view-transition-name: kt-ust-cubuk;
|
||||
}
|
||||
.kt-tema-gecis .kt-ust-cubuk {
|
||||
view-transition-name: none;
|
||||
}
|
||||
::view-transition-group(kt-ust-cubuk) {
|
||||
animation: none;
|
||||
z-index: 100;
|
||||
}
|
||||
::view-transition-old(kt-ust-cubuk) {
|
||||
display: none;
|
||||
}
|
||||
::view-transition-new(kt-ust-cubuk) {
|
||||
animation: none;
|
||||
}
|
||||
::view-transition-old(.kt-sayfa-cik) {
|
||||
animation: 120ms var(--egri-standart) both kt-sayfa-sol reverse;
|
||||
}
|
||||
::view-transition-new(.kt-sayfa-gir) {
|
||||
animation:
|
||||
200ms var(--egri-standart) 80ms both kt-sayfa-sol,
|
||||
280ms var(--egri-giris) both kt-sayfa-yuksel;
|
||||
}
|
||||
@keyframes kt-sayfa-sol {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@keyframes kt-sayfa-yuksel {
|
||||
from {
|
||||
translate: 0 8px;
|
||||
}
|
||||
to {
|
||||
translate: 0 0;
|
||||
}
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
::view-transition-old(.kt-sayfa-cik),
|
||||
::view-transition-new(.kt-sayfa-gir) {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ana sayfa ilk ekranının girişi (DESIGN.md §6 "sayfa başına tek orkestre
|
||||
giriş", §11.2): önce harita belirir, sonra başlık, sonra form netleşerek
|
||||
gelir — sırayla, üst üste binmeden. `--sira` ile kademelenir. Başlık LCP
|
||||
öğesi olduğundan hiçbir kare tam saydam başlamaz (opaklık 0,35'ten). */
|
||||
.kt-hero-gir {
|
||||
animation: kt-hero-gir 0.7s var(--egri-giris) both;
|
||||
animation-delay: calc(var(--sira, 0) * 110ms + 80ms);
|
||||
}
|
||||
.kt-hero-harita {
|
||||
animation: kt-hero-harita 1.1s var(--egri-standart) both;
|
||||
}
|
||||
@keyframes kt-hero-gir {
|
||||
from {
|
||||
opacity: 0.35;
|
||||
filter: blur(10px);
|
||||
translate: 0 12px;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
filter: blur(0);
|
||||
translate: 0 0;
|
||||
}
|
||||
}
|
||||
@keyframes kt-hero-harita {
|
||||
from {
|
||||
opacity: 0;
|
||||
scale: 0.985;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
scale: 1;
|
||||
}
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.kt-hero-gir,
|
||||
.kt-hero-harita {
|
||||
animation: kt-unblur-sade 0.25s ease-out both;
|
||||
}
|
||||
}
|
||||
|
||||
/* "Nasıl çalışır" adımları arasındaki çizgi (DESIGN.md §4.32): adımın rakamı
|
||||
biçimine dönüşüp geri toplanınca (adim-akisi.tsx `data-dolu` yazar) soldan
|
||||
maviyle dolar. JS'siz istemcide dolu durur. */
|
||||
@media (scripting: enabled) {
|
||||
.kt-cizgi-dol {
|
||||
transform: scaleX(0);
|
||||
transform-origin: left;
|
||||
transition: transform 0.6s var(--egri-guclu-cikis);
|
||||
}
|
||||
.kt-cizgi-dol[data-dolu] {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.kt-cizgi-dol {
|
||||
transform: none;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Piksel morf (components/piksel-morf.tsx, DESIGN.md §4.38): her piksel bir
|
||||
<g>; yeri `translate` ile verilir ve kare değişince yeni hücresine süzülür.
|
||||
`gizli` küçülüp söner. Turuncu vurgu, doku karesinin üstündeki ikinci
|
||||
karedir: çabuk ısınır, yavaş soğur (ısı fırçasıyla aynı asimetri).
|
||||
Sekme yok (§6); kayma süresi sahneye göre `--morf-sure` ile ayarlanır. */
|
||||
.kt-morf-px {
|
||||
transform-box: fill-box;
|
||||
transform-origin: center;
|
||||
transition:
|
||||
translate var(--morf-sure, 0.55s) var(--egri-standart) var(--g, 0ms),
|
||||
opacity 0.3s var(--egri-standart) var(--g, 0ms),
|
||||
scale 0.3s var(--egri-standart) var(--g, 0ms);
|
||||
}
|
||||
.kt-morf-px[data-ton="gizli"] {
|
||||
opacity: 0;
|
||||
scale: 0.6;
|
||||
}
|
||||
.kt-morf-vurgu {
|
||||
opacity: 0;
|
||||
fill-opacity: 0.7;
|
||||
transition: opacity 1.2s var(--egri-standart);
|
||||
}
|
||||
.kt-morf-taban {
|
||||
transition: opacity 0.6s var(--egri-standart);
|
||||
}
|
||||
.kt-morf-px[data-ton="vurgu"] > .kt-morf-taban {
|
||||
opacity: 0;
|
||||
transition: opacity 0.25s var(--egri-standart) var(--g, 0ms);
|
||||
}
|
||||
.kt-morf-px[data-ton="vurgu"] > .kt-morf-vurgu {
|
||||
opacity: 1;
|
||||
animation:
|
||||
kt-morf-isin 0.25s var(--egri-standart) var(--g, 0ms) backwards,
|
||||
kt-morf-nabiz 3.6s ease-in-out var(--n, 0s) infinite;
|
||||
}
|
||||
@keyframes kt-morf-isin {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes kt-morf-nabiz {
|
||||
50% {
|
||||
fill-opacity: 0.35;
|
||||
}
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.kt-morf-px,
|
||||
.kt-morf-taban,
|
||||
.kt-morf-vurgu {
|
||||
transition: none;
|
||||
}
|
||||
.kt-morf-px[data-ton="vurgu"] > .kt-morf-vurgu {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Kendini çizen tik (landing-parcalari.tsx, DESIGN.md §5.2.3): karşılaştırma
|
||||
tablosu ekrana girince (Reveal → .kt-reveal-in) önce halka, sonra işaret
|
||||
çizilir; satır gecikmesi `--tik-g` ile gelir. `pathLength="1"` bekler. */
|
||||
@media (scripting: enabled) and (prefers-reduced-motion: no-preference) {
|
||||
.kt-reveal .kt-tik-ciz {
|
||||
stroke-dasharray: 1;
|
||||
stroke-dashoffset: 1;
|
||||
opacity: 0;
|
||||
transition:
|
||||
stroke-dashoffset 0.5s var(--egri-standart) calc(var(--tik-g, 0ms) + 250ms),
|
||||
opacity 0.01s linear calc(var(--tik-g, 0ms) + 250ms);
|
||||
}
|
||||
.kt-reveal .kt-tik-isaret {
|
||||
transition-duration: 0.3s, 0.01s;
|
||||
transition-delay:
|
||||
calc(var(--tik-g, 0ms) + 600ms),
|
||||
calc(var(--tik-g, 0ms) + 600ms);
|
||||
}
|
||||
.kt-reveal.kt-reveal-in .kt-tik-ciz {
|
||||
stroke-dashoffset: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hero haritasının serpintisi (turkey-pixel-map.tsx, DESIGN.md §11.1 V2):
|
||||
piksel bir süre yerinde durur, kopup `--d` birim (10 = bir hücre) düşerken
|
||||
söner, sonra yerinde yeniden belirir. Süre ve faz satır içi. */
|
||||
@keyframes kt-serpinti {
|
||||
0%,
|
||||
42% {
|
||||
translate: 0 0;
|
||||
opacity: 1;
|
||||
animation-timing-function: ease-in;
|
||||
}
|
||||
76% {
|
||||
translate: 0 calc(var(--d, 20) * 1px);
|
||||
opacity: 0;
|
||||
animation-timing-function: step-end;
|
||||
}
|
||||
80% {
|
||||
translate: 0 0;
|
||||
opacity: 0;
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
100% {
|
||||
translate: 0 0;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.kt-serpinti {
|
||||
animation: kt-serpinti 6s infinite;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.kt-serpinti {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Piksel şeridi (components/piksel-serit.tsx, DESIGN.md §4.38): kaydırmaya
|
||||
bağlı sütun. Okuma çizgisini geçen piksel `data-yandi` alır ve turuncuya
|
||||
ısınır; geri kaydırınca yavaş soğur. Şeridin ucu (`data-uc`) tam turuncu. */
|
||||
.kt-serit-px {
|
||||
fill: currentColor;
|
||||
fill-opacity: var(--op, 0.2);
|
||||
transition:
|
||||
fill 0.9s var(--egri-standart),
|
||||
fill-opacity 0.9s var(--egri-standart);
|
||||
}
|
||||
.kt-serit-px[data-yandi] {
|
||||
fill: var(--color-orange-500);
|
||||
fill-opacity: 0.62;
|
||||
transition-duration: 0.16s;
|
||||
}
|
||||
.kt-serit-px[data-uc] {
|
||||
fill-opacity: 1;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.kt-serit-px {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ import Script from "next/script";
|
||||
import { Cal_Sans, Geist_Mono, Bricolage_Grotesque } from "next/font/google";
|
||||
import localFont from "next/font/local";
|
||||
import { BildirimYigini } from "@/components/ui/bildirim";
|
||||
import { SayfaGecisi } from "@/components/sayfa-gecisi";
|
||||
import { SiteHeader } from "@/components/site-header";
|
||||
import { SiteTopBanner } from "@/components/site-top-banner";
|
||||
import { ListeCekmecesiLazy } from "@/features/liste/components/liste-cekmecesi-lazy";
|
||||
import { UcusKatmani } from "@/features/liste/components/ucus-katmani";
|
||||
import { KaydetBannerLazy } from "@/features/liste/components/kaydet-banner-lazy";
|
||||
import { TercihProfiliKapisiLazy } from "@/features/liste/components/tercih-profili-kapisi-lazy";
|
||||
import { HeaderGate } from "@/components/header-gate";
|
||||
@@ -117,12 +119,14 @@ export default function RootLayout({
|
||||
<div
|
||||
id="icerik"
|
||||
tabIndex={-1}
|
||||
className="flex min-h-0 flex-1 flex-col pb-28 outline-none print:pb-0"
|
||||
className="flex min-h-0 flex-1 flex-col pb-28 outline-none has-[[data-site-footer]]:pb-0 print:pb-0"
|
||||
>
|
||||
{children}
|
||||
<SayfaGecisi>{children}</SayfaGecisi>
|
||||
</div>
|
||||
{/* Manuel 24'lük liste çekmecesi + "+" uçuş katmanı (navbar'dan açılır) */}
|
||||
{/* Manuel 24'lük liste çekmecesi (navbar'dan açılır) */}
|
||||
<ListeCekmecesiLazy />
|
||||
{/* "+" ile eklemede rozete uçan piksel kare (DESIGN.md §4.37) */}
|
||||
<UcusKatmani />
|
||||
{/* Girişsiz + 3 seçimli kullanıcıya "listen yalnızca bu tarayıcıda" banner'ı */}
|
||||
<KaydetBannerLazy />
|
||||
{/* İlk "+" için sıra + sihirbaz kapısı */}
|
||||
|
||||
26
src/app/manifest.ts
Normal file
26
src/app/manifest.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { MetadataRoute } from "next";
|
||||
|
||||
// Ana ekrana eklenebilir kabuk (DESIGN.md §11.4): telefonda "Ana ekrana ekle"
|
||||
// ile site tarayıcı çubuğu olmadan, uygulama gibi açılır. Yalnız kurulum
|
||||
// kabuğudur; service worker ve çevrimdışı çalışma yok. Renkler gövde zemini
|
||||
// (bg-slate-50); koyu temada adres çubuğu rengini tema-saglayici.tsx sürer.
|
||||
export default function manifest(): MetadataRoute.Manifest {
|
||||
return {
|
||||
name: "KolayTercih — YKS Tercih Danışmanı",
|
||||
short_name: "KolayTercih",
|
||||
description:
|
||||
"YKS sıralamana göre gerçek YÖK Atlas verisiyle dengeli 24 tercihlik liste.",
|
||||
lang: "tr",
|
||||
start_url: "/",
|
||||
scope: "/",
|
||||
display: "standalone",
|
||||
orientation: "portrait",
|
||||
background_color: "#f8fafc",
|
||||
theme_color: "#f8fafc",
|
||||
icons: [
|
||||
{ src: "/pwa-ikon.svg", sizes: "any", type: "image/svg+xml", purpose: "any" },
|
||||
{ src: "/pwa-ikon-maskable.svg", sizes: "any", type: "image/svg+xml", purpose: "maskable" },
|
||||
{ src: "/apple-icon.png", sizes: "180x180", type: "image/png", purpose: "any" },
|
||||
],
|
||||
};
|
||||
}
|
||||
237
src/app/page.tsx
237
src/app/page.tsx
@@ -10,23 +10,22 @@ import {
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { Suspense } from "react";
|
||||
import { AdimAkisi } from "@/features/pazarlama/components/adim-akisi";
|
||||
import { BolumGecisi } from "@/features/pazarlama/components/bolum-gecisi";
|
||||
import { ComparisonCell } from "@/features/pazarlama/components/landing-parcalari";
|
||||
import { HeroBaslik } from "@/features/pazarlama/components/hero-baslik";
|
||||
import { KapanisCta } from "@/features/pazarlama/components/hero-focus-button";
|
||||
import { HeroForm } from "@/features/sihirbaz/components/hero-form";
|
||||
import { Parallax } from "@/components/parallax";
|
||||
import { Reveal } from "@/features/pazarlama/components/reveal";
|
||||
import { SayanSayi } from "@/features/pazarlama/components/sayan-sayi";
|
||||
import { SorunSahnesi } from "@/features/pazarlama/components/sorun-sahneleri";
|
||||
import { SosyalKanitBandi } from "@/features/pazarlama/components/sosyal-kanit";
|
||||
import { TurkeyPixelMap } from "@/features/pazarlama/components/turkey-pixel-map";
|
||||
import { SuzulenHaplar } from "@/features/pazarlama/components/suzulen-haplar";
|
||||
import { VeriAkisi } from "@/features/pazarlama/components/veri-akisi";
|
||||
import { katalogHaplari } from "@/features/pazarlama/landing-verisi";
|
||||
import {
|
||||
PixelDivider,
|
||||
PixelRakam,
|
||||
PixelSahne,
|
||||
SectionEyebrow,
|
||||
} from "@/components/pixel-decor";
|
||||
import { SectionEyebrow } from "@/components/pixel-decor";
|
||||
import { PikselCtaYuzeyi } from "@/components/piksel-cta-yuzeyi";
|
||||
import { URUNLER } from "@/lib/credits";
|
||||
import {
|
||||
@@ -68,78 +67,57 @@ const TOPUP_FIYATI = (URUNLER.topup.amountKurus / 100).toLocaleString("tr-TR", {
|
||||
maximumFractionDigits: 0,
|
||||
});
|
||||
|
||||
// Sorun kartlarının piksel sahneleri (DESIGN.md §4.31): hero haritasıyla aynı
|
||||
// ızgara dili — `#` doku, `-` silik, `o` turuncu vurgu. Her biri kartın
|
||||
// söylediğini çizer: binlerce programın içinde tam 24 seçim; her yıl başka
|
||||
// boyda çubuklar ve yalnız sonuncusu işaretli; basamak basamak tırmanan fiyat.
|
||||
const SAHNE_IHTIMAL = [
|
||||
"o#o#######o##o##o#######",
|
||||
"####o###o#########o###o#",
|
||||
"######o####o###o########",
|
||||
"#o##o###################",
|
||||
"######o##o####o####o##o#",
|
||||
"#o##########o####o######",
|
||||
".###..o#......####.#..o.",
|
||||
"..#...###...##......#...",
|
||||
];
|
||||
const SAHNE_YILLAR = [
|
||||
"........................",
|
||||
".................##.....",
|
||||
".........##......##.....",
|
||||
".##......##......##.....",
|
||||
".##......##..##..##..oo.",
|
||||
".##..##..##..##..##..##.",
|
||||
".##..##..##..##..##..##.",
|
||||
".##..##..##..##..##..##.",
|
||||
];
|
||||
const SAHNE_FIYAT = [
|
||||
".....................ooo",
|
||||
"..................###---",
|
||||
"...............###------",
|
||||
"............###---------",
|
||||
".........###------------",
|
||||
"......###---------------",
|
||||
"...###------------------",
|
||||
"###---------------------",
|
||||
];
|
||||
// Bölüm ritmi (DESIGN.md §5.1; Bilal, 11 Eki 2026: "section'lar arası es yok,
|
||||
// her şey çok sıkışık"). Bölümler arasında piksel şeridi durur (§4.38) ve
|
||||
// çevresinde boşluk ister: şerit üst bölümün içeriğinden uzak, alt bölümün
|
||||
// rozetine yakındır — bir bölümü bitirip sıradakine iner. Alt boşluk bu yüzden
|
||||
// üsttekinden bir kademe büyük. Şerit geçiş çizgisinin iki yanına telefonda
|
||||
// 56, sm'den itibaren 75 px taşar; kalan nefes telefonda 56/40, sm'de 85/53,
|
||||
// lg'de 101/69 px (üst bölüme/alt bölüme).
|
||||
const BOLUM_BOSLUGU = "pt-24 pb-28 sm:pt-32 sm:pb-40 lg:pt-36 lg:pb-44";
|
||||
|
||||
// Sorun kartlarının piksel sahneleri (DESIGN.md §4.31, §4.38): hero haritasıyla
|
||||
// aynı ızgara dili. Her biri kartın söylediğini hareketle çizer: binlerce
|
||||
// programın içinde ısınıp sönen tam 24 seçim; her yıl bir sola kayan, sağdan
|
||||
// başka boyda gelen çubuklar; basamak basamak tırmanan fiyat. Izgaralar
|
||||
// `sorun-sahneleri.tsx`te.
|
||||
const problems = [
|
||||
{
|
||||
sahne: SAHNE_IHTIMAL,
|
||||
sahne: "ihtimal",
|
||||
title: "24 tercih, binlerce ihtimal",
|
||||
description: `${PROGRAM_SAYISI_CUMLE} lisans ve önlisans programı arasından sana uyan 24 tanesini doğru sırayla dizmek, tablolarla boğuşarak günler alıyor.`,
|
||||
},
|
||||
{
|
||||
sahne: SAHNE_YILLAR,
|
||||
sahne: "yillar",
|
||||
title: "Geçen yılın puanı yetmiyor",
|
||||
description:
|
||||
"Taban sıralamaları her yıl kayıyor; tek yıla bakmak riski gizler.",
|
||||
},
|
||||
{
|
||||
sahne: SAHNE_FIYAT,
|
||||
sahne: "fiyat",
|
||||
title: "Danışman lüks haline geldi",
|
||||
description:
|
||||
"Armut'ta tercih danışmanlığı ilanları ortalama 1.000–3.000 TL (Eylül 2026). Ücretsiz robotların çoğu ise yalnızca liste filtreliyor; listeyi kurup gerekçesini yazmıyor.",
|
||||
},
|
||||
];
|
||||
] as const;
|
||||
|
||||
const steps = [
|
||||
{
|
||||
step: 1,
|
||||
title: "Sıralamanı ve hedeflerini anlat",
|
||||
description:
|
||||
no: 1,
|
||||
baslik: "Sıralamanı ve hedeflerini anlat",
|
||||
aciklama:
|
||||
"YKS başarı sıralamanı gir; sıralamana uygun ilgi alanı, şehir ve üniversite tercihlerini birkaç adımda işaretle.",
|
||||
},
|
||||
{
|
||||
step: 2,
|
||||
title: `Son ${VERI_YIL_SAYISI} yılın resmî verisi taransın`,
|
||||
description:
|
||||
no: 2,
|
||||
baslik: `Son ${VERI_YIL_SAYISI} yılın resmî verisi taransın`,
|
||||
aciklama:
|
||||
"YÖK Atlas'ın taban sıralaması trendleri, kontenjan değişimleri ve doluluk oranları sıralamana göre elensin; yapay zekâ yalnızca bu havuzdan seçer, dışına çıkamaz.",
|
||||
},
|
||||
{
|
||||
step: 3,
|
||||
title: "Dengeli 24'lük listeni al",
|
||||
description:
|
||||
no: 3,
|
||||
baslik: "Dengeli 24'lük listeni al",
|
||||
aciklama:
|
||||
"Hayal, dengeli ve güvenli dilimlerine dağıtılmış, her satırı gerekçeli tercih listeni indir; aklına takılanı listen üzerinden yapay zekâya sor.",
|
||||
},
|
||||
] as const;
|
||||
@@ -270,19 +248,23 @@ export default function Home() {
|
||||
aria-hidden
|
||||
// SVG altta 9 hücrelik serpinti payı taşıdığı için silüet merkezi
|
||||
// yüksekliğin %37,1'inde; -1/2 yerine bu oranla hizalanır
|
||||
className="pointer-events-none absolute left-1/2 top-[43%] -z-10 hidden -translate-x-1/2 -translate-y-[37.14%] sm:block"
|
||||
// Telefonda harita gizlenmez (DESIGN.md §11.2-9): ekrandan taşacak
|
||||
// kadar iri durur, başlığın üstündeki boşluğu doldurur ve serpinti
|
||||
// başlığa doğru dökülür.
|
||||
className="kt-hero-harita pointer-events-none absolute left-1/2 top-[27%] -z-10 -translate-x-1/2 -translate-y-[37.14%] sm:top-[43%]"
|
||||
>
|
||||
{/* Arka katman: harita scroll'un gerisinde kalır, derinlik verir */}
|
||||
<Parallax fromStart strength={-52}>
|
||||
{/* Harita orta ekranlarda nefes payı bırakır, ekranla birlikte
|
||||
ölçeklenir ve çok geniş ekranda 1665px tavanına oturur;
|
||||
mobilde tümüyle gizli. */}
|
||||
<TurkeyPixelMap className="w-[min(1665px,82vw)] max-w-none text-primary" />
|
||||
telefonda ekranın 1,7 katı (kenarlardan taşar, kırpılır). */}
|
||||
<TurkeyPixelMap className="w-[170vw] max-w-none text-primary sm:w-[min(1665px,82vw)]" />
|
||||
</Parallax>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute inset-0 -z-10 bg-[radial-gradient(ellipse_58%_52%_at_50%_42%,color-mix(in_oklab,var(--color-slate-50)_90%,transparent)_25%,transparent_75%)]"
|
||||
// Okunurluk perdesi: telefonda başlık daha aşağıda ve dar, perde ona göre
|
||||
className="pointer-events-none absolute inset-0 -z-10 bg-[radial-gradient(ellipse_95%_38%_at_50%_62%,color-mix(in_oklab,var(--color-slate-50)_92%,transparent)_35%,transparent_80%)] sm:bg-[radial-gradient(ellipse_58%_52%_at_50%_42%,color-mix(in_oklab,var(--color-slate-50)_90%,transparent)_25%,transparent_75%)]"
|
||||
/>
|
||||
<Parallax
|
||||
fromStart
|
||||
@@ -292,22 +274,21 @@ export default function Home() {
|
||||
<HeroBaslik />
|
||||
<div
|
||||
id="hero-form"
|
||||
className="mt-6 flex w-full scroll-mt-28 justify-center"
|
||||
className="kt-hero-gir mt-6 flex w-full scroll-mt-28 justify-center"
|
||||
style={{ "--sira": 2 } as React.CSSProperties}
|
||||
>
|
||||
<HeroForm />
|
||||
</div>
|
||||
</Parallax>
|
||||
</section>
|
||||
|
||||
{/* Hero → Problem geçişi: border yerine piksel ayraç */}
|
||||
<div aria-hidden className="mx-auto w-56">
|
||||
<Parallax strength={26}>
|
||||
<PixelDivider seed={11} />
|
||||
</Parallax>
|
||||
</div>
|
||||
{/* Hero → Problem geçişi: border ya da ayraç yerine piksel şeridi.
|
||||
Haritanın serpintisinden başlar, kaydırdıkça turuncuya ısınarak
|
||||
bölümden bölüme iner (DESIGN.md §4.38). */}
|
||||
<BolumGecisi tohum={11} ust={6} ustTelefon={6} ustKisaTelefon={3} />
|
||||
|
||||
{/* Problem */}
|
||||
<section className="py-20 sm:py-24">
|
||||
<section className={BOLUM_BOSLUGU}>
|
||||
<div className="mx-auto max-w-6xl px-4">
|
||||
{/* Başlık gövdeden hafif hızlı akar — katman derinliği */}
|
||||
<Parallax strength={16}>
|
||||
@@ -322,13 +303,18 @@ export default function Home() {
|
||||
</Reveal>
|
||||
</Parallax>
|
||||
{/* Sahne kartı (DESIGN.md §4.31): üstte hero'nun piksel diliyle
|
||||
kartın söylediğini çizen sahne, altında başlık ve metin. */}
|
||||
<Parallax strength={7} className="mt-12 grid gap-6 md:grid-cols-3">
|
||||
kartın söylediğini çizen sahne, altında başlık ve metin.
|
||||
Sahneler sırayla oynar; karta gelince o sahne hemen oynar
|
||||
(`data-sahne-karti`, sorun-sahneleri.tsx). */}
|
||||
<Parallax strength={7} className="mt-12 grid gap-6 sm:mt-16 md:grid-cols-3">
|
||||
{problems.map((p, i) => (
|
||||
<Reveal key={p.title} delay={i * 0.08} className="h-full">
|
||||
<div className="flex h-full flex-col overflow-hidden rounded-2xl border border-slate-200/70 bg-card shadow-sm transition-[translate,border-color,box-shadow] duration-300 hover:-translate-y-1 hover:border-orange-500 hover:shadow-lg hover:shadow-orange-500/5">
|
||||
<div
|
||||
data-sahne-karti=""
|
||||
className="flex h-full flex-col overflow-hidden rounded-2xl border border-slate-200/70 bg-card shadow-sm transition-[translate,border-color,box-shadow] duration-300 hover:-translate-y-1 motion-reduce:hover:translate-y-0 hover:border-orange-500 hover:shadow-lg hover:shadow-orange-500/5"
|
||||
>
|
||||
<div className="border-b border-slate-100 bg-slate-50 px-6 py-8">
|
||||
<PixelSahne izgara={p.sahne} />
|
||||
<SorunSahnesi tur={p.sahne} sira={i} />
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<h3 className="font-heading text-xl font-bold text-balance">
|
||||
@@ -346,14 +332,10 @@ export default function Home() {
|
||||
</section>
|
||||
|
||||
{/* Problem → Nasıl çalışır geçişi */}
|
||||
<div aria-hidden className="mx-auto w-56">
|
||||
<Parallax strength={26}>
|
||||
<PixelDivider seed={17} />
|
||||
</Parallax>
|
||||
</div>
|
||||
<BolumGecisi tohum={17} />
|
||||
|
||||
{/* How it works */}
|
||||
<section id="nasil-calisir" className="scroll-mt-24 py-20 sm:py-24">
|
||||
<section id="nasil-calisir" className={`scroll-mt-24 ${BOLUM_BOSLUGU}`}>
|
||||
<div className="mx-auto max-w-6xl px-4">
|
||||
<Parallax strength={16}>
|
||||
<Reveal className="flex flex-col items-center gap-4 text-center">
|
||||
@@ -362,7 +344,7 @@ export default function Home() {
|
||||
{/* Kuralların şeffaf anlatımına köprü: /meraklisina */}
|
||||
<Link
|
||||
href="/meraklisina"
|
||||
className="group inline-flex items-center gap-1.5 rounded-full bg-orange-500 px-3.5 py-1.5 font-bricolage text-xs font-semibold text-white transition-colors duration-200 hover:bg-orange-600"
|
||||
className="group inline-flex min-h-8 items-center gap-1 rounded-full px-2 text-sm font-semibold text-slate-600 transition-colors duration-200 hover:text-orange-500"
|
||||
>
|
||||
Meraklısına
|
||||
<ArrowUpRight
|
||||
@@ -381,34 +363,10 @@ export default function Home() {
|
||||
</Reveal>
|
||||
</Parallax>
|
||||
{/* Adımlar kartsız (DESIGN.md §4.32): numara hero'nun piksel
|
||||
diliyle iri rakam, md'den itibaren rakamlar düz çizgiyle
|
||||
birbirine bağlanır. */}
|
||||
<Parallax strength={7} className="mt-12">
|
||||
<ol className="grid gap-12 md:grid-cols-3 md:gap-8">
|
||||
{steps.map((s, i) => (
|
||||
<li key={s.step}>
|
||||
<Reveal
|
||||
delay={i * 0.08}
|
||||
className="relative flex flex-col items-center text-center"
|
||||
>
|
||||
<PixelRakam rakam={s.step} className="h-24 w-auto" />
|
||||
{i < steps.length - 1 ? (
|
||||
<span
|
||||
aria-hidden
|
||||
className="absolute top-12 left-[calc(50%+4.5rem)] hidden h-0.5 w-[calc(100%-7rem)] -translate-y-1/2 rounded-full bg-slate-200 md:block"
|
||||
/>
|
||||
) : null}
|
||||
<h3 className="mt-6 font-heading text-xl font-bold text-balance">
|
||||
<span className="sr-only">{s.step}. adım: </span>
|
||||
{s.title}
|
||||
</h3>
|
||||
<p className="mt-2 max-w-sm text-base leading-relaxed text-slate-600">
|
||||
{s.description}
|
||||
</p>
|
||||
</Reveal>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
diliyle iri rakam; her rakam sırayla adımının biçimine dönüşüp
|
||||
geri toplanır, md'den itibaren aradaki düz çizgi dolar. */}
|
||||
<Parallax strength={7} className="mt-12 sm:mt-16">
|
||||
<AdimAkisi adimlar={steps} yilSayisi={VERI_YIL_SAYISI} />
|
||||
</Parallax>
|
||||
|
||||
{/* Veri akışı + veriye dayalı güven + dürüstlük notu (DESIGN.md
|
||||
@@ -416,10 +374,10 @@ export default function Home() {
|
||||
ekranda solda dürüstlük notu (sola yaslı), ortada animasyon,
|
||||
sağda (YÖK Atlas girdilerinin yanında) üç sayı (sağa yaslı).
|
||||
lg altında hepsi ortalı; sıra: animasyon → sayılar → not. */}
|
||||
<Parallax strength={10} className="mt-20">
|
||||
<Parallax strength={10} className="mt-24 sm:mt-32">
|
||||
<Reveal
|
||||
delay={0.1}
|
||||
className="grid items-center gap-12 lg:grid-cols-[minmax(0,1fr)_22rem_minmax(0,1fr)] lg:gap-8 xl:grid-cols-[minmax(0,1fr)_24rem_minmax(0,1fr)] xl:gap-12"
|
||||
className="grid items-center gap-16 lg:grid-cols-[minmax(0,1fr)_22rem_minmax(0,1fr)] lg:gap-8 xl:grid-cols-[minmax(0,1fr)_24rem_minmax(0,1fr)] xl:gap-12"
|
||||
>
|
||||
<VeriAkisi className="lg:order-2" />
|
||||
|
||||
@@ -432,7 +390,7 @@ export default function Home() {
|
||||
{stat.label}
|
||||
</dt>
|
||||
<dd className="order-1 font-heading text-4xl font-bold leading-none tracking-tight text-primary sm:text-5xl">
|
||||
{stat.value}
|
||||
<SayanSayi deger={stat.value} />
|
||||
{stat.birim ? (
|
||||
<span className="ml-1.5 font-sans text-base font-semibold tracking-normal text-slate-400">
|
||||
{stat.birim}
|
||||
@@ -463,14 +421,10 @@ export default function Home() {
|
||||
</section>
|
||||
|
||||
{/* Nasıl çalışır → Karşılaştırma geçişi */}
|
||||
<div aria-hidden className="mx-auto w-56">
|
||||
<Parallax strength={26}>
|
||||
<PixelDivider seed={23} />
|
||||
</Parallax>
|
||||
</div>
|
||||
<BolumGecisi tohum={23} />
|
||||
|
||||
{/* Comparison */}
|
||||
<section id="karsilastirma" className="scroll-mt-24 py-20 sm:py-24">
|
||||
<section id="karsilastirma" className={`scroll-mt-24 ${BOLUM_BOSLUGU}`}>
|
||||
<div className="mx-auto max-w-4xl px-4">
|
||||
<Parallax strength={16}>
|
||||
<Reveal className="flex flex-col items-center gap-4 text-center">
|
||||
@@ -488,7 +442,7 @@ export default function Home() {
|
||||
üstte yapışık durur (site başlığının hemen altında: telefonda 96 px,
|
||||
sm'den itibaren 128 px — ölçüldü).
|
||||
lg'de klasik dört sütunlu tablo. */}
|
||||
<Parallax strength={7} className="mt-12">
|
||||
<Parallax strength={7} className="mt-12 sm:mt-16">
|
||||
<Reveal delay={0.08}>
|
||||
<div className="rounded-2xl border border-slate-200/70 bg-card shadow-sm lg:hidden">
|
||||
<div
|
||||
@@ -502,8 +456,12 @@ export default function Home() {
|
||||
</span>
|
||||
</div>
|
||||
<ul className="divide-y divide-slate-100">
|
||||
{comparison.map((row) => (
|
||||
<li key={row.feature} className="px-3 py-5">
|
||||
{comparison.map((row, i) => (
|
||||
<li
|
||||
key={row.feature}
|
||||
className="px-3 py-5"
|
||||
style={{ "--tik-g": `${i * 70}ms` } as React.CSSProperties}
|
||||
>
|
||||
<p className="px-1 text-center text-base font-semibold leading-snug text-slate-900">
|
||||
{row.feature}
|
||||
</p>
|
||||
@@ -551,10 +509,11 @@ export default function Home() {
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{comparison.map((row) => (
|
||||
{comparison.map((row, i) => (
|
||||
<TableRow
|
||||
key={row.feature}
|
||||
className="hover:bg-transparent"
|
||||
style={{ "--tik-g": `${i * 70}ms` } as React.CSSProperties}
|
||||
>
|
||||
<TableCell className="whitespace-normal py-4 pl-5 text-base font-semibold text-slate-900">
|
||||
{row.feature}
|
||||
@@ -582,14 +541,10 @@ export default function Home() {
|
||||
</section>
|
||||
|
||||
{/* Karşılaştırma → Fiyat geçişi */}
|
||||
<div aria-hidden className="mx-auto w-56">
|
||||
<Parallax strength={26}>
|
||||
<PixelDivider seed={29} />
|
||||
</Parallax>
|
||||
</div>
|
||||
<BolumGecisi tohum={29} />
|
||||
|
||||
{/* Fiyat */}
|
||||
<section id="fiyat" className="scroll-mt-24 py-20 sm:py-24">
|
||||
<section id="fiyat" className={`scroll-mt-24 ${BOLUM_BOSLUGU}`}>
|
||||
<div className="mx-auto max-w-4xl px-4">
|
||||
<Parallax strength={16}>
|
||||
<Reveal className="flex flex-col items-center gap-4 text-center">
|
||||
@@ -610,7 +565,7 @@ export default function Home() {
|
||||
|
||||
<Parallax
|
||||
strength={7}
|
||||
className="mt-12 grid gap-6 sm:grid-cols-[1fr_auto]"
|
||||
className="mt-12 grid gap-6 sm:mt-16 sm:grid-cols-[1fr_auto]"
|
||||
>
|
||||
<Reveal
|
||||
delay={0.08}
|
||||
@@ -648,10 +603,7 @@ export default function Home() {
|
||||
<SosyalKanitBandi />
|
||||
</Suspense>
|
||||
|
||||
<Button
|
||||
asChild
|
||||
className="h-12 w-full cursor-pointer bg-orange-500 px-8 text-white transition-colors duration-200 hover:bg-orange-600"
|
||||
>
|
||||
<Button asChild size="lg" variant="cta" className="w-full">
|
||||
<Link href="/paket?kaynak=fiyat_paket">
|
||||
{PAKET_FIYATI} TL — Paketi incele
|
||||
<ArrowRight className="size-4" aria-hidden />
|
||||
@@ -677,11 +629,7 @@ export default function Home() {
|
||||
+{URUNLER.topup.credits} soru hakkı, {TOPUP_FIYATI} TL. Pakete
|
||||
ek olarak, istediğin zaman.
|
||||
</p>
|
||||
<Button
|
||||
asChild
|
||||
variant="outline"
|
||||
className="mt-4 h-11 w-full cursor-pointer"
|
||||
>
|
||||
<Button asChild variant="ikincil" className="mt-4 h-11 w-full">
|
||||
<Link href="/paket?kaynak=fiyat_kredi">
|
||||
+{URUNLER.topup.credits} kredi — {TOPUP_FIYATI} TL
|
||||
<ArrowRight className="size-4" aria-hidden />
|
||||
@@ -695,12 +643,8 @@ export default function Home() {
|
||||
{/* Katalog: süzülen haplar (DESIGN.md §4.30) */}
|
||||
{haplar.length >= 6 ? (
|
||||
<>
|
||||
<div aria-hidden className="mx-auto w-56">
|
||||
<Parallax strength={26}>
|
||||
<PixelDivider seed={41} />
|
||||
</Parallax>
|
||||
</div>
|
||||
<section id="katalog" className="scroll-mt-24 py-20 sm:py-24">
|
||||
<BolumGecisi tohum={41} />
|
||||
<section id="katalog" className={`scroll-mt-24 ${BOLUM_BOSLUGU}`}>
|
||||
<div className="mx-auto max-w-6xl px-4">
|
||||
<Parallax strength={16}>
|
||||
<Reveal className="flex flex-col items-center gap-4 text-center">
|
||||
@@ -719,8 +663,7 @@ export default function Home() {
|
||||
<Button
|
||||
asChild
|
||||
size="lg"
|
||||
variant="outline"
|
||||
className="cursor-pointer px-6 transition-[background-color,border-color,color,transform] duration-150 hover:border-orange-500 hover:bg-orange-50/60 hover:text-orange-500 active:scale-[0.97]"
|
||||
variant="ikincil"
|
||||
>
|
||||
<Link href="/bolumler">
|
||||
Tüm bölümleri gör
|
||||
@@ -734,14 +677,10 @@ export default function Home() {
|
||||
) : null}
|
||||
|
||||
{/* Fiyat → SSS geçişi */}
|
||||
<div aria-hidden className="mx-auto w-56">
|
||||
<Parallax strength={26}>
|
||||
<PixelDivider seed={31} />
|
||||
</Parallax>
|
||||
</div>
|
||||
<BolumGecisi tohum={31} />
|
||||
|
||||
{/* FAQ */}
|
||||
<section id="sss" className="scroll-mt-24 py-20 sm:py-24">
|
||||
<section id="sss" className={`scroll-mt-24 ${BOLUM_BOSLUGU}`}>
|
||||
<div className="mx-auto max-w-3xl px-4">
|
||||
<Parallax strength={16}>
|
||||
<Reveal className="flex flex-col items-center gap-4 text-center">
|
||||
@@ -756,7 +695,7 @@ export default function Home() {
|
||||
<Accordion
|
||||
type="single"
|
||||
collapsible
|
||||
className="mt-10 rounded-2xl border border-slate-200/70 bg-card px-6 shadow-sm"
|
||||
className="mt-12 rounded-2xl border border-slate-200/70 bg-card px-6 shadow-sm sm:mt-16"
|
||||
>
|
||||
{faqs.map((faq) => (
|
||||
<AccordionItem
|
||||
@@ -779,7 +718,7 @@ export default function Home() {
|
||||
</section>
|
||||
|
||||
{/* Final CTA — tam genişlik bant yerine yüzen, köşeleri yumuşak panel */}
|
||||
<section className="px-4 pb-24 pt-6 sm:px-6">
|
||||
<section className="px-4 pb-24 pt-6 sm:px-6 sm:pb-32">
|
||||
<Parallax strength={12} className="mx-auto max-w-6xl">
|
||||
<Reveal>
|
||||
{/* Kapanış yüzeyi (DESIGN.md §10.8): koyu kart + 24'lük
|
||||
|
||||
Reference in New Issue
Block a user