Compare commits

..

2 Commits

Author SHA1 Message Date
bilalgursen
c90c186b86 fix(tasarim): kapanış kartında piksel tablosunun arkası boş — dokuya bosBant/yogunluk
All checks were successful
Deploy / deploy (push) Successful in 4m30s
Bilal (24 Eyl 2026): ana sayfadaki piksel tablosunun arkasına çok piksel
yığılmıştı. PixelMasonry/PikselZemin boş bant ve yoğunluk alıyor;
kapanış kartı lg'de doku yalnız sol %16'da ve seyrek, altında liste
üstte ortada boş bantta. DESIGN.md §10.8: kreatif ile doku üst üste binmez.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 12:18:39 +03:00
bilalgursen
09c6868089 feat(tasarim): her CTA benzeri yüzeyin arkasında masonry piksel zemin (PikselZemin)
Bilal'in isteği (24 Eyl 2026): piksel arka planı her CTA benzeri kısmın
arkasında, büyüklü küçüklü masonry. Tek kaynak PikselZemin (açık kartta
silik mavi, koyu/mavi yüzeyde beyaz); CtaBandi ve PikselCtaYuzeyi de
onu kullanır. Eklenen yüzeyler: /sonuc "listen hazır" kartı, /listem
dört üretim kartı + boş durum + kilit kartı, tadımlık kartı, sıralama-yok
sayfası, meraklısına kapanış kartı, ana sayfa ve /paket ana paket kartı,
kaydet bandı, katalog üstü kompakt satır, arama penceresi kartı.
Kapsam dışı (form, durum kartı, modal düğmesi, 404) DESIGN.md §10.8'de.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 12:16:54 +03:00
17 changed files with 138 additions and 38 deletions

View File

@@ -689,7 +689,20 @@ karar.
`-inset-y-10`, h2 `font-heading text-2xl font-bold sm:text-3xl`, paragraf `text-white/90`. `-inset-y-10`, h2 `font-heading text-2xl font-bold sm:text-3xl`, paragraf `text-white/90`.
- Kullanıldığı yerler: `/sonuc`'taki iki sihirbaz paneli, katalog altındaki `CtaSiraForm` - Kullanıldığı yerler: `/sonuc`'taki iki sihirbaz paneli, katalog altındaki `CtaSiraForm`
kart varyantı, "Kendi Listem" çekmecesinin altı (kompakt). Aynı sayfadaki iki band farklı kart varyantı, "Kendi Listem" çekmecesinin altı (kompakt). Aynı sayfadaki iki band farklı
`seed` alır. Band üstündeki ikincil metin `text-white/90`, ince yazı `text-white/70`; `seed` alır.
**Her CTA benzeri yüzeyin arkasında piksel zemin — `PikselZemin`, `src/components/piksel-zemin.tsx`**
(Bilal, 24 Eyl 2026: "piksel arka planları her CTA benzeri kısmın arkasında olmalı, masonry gibi
büyüklü küçüklü"). CTA benzeri yüzey = tek bir aksiyona çağıran kart, bant, boş durum ya da
sayfa bölümü. Kap `relative isolate overflow-hidden`; zemin ilk çocuk. Açık kartta
`ton="acik"` (silik mavi, `text-primary/70`), mavi bant ve koyu kartta `ton="koyu"` (beyaz).
`CtaBandi` ve `PikselCtaYuzeyi` de bunu kullanır. Kapsananlar: `/sonuc` "24 tercihlik listen
hazır" kartı, `/listem` dört üretim kartı + boş durum + kilit kartı, tadımlık kartı,
sıralama-yok sayfası, meraklısına kapanış kartı, ana sayfa ve `/paket` ana paket kartı, kaydet
bandı, katalog üstü kompakt satır, arama penceresi kartı. **Kapsam dışı:** form kartları (giriş,
ödeme formu), durum kartları (ödeme sonucu), modal düğmeleri, hata/404 sayfası, yan kredi
kartları — bunlar tek aksiyona çağıran yüzey değil. Yeni bir CTA yüzeyi açan ajan zemini
ekler ve bu listeye yazar. Band üstündeki ikincil metin `text-white/90`, ince yazı `text-white/70`;
`slate-300`/`slate-400` yazılmaz. `slate-300`/`slate-400` yazılmaz.
**Kapanış yüzeyi — `PikselCtaYuzeyi`, `src/components/piksel-cta-yuzeyi.tsx` (yalnız ana **Kapanış yüzeyi — `PikselCtaYuzeyi`, `src/components/piksel-cta-yuzeyi.tsx` (yalnız ana
@@ -705,6 +718,11 @@ yok** (Bilal, 24 Eyl 2026: kaldırıldı); `FUNNEL_METINLERI` yalnız arama penc
içindeki düğmeye imleç/odak gelince 24 kare birden dolar; dokunmada basılan kare ve komşuları içindeki düğmeye imleç/odak gelince 24 kare birden dolar; dokunmada basılan kare ve komşuları
nabız atar (`kt-piksel-nabiz`). Hareketi azalt: dolu, durağan. Renk her zaman nabız atar (`kt-piksel-nabiz`). Hareketi azalt: dolu, durağan. Renk her zaman
`PikselListeAciklama` metniyle. `PikselListeAciklama` metniyle.
- Piksel tablosunun arkası boş kalır (Bilal, 24 Eyl 2026: "arkasına çok piksel yığılmış"):
telefon/tablette liste üstte ortada, dokunun boş bandında; `lg`'de liste sağ sütunda olduğu
için doku yalnız sol %16'da ve seyrek (`PikselZemin bosBant={[0.16, 1]} yogunluk={0.7}`).
Kural genel: bir kreatif ya da iri sayı piksel zeminin üstüne geliyorsa `bosBant` ile o
bölge boşaltılır; doku ile kreatif üst üste binmez.
- Bu yüzey **sayfada bir kez** ve yalnız kapanışta durur; ara paneller ve çekmece mavi banttır. - Bu yüzey **sayfada bir kez** ve yalnız kapanışta durur; ara paneller ve çekmece mavi banttır.
Yeni bir kreatif fikri (veriyi piksele çeviren, imlece tepki veren) önce burada denenir. Yeni bir kreatif fikri (veriyi piksele çeviren, imlece tepki veren) önce burada denenir.
- Çekmece başlığında denenen "gerçek liste" pikseli (12×2, her kare tercihin riski) **kaldırıldı**: - Çekmece başlığında denenen "gerçek liste" pikseli (12×2, her kare tercihin riski) **kaldırıldı**:

View File

@@ -28,6 +28,7 @@ import {
SectionEyebrow, SectionEyebrow,
} from "@/components/pixel-decor"; } from "@/components/pixel-decor";
import { PikselCtaYuzeyi } from "@/components/piksel-cta-yuzeyi"; import { PikselCtaYuzeyi } from "@/components/piksel-cta-yuzeyi";
import { PikselZemin } from "@/components/piksel-zemin";
import { MAX_REVIZYON, URUNLER } from "@/lib/credits"; import { MAX_REVIZYON, URUNLER } from "@/lib/credits";
import { VERI_YIL_SAYISI } from "@/lib/veri-kapsami"; import { VERI_YIL_SAYISI } from "@/lib/veri-kapsami";
import { import {
@@ -554,8 +555,9 @@ export default function Home() {
> >
<Reveal <Reveal
delay={0.08} delay={0.08}
className="rounded-2xl border-2 border-orange-500 bg-white p-8 shadow-sm" className="relative isolate overflow-hidden rounded-2xl border-2 border-orange-500 bg-white p-8 shadow-sm"
> >
<PikselZemin seed={43} />
<div className="flex flex-wrap items-baseline justify-between gap-2"> <div className="flex flex-wrap items-baseline justify-between gap-2">
<h3 className="font-heading text-xl font-bold"> <h3 className="font-heading text-xl font-bold">
{URUNLER.paket.label} {URUNLER.paket.label}

View File

@@ -1,5 +1,6 @@
"use client"; "use client";
import { PikselZemin } from "@/components/piksel-zemin";
import { useState } from "react"; import { useState } from "react";
import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { import {
@@ -70,8 +71,9 @@ export function AramaFunnelKarti({ onSec }: { onSec: () => void }) {
return ( return (
<section <section
aria-label="Tercih listeni kur" aria-label="Tercih listeni kur"
className="mt-5 overflow-hidden rounded-2xl border border-blue-400/20 bg-slate-950 px-4 py-5 text-white sm:px-5" className="relative isolate mt-5 overflow-hidden rounded-2xl border border-blue-400/20 bg-slate-950 px-4 py-5 text-white sm:px-5"
> >
<PikselZemin seed={61} ton="koyu" />
<div className="grid items-center gap-4 sm:grid-cols-[minmax(0,1fr)_auto] sm:gap-5"> <div className="grid items-center gap-4 sm:grid-cols-[minmax(0,1fr)_auto] sm:gap-5">
<div className="min-w-0"> <div className="min-w-0">
<TypingAnimation <TypingAnimation

View File

@@ -1,7 +1,7 @@
"use client"; "use client";
import type { ComponentProps, Ref } from "react"; import type { ComponentProps, Ref } from "react";
import { PixelMasonry } from "@/components/pixel-decor"; import { PikselZemin } from "@/components/piksel-zemin";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
/** /**
@@ -44,15 +44,7 @@ export function CtaBandi({
)} )}
{...props} {...props}
> >
<div <PikselZemin seed={seed} ton="koyu" className={buyuk ? "-inset-y-10" : undefined} />
aria-hidden
className={cn(
"pointer-events-none absolute inset-x-0 -z-10 text-white",
buyuk ? "-inset-y-10" : "-inset-y-6",
)}
>
<PixelMasonry seed={seed} />
</div>
{children} {children}
</Etiket> </Etiket>
); );

View File

@@ -2,7 +2,7 @@
import { useState, type ReactNode } from "react"; import { useState, type ReactNode } from "react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { PixelMasonry } from "@/components/pixel-decor"; import { PikselZemin } from "@/components/piksel-zemin";
import { PikselListe, PikselListeAciklama } from "@/components/piksel-liste"; import { PikselListe, PikselListeAciklama } from "@/components/piksel-liste";
/** /**
@@ -13,6 +13,9 @@ import { PikselListe, PikselListeAciklama } from "@/components/piksel-liste";
* düğmeye ya da linke imleç/odak gelince 24 kare birden dolar — "listen * düğmeye ya da linke imleç/odak gelince 24 kare birden dolar — "listen
* böyle hazır olur". Yazılıp silinen satır yok (Bilal, 24 Eyl 2026). * böyle hazır olur". Yazılıp silinen satır yok (Bilal, 24 Eyl 2026).
*/ */
// Geniş ekranda doku yalnız genişliğin ilk %16'sında (metnin sol boşluğu)
const SOL_KENAR: [number, number] = [0.16, 1];
export function PikselCtaYuzeyi({ export function PikselCtaYuzeyi({
children, children,
className, className,
@@ -39,12 +42,18 @@ export function PikselCtaYuzeyi({
className, className,
)} )}
> >
<div {/* Telefon/tablet: liste üstte ortada, dokunun boş bandında. Geniş
aria-hidden ekranda liste sağ sütunda — orası tamamen boş kalır, doku yalnız
className="pointer-events-none absolute inset-x-0 -inset-y-10 -z-10 text-white" sol kenarda ve seyrek (Bilal, 24 Eyl 2026: "tablonun arkasına çok
> piksel yığılmış"). */}
<PixelMasonry seed={seed} /> <PikselZemin seed={seed} ton="koyu" className="-inset-y-10 lg:hidden" />
</div> <PikselZemin
seed={seed}
ton="koyu"
bosBant={SOL_KENAR}
yogunluk={0.7}
className="-inset-y-10 hidden lg:block"
/>
<div className="grid items-center gap-8 px-5 py-10 sm:px-10 sm:py-12 lg:grid-cols-[minmax(0,1fr)_16rem] lg:gap-12"> <div className="grid items-center gap-8 px-5 py-10 sm:px-10 sm:py-12 lg:grid-cols-[minmax(0,1fr)_16rem] lg:gap-12">
<div className="flex min-w-0 flex-col items-center text-center lg:items-start lg:text-left"> <div className="flex min-w-0 flex-col items-center text-center lg:items-start lg:text-left">
{children} {children}

View File

@@ -0,0 +1,44 @@
"use client";
import { PixelMasonry } from "@/components/pixel-decor";
import { cn } from "@/lib/utils";
/**
* CTA yüzeylerinin piksel zemini (DESIGN.md §10.8) — tek kaynak. Bilal'in
* kararı (24 Eyl 2026): "piksel arka planları her CTA benzeri kısmın
* arkasında olmalı, masonry gibi büyüklü küçüklü." Kap `relative isolate
* overflow-hidden` olmalı; zemin ilk çocuk olarak konur.
*
* - `acik`: beyaz/açık kart — silik mavi kareler.
* - `koyu`: mavi bant ya da koyu kart — beyaz kareler.
*/
export function PikselZemin({
seed = 7,
ton = "acik",
bosBant,
yogunluk,
className,
}: {
/** Aynı ekrandaki iki yüzey farklı desen alsın */
seed?: number;
ton?: "acik" | "koyu";
/** Boş bırakılacak yatay bant (varsayılan orta %22–78); bkz. PixelMasonry */
bosBant?: [number, number];
/** Doluluk çarpanı */
yogunluk?: number;
/** Dikey taşma payı vb. (varsayılan `-inset-y-6`) */
className?: string;
}) {
return (
<div
aria-hidden
className={cn(
"pointer-events-none absolute inset-x-0 -inset-y-6 -z-10",
ton === "acik" ? "text-primary/70" : "text-white",
className,
)}
>
<PixelMasonry seed={seed} bosBant={bosBant} yogunluk={yogunluk} />
</div>
);
}

View File

@@ -80,11 +80,21 @@ function pixelRect(
* (§1.1 "doğal duruş"). Durağan zemin: imleç ısı fırçasına katılmaz * (§1.1 "doğal duruş"). Durağan zemin: imleç ısı fırçasına katılmaz
* (Bilal, 24 Eyl 2026: "üzerine gelindiğinde bir şey olmasın"). * (Bilal, 24 Eyl 2026: "üzerine gelindiğinde bir şey olmasın").
*/ */
// Varsayılan boş bant modül sabiti: her render'da yeni dizi olup useMemo'yu
// boşa düşürmesin (rendering-hoist-jsx)
const BOS_BANT_VARSAYILAN: [number, number] = [0.22, 0.78];
export function PixelMasonry({ export function PixelMasonry({
seed = 7, seed = 7,
bosBant = BOS_BANT_VARSAYILAN,
yogunluk = 1,
className, className,
}: { }: {
seed?: number; seed?: number;
/** Genişliğin bu oranları arasındaki bant boş kalır (içerik orada) */
bosBant?: [number, number];
/** Doluluk çarpanı; 1 varsayılan, 0.6 seyrek */
yogunluk?: number;
className?: string; className?: string;
}) { }) {
const COLS = 42; const COLS = 42;
@@ -96,8 +106,8 @@ export function PixelMasonry({
const cx = (COLS - 1) / 2; const cx = (COLS - 1) / 2;
const cy = (ROWS - 1) / 2; const cy = (ROWS - 1) / 2;
const maxD = Math.sqrt(cx * cx + cy * cy); const maxD = Math.sqrt(cx * cx + cy * cy);
const BAND_MIN = COLS * 0.22; const BAND_MIN = COLS * bosBant[0];
const BAND_MAX = COLS * 0.78; const BAND_MAX = COLS * bosBant[1];
const sonuc: ReactNode[] = []; const sonuc: ReactNode[] = [];
let accentIdx = 0; let accentIdx = 0;
@@ -145,10 +155,10 @@ export function PixelMasonry({
if (mcx > BAND_MIN && mcx < BAND_MAX) continue; if (mcx > BAND_MIN && mcx < BAND_MAX) continue;
// Kenarlara doğru artan doluluk // Kenarlara doğru artan doluluk
const d = Math.sqrt((mcx - cx) ** 2 + (oy + MAKRO / 2 - cy) ** 2) / maxD; const d = Math.sqrt((mcx - cx) ** 2 + (oy + MAKRO / 2 - cy) ** 2) / maxD;
const density = 0.3 + 0.6 * d * d; const density = (0.3 + 0.6 * d * d) * yogunluk;
if (hash(mx, my, seed) > density) { if (hash(mx, my, seed) > density) {
// Boş makro hücrede ara sıra tek küçük piksel: doku kopmasın // Boş makro hücrede ara sıra tek küçük piksel: doku kopmasın
if (hash(mx, my, seed + 3) < 0.25) { if (hash(mx, my, seed + 3) < 0.25 * yogunluk) {
const px = ox + Math.floor(hash(mx, my, seed + 4) * MAKRO); const px = ox + Math.floor(hash(mx, my, seed + 4) * MAKRO);
const py = oy + Math.floor(hash(mx, my, seed + 5) * MAKRO); const py = oy + Math.floor(hash(mx, my, seed + 5) * MAKRO);
sonuc.push(kare(px, py, 1, `k-${mx}-${my}`, false)); sonuc.push(kare(px, py, 1, `k-${mx}-${my}`, false));
@@ -171,7 +181,7 @@ export function PixelMasonry({
} }
} }
return sonuc; return sonuc;
}, [seed]); }, [seed, bosBant, yogunluk]);
return ( return (
<svg <svg

View File

@@ -6,6 +6,7 @@
// hariç her yerde görünür; kapatma oturum boyunca kalıcıdır (sessionStorage). // hariç her yerde görünür; kapatma oturum boyunca kalıcıdır (sessionStorage).
// Exit-intent/agresif popup YOK (PRD kuralı). // Exit-intent/agresif popup YOK (PRD kuralı).
import { PikselZemin } from "@/components/piksel-zemin";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Link from "next/link"; import Link from "next/link";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
@@ -60,8 +61,9 @@ export function KaydetBanner() {
ref={gorunumRef} ref={gorunumRef}
// Telefonda metin kendi satırında, düğme altında tam genişlik: // Telefonda metin kendi satırında, düğme altında tam genişlik:
// yan yana dizince başlık kelime kelime kırılıyordu (375 px). // yan yana dizince başlık kelime kelime kırılıyordu (375 px).
className="pointer-events-auto relative mx-auto flex max-w-xl flex-wrap items-center gap-3 rounded-2xl border border-slate-200 bg-white p-4 pr-14 shadow-xl sm:flex-nowrap sm:gap-4" className="pointer-events-auto relative isolate mx-auto flex max-w-xl flex-wrap items-center gap-3 overflow-hidden rounded-2xl border border-slate-200 bg-white p-4 pr-14 shadow-xl sm:flex-nowrap sm:gap-4"
> >
<PikselZemin seed={47} />
{/* Kullanıcının o anki işletim sisteminin logosu: "bu tarayıcıda" {/* Kullanıcının o anki işletim sisteminin logosu: "bu tarayıcıda"
mesajını somutlaştırır */} mesajını somutlaştırır */}
<div className="flex size-10 shrink-0 items-center justify-center rounded-xl border border-slate-200 bg-slate-50 text-slate-700"> <div className="flex size-10 shrink-0 items-center justify-center rounded-xl border border-slate-200 bg-slate-50 text-slate-700">

View File

@@ -1,3 +1,4 @@
import { PikselZemin } from "@/components/piksel-zemin";
import { Suspense } from "react"; import { Suspense } from "react";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
@@ -58,7 +59,8 @@ export function PaketSatinAl() {
className="mt-10 grid gap-6 sm:grid-cols-[1fr_auto]" className="mt-10 grid gap-6 sm:grid-cols-[1fr_auto]"
> >
{/* Ana paket */} {/* Ana paket */}
<section className="rounded-2xl border-2 border-orange-500 bg-white p-8 shadow-sm"> <section className="relative isolate overflow-hidden rounded-2xl border-2 border-orange-500 bg-white p-8 shadow-sm">
<PikselZemin seed={43} />
<div className="flex items-baseline justify-between"> <div className="flex items-baseline justify-between">
<h2 className="font-heading text-xl font-bold"> <h2 className="font-heading text-xl font-bold">
Tercih Dönemi Paketi Tercih Dönemi Paketi

View File

@@ -7,6 +7,7 @@
// context'ten okur. (Sihirbaz hunisi statik bir şemayla anlatılır — bkz. // context'ten okur. (Sihirbaz hunisi statik bir şemayla anlatılır — bkz.
// huni-semasi.tsx.) // huni-semasi.tsx.)
import { PikselZemin } from "@/components/piksel-zemin";
import { import {
createContext, createContext,
useContext, useContext,
@@ -234,7 +235,8 @@ export function DemoKapanisCta() {
const profil = useTercihProfili(); const profil = useTercihProfili();
return ( return (
<div className="rounded-2xl border border-slate-200 bg-white p-6 text-center"> <div className="relative isolate overflow-hidden rounded-2xl border border-slate-200 bg-white p-6 text-center">
<PikselZemin seed={41} />
<p className="font-heading text-base font-bold text-slate-900"> <p className="font-heading text-base font-bold text-slate-900">
{profil {profil
? "Kuralları öğrendin — sayılar zaten senin sıralamanla hesaplandı." ? "Kuralları öğrendin — sayılar zaten senin sıralamanla hesaplandı."

View File

@@ -22,6 +22,7 @@
// Kural: action cevabının hemen ardından yönlendiriciye TEK aksiyon gönder // Kural: action cevabının hemen ardından yönlendiriciye TEK aksiyon gönder
// (router.replace); ikinci aksiyon (refresh) ancak o gezinme bitince. // (router.replace); ikinci aksiyon (refresh) ancak o gezinme bitince.
import { PikselZemin } from "@/components/piksel-zemin";
import { useEffect, useRef, useState, type ReactNode } from "react"; import { useEffect, useRef, useState, type ReactNode } from "react";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
@@ -264,7 +265,8 @@ export function ListeUretici({
// Paketli kullanıcıya paket satma: onun ürünü top-up. Liste ve sohbet // Paketli kullanıcıya paket satma: onun ürünü top-up. Liste ve sohbet
// geçmişi kalıcı; kredi yükleyince kaldığı yerden devam eder. // geçmişi kalıcı; kredi yükleyince kaldığı yerden devam eder.
return ( return (
<section className="mx-auto w-full max-w-2xl rounded-2xl border border-slate-200 bg-white p-8 text-center sm:p-12"> <section className="relative isolate mx-auto w-full max-w-2xl overflow-hidden rounded-2xl border border-slate-200 bg-white p-8 text-center sm:p-12">
<PikselZemin seed={23} />
<Lock className="mx-auto size-8 text-slate-400" aria-hidden /> <Lock className="mx-auto size-8 text-slate-400" aria-hidden />
<h1 className="mt-4 font-heading text-xl font-bold"> <h1 className="mt-4 font-heading text-xl font-bold">
Kredin bu liste için yetmiyor Kredin bu liste için yetmiyor
@@ -294,7 +296,8 @@ export function ListeUretici({
if (durum.ad === "hata") { if (durum.ad === "hata") {
return ( return (
<section className="mx-auto w-full max-w-2xl rounded-2xl border border-slate-200 bg-white p-8 text-center sm:p-12"> <section className="relative isolate mx-auto w-full max-w-2xl overflow-hidden rounded-2xl border border-slate-200 bg-white p-8 text-center sm:p-12">
<PikselZemin seed={23} />
<h1 className="font-heading text-xl font-bold">Liste kurulamadı</h1> <h1 className="font-heading text-xl font-bold">Liste kurulamadı</h1>
<p className="mx-auto mt-2 max-w-md text-sm text-slate-600"> <p className="mx-auto mt-2 max-w-md text-sm text-slate-600">
{durum.mesaj} {durum.mesaj}
@@ -344,7 +347,8 @@ function SunucuYokEkrani() {
} }
return ( return (
<section className="mx-auto w-full max-w-2xl rounded-2xl border border-slate-200 bg-white p-8 text-center sm:p-12"> <section className="relative isolate mx-auto w-full max-w-2xl overflow-hidden rounded-2xl border border-slate-200 bg-white p-8 text-center sm:p-12">
<PikselZemin seed={23} />
<h1 className="font-heading text-xl font-bold"> <h1 className="font-heading text-xl font-bold">
Listen hazır, sunucuya şu an ulaşamıyoruz Listen hazır, sunucuya şu an ulaşamıyoruz
</h1> </h1>
@@ -375,7 +379,8 @@ function SunucuYokEkrani() {
* "Tekrar dene" yok: tekrar taze requestId üretip ikinci üretimi başlatırdı. */ * "Tekrar dene" yok: tekrar taze requestId üretip ikinci üretimi başlatırdı. */
function SuruyorEkrani() { function SuruyorEkrani() {
return ( return (
<section className="mx-auto w-full max-w-2xl rounded-2xl border border-slate-200 bg-white p-8 text-center sm:p-12"> <section className="relative isolate mx-auto w-full max-w-2xl overflow-hidden rounded-2xl border border-slate-200 bg-white p-8 text-center sm:p-12">
<PikselZemin seed={23} />
<h1 className="font-heading text-xl font-bold">Listen zaten hazırlanıyor</h1> <h1 className="font-heading text-xl font-bold">Listen zaten hazırlanıyor</h1>
<p className="mx-auto mt-2 max-w-md text-sm text-slate-600"> <p className="mx-auto mt-2 max-w-md text-sm text-slate-600">
Bunu başka bir sekmede başlatmışsın. İki kez kurmayalım — kredin bu Bunu başka bir sekmede başlatmışsın. İki kez kurmayalım — kredin bu

View File

@@ -4,6 +4,7 @@
// ekranda. Harita ⇄ liste köprüsü (pinden satıra, satırdan haritaya) ve // ekranda. Harita ⇄ liste köprüsü (pinden satıra, satırdan haritaya) ve
// paketsiz paywall kartları burada; veri çekimi sunucuda (page.tsx) kalır. // paketsiz paywall kartları burada; veri çekimi sunucuda (page.tsx) kalır.
import { PikselZemin } from "@/components/piksel-zemin";
import { useMemo, useRef, useState } from "react"; import { useMemo, useRef, useState } from "react";
import Link from "next/link"; import Link from "next/link";
import { import {
@@ -337,8 +338,9 @@ export function ListemGovde({
<section <section
ref={kilitKartRef} ref={kilitKartRef}
className="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm" className="relative isolate overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm"
> >
<PikselZemin seed={59} />
<div className="border-b border-slate-100 p-6"> <div className="border-b border-slate-100 p-6">
<h3 className="font-heading text-xl font-bold tracking-tight"> <h3 className="font-heading text-xl font-bold tracking-tight">
Tablo ücretsiz. Tablo ücretsiz.

View File

@@ -19,6 +19,7 @@ import { raporMaskele, ACIK_SATIR } from "@/lib/rapor-maske";
import type { Mesaj } from "./sohbet-client"; import type { Mesaj } from "./sohbet-client";
import { ListemGovde } from "./listem-govde"; import { ListemGovde } from "./listem-govde";
import { ListemBosCta } from "./listem-bos-cta"; import { ListemBosCta } from "./listem-bos-cta";
import { PikselZemin } from "@/components/piksel-zemin";
import { ListeUretici } from "./liste-uretici"; import { ListeUretici } from "./liste-uretici";
import { UretimBasladiOlayi } from "./uretim-basladi-olayi"; import { UretimBasladiOlayi } from "./uretim-basladi-olayi";
@@ -65,7 +66,8 @@ export async function ListemIcerik({
if (!raporVar) { if (!raporVar) {
const bosDurum = ( const bosDurum = (
<section className="mx-auto flex max-w-2xl flex-col items-center px-4 py-16 text-center"> <section className="relative isolate mx-auto flex max-w-2xl flex-col items-center overflow-hidden px-4 py-16 text-center">
<PikselZemin seed={29} />
<Sparkles className="size-10 text-primary" aria-hidden /> <Sparkles className="size-10 text-primary" aria-hidden />
<h1 className="mt-4 font-heading text-2xl font-bold"> <h1 className="mt-4 font-heading text-2xl font-bold">
Henüz Yapay Zeka listen yok Henüz Yapay Zeka listen yok

View File

@@ -8,6 +8,7 @@
// varsa /api/tadimlik'ten kategoriye özel satır çekilip yerinde değiştirilir // varsa /api/tadimlik'ten kategoriye özel satır çekilip yerinde değiştirilir
// (seçim sunucuda kalır). Havuzda içerik yoksa hiç render edilmez. // (seçim sunucuda kalır). Havuzda içerik yoksa hiç render edilmez.
import { PikselZemin } from "@/components/piksel-zemin";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import Link from "next/link"; import Link from "next/link";
import { ArrowRight } from "lucide-react"; import { ArrowRight } from "lucide-react";
@@ -150,7 +151,8 @@ export function TadimlikSatiri({
</p> </p>
</div> </div>
<div className="mt-6 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm sm:p-6"> <div className="relative isolate mt-6 overflow-hidden rounded-2xl border border-slate-200 bg-white p-4 shadow-sm sm:p-6">
<PikselZemin seed={31} />
{/* Rapor listesinin satırıyla aynı düzen (§4.15): ad sarar, {/* Rapor listesinin satırıyla aynı düzen (§4.15): ad sarar,
üniversite adı altında, taban sıra sağda iri. */} üniversite adı altında, taban sıra sağda iri. */}
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">

View File

@@ -23,6 +23,7 @@ import { authClient } from "@/lib/auth-client";
import { olay, siraKovasi } from "@/lib/analitik"; import { olay, siraKovasi } from "@/lib/analitik";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { CtaBandi } from "@/components/cta-bandi"; import { CtaBandi } from "@/components/cta-bandi";
import { PikselZemin } from "@/components/piksel-zemin";
import { onbellekliJsonGetir } from "@/lib/istek-cache"; import { onbellekliJsonGetir } from "@/lib/istek-cache";
import { useTercihProfili } from "@/features/liste/hooks/use-tercih-profili"; import { useTercihProfili } from "@/features/liste/hooks/use-tercih-profili";
import type { SihirbazFacetleri } from "@/types/yokatlas"; import type { SihirbazFacetleri } from "@/types/yokatlas";
@@ -373,10 +374,11 @@ export function CtaSiraForm({
<section <section
aria-labelledby={baslikId} aria-labelledby={baslikId}
className={cn( className={cn(
"rounded-2xl border p-4 transition-colors duration-200 sm:p-5", "relative isolate overflow-hidden rounded-2xl border p-4 transition-colors duration-200 sm:p-5",
profil ? "border-primary/20 bg-primary/5" : "border-slate-200 bg-white", profil ? "border-primary/20 bg-primary/5" : "border-slate-200 bg-white",
)} )}
> >
<PikselZemin seed={53} />
<div className="grid"> <div className="grid">
<div <div
ref={profil ? undefined : formRef} ref={profil ? undefined : formRef}

View File

@@ -8,6 +8,7 @@ import { animate } from "motion/mini";
import { useReducedMotion } from "motion/react"; import { useReducedMotion } from "motion/react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { CtaBandi } from "@/components/cta-bandi"; import { CtaBandi } from "@/components/cta-bandi";
import { PikselZemin } from "@/components/piksel-zemin";
import { useGorunumOlayi } from "@/components/use-gorunum-olayi"; import { useGorunumOlayi } from "@/components/use-gorunum-olayi";
import { olay, olayBirKez, siraKovasi } from "@/lib/analitik"; import { olay, olayBirKez, siraKovasi } from "@/lib/analitik";
import type { SihirbazFacetleri } from "@/types/yokatlas"; import type { SihirbazFacetleri } from "@/types/yokatlas";
@@ -262,7 +263,8 @@ export function SihirbazBolumu({
edilmez; panel yalnızca funnel CTA'larını gösterir, üretim ve edilmez; panel yalnızca funnel CTA'larını gösterir, üretim ve
listenin kendisi /listem'de yaşar. */} listenin kendisi /listem'de yaşar. */}
{raporVar ? ( {raporVar ? (
<section className="mt-16 rounded-2xl border border-slate-200 bg-white p-8 text-center sm:p-12"> <section className="relative isolate mt-16 overflow-hidden rounded-2xl border border-slate-200 bg-white p-8 text-center sm:p-12">
<PikselZemin seed={21} />
<h2 className="font-heading text-2xl font-bold sm:text-3xl"> <h2 className="font-heading text-2xl font-bold sm:text-3xl">
24 tercihlik listen hazır 24 tercihlik listen hazır
</h2> </h2>

View File

@@ -4,6 +4,7 @@
// sayfayı tazele (URL değişmez); yoksa kapıyı aç. Sıra bilgisi localStorage'daki // sayfayı tazele (URL değişmez); yoksa kapıyı aç. Sıra bilgisi localStorage'daki
// TercihProfili ile tüm uygulamada ortak, sunucu tablosu çerezden okur. // TercihProfili ile tüm uygulamada ortak, sunucu tablosu çerezden okur.
import { PikselZemin } from "@/components/piksel-zemin";
import { useEffect } from "react"; import { useEffect } from "react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import Link from "next/link"; import Link from "next/link";
@@ -42,7 +43,8 @@ export function SiraGerekli() {
} }
return ( return (
<main className="mx-auto flex max-w-2xl flex-col items-center px-4 py-24 text-center"> <main className="relative isolate mx-auto flex max-w-2xl flex-col items-center overflow-hidden px-4 py-24 text-center">
<PikselZemin seed={37} />
<h1 className="font-heading text-3xl font-bold text-balance text-slate-900 sm:text-4xl"> <h1 className="font-heading text-3xl font-bold text-balance text-slate-900 sm:text-4xl">
Önce sıralamanı gir Önce sıralamanı gir
</h1> </h1>