# 09 — Tasarım: `/sonuc` haritası en üste + üniversite sayfasında tür > Temel: `is/0923-harita-ve-tur` worktree'si, `origin/main` (c5769e2). Karşılaştırma için > `gece/0922-urap` dalı `git show` ile okundu. Tarayıcı/dev sunucusu açılmadı, kod değiştirilmedi. > Piksel ve CLS değerleri **token/kod okumasından hesaplandı**, ölçülmedi — hesap §1.2'de açık. > Her iki talimat da "yapılacak" kabul edildi; aşağıdakiler *nasıl* yapılacağıdır. --- ## Özet — 5 madde 1. **Harita en üste çıkabilir ve CLS bedeli sıfıra inebilir.** Tek gereken, listenin *sayısını* sunucunun hidrasyondan önce bilmesi. Profil zaten `kolaytercih.profil` çerezi ile sunucuya aynalanıyor (`sihirbaz-profil.ts:51-57`); aynı desenle `kolaytercih.liste-sayi` çerezi yazılır, `/sonuc` bunu okuyup haritaya **tam ölçülü** yer ayırır. Uygulama yükü: 3 dosya, ~25 satır. 2. **"Boşken hiç gözükmesin" şartı bozulmuyor.** Çerez yoksa ya da 0 ise `ManuelHarita` bugünkü gibi `null` döner — ilk kez gelen ziyaretçi için DOM'da hiçbir düğüm yoktur, dolayısıyla tadımlığın `first:mt-0` kuralı (`tadimlik-satiri.tsx:134`) da bozulmaz. **İlk ziyaretçide sıra değişmiyor — doğrulandı** (`manuel-harita.tsx:71` `return null`). 3. **Tadımlığın kaybı kabul edilebilir ve dar.** Kayıp yalnız "girişsiz + kayıtlı listesi olan" ziyaretçide var. 375×812'de tadımlığın rozeti, H2'si ve açıklaması harita eklendikten sonra da katlamanın üstünde kalıyor (hesap §1.4); yalnız örnek satır kartı aşağı iniyor. Bu ziyaretçi "bu ürün ne?" aşamasını geçmiş, ekranda önce kendi listesini görmesi doğru. 4. **Üniversite sayfasında tür, ızgaraya değil kimlik satırına gider.** Izgara 5 karta çıkamaz: `lg:grid-cols-[1fr_1fr_1.35fr] lg:grid-rows-2` şablonunda harita 2 satır kaplıyor, kartlar için **tam 4 hücre** var (`universite-icerik.tsx:291`, `universite-konum-haritasi.tsx:77`). Tür zaten bir *metrik* değil *kategori*; sitenin geri kalanında (4 ayrı dosyada) başlığın altındaki meta satırında çip olarak yaşıyor. Aynı çip H1'in altına konur, ızgara 4 sayısal kart kalır. 5. **Yan bulgu: `unitur` etiketinin 4 farklı sözlüğü var** ve ikisi bozuk metin basıyor (`universite-icerik.tsx:318` ve `bolum-program-listesi.tsx:227` → "Kktc", "Vakıf Myo"). `UNITUR_ETIKET` (bugün `gece/0922-urap`'ta, bileşenin içinde) `src/lib/`'e taşınmalı — tıpkı `DILIM_ETIKET`'in `src/lib/risk.ts`'te tek kaynak olması gibi (AGENTS.md). --- # Talimat 1 — `/sonuc`'ta harita en üste ## 1.1 Bugünkü durum (doğrulandı) | Olgu | Kanıt | |---|---| | Sıra: tadımlık → tablo → harita | `src/app/sonuc/page.tsx:118, 124, 131` | | Liste boşken harita hiç çizilmiyor | `manuel-harita.tsx:71` (`if (liste.length === 0) return null`) | | Sunucu snapshot'ı **her zaman** boş | `liste-store.ts:30, 86` (`getServerSnapshot → BOS_LISTE`) | | Liste yalnız localStorage'ta | `liste-store.ts:27, 42-46` | | Profil ise sunucuya **çerezle** aynalanıyor | `sihirbaz-profil.ts:51-57`, `sonuc/page.tsx:65` | | Store her sayfada hidrasyonda okunuyor | `layout.tsx:98-102` → `ListeCekmecesiLazy`/`KaydetBannerLazy` → `useManuelListe` | Son satır kritik: liste store'u **site genelinde**, ilk açılan sayfada yükleniyor. Yani yazılacak çerez, kullanıcı `/sonuc`'a varmadan önce (ana sayfada) yazılmış oluyor. ## 1.2 CLS hesabı — "368 px" rakamı masaüstündü `main` = `max-w-4xl px-4` (`sonuc/page.tsx:99`). Harita `w-full`, viewBox `1050×447` (`harita.ts:10-13`, `tercih-haritasi.tsx:46-51`), iskelet de aynı oranı rezerve ediyor (`tercih-haritasi-lazy.tsx:14`). | Ekran | İçerik genişliği | Harita yüksekliği | |---|---|---| | 375 px (mobil, trafiğin ~%90'ı) | 343 px | 343 × 447/1050 = **146 px** | | ≥ 928 px (masaüstü) | 864 px | 864 × 447/1050 = **368 px** | Yani `sonuc/page.tsx:107`'deki "~368 px" **masaüstü** rakamı; mobilde 146 px (+ bugünkü `mt-16` = 64 px). CLS = etki payı × mesafe payı: | Senaryo | Etki payı | Mesafe payı | **CLS** | |---|---|---|---| | Bugün (harita altta), dönüş ziyaretçisi | ~0 (kayan içerik ~2,4 ekran aşağıda) | — | **~0** | | Harita üstte, yer ayırmadan, 375×812 | (812−220)/812 ≈ 0,73 | 146/812 = 0,18 | **~0,13** ("iyileştirilmeli") | | Harita üstte, yer ayırmadan, 1280×800 | ~0,75 | 368/800 = 0,46 | **~0,35** ("kötü") | | **Harita üstte + çerezle yer ayrılmış** | 0 | 0 | **0** | (Hesap; ölçüm değil. 220 px = bant + başlık + `py-12`, önceki raporun tahmini.) ## 1.3 İki alternatif ### Alternatif A — liste sayısı çerezi, tek yuva (ÖNERİLEN) ``` 375 px · /sonuc · DÖNÜŞ ziyaretçisi (çerez: liste-sayi=7) ┌───────────────────────────────┐ ← ilk boyada sunucudan gelen HTML │ [bant] [başlık] │ │ │ │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ 146 px · HaritaIskeleti │ ░░░ harita yer tutucu ░░░ │ (aspect-[1050/447], tam ölçü) │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │ │ │ ◇ Yapay zekâdan örnek satır │ chunk inince yer tutucunun │ Yapay zekâ listendeki her │ İÇİ dolar; hiçbir şey kaymaz │ satırı böyle anlatır │ └───────────────────────────────┘ 375 px · /sonuc · İLK KEZ gelen ziyaretçi (çerez yok) ┌───────────────────────────────┐ │ [bant] [başlık] │ │ │ │ ◇ Yapay zekâdan örnek satır │ ← harita DOM'da hiç yok, │ Yapay zekâ listendeki her │ tadımlık first:mt-0'ı korur: │ satırı böyle anlatır │ BUGÜNKÜ EKRANIN AYNISI └───────────────────────────────┘ ``` - **CLS:** çerez isabetliyse **0**. Çerez yoksa ama liste varsa (aşağıdaki dar kaçak) 0,13 (mobil). - **Çerezin kaçacağı tek durum:** dağıtım sonrası ilk sayfa açılışı (kullanıcı başına **bir kez**; ana sayfada yazıldığı için `/sonuc`'a client-side geçişte bile kapanır) ve localStorage yazma hatası. Safari ITP kaçağı **yok sayılabilir**: ITP script'le yazılan çerezi 7 günde siler ama aynı koşulda localStorage'ı da siler — ikisi birlikte ölür, ayrışmaz. Aynı risk zaten `kolaytercih.profil` çerezinde de var, yeni bir sınıf risk getirmiyor. ### Alternatif B — çift yuva (çerez varsa üstte, yoksa altta) `ManuelHarita` iki konumda da yazılır; sunucu çereze bakarak hangisinin aktif olacağını seçer. ``` çerez > 0 → [HARİTA] → tadımlık → tablo → sihirbaz çerez yok → tadımlık → tablo → [HARİTA] → sihirbaz (bugünkü davranış) ``` - **CLS:** her durumda ~0. Kaçak senaryoda bile sıfır, çünkü harita eski (güvenli) yerine düşer. - **Bedeli:** aynı bileşen iki DOM konumunda yaşar — AGENTS.md'nin "bir eleman iki yerde görünüyorsa tek kaynaktan" ilkesine biçimsel olarak uymasa da okuma yükü artar; ve kullanıcı bazı ziyaretlerde haritayı üstte, bazılarında altta görür. **Tutarsızlık, kazanılan 0,13'lük tek seferlik CLS'ten daha pahalı.** ### Hüküm: **A** B'nin kapattığı tek delik, kullanıcı başına bir kez yaşanan ve "iyileştirilmeli" bandındaki (kırmızı değil) bir kayma. Bunun için haritanın sabit bir yeri olmamasını kabul etmek doğru takas değil. ## 1.4 Tadımlık ne kaybediyor — hüküm: kabul edilebilir | Ziyaretçi | Sıra değişiyor mu? | Tadımlık nerede? | |---|---|---| | İlk kez gelen (liste boş) | **Hayır** — harita hiç render edilmez | Bugünkü yerinde, ~220 px | | Girişli (tadımlık zaten yok, `tadimlik-bolumu.tsx:19`) | Evet, harita tablonun üstüne çıkar | — | | **Girişsiz + kayıtlı listesi olan** | Evet | ~220 → **~366 px** | Üçüncü satır için 375×812 hesabı (koddan çıkarım): tadımlığın `SectionEyebrow`'u, H2'si ("Yapay zekâ listendeki her satırı böyle anlatır") ve üç satırlık açıklaması katlamanın üstünde kalıyor; aşağı inen şey örnek satır kartının gövdesi ve CTA'sı. 375×667'de (iPhone SE) rozet + H2 görünür, paragraf yarılanır. **Hüküm: kabul edilebilir.** Gerekçe kaybın büyüklüğü değil, kime denk geldiği: tadımlık "ödeme öncesi tek gerçek önizleme" argümanı **ürünü ilk kez gören** kişi içindir ve o kişide sıra hiç değişmiyor. Zaten liste kurmuş ziyaretçi için ekranın ilk işi, kendi kurduğu listeyi göstermektir; tadımlığı onun üstüne koymak "sana hâlâ ürünü tanıtıyorum" demek olurdu. ## 1.5 Uygulayıcıya tarif (yazılımcı) **Dosya 1 — `src/features/liste/liste-store.ts`** 1. `PROFIL_CEREZ_ADI` deseninin eşi olarak sabit ekle: `LISTE_SAYI_CEREZ_ADI = "kolaytercih.liste-sayi"` (sabitin yeri: bu dosya; sunucu tarafı buradan import eder — `liste-store.ts` `"use client"` olduğu için sabiti `liste-dogrula.ts`'e koymak daha temiz, orası zaten sunucu action'larıyla paylaşılıyor, bkz. `:23-25`). 2. `sayiCereziniYaz(n: number)` — `profilCerezineYaz` (`sihirbaz-profil.ts:51-57`) ile **birebir aynı** öznitelikler: `path=/; max-age=31536000; samesite=lax` + https'te `secure`. Değer düz tamsayı. 3. Üç çağrı yeri (listenin değiştiği her yer): - `kaydet()` içinde (`:42-46`) — ekle/çıkar/sırala/temizle hepsi buradan geçiyor; - `yukle()` içinde, `liste = dogrula(...)` satırından sonra (`:53`) — dağıtım sonrası ilk açılışta çerezi kendiliğinden doğurur; - `"storage"` dinleyicisinde `liste` atandıktan sonra (`:64`) — başka sekmedeki değişiklik. **Dosya 2 — `src/features/liste/components/manuel-harita.tsx`** 4. İmza: `export function ManuelHarita({ yerAyir = false }: { yerAyir?: boolean })`. 5. `:71` → `if (liste.length === 0 && !yerAyir) return null;` Boş+`yerAyir` dalında **ayrı bir iskelet dalı yazma**: fonksiyon akmaya devam etsin, `pinler` boş dizi olur, `TercihHaritasiLazy` monte olur ve chunk inene kadar zaten `HaritaIskeleti`'ni gösterir (`tercih-haritasi-lazy.tsx:10-21`). Böylece (a) yer tutucu ile gerçek harita **aynı** kutudan gelir, yükseklik ayrışamaz; (b) 197 KB'lık chunk hidrasyonu beklemeden inmeye başlar. 6. `:78` `
` → `
`. Harita artık `main`'in ilk çocuğu; üst boşluğu `main`'in `py-12`'sinden alır (bu, `mt-16`'nın eklenmesinden önceki durumdu). `:73-76`'daki yorum tersini anlatıyor, **aynı commit'te** düzeltilmeli. **Dosya 3 — `src/app/sonuc/page.tsx`** 7. `cookies()` zaten okunuyor (`:56`): `const listeSayisi = Number.parseInt(cerezler.get(LISTE_SAYI_CEREZ_ADI)?.value ?? "", 10);` → `const yerAyir = Number.isFinite(listeSayisi) && listeSayisi > 0;` 8. `` satırını `:131`'den **`:118`'in üstüne** taşı. Yeni sıra: **harita → tadımlık → tablo → sihirbaz → dipnot.** 9. `:100-114`'teki yorum bloğu bugünkü kararın tersini belgeliyor; yerine yeni gerekçe (çerezle yer ayırma) yazılmalı — yoksa bir sonraki oturum haritayı yine aşağı alır. 10. `tadimlik-satiri.tsx:127-133`'teki yorum ("harita artık tablonun altında, üstte hiçbir kardeş yok") da yalan olur. `first:mt-0` **kuralı doğru çalışmaya devam eder** (harita varken tadımlık ikinci çocuk olur ve `mt-16` alır, harita yokken ilk çocuktur ve `mt-0` alır) — düzeltilecek olan yalnız yorumun metni. **Kabul kriterleri** 1. Listesi olmayan ziyaretçinin `/sonuc` HTML'inde harita ile ilgili **hiçbir düğüm yok** (`grep -c "aspect-\[1050/447\]"` = 0) ve tadımlık `
`'ı `main`'in ilk çocuğu. 2. `kolaytercih.liste-sayi=7` çereziyle istenen `/sonuc` HTML'inde, tadımlık `
`'ından **önce** `aspect-[1050/447]` kutusu var. 3. Listeye ilk `+`'dan sonra sayfa yenilenince harita en üstte ve **yukarı kayma yok**. 4. Listeyi tamamen boşaltıp sayfayı yenileyince harita yok (çerez 0 yazılmış olmalı). **Büyüklük: S** (3 dosya, ~25 satır + 3 yorum bloğu). ## 1.6 Aynı işte kapanması gereken iki küçük delik | # | Yer | Sorun | Düzeltme | |---|---|---|---| | H1 | `tercih-haritasi-lazy.tsx:14` | `animate-pulse` yer tutucu artık **katlamanın üstünde** ve `prefers-reduced-motion` okunmuyor (WCAG 2.2.2'nin ruhu; sürekli, durdurulamaz hareket) | `motion-reduce:animate-none` ekle (tek sınıf) | | H2 | `manuel-harita.tsx:84-86` → `yurtdisi-haritalar-lazy.tsx:8-11` | `dynamic(..., { ssr:false })` ama `loading` **yok**; chunk inince ~150 px'lik şerit yoktan belirir. Bugün katlamanın altındaydı, yarın üstünde olacak | `loading: () =>
` ya da eşdeğer sabit yükseklik | **Artık kalan (kabul edilen) kayma:** listesinde KKTC/yurt dışı program olan kullanıcıda yurt dışı şeridi + "N tercih haritada gösterilemiyor" satırı sunucuda bilinemez; hidrasyonda belirir. H2 uygulanırsa bu ~150 px'lik tek kaymaya iner (mobil CLS ~0,11). Bunu da sıfırlamak çereze ikinci bir alan yazmayı gerektirir — **önermiyorum**, kazanç bedeli haklı çıkarmıyor. ## 1.7 Reddedilen seçenekler - **Her zaman sabit yükseklikli yer tutucu:** Bilal'in "liste eklenmezse gözükmesin" şartını doğrudan çiğniyor. - **Harita üstte ama küçük/sabit, büyümeyen biçim:** sorun haritanın *boyutu* değil, `null`→görünür geçişi. 60 px'lik bir şerit bile aynı kaymayı üretir, üstelik harita okunmaz hale gelir. - **Girişli kullanıcıda listeyi DB'den okumak:** `/sonuc` bugün oturum açmıyor; oturum + sorgu eklemek sayfayı yavaşlatır ve **girişsiz** çoğunluğu (funnel'ın asıl kitlesi) hiç kapsamaz. Çerez ikisini birden çözüyor. --- # Talimat 2 — Üniversite sayfasında "tür" ## 2.1 Izgara gerçekte 4 kart değil, 4 **hücre** ``` universite-icerik.tsx:291 grid-cols-2 ← mobil lg:grid-cols-[1fr_1fr_1.35fr] lg:grid-rows-2 ← masaüstü universite-konum-haritasi.tsx:77 col-span-2 lg:col-start-3 lg:row-start-1 lg:row-span-2 ``` Masaüstünde harita 3. sütunu **iki satır boyunca** kaplıyor; kartlara kalan yer 2 sütun × 2 satır = **tam 4 hücre**. 5. kart şablonu bozar: ya 3. bir satır açılır (harita yanında boşluk kalır) ya da `grid-rows-2` elle 3'e çıkarılır ve haritanın oranı bozulur. Mobilde 5. kart 2 sütunlu ızgarada **tek başına** bir satıra düşer (yanında boşluk) ve ilk ekrandan ~90 px daha gider. → **(a) "ızgara 5 karta çıksın mı?" cevabı: hayır.** Gerekçe estetik değil, şablonun kendisi. ## 2.2 Asıl mesele: tür bir metrik değil Izgaradaki diğer üç kart sayısal ve karşılaştırılabilir (Program, Fakülte/Birim, En iyi sıralama, URAP). "Devlet" bir ölçüm değil, kurumun **kimliği**. Sitenin geri kalanı bunu zaten böyle işliyor: başlığın altındaki meta satırında küçük bir çip olarak, dört ayrı yerde **birebir aynı sınıf setiyle**: ``` shrink-0 rounded px-1 py-px text-[10px] font-medium leading-4 DEVLET → bg-slate-100 text-slate-600 (kontrast 6,92:1 ✅) diğer → bg-violet-50 text-violet-600 (kontrast 5,19:1 ✅ — hesap: WCAG 2.x, #7C3AED/#F5F3FF) ``` `program-tablosu.tsx:328-336` · `rapor-listesi.tsx:225-234` · `tadimlik-satiri.tsx:160-166` · `bolum-program-listesi.tsx:220-229` Üniversite **detay** sayfası, bu çipi taşımayan tek yüzey. Eksik olan şey kart değil, çip. ## 2.3 İki alternatif ### Alternatif A — çip, H1'in altındaki meta satırında (ÖNERİLEN) ``` 390 px · /universite/koc-universitesi · ilk ekran Ana Sayfa › Üniversiteler › Koç Üniversitesi ┌────┐ Koç Üniversitesi │logo│ Taban Puanları │ 64 │ 2026 └────┘ [Vakıf] İstanbul ← EKLENEN TEK SATIR (mt-3, ~22 px) çip sınıfı: program-tablosu.tsx:329 ile birebir Koç Üniversitesi (İstanbul), vakıf üniversitesi; ← gece/0922-urap'taki 2026 YKS yerleştirme sonuçlarına göre … telafi cümlesi KALIR [ Sıralaman Koç tabanlarının neresinde? → ] ┌─ harita (col-span-2, min-h-44) ──────────┐ │ │ └──────────────────────────────────────────┘ ┌───────────────┐┌───────────────┐ │ PROGRAM ││ FAKÜLTE/BİRİM │ │ 78 ││ 12 │ └───────────────┘└───────────────┘ ┌───────────────┐┌───────────────┐ │ URAP TÜRKİYE ││ EN İYİ SIRALAMA│ ← 4 hücre, hepsi sayısal │ 34 / 198 ││ 412 │ │ 2025-2026 ││ │ └───────────────┘└───────────────┘ ``` - **Yeni bileşen yok**, yeni sınıf yok: çip dört yerde duran sınıf setinin beşinci kopyası (ya da — tercihen — hepsinin çıkarılacağı tek kaynak, bkz. §2.5). - **Türün kaç kez geçtiği:** çip 1 + cümle 1 = 2, biri taranabilir, biri okunabilir. Kart yok. - **Mobil:** H1 bloğu (logo `size-16` + `text-3xl`) 390 px'te zaten ~180 px; çip satırı 22 px ekler, taşma riski yok (`flex items-center gap-1.5`, çip `shrink-0`, il `truncate`). - **Erişilebilirlik:** çip metni okunur ("Vakıf"), renk tek başına anlam taşımıyor — AGENTS.md'nin risk dili kuralının aynı mantığı. - **Gereken tek yan değişiklik:** URAP'ı olmayan 31 sayfada 4. hücre bugün "Tür" kartı olarak dolduruluyor (`gece/0922-urap`, `:394-417`). Çip geldiğinde o sayfalarda tür **üç kez** yazar. 4. hücre oraya **"Toplam kontenjan"** olur (`uni.stats.toplamKontenjan`, `lib/katalog.ts:380-401`). Veri kontrolü: `data/yokatlas.db` — toplam kontenjanı 0 olan **hiçbir üniversite yok** (`SELECT … GROUP BY universite HAVING SUM(COALESCE(kontenjan2026,0))=0` → 0 satır), yani bu hücrede asla "—" çıkmaz. Precedent var: `minSira` de hem paragrafta hem kartta geçiyor. ### Alternatif B — "Tür" kartı geri gelir, URAP satır olur ``` Koç Üniversitesi (İstanbul), vakıf üniversitesi; 2026 YKS … URAP Türkiye sıralaması: 34 / 198 (2025-2026) ← paragrafın altına yeni satır ┌ PROGRAM 78 ┐┌ FAKÜLTE 12 ┐ ┌ TÜR Vakıf ┐┌ EN İYİ 412 ┐ ``` - Artısı: diff küçük, `gece/0922-urap`'ın kart kısmı geri alınır. - Eksileri: (a) **sayı prozaya gömülür** — URAP taranmak için var, "34/198" cümle içinde aranır hale gelir; tasarımın dünkü gerekçesinin tersi. (b) Izgarada kategori/metrik karışımı sürer. (c) Tür yine `text-2xl font-bold` ile "Vakıf MYO" gibi uzun değerlerde 190 px'lik karta sığmaz. (d) Çip eksikliği kapanmaz: detay sayfası hâlâ sitenin geri kalanından farklı konuşur. ### Hüküm: **A** Bilal'in "tür olsun" isteği A'da daha güçlü karşılanıyor: tür artık **ızgaranın dördüncü kutusunda, ekranın ortasında** değil, **üniversitenin adının hemen altında** — hem ilk bakışta hem her sayfa genişliğinde. Ve site genelindeki çiple aynı dili konuşuyor. ## 2.4 Uygulayıcıya tarif (yazılımcı) Taban: `gece/0922-urap` (orada `UNITUR_ETIKET` ve telafi cümlesi hazır). Dal bayatsa önce `origin/main` üstüne yeniden kurulmalı. 1. `UNITUR_ETIKET`i (`universite-icerik.tsx:54-66`) **`src/lib/unitur.ts`**'e taşı ve üçüncü alan ekle: `{ kart, cumle, cip }`. `cip` = çipte görünen kısa metin (`Devlet`, `Vakıf`, `Vakıf MYO`, `KKTC`, `Yurt dışı (kamu)`, `Yurt dışı (vakıf)`) — `kart` ile aynı değerler, ama `kart` alanı §2.4-3 ile ölüyorsa ikisi birleştirilebilir. Gerekçe: aynı bilginin ekrandaki etiketi tek kaynaktan gelmeli — `DILIM_ETIKET`'in `src/lib/risk.ts`'te durması gibi (AGENTS.md). 2. `universite-icerik.tsx`, H1 bloğunun (`:253-259`) **hemen altına**, `ilkSayfa` koşulundan **bağımsız** (sayfalama sayfalarında da üniversite kimliği aynı): ```

…çip… {il}

``` Çipin sınıfı `program-tablosu.tsx:329-333`'ten **kopyalanır**, yeni varyant üretilmez. Renk kuralı da aynı: `DEVLET` → slate, diğer her şey → violet. `uni.unitur` NULL ise satırın tamamı render edilmez (kopuk boşluk kalmaz). 3. Izgaradaki 4. hücre (`gece/0922-urap`, `:394-417`): `uni.urap ? URAP : Tür` koşulu → `uni.urap ? URAP : Toplam kontenjan`. `dt`: "Toplam kontenjan", `dd`: `sayi(uni.stats.toplamKontenjan)`. 4. `bolum-program-listesi.tsx:227` `trBaslikDuzeni(p.unitur)` → `UNITUR_ETIKET[p.unitur].cip`. Bugün o çip "Kktc" / "Vakıf Myo" / "Yurtdısı Kamu" basıyor (`slug.ts:56-64` tr-TR küçültmesi). **Aynı commit'e girmeli**: tek kaynak taşınırken düzelmeyen tek tüketici kalmasın. **Kabul kriterleri** 1. `bogazici-universitesi.html` → çipte "Devlet" **1 kez**, özet cümlesinde "devlet üniversitesi" **1 kez**, ızgarada "Tür" **0 kez**. 2. `dogu-akdeniz-universitesi.html` (URAP yok) → çipte "KKTC", 4. hücrede "Toplam kontenjan" + sayı, hiçbir hücrede "—" yok. 3. `/bolum/...` sayfalarında KKTC programının çipinde "KKTC" yazıyor, "Kktc" değil. 4. 390 px'te H1 bloğu + çip satırı yatay taşma üretmiyor (uzun üniversite adı: "İstanbul Medeniyet Üniversitesi"). 5. `pnpm build` temiz; 229 üniversite sayfasının 229'unda çip var. **Büyüklük: S** (2 yeni/taşınan dosya + 3 dokunuş, ~50 satır). ## 2.5 Aynı işte kapanacak tutarlılık borcu | Yer | Sorun | Kanonik kaynak | |---|---|---| | `program-tablosu.tsx:329` · `rapor-listesi.tsx:227` · `tadimlik-satiri.tsx:161` · `bolum-program-listesi.tsx:222` | Aynı çip sınıf seti **4 kez elle kopyalanmış**; 5.'si ekleniyor | AGENTS.md "ortak elemanlar tek kaynaktan". **Öneri:** `src/components/unitur-cipi.tsx` — küçük, tek amaçlı sunum bileşeni. Bu, "yeni bileşen tasarlama" yasağının kapsamına girmez: yeni bir *tasarım* değil, var olan beş kopyanın tek kaynağa alınmasıdır. Ayrı ve tek amaçlı commit. | | `app/api/katalog-ara/route.ts:49-56` | `unitur` için **beşinci** sözlük: "KKTC / Yurt dışı" (arama sonucunda böyle, sayfada "KKTC") | Aynı `UNITUR_ETIKET`. Düşük öncelik; ayrı iş. | | `program-tablosu.tsx:175` | Root `
` — `first:mt-0` yok. Girişli + listesiz kullanıcıda (tadımlık yok, harita yok) tablo `main`'in ilk çocuğu olur, tepede 64 px ölü alan kalır | `tadimlik-satiri.tsx:134` deseni (`mt-16 first:mt-0`). XS, Talimat 1 ile aynı commit'e sığar | --- ## Bilal'in gözle kontrol etmesi gerekenler (375–390 px) 1. **`/sonuc`, listeye 1-2 program ekledikten sonra sayfayı yenile.** Harita en üstte mi, açılışta yukarı/aşağı zıplama var mı? (Talimat 1'in tek gerçek testi.) 2. **`/sonuc`, tarayıcı verisini sildikten sonra ilk kez.** Harita hiç yok mu, tadımlık en üstte mi ve üstünde fazladan boşluk var mı? (`first:mt-0` kontrolü.) 3. **`/sonuc`, girişsiz + listeli.** Tadımlığın başlığı ("Yapay zekâ listendeki her satırı böyle anlatır") hâlâ katlamanın üstünde mi? Hükmüm buna dayanıyor (§1.4). 4. **Listesinde KKTC/yurt dışı programı olan bir listeyle `/sonuc`.** Türkiye haritasının altındaki yurt dışı şeridi geç belirip içeriği itiyor mu? (§1.6-H2) 5. **`/universite/koc-universitesi` ve `/universite/dogu-akdeniz-universitesi`.** Üniversite adının altındaki tür çipi ilk bakışta görülüyor mu, yoksa çok mu küçük kalıyor? (A alternatifinin tek öznel riski budur; "daha büyük olsun" dersen o **yeni bir çip varyantı** demektir ve site genelindeki dört kopyayı da etkiler → `[BİLAL]` kararı.) ## Bilal'den istenen (3 madde) 1. **Talimat 1 için A'yı onayla** (liste sayısı çerezi + harita en üstte). Yeni bir işlevsel çerez yazılacak; kişisel veri taşımıyor, `kolaytercih.profil` ile aynı sınıf. **~2 dk.** 2. **Talimat 2 için A'yı onayla**: tür = ad altındaki çip, URAP'ı olmayan 31 sayfada 4. kart "Toplam kontenjan" olur. **~2 dk.** 3. **§2.5'teki `UniturCipi` tek-kaynak commit'i** ayrı iş olarak backlog'a girsin mi (5 kopya → 1 bileşen)? **~1 dk.**