/* ==========================================================================
   OkuYorum — CİLT & MÜREKKEP · hareket katmanı
   --------------------------------------------------------------------------
   TEZ: Her nesnenin bir kütlesi var ve o kütle kâğıt cinsindendir. Kitap
   kapağı ağırdır, sırtından kalkar ve zıplamaz; mürekkep hafiftir, emilir ve
   yaylanır. Hareketin süresi, aşma miktarı ve mesafesi daima bu kütleden
   türetilir — asla bileşenden değil.

   YÜKLEME SIRASI (app/Views/layouts/app.php):
     app.css  →  BU DOSYA  →  themes/<ad>/stil.css
                             →  <style> Theme::cssDegiskenleri()
                             →  <style> Theme::ozelCss()   (en son söz)
   Yani burası app.css'i ezer; tema ve yönetici özel CSS'i de burayı ezebilir.
   Bu bilinçlidir: yöneticinin hareketi kısma hakkı elinden alınmamalıdır.

   KURALLAR
   - app.css'teki --gecis DEĞİŞTİRİLMEZ (15 seçici ona bağlı, geriye uyum).
   - Yalnız transform ve opacity anime edilir (compositor). width/height/top/
     left animasyonu yoktur; mevcut olanlar da burada söndürülür.
   - !important YALNIZCA "GÜVENLİK AĞI" bölümünde (erişilebilirlik + baskı).
   - Sabit renk yoktur; her renk var(--...) üzerinden gelir → 3 tema + koyu
     tema uyumu kendiliğinden sağlanır.
   - Her token yedekli çağrılır: var(--h-sure-4, 380ms). Sebep: layouts/
     admin.php Theme::cssDegiskenleri()'ni hiç basmaz; ayrıca buradan
     kopyalanan bir kural Theme::ozelCss() içinde de çalışsın.
   ========================================================================== */


/* ==========================================================================
   A) TOKEN KATMANI
   NEDEN: Süre, eğri ve mesafe tek tek bileşenlerde değil, tek bir kütle
   merdiveninde tanımlanır. Böylece "kart ne kadar sürede açılır" sorusunun
   cevabı 40 ayrı yerde değil, tek satırda durur.
   ========================================================================== */
:root {
  /* ---- TEK ÇARPAN: sistemin ana şalteri --------------------------------
     html[data-hareket] özniteliğinden türetilir (B bölümü).
     Theme.php'ye HİÇ dokunulmaz. */
  /* İKİ AYRI ÇARPAN, ÇARPILARAK BİRLEŞİR.
     Sebep: tek bir --h-carpan kullanılırsa html[data-stil="gece"] (0,1,1)
     ile html[data-hareket="kapali"] (0,1,1) aynı özgüllükte olur ve
     kaynakta SONRA geleni kazanır. Tema bloğu seviye bloğundan sonra
     yazılınca "hareketi kapat" gece/kâğıt temasında hiç çalışmazdı.
     Çarpanları ayırmak sıralamayı önemsiz kılar: 0 × .85 = 0. */
  --h-tema-carpan: 1;      /* tema kısması  (C bölümü)  */
  --h-seviye-carpan: 1;    /* yönetici ayarı (B bölümü) */
  --h-carpan: calc(var(--h-tema-carpan, 1) * var(--h-seviye-carpan, 1));

  /* ---- SÜRE MERDİVENİ — 5 basamak, kütleye göre ------------------------
     Kural: bir hareket ne kadar çok piksel süpürüyorsa o kadar uzun sürer. */
  --h-sure-1: calc(120ms * var(--h-carpan, 1));  /* anlık: :active, renk, zemin      */
  --h-sure-2: calc(180ms * var(--h-carpan, 1));  /* TÜY:   rozet, tik, sayaç, kalp    */
  --h-sure-3: calc(260ms * var(--h-carpan, 1));  /* KÂĞIT: düğme, menü, etiket        */
  --h-sure-4: calc(380ms * var(--h-carpan, 1));  /* CİLT:  kart, kapak, modal, avatar */
  --h-sure-5: calc(520ms * var(--h-carpan, 1));  /* RAF:   sayfa geçişi, spoiler      */
  /* KURAL: çıkış girişin %60'ı, ASLA daha uzun değil.
     Kâğıdı masadan kaldırmak, masaya koymaktan kolaydır. */
  --h-sure-cikis: calc(var(--h-sure-3, 260ms) * .6);

  /* ---- EASING — 3 düz eğri ---------------------------------------------- */
  --h-egri-surekli: cubic-bezier(.4, 0, .2, 1);   /* renk/gölge/opaklık; --gecis ile aynı his */
  --h-egri-kagit:   cubic-bezier(.32, .72, 0, 1); /* ağır: hızlı çık, uzun yerleş             */
  --h-egri-cikis:   cubic-bezier(.4, 0, 1, 1);    /* kapanış: çıkışta ASLA aşma yok           */

  /* ---- EASING — 3 yay ---------------------------------------------------
     Sönümlü yay denklemi p(t)=1-e^(-zw·t)(cos(wd·t)+(zw/wd)·sin(wd·t))
     23 noktada örneklendi; uçlar 0 ve 1'e sabitlendi.
     Kapsam: Chrome/Edge 113+, Firefox 112+, Safari/iOS 17.2+ (~%90).
     Tanınmazsa tarayıcı bildirimi yok sayar → sessiz, güvenli bozulma. */

  /* GÜVENLİ TABAN — linear() tanınmayan tarayıcıda (iOS/Safari < 17.2)
     bu değerler geçerli kalır. linear() doğrudan token'a yazılsaydı,
     tanınmayan değer yüzünden onu KULLANAN kısayol bildirimin TAMAMI
     düşerdi (var() yedeği burada devreye girmez, çünkü token tanımlıdır). */
  --h-yay-tuy:   cubic-bezier(.2, .9, .3, 1.18);
  --h-yay-kagit: cubic-bezier(.22, .88, .3, 1.06);
  --h-yay-cilt:  cubic-bezier(.25, .85, .3, 1.02);
}

/* Gerçek yay fiziği yalnız linear() destekleniyorsa. */
@supports (transition-timing-function: linear(0, 1)) {
  :root {
  /* TÜY — zeta .50 · aşma %16,2 · 2 salınım · yalnız <200ms mikro etkileşim */
  --h-yay-tuy: linear(0, .073, .249, .469, .687, .875, 1.016, 1.107, 1.153, 1.162, 1.146, 1.116, 1.079, 1.044, 1.015, .994, .98, .974, .974, .977, .982, .988, 1);

  /* KÂĞIT — zeta .63 · aşma %7,7 · 1 salınım */
  --h-yay-kagit: linear(0, .063, .211, .394, .577, .739, .869, .964, 1.027, 1.062, 1.077, 1.077, 1.068, 1.054, 1.04, 1.026, 1.015, 1.006, 1, .996, .994, .994, 1);

  /* CİLT — zeta .72 · aşma %3,8 · salınımsız oturma */
  --h-yay-cilt: linear(0, .063, .207, .381, .551, .7, .819, .908, .969, 1.008, 1.029, 1.037, 1.038, 1.034, 1.028, 1.021, 1.015, 1.009, 1.005, 1.002, 1, .999, 1);
  }
}

:root {

  /* YAY KULLANIM KURALI (ihlal edilirse yön zevksizleşir):
     Yay YALNIZCA kullanıcı eylemine tepkide — tıklama, durum değişimi.
     :hover'da ve pasif giriş animasyonlarında YAY YASAK.
     Gerekçe: linear() atalet taşımaz; yarıda kesilen geçiş "duvara çarpıp"
     anında geri döner. 40 kapaklı ızgarada fare hızla gezinirse jöle olur. */

  /* ---- MESAFE — kütleyle TERS orantılı ---------------------------------
     Ağır nesne az yol gider, geç oturur. 12px'i geçen öteleme YOK. */
  --h-yol-tuy:   calc(10px * var(--h-carpan, 1));
  --h-yol-kagit: calc(6px  * var(--h-carpan, 1));
  --h-yol-cilt:  calc(4px  * var(--h-carpan, 1));
  --h-yol-raf:   calc(3px  * var(--h-carpan, 1));

  /* ---- AÇI — 10deg üstü oyuncak hissi verir, üstüne çıkılmaz ---------- */
  --h-don-sirt:   calc(7deg  * var(--h-carpan, 1)); /* kapak sırtını gösteren rotateY   */
  --h-don-yaprak: calc(.4deg * var(--h-carpan, 1)); /* masaya konan kâğıdın hizasızlığı */
  --h-derinlik: 900px;                              /* perspective — KARTA, ızgaraya DEĞİL */

  /* ---- GÖLGE MERDİVENİ — yükselme = gölge, dekorasyon değil ------------
     Yükselmeden gölge büyütmek YASAK. Yedek zinciri zorunlu:
     --golge-m/--golge-s YALNIZ themes/gece/stil.css'te tanımlı. */
  --h-golge-0: var(--golge-1, 0 1px 2px rgba(28,25,20,.06));
  --h-golge-1: var(--golge-2, 0 2px 8px rgba(28,25,20,.08));
  --h-golge-2: var(--golge-3, 0 8px 28px rgba(28,25,20,.12));
  --h-golge-3: var(--golge-4, 0 20px 50px rgba(28,25,20,.18));

  /* ---- ÖLÇÜ–SÜRE EŞLEŞMESİ (bozulursa hareket sahte hisseder) ----------
       --h-yol-tuy   ↔ --h-sure-2      Küçük mesafe + uzun süre = sürünme.
       --h-yol-kagit ↔ --h-sure-3      Büyük mesafe + kısa süre = ışınlanma.
       --h-yol-cilt  ↔ --h-sure-4
       --h-yol-raf   ↔ --h-sure-5                                        */
}


/* ==========================================================================
   B) SEVİYE ŞALTERİ — yönetici ayarı
   NEDEN: Öznitelik seçicisinin özgüllüğü (0,1,1); Theme::cssDegiskenleri()'nin
   <head> sonunda bastığı düz :root (0,1,0) bunu EZEMEZ. Böylece Theme.php'ye
   hiç dokunmadan, tek bir HTML özniteliğiyle tüm site susturulabilir.
   layouts/app.php:41 → data-hareket="<?= e(setting('hareket_seviye','tam')) ?>"
   ========================================================================== */
html[data-hareket="kapali"] { --h-seviye-carpan: 0; }    /* tüm süreler 0ms, açılar 0deg */
html[data-hareket="az"]     { --h-seviye-carpan: .5; }
html[data-hareket="normal"] { --h-seviye-carpan: 1; }
html[data-hareket="tam"]    { --h-seviye-carpan: 1; }   /* "normal" ile eş anlamlı */
html[data-hareket="zengin"] { --h-seviye-carpan: 1.35; }

/* "az" = YALNIZCA opaklık ve renk geçişleri.
   Çarpanı yarıya indirmek yetmez; öteleme ve dönme tamamen sıfırlanır ki
   ekranda hiçbir şey YER DEĞİŞTİRMESİN. Süsleyici keyframe'ler F bölümünün
   sonundaki toplu blokta ayrıca kapatılır. */
html[data-hareket="az"] {
  --h-yol-tuy: 0px; --h-yol-kagit: 0px; --h-yol-cilt: 0px; --h-yol-raf: 0px;
  --h-don-sirt: 0deg; --h-don-yaprak: 0deg;
}


/* ==========================================================================
   C) TEMA FARKLILAŞTIRMASI
   NEDEN: Düz :root ÇALIŞMAZ — Theme::cssDegiskenleri() tüm <link>'lerden
   SONRA basılıyor ve o da :root (0,1,0). html[data-stil] (0,1,1) gerekir.
   data-stil özniteliği layouts/app.php:41'de basılıyor.

   DİKKAT: burada --h-carpan DEĞİL, --h-tema-carpan yazılır. İkisi A
   bölümünde çarpılarak birleşir; böylece bu blokla B bölümünün kaynak
   sırası hangisi olursa olsun "hareketi kapat" her temada çalışır.
   ========================================================================== */

/* Basılı kitap sakindir ve eğilmez: kâğıt teması hareketi kısar, 3B'yi kapatır. */
html[data-stil="kagit"] {
  --h-tema-carpan: .75;
  --h-don-sirt: 0deg;
  --h-yay-tuy: var(--h-egri-surekli, cubic-bezier(.4, 0, .2, 1));
}

/* Koyu zeminde hareket daha çok göze çarpar: doz düşürülür. */
html[data-stil="gece"] {
  --h-tema-carpan: .85;
  --h-don-sirt: calc(5deg * var(--h-carpan, 1));
}


/* ==========================================================================
   D) GÜVENLİK AĞI — erişilebilirlik, baskı ve tek anahtarla kapatma
   NEDEN: Bu bir hareket değil, SİGORTA. Her şeyden önce yazılır.
   app.css:833'teki mevcut reduced-motion bloğu delay'leri SIFIRLAMIYOR;
   kademeli girişler eklendiğinde içerik gecikme boyunca opacity:0'da kalıp
   GÖRÜNMEZ olurdu. app.css:827'deki print bloğu da opacity:0 başlangıçlı
   animasyonları hesaba katmıyor; bu katman olmadan baskı BOŞ KÂĞIT çıkardı.
   Dosyadaki TEK !important kullanımı buradadır.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {

  /* ÖZGÜLLÜK KRİTİK: düz :root yazılırsa Theme::cssDegiskenleri()'nin
     <head> sonundaki :root'u onu ezer. html:root (0,1,1) kazanır.
     Çarpan 0 → calc(120ms*0)=0ms, calc(7deg*0)=0deg: sistem tek satırda susar. */
  html:root { --h-carpan: 0; }

  /* app.css'teki bloğun eksiği: gecikmeler. Sıfırlanmazsa kademeli giriş
     animasyonlarında içerik gecikme süresince görünmez kalır. */
  *, *::before, *::after {
    animation-delay: 0ms !important;
    transition-delay: 0ms !important;
  }

  /* View Transitions sözde elemanları `*` seçicisiyle EŞLEŞMEZ; API
     prefers-reduced-motion'a kendiliğinden UYMAZ. Elle kapatılır. */
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}

@media print {
  /* Baskıda hiçbir öğe "henüz belirmemiş" halde kalmamalı.
     opacity:0 / transform ile başlayan her giriş son durumuna sabitlenir. */
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
    opacity: 1 !important;
    transform: none !important;
    visibility: visible !important;
  }
  /* Üstteki toplu transform:none, anahtarın açık/kapalı halini de silerdi.
     Ayar sayfası çıktısında "açık" görünmesi gereken tek konumsal transform. */
  .anahtar input:checked + .kaydir::before { transform: translateX(18px) !important; }
}


/* ==========================================================================
   E1) SESSİZ YÜZEYLER — geçişsiz bileşenlerin toplu uyumu
   NEDEN: En yüksek cila/bayt oranı, sıfır risk. Bu bileşenlerin hepsi :hover
   veya .aktif durumunda renk/zemin değiştiriyor ama HİÇ transition taşımıyor
   — geçişler ANİDEN atlıyor.

   DİKKAT 1: `transition: var(--gecis)` kısayolu KULLANILMAZ; o "all" demektir
   ve iOS Safari'de layout/paint özelliklerini de kapsayıp jank üretir.
   app.css'teki 17 bildirimin hepsi bu hatayı yapıyor, yeni katman tekrarlamaz.
   DİKKAT 2: transform ASLA bu listeye eklenmez — .kitap-ad/.iki-satir vb.
   -webkit-line-clamp kullanıyor, iOS'ta transform altında kırpma noktası
   yeniden hesaplanıp metin titriyor.
   ========================================================================== */
.etiket, .etiket-ana, .etiket-basari, .etiket-uyari, .etiket-hata,
.etiket-bilgi, .etiket-bot,
.arama-oge,
.sayfalama a,
.dugme-grup > *,
.yorum-alt button,
.sayac b,
.alt a,
.ileti-ad, .ileti-baslik a, .ileti-meta a,
.ileti-kitap,
.baglanti-dugme,
.acilir-menu a, .acilir-menu button,
.mobil-alt-menu a,
.kapak-etiket {
  transition:
    color            var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    background-color var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    border-color     var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    opacity          var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1));
}

/* Tablo satırında yalnız zemin: satıra transform vermek tabloyu bozar. */
.tablo tbody tr {
  transition: background-color var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1));
}


/* ==========================================================================
   E2) BASMA — transform kanalı mimarisi ve dokunsal geri bildirim
   NEDEN (ZORUNLU MİMARİ): transform TEK bir özelliktir; üzerine yazan her
   kural öncekini siler. app.css:249 (.dugme:active translateY) ve app.css:536
   (.anahtar kaydırma) doğrudan risk altında. Çözüm: her bileşene bir ARA
   DEĞİŞKEN KANALI kurulur, alt durumlar sadece kanalın değerini değiştirir.

   KRİTİK: app.css'teki .dugme:active (0,2,0) bizim .dugme'mizden (0,1,0) daha
   özgül olduğu için :active kuralı burada AYNEN YENİDEN YAZILMALI — bu dosya
   app.css'ten sonra yüklendiği için eşit özgüllükte kazanır.

   KONUMLANDIRMA amaçlı transform'lara ASLA dokunulmaz:
   .arama-kutu .ikon-sol (app.css:198), .bildirim-yigini (568),
   .grafik-cubuk::after (728) — kanal eklenirse öğe ekranda kayar.
   Yay easing KULLANILMAZ: kesilebilen mikro etkileşimde jöle hissi doğurur.
   ========================================================================== */
.dugme {
  transform: translateY(var(--h-dugme-y, 0)) scale(var(--h-dugme-olcek, 1));
  transition:
    transform        var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    background-color var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    border-color     var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    color            var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    box-shadow       var(--h-sure-2, 180ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    opacity          var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1));
}
.dugme:active { --h-dugme-y: 1px; --h-dugme-olcek: .985; }

.simge-dugme {
  transform: scale(var(--h-simge-olcek, 1));
  transition:
    transform        var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    background-color var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    color            var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1));
}
.simge-dugme:active { --h-simge-olcek: .92; }

.durum-dugme {
  transform: translateY(var(--h-durum-dugme-y, 0));
  transition:
    transform        var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    background-color var(--h-sure-2, 180ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    border-color     var(--h-sure-2, 180ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    color            var(--h-sure-2, 180ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1));
}
.durum-dugme:active { --h-durum-dugme-y: 1px; }

.eylem {
  transform: scale(var(--h-eylem-olcek, 1));
  transition:
    transform        var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    background-color var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    color            var(--h-sure-2, 180ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1));
}
.eylem:active { --h-eylem-olcek: .94; }

/* transform kanalı KENDİ geçişini taşımalı: E1'in toplu bildirimi yalnız
   color/background-color/border-color listeliyor, transform orada yok.
   Bildirimi olmayan bir transform anında sıçrar — aynı ağırlıktaki
   .dugme:active 120ms'de inerken .sayfalama a ışınlanıyordu. */
.sayfalama a {
  transform: translateY(var(--h-sayfa-y, 0));
  transition:
    transform        var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    color            var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    background-color var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    border-color     var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1));
}
.sayfalama a:active { --h-sayfa-y: 1px; }

/* Anahtar (toggle): app.css:536'daki translateX(18px) kanal içine alınır,
   ayrıca `all` yerine açık özellik listesi verilir. */
.anahtar .kaydir {
  transition: background-color var(--h-sure-2, 180ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1));
}
.anahtar .kaydir::before {
  transform: translateX(var(--h-anahtar-x, 0px));
  transition: transform var(--h-sure-2, 180ms) var(--h-egri-kagit, cubic-bezier(.32,.72,0,1));
}
.anahtar input:checked + .kaydir::before { --h-anahtar-x: 18px; }


/* ==========================================================================
   E3) SIRTTAN KALKAN KAPAK — 3B kitap sırtı
   NEDEN: Sitenin en görünür imzası. Kitap düz bir dikdörtgen değil, sırtı
   olan bir cisimdir; kalkarken sırtını gösterir.

   Perspektif KARTA verilir, .kitap-izgara'ya DEĞİL — ızgaraya verilirse tüm
   kartlar tek kaçış noktasını paylaşır ve kenardakiler çarpılır.
   Dönme ekseni rastgele değil: app.css:330'daki .kapak::after sırt
   gradyanının (inset:0 auto 0 0; width:5px) tam üzerinde.
   Yay KULLANILMAZ (hover kesilebilir). will-change yalnız :hover içinde —
   ızgarada onlarca kapak var, kalıcı katman mobilde belleği patlatır.
   ========================================================================== */
/* Perspective YALNIZ hover yeteneği olan cihazda ve YALNIZ etkileşim
   anında. Koşulsuz verilirse ızgaradaki her kart bir 3B render bağlamı
   kurar; dokunmatikte kapak hiç dönmediği için bu maliyet tamamen boşa
   gider. Bu dosyanın kendi kuralı buydu, uygulanmamıştı. */
@media (hover: hover) {
  .kitap-kart:hover,
  .kitap-kart:focus-within {
    perspective: var(--h-derinlik, 900px);
    perspective-origin: 50% 42%;
  }
}

/* DİNLENME HALİNDE 3B YOK. transform'a rotateY(0deg) yazmak, ızgaradaki
   onlarca kapağın hepsini 3B render bağlamına sokup katmana terfi ettirir
   ve mobilde belleği yer. Bu yüzden rotateY YALNIZCA :hover/:focus-within
   kuralında, tek elemanda görünür. Bu dosyada .kapak'ın transform'unu
   yazan tam olarak iki kural vardır — üçüncüsü eklenmemelidir. */
.kapak {
  transform-origin: 0% 50%;
  /* Dinlenmede transform YOK. Daha önce burada boş bir değişken kanalı
     (translateY(var(--h-kapak-y,0))) vardı; hiçbir yerde atanmadığı için
     her kapağa kalıcı bir transform bırakıyor, gereksiz yığın bağlamı
     yaratıyordu. */
  transform: none;
  /* app.css:327'deki `transition: var(--gecis)` (= all) burada açık
     özellik listesiyle DEĞİŞTİRİLİR. */
  transition:
    transform  var(--h-sure-4, 380ms) var(--h-egri-kagit, cubic-bezier(.32,.72,0,1)),
    box-shadow var(--h-sure-4, 380ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1));
}

/* Sırt kalınlaşması width ile DEĞİL scaleX ile yapılır: mutlak konumlu
   sözde eleman bile olsa width bir layout özelliğidir. */
.kapak::after {
  transform-origin: 0 50%;
  transform: scaleX(var(--h-sirt-olcek, 1));
  transition: transform var(--h-sure-4, 380ms) var(--h-egri-kagit, cubic-bezier(.32,.72,0,1));
}

/* focus-within şart: klavye kullanıcısı da görsün. */
.kitap-kart:hover .kapak,
.kitap-kart:focus-within .kapak {
  /* Mevcut translateY(-3px) korunur ama artık çarpana bağlı: kapali'da 0. */
  transform:
    translateY(calc(-3px * var(--h-carpan, 1)))
    rotateY(calc(-1 * var(--h-don-sirt, 7deg)));
  --h-sirt-olcek: 1.8;                 /* sırt 5px → ~9px kalınlaşır */
  box-shadow: var(--h-golge-2, 0 8px 28px rgba(28,25,20,.12));
  will-change: transform;
}


/* ==========================================================================
   E4) MÜREKKEP EMİLMESİ — beğeni
   NEDEN: Konfeti/parçacık YOK. Kalem kâğıda değiyor ve mürekkep yayılıyor.
   İki katman: ikonun dolması + kâğıda emilen leke.

   `fill: transparent` taban değeri ŞART: none → currentColor geçişi kesiklidir,
   oynamaz. Aktif hali (0,3,1) ile app.css:477'nin (.eylem.aktif svg, 0,2,1)
   üzerine açıkça geri yazılır, yoksa beğenilmiş kalp boş görünür.

   `h-canli` sınıfını hareket.js capture fazında ekler. Onsuz animasyon
   oynamaz — sunucudan .aktif gelen 20 kalbin sayfa açılışında hep birden
   patlaması böyle önlenir.

   ASİMETRİK GERİ ALMA: .aktif kalkınca hiçbir animasyon yok; fill yalnızca
   soluklaşır. Geri alma bir DÜZELTMEDİR, kutlama değil.
   ========================================================================== */
.eylem[data-begen] { position: relative; }

.eylem[data-begen] svg {
  fill: transparent;
  transform: scale(var(--h-kalp-olcek, 1));
  /* Çıkış (beğeniyi geri alma) kısa ve aşmasız. */
  transition:
    fill  var(--h-sure-cikis, 156ms) var(--h-egri-cikis, cubic-bezier(.4,0,1,1)),
    color var(--h-sure-cikis, 156ms) var(--h-egri-cikis, cubic-bezier(.4,0,1,1));
}
.eylem[data-begen].aktif svg {
  fill: currentColor;
  transition:
    fill  var(--h-sure-3, 260ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    color var(--h-sure-2, 180ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1));
}

/* Emilen leke: 34px'lik daire, 10px padding + 17px ikonun merkezine oturur. */
.eylem[data-begen]::before {
  content: '';
  position: absolute;
  left: 2px; top: 50%;
  width: 34px; height: 34px; margin-top: -17px;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(circle,
              color-mix(in srgb, var(--hata) 24%, transparent) 0%,
              transparent 68%);
}

/* Yay YALNIZ burada ve E5'te — kütle = tüy. */
.eylem[data-begen].h-canli.aktif::before {
  animation: h-leke var(--h-sure-3, 260ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1));
}
.eylem[data-begen].h-canli.aktif svg {
  animation: h-kalp var(--h-sure-2, 180ms) var(--h-yay-tuy, cubic-bezier(.2,.9,.3,1.2));
}


/* ==========================================================================
   E5) YILDIZ DALGASI — 10'lu puan seçici
   NEDEN: Sitenin en duygusal eylemi; şu an 10 yıldız aynı anda renk atlıyor.
   Mürekkep soldan (1. yıldız) sağa (imleç) akmalı.

   DOM sırası book/detay.php:131'de i=10'dan 1'e; .puan-secici direction:rtl
   olduğu için nth-of-type(1)=10 puanı, nth-of-type(10)=1 puanı ve GÖRSEL sıra
   soldan sağa 1→10. Bu yüzden gecikme ters indekslenir: 1. yıldızda 0.
   D bölümü transition-delay'i sıfırladığı için reduced-motion'da içerik
   kaybı riski yok (renk zaten anında uygulanır).
   ========================================================================== */
.puan-secici label {
  transform: scale(var(--h-yildiz-olcek, 1)) rotate(var(--h-yildiz-don, 0deg));
  transition:
    color     var(--h-sure-1, 120ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    transform var(--h-sure-2, 180ms) var(--h-yay-tuy, cubic-bezier(.2,.9,.3,1.2));
  transition-delay: calc(var(--h-sira, 0) * 14ms * var(--h-carpan, 1));
}
.puan-secici label:nth-of-type(1)  { --h-sira: 9; }
.puan-secici label:nth-of-type(2)  { --h-sira: 8; }
.puan-secici label:nth-of-type(3)  { --h-sira: 7; }
.puan-secici label:nth-of-type(4)  { --h-sira: 6; }
.puan-secici label:nth-of-type(5)  { --h-sira: 5; }
.puan-secici label:nth-of-type(6)  { --h-sira: 4; }
.puan-secici label:nth-of-type(7)  { --h-sira: 3; }
.puan-secici label:nth-of-type(8)  { --h-sira: 2; }
.puan-secici label:nth-of-type(9)  { --h-sira: 1; }
.puan-secici label:nth-of-type(10) { --h-sira: 0; }

/* Özgüllük (0,3,2): aşağıdaki :checked kuralını (0,2,2) yener.
   Sebep: puan verdikten sonra imleç yıldızların üzerinde gezinirken
   büyüme geri bildirimi kaybolmamalı — kullanıcı puanını değiştirebilir. */
.puan-secici:hover label:hover,
.puan-secici:hover label:hover ~ label { --h-yildiz-olcek: 1.1; }

/* Seçim tamamlandığında dalga durur, işaret mürekkeple mühürlenir.
   :not(:hover) — imleç çekilince mühür geri gelir, üzerindeyken dalga sürer. */
.puan-secici:not(:hover) input:checked ~ label { --h-yildiz-olcek: 1; }

/* MÜHÜR .h-canli'ye BAĞLI: `checked` sunucudan basılıyor
   (book/detay.php:131). Bağlanmasaydı sayfa her açıldığında mühür
   kendiliğinden oynardı — kullanıcı eylemi olmadan hareket = WCAG 2.2.2
   ihlali ve dikkat dağıtıcı. h-canli'yi yalnız hareket.js tıklama/değişim
   anında ekler. */
.puan-secici.h-canli input:checked + label {
  --h-yildiz-don: -4deg;
  animation: h-yildiz-muhur var(--h-sure-2, 180ms) var(--h-yay-tuy, cubic-bezier(.2,.9,.3,1.2));
}


/* ==========================================================================
   E6) PELÜR KÂĞIDI — spoiler örtüsünün sıyrılması
   NEDEN: Şu an her iki katman da ANİ (app.css:465-466). En yüksek algılanan
   kazanç/maliyet oranı.

   İMZA AYRINTI: blur, örtü KALKMAYA BAŞLADIKTAN 60ms SONRA çözülür. İki
   katmanın gecikmeli ilerlemesi fiziksel tabaka hissini yaratan tek ayrıntı;
   eşzamanlı yapılırsa jenerik bir "reveal" olur.

   display:none yerine visibility+transform kullanılır: @starting-style /
   allow-discrete gerekmez, her tarayıcıda çalışır ve visibility örtüyü
   erişilebilirlik ağacından da çıkarır. .spoiler-ort'ta zaten
   overflow:hidden var (app.css:456), örtü kutunun dışına taşmaz.
   ========================================================================== */
.spoiler-ort .spoiler-ic {
  transition: filter var(--h-sure-4, 380ms) var(--h-egri-kagit, cubic-bezier(.32,.72,0,1));
  transition-delay: calc(40ms * var(--h-carpan, 1));
}

.spoiler-ort .spoiler-uyari {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition:
    opacity    var(--h-sure-4, 380ms) var(--h-egri-kagit, cubic-bezier(.32,.72,0,1)),
    transform  var(--h-sure-4, 380ms) var(--h-egri-kagit, cubic-bezier(.32,.72,0,1)),
    visibility 0s;
}

/* app.css:466'daki `display:none` burada geçersiz kılınır (eşit özgüllük,
   sonraki dosya). Örtü yukarı doğru sıyrılır. */
.spoiler-ort.acik .spoiler-uyari {
  display: grid;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-100%);
  transition:
    opacity    var(--h-sure-4, 380ms) var(--h-egri-kagit, cubic-bezier(.32,.72,0,1)),
    transform  var(--h-sure-4, 380ms) var(--h-egri-kagit, cubic-bezier(.32,.72,0,1)),
    visibility 0s linear var(--h-sure-4, 380ms);
}


/* ==========================================================================
   E7) KÂĞIDIN MASAYA KONMASI — modal, açılır menü, arama sonucu
   NEDEN: Şu an yalnız GİRİŞ animasyonu var; kapanış anî, çünkü hepsi
   display:none ↔ .acik ile çalışıyor ve app.js sınıfı ANINDA kaldırıyor.

   İKİ KAZANÇ:
   (a) çıkış animasyonu artık VAR (giriş yavaş, çıkış %60 hızlı),
   (b) backdrop-filter yalnız açıkken uygulanıyor — kapalıyken iOS Safari'de
       gereksiz compositing katmanı oluşmuyor.

   İMZA AYRINTI: --h-don-yaprak .4deg. Kâğıt masaya asla tam hizalı düşmez;
   0.4 derecenin üstü anında ucuz görünür — dozun kendisi tasarım kararıdır.

   ARKA PLAN GERİ ÇEKİLMESİ BİLİNÇLİ OLARAK YAPILMIYOR: #ana'yı ölçeklemek
   backdrop-filter'lı perde ile birleşince iOS'ta 60fps'i kesin düşürür.
   ========================================================================== */
.modal-perde {
  display: flex;
  visibility: hidden;
  /* app.css'te display:none idi; geçiş için display:flex'e çevrildi.
     visibility:hidden eleman LAYOUT'A KATILIR — kitaplık modalı her
     girişli sayfada basıldığı için bedava bir layout maliyeti doğurdu.
     content-visibility:hidden alt ağacın layout'unu atlar, geçiş yine
     çalışır (kapalıyken zaten görünmez). */
  content-visibility: hidden;
  opacity: 0;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  animation: none;
  transition:
    opacity    var(--h-sure-cikis, 156ms) var(--h-egri-cikis, cubic-bezier(.4,0,1,1)),
    visibility 0s linear var(--h-sure-cikis, 156ms);
}
.modal-perde.acik {
  visibility: visible;
  content-visibility: visible;
  opacity: 1;
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  animation: none;
  transition:
    opacity    var(--h-sure-3, 260ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    visibility 0s;
}

.modal {
  animation: none;
  opacity: 0;
  transform: translateY(var(--h-yol-tuy, 10px)) scale(.985) rotate(var(--h-don-yaprak, .4deg));
  transition:
    opacity   var(--h-sure-cikis, 156ms) var(--h-egri-cikis, cubic-bezier(.4,0,1,1)),
    transform var(--h-sure-cikis, 156ms) var(--h-egri-cikis, cubic-bezier(.4,0,1,1));
}
.modal-perde.acik .modal {
  opacity: 1;
  transform: none;
  transition:
    transform var(--h-sure-4, 380ms) var(--h-egri-kagit, cubic-bezier(.32,.72,0,1)),
    opacity   var(--h-sure-3, 260ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1));
}

/* Açılır menü: kâğıt yukarıdan aşağı iner. */
.acilir-menu {
  display: block;
  visibility: hidden;
  opacity: 0;
  animation: none;
  transform: translateY(calc(-1 * var(--h-yol-kagit, 6px)));
  transition:
    opacity    var(--h-sure-cikis, 156ms) var(--h-egri-cikis, cubic-bezier(.4,0,1,1)),
    transform  var(--h-sure-cikis, 156ms) var(--h-egri-cikis, cubic-bezier(.4,0,1,1)),
    visibility 0s linear var(--h-sure-cikis, 156ms);
}
.acilir-menu.acik {
  display: block;
  visibility: visible;
  opacity: 1;
  animation: none;
  transform: none;
  transition:
    opacity    var(--h-sure-3, 260ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    transform  var(--h-sure-3, 260ms) var(--h-egri-kagit, cubic-bezier(.32,.72,0,1)),
    visibility 0s;
}

/* Arama sonucunun ŞU AN HİÇ giriş animasyonu yok — bedava kazanç. */
.arama-sonuc {
  display: block;
  visibility: hidden;
  opacity: 0;
  transform: translateY(calc(-1 * var(--h-yol-kagit, 6px)));
  transition:
    opacity    var(--h-sure-cikis, 156ms) var(--h-egri-cikis, cubic-bezier(.4,0,1,1)),
    transform  var(--h-sure-cikis, 156ms) var(--h-egri-cikis, cubic-bezier(.4,0,1,1)),
    visibility 0s linear var(--h-sure-cikis, 156ms);
}
.arama-sonuc.acik {
  display: block;
  visibility: visible;
  opacity: 1;
  transform: none;
  transition:
    opacity    var(--h-sure-3, 260ms) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)),
    transform  var(--h-sure-3, 260ms) var(--h-egri-kagit, cubic-bezier(.32,.72,0,1)),
    visibility 0s;
}

/* Mobilde modal zaten alttan yapışan bir sheet (app.css:803).
   Kayan bir sheet eğik durmaz: dönme kaldırılır, giriş aşağıdan gelir. */
@media (max-width: 720px) {
  .modal {
    transform: translateY(100%);
    /* Kendi çıkış süresi: --h-sure-cikis (156ms) burada YANLIŞTI —
       sheet ekran yüksekliği kadar yol gidiyor, sistemdeki en uzun
       mesafe. 156ms'de bu yolu almak ışınlanma gibi görünür. 260ms
       hâlâ 380ms'lik girişin %68'i, yani "çıkış girişten kısadır"
       kuralı korunuyor. */
    transition:
      transform var(--h-sure-3, 260ms) var(--h-egri-cikis, cubic-bezier(.4,0,1,1)),
      opacity   var(--h-sure-cikis, 156ms) var(--h-egri-cikis, cubic-bezier(.4,0,1,1));
  }
  .modal-perde.acik .modal { transform: none; }
}


/* ==========================================================================
   E8) YER İMİ — ilerleme ve puan dağılımı çubukları
   NEDEN: Gerçek bir performans BORCUNUN kapatılması. app.css:648
   (.ilerleme-dolu transition: width .4s) ve app.css:381 (.dagilim-dolu
   width .5s) LAYOUT tetikliyor ve bu iki sınıf 15+ görünümde geçiyor.

   Genişlik satır içi style="width:X%" ile PHP'den basıldığı için markup
   DEĞİŞTİRİLMEZ; sunucudan gelen statik çubukların width geçişi kapatılır,
   yerine compositor'da çalışan bir scaleX açılışı (F bölümü) gelir.

   İSTİSNA: app.js:632'nin canlı güncellediği tek eleman —
   [data-ilerleme-cubuk]. Orada width geçişi KORUNUR ama süresi tokene
   bağlanır: tek eleman, tek kullanıcı eylemi, sayfada bir kez. Layout
   maliyeti kabul edilebilir ve markup'a dokunmamak buna değer.
   ========================================================================== */
.ilerleme-dolu,
.dagilim-dolu {
  transform-origin: 0 50%;
  transition: none;
}

.ilerleme-dolu[data-ilerleme-cubuk] {
  transition: width var(--h-sure-4, 380ms) var(--h-egri-kagit, cubic-bezier(.32,.72,0,1));
}

/* Puan dağılımı listesi (book/detay.php:253-256, 10 satır): raf gibi
   yukarıdan aşağı dolsun diye her satıra bir sıra indeksi verilir.
   Bu indeks F bölümündeki animation-range kaydırmasında kullanılır. */
.dagilim-satir:nth-child(1)  .dagilim-dolu { --h-sira: 0; }
.dagilim-satir:nth-child(2)  .dagilim-dolu { --h-sira: 1; }
.dagilim-satir:nth-child(3)  .dagilim-dolu { --h-sira: 2; }
.dagilim-satir:nth-child(4)  .dagilim-dolu { --h-sira: 3; }
.dagilim-satir:nth-child(5)  .dagilim-dolu { --h-sira: 4; }
.dagilim-satir:nth-child(6)  .dagilim-dolu { --h-sira: 5; }
.dagilim-satir:nth-child(7)  .dagilim-dolu { --h-sira: 6; }
.dagilim-satir:nth-child(8)  .dagilim-dolu { --h-sira: 7; }
.dagilim-satir:nth-child(9)  .dagilim-dolu { --h-sira: 8; }
.dagilim-satir:nth-child(10) .dagilim-dolu { --h-sira: 9; }


/* ==========================================================================
   E9) RAFA YERLEŞTİ — okuma durumu geri bildirimi
   NEDEN: "Okudum" demek bir kayıt işlemi değil, kitabı kendi rafına
   koymaktır. Geri bildirim nesnenin ÜZERİNDE olmalı, sadece bir balonda değil.

   .kapak'ta zaten overflow:hidden var (app.css:324), şerit gerçekten
   kapağın içinden yükselir. Sunucudan gelen şerit yerinde durur; yalnız
   YENİ işaretlemede hareket eder — bunu `h-canli` sınıfı ayırır.

   UÇAN KİTAP KLONU BİLİNÇLİ OLARAK YAPILMIYOR: .mobil-alt-menu'de
   backdrop-filter:blur(12px) var (app.css:809) ve klon onun üstünden
   geçerken blur her karede yeniden hesaplanır.
   ========================================================================== */
.kapak-durum {
  transform: translateY(var(--h-durum-y, 0));
  transition: transform var(--h-sure-3, 260ms) var(--h-egri-kagit, cubic-bezier(.32,.72,0,1));
}
/* hareket.js yeni oluşturduğu şeridi önce aşağıda tutar, sonra sınıfı
   kaldırıp 0'a çeker. Adı `h-yeni` DEĞİL: o sınıf akış girişine ait
   (E10) ve bir keyframe taşıyor — ikisi karışmamalı. */

/* Mühür: 0.96'dan 1'e, tek aşma. Kütle = kâğıt. */
.durum-dugme.h-canli.aktif {
  animation: h-muhur var(--h-sure-2, 180ms) var(--h-yay-kagit, cubic-bezier(.2,.9,.3,1.05));
}
.eylem.h-canli[data-durum] {
  animation: h-muhur var(--h-sure-2, 180ms) var(--h-yay-kagit, cubic-bezier(.2,.9,.3,1.05));
}

/* Rozet ve sayaç değişimi (bildirim/mesaj) — tüy kütlesi, kısa ve yaylı. */
.rozet.h-canli {
  animation: h-rozet-tik var(--h-sure-2, 180ms) var(--h-yay-tuy, cubic-bezier(.2,.9,.3,1.2));
}


/* ==========================================================================
   E10) YAPRAK YAPRAK — yeni içeriğin akışa girmesi
   NEDEN: Sunucudan gelen HTML şu an ANİMASYONSUZ beliriyor ve app.js hiçbir
   özel olay yayımlamıyor. hareket.js MutationObserver ile yeni düğüme
   `h-yeni` sınıfı ve --h-sira indeksi verir.

   YAY YOK — pasif giriş. 20 kart aynı anda yaylanırsa "jöle" hissi doğar.
   KADEME 6 KARTLA SINIRLI (hareket.js tarafında kırpılır): 7. karttan sonra
   gecikme sabitlenir, yoksa 20 kartlık sayfada 900ms bekleme olur.

   NOT: .ileti'ye `content-visibility: auto` BİLİNÇLİ OLARAK VERİLMEDİ.
   content-visibility paint containment uygular; .ileti içinde
   .acilir-menu (position:absolute, kartın alt kenarını aşabilir) olduğu
   için ileti menüsü kırpılırdı. Kazanç, kırık menüye değmez.
   ========================================================================== */
.ileti.h-yeni,
.yorum.h-yeni,
.kitap-kart.h-yeni {
  animation: h-yaprak var(--h-sure-4, 380ms) var(--h-egri-kagit, cubic-bezier(.32,.72,0,1)) both;
  animation-delay: calc(var(--h-sira, 0) * 45ms * var(--h-carpan, 1));
}

/* Kullanıcının KENDİ yazdığı içerik farklı davranır: zemin vurgusu.
   app.js:722'deki mevcut #yorum- çapa vurgusuyla aynı dil — "bu senin". */
.ileti.h-benim,
.yorum.h-benim {
  animation: h-benim calc(var(--h-sure-5, 520ms) * 1.8) var(--h-egri-surekli, cubic-bezier(.4,0,.2,1));
}


/* ==========================================================================
   E11) MARJ ÇİZGİSİ — alıntının kenar hazırlığı
   NEDEN: Kenar boşluğuna kurşun kalemle işaret koyma (marginalia) jesti;
   okurun kitapla kurduğu en kişisel eylem. app.css:449'daki border-left,
   görünümü birebir korunarak bir sözde elemana çevrilir ki ÇİZİLEBİLSİN
   (border animasyonlanamaz, transform animasyonlanır).

   Buradaki kural yalnız YAPIYI kurar; çizilme hareketi F bölümünde,
   @supports içinde. SIRA KRİTİK: scaleY(1) VARSAYILAN durumdur.
   ========================================================================== */
.alinti-govde {
  position: relative;
  border-left: 0;
  padding-left: 19px;   /* 16px padding + 3px eski kenarlık = aynı optik hiza */
}
.alinti-govde::before {
  content: '';
  position: absolute;
  inset: 0 auto 0 0;
  width: 3px;
  border-radius: 2px;
  background: var(--ana);
  transform-origin: 50% 0;
  pointer-events: none;
}


/* ==========================================================================
   E12) BASKI PRESİ — iskelet (yükleniyor) parlaması
   NEDEN: app.css:764'teki @keyframes parla, background-position anime ediyor
   — bu bir PAINT işidir ve her karede yeniden boyama gerektirir. Aynı etki
   translateX ile compositor'da elde edilir.
   ========================================================================== */
.iskelet {
  position: relative;
  overflow: hidden;
  background: var(--yuzey-2);
  animation: none;
}
.iskelet::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, var(--yuzey-3), transparent);
  transform: translateX(-100%);
  animation: h-pres 1.4s var(--h-egri-surekli, cubic-bezier(.4,0,.2,1)) infinite;
}


/* ==========================================================================
   F) KAYDIRMA SÜRÜMLÜ HAREKETLER (scroll-driven)
   NEDEN: Sıfır JS, compositor iş parçacığında. @supports ZORUNLU ve SIRA
   KRİTİK: varsayılan durum HER ZAMAN İÇERİĞİN GÖRÜNÜR OLDUĞU DURUMDUR;
   gizleme yalnız destek varken devreye girer. Tersi yazılırsa Safari 26
   altındaki iOS'ta içerik KALICI OLARAK KAYBOLUR.

   Blok ayrıca (prefers-reduced-motion: no-preference) ile sarılır — zaman
   çizelgesine bağlı animasyonlar süre tokeninden etkilenmez, bu yüzden
   D bölümündeki çarpan onları susturamaz.
   ========================================================================== */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    /* ZORUNLU ÖN KOŞUL: app.css'te bu iki kap overflow:hidden taşıyor,
       bu da onları KAYDIRMA KABI yapar. Kaydırma kabı olan bir ebeveyn
       view() zaman çizelgesini kendi (kaydırmayan) kutusuna bağlar ve
       animasyon hiç ilerlemez. overflow:clip görsel kırpmayı ve
       border-radius davranışını aynen korur ama kaydırma kabı YARATMAZ. */
    .ilerleme-cubuk,
    .dagilim-cubuk { overflow: clip; }

    /* Rafa diziliş: klasik scroll-reveal aşağıdan yukarı kayar; burada kart
       raftan öne gelir. JS KADEME YOK — her kart kendi görüş zaman
       çizelgesini kullanır, bu yüzden AJAX ile sonradan eklenenler de
       otomatik çalışır ve hiçbir öğe "geç kalmış gibi" zıplamaz.
       rotateX/perspektif KULLANILMAZ: .kitap-ad/.kitap-yazar
       -webkit-line-clamp taşıyor, 3B dönüş altında iOS'ta metin titrer. */
    .kitap-izgara > .kitap-kart {
      animation: h-rafa-dizil linear both;
      animation-timeline: view(block);
      animation-range: entry 0% entry 45%;
    }

    /* Yer imi: çubuk kaydırıldıkça soldan sağa dolar. scaleX — width değil. */
    .ilerleme-dolu,
    .dagilim-dolu {
      animation: h-serit-dolus linear both;
      animation-timeline: view(block);
      animation-range: entry 15% entry 65%;
    }

    /* Dağılım listesinde kademe: animation-delay scroll zaman çizelgesinde
       çalışmaz, bu yüzden kaydırma ARALIĞI ötelenir. Geçersiz bir calc
       olsa bile geri düşüş "normal" aralıktır — çubuk yine dolar. */
    .dagilim-satir .dagilim-dolu {
      animation-range:
        entry calc(15% + var(--h-sira, 0) * 2%)
        entry calc(65% + var(--h-sira, 0) * 2%);
    }

    /* Marj çizgisi: kaydırdıkça yukarıdan aşağı, elle çekilmiş gibi çizilir.
       Yalnız .alinti-govde taşıyan iletilere uygulanır (20 kartlık akışta
       ortalama 3-4 tanesi) — her karta değil, gürültü olmaz. */
    .alinti-govde::before {
      /* Zaman çizelgesine bağlı animasyonda animation-duration YOK SAYILIR:
         ilerlemeyi kaydırma belirler. Süre yazmak yanıltıcıydı; easing de
         aralığın içinde uygulanır, o yüzden linear bırakılıyor. */
      animation: h-marj linear both;
      animation-timeline: view(block);
      animation-range: entry 10% entry 55%;
    }
  }
}

/* ---- SEVİYE EZMELERİ (F bölümünden SONRA olmalı) ----------------------
   NEDEN: Zaman çizelgesine bağlı animasyonlar --h-carpan'dan etkilenmez;
   "kapali" ve "az" seviyelerinde bunlar AÇIKÇA kapatılmalıdır. Özgüllük
   html[...] öneki sayesinde (0,3,1) — @supports içindeki kuralları
   !important'sız yener. Kapanınca öğe VARSAYILAN, yani görünür halde kalır. */
html[data-hareket="kapali"] .kitap-izgara > .kitap-kart,
html[data-hareket="az"]     .kitap-izgara > .kitap-kart,
html[data-hareket="kapali"] .ilerleme-dolu,
html[data-hareket="az"]     .ilerleme-dolu,
html[data-hareket="kapali"] .dagilim-dolu,
html[data-hareket="az"]     .dagilim-dolu,
html[data-hareket="kapali"] .alinti-govde::before,
html[data-hareket="az"]     .alinti-govde::before,
html[data-hareket="kapali"] .iskelet::after,
html[data-hareket="az"]     .iskelet::after {
  animation: none;
}

/* ---- "Kaydırmayla ortaya çıkma" ayrı şalteri --------------------------
   Yönetici seviyeyi "tam" bırakıp yalnız kaydırma canlanmasını kapatmak
   isteyebilir (uzun akışlarda bazı kullanıcılar bunu rahatsız edici bulur).
   layouts/app.php bunu data-hareket-kaydirma="0" olarak basar. */
html[data-hareket-kaydirma="0"] .kitap-izgara > .kitap-kart,
html[data-hareket-kaydirma="0"] .ilerleme-dolu,
html[data-hareket-kaydirma="0"] .dagilim-dolu,
html[data-hareket-kaydirma="0"] .dagilim-satir .dagilim-dolu,
html[data-hareket-kaydirma="0"] .alinti-govde::before {
  animation: none;
}

/* "az" ve "kapali": süsleyici mikro hareketler tamamen susar.
   Renk ve opaklık geçişleri KALIR — geri bildirim kaybolmaz. */
html[data-hareket="kapali"] .eylem[data-begen].h-canli.aktif::before,
html[data-hareket="az"]     .eylem[data-begen].h-canli.aktif::before,
html[data-hareket="kapali"] .eylem[data-begen].h-canli.aktif svg,
html[data-hareket="az"]     .eylem[data-begen].h-canli.aktif svg,
html[data-hareket="kapali"] .durum-dugme.h-canli.aktif,
html[data-hareket="az"]     .durum-dugme.h-canli.aktif,
html[data-hareket="kapali"] .eylem.h-canli[data-durum],
html[data-hareket="az"]     .eylem.h-canli[data-durum],
html[data-hareket="kapali"] .rozet.h-canli,
html[data-hareket="az"]     .rozet.h-canli,
html[data-hareket="kapali"] .puan-secici.h-canli input:checked + label,
html[data-hareket="az"]     .puan-secici.h-canli input:checked + label {
  animation: none;
}


/* ==========================================================================
   G) KAPAK MORPH'U — paylaşılan eleman geçişleri (View Transitions)
   NEDEN: Kullanıcı tıkladığı kapağın kaybolup yeniden doğmasını değil,
   KİTABI ELİNE ALDIĞINI görür. Cross-document; sıfır JS gerektiren temel.
   Firefox kuralı yok sayar — fallback kodu gerekmez.

   view-transition-name ŞABLONDAN BASILAMAZ: aynı kitap tek sayfada birden
   çok yerde görünebiliyor ve sonsuz kaydırma sürekli yeni kopya ekliyor;
   YİNELENEN AD, geçişi HATA VERMEDEN, SESSİZCE komple iptal eder. Bu yüzden
   adı hareket.js tıklama anında YALNIZ tıklanan elemana atar.

   KAPATMA: @view-transition bir at-rule olduğu için öznitelik seçicisiyle
   koşullanamaz; html[data-hareket="kapali"] durumunu hareket.js pageswap'te
   e.viewTransition.skipTransition() ile karşılar.

   ÖN KOŞUL: book/detay.php:33'teki style="width:100%" kaldırılmış olmalı,
   yoksa mobilde kapak ekran genişliğine çıkar ve morph yıkılır.
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}

/* Kök çapraz solma: ayarlanmazsa tarayıcı varsayılanı (250ms ease) kalır
   ve sistemin süre merdivenine ait olmayan tek hareket o olurdu. */
::view-transition-group(root) {
  animation-duration: var(--h-sure-3, 260ms);
  animation-timing-function: var(--h-egri-surekli, cubic-bezier(.4,0,.2,1));
}

/* Kapak: kütle = raf. 520ms'i AŞMAZ — eski görüntü donuk bir anlık
   görüntüdür ve animasyon boyunca tıklanamaz; uzun süre yavaşlık hissettirir. */
::view-transition-group(kapak) {
  animation-duration: var(--h-sure-5, 520ms);
  animation-timing-function: var(--h-egri-kagit, cubic-bezier(.32,.72,0,1));
}
::view-transition-image-pair(kapak) { overflow: hidden; }
::view-transition-old(kapak),
::view-transition-new(kapak) { object-fit: cover; height: 100%; }

/* Kimlik (avatar): kapaktan KISA tutulur — küçük nesne, kısa yol.
   Kütle-süre kuralı: hafif nesne çabuk varır. */
::view-transition-group(kimlik) {
  animation-duration: var(--h-sure-3, 260ms);
  animation-timing-function: var(--h-egri-kagit, cubic-bezier(.32,.72,0,1));
}
::view-transition-old(kimlik),
::view-transition-new(kimlik) { object-fit: cover; border-radius: 50%; }

/* KABUK SABİT KALIR: .ust ve .mobil-alt-menu'nün ikisinde de
   backdrop-filter: blur(12px) var; morph edilirlerse iOS'ta blur her karede
   yeniden hesaplanır. Ad verilip çapraz solmadan çıkarılırlar. */
.ust { view-transition-name: kabuk-ust; }
.mobil-alt-menu { view-transition-name: kabuk-alt; }

::view-transition-old(kabuk-ust),
::view-transition-new(kabuk-ust),
::view-transition-old(kabuk-alt),
::view-transition-new(kabuk-alt) { animation: none; }


/* ==========================================================================
   H) DOKUNMATİK — hover yok, jank var
   NEDEN: Dokunmatik cihazlarda :hover "yapışır" ve 3B dönme dokunma sonrası
   ekranda asılı kalır. Kapak eğimi ve yükselmesi burada kapatılır; renk
   geri bildirimleri (E1) korunur.
   ========================================================================== */
@media (hover: none) {
  /* Dokunmatikte :hover "yapışır" — dokunulan kart parmak çekildikten
     sonra da eğik kalır. Üç bildirimin ÜÇÜ de sıfırlanmalı: transform,
     sırt kalınlığı ve will-change. Önceki sürümde :focus-within kolunda
     son ikisi unutulmuştu; dokunulan kartın sırtı kalın kalıyor ve o
     eleman kalıcı olarak ayrı bir compositor katmanında tutuluyordu. */
  .kitap-kart:hover .kapak,
  .kitap-kart:focus-within .kapak {
    transform: none;
    --h-sirt-olcek: 1;
    will-change: auto;
  }
  .puan-secici:hover label:hover,
  .puan-secici:hover label:hover ~ label { --h-yildiz-olcek: 1; }
}


/* ==========================================================================
   I) @keyframes
   NEDEN: Mevcut adlarla çakışma YASAK — app.css'te balonGir, soluk, modalGir,
   menuGir, don, parla, yaziyor tanımlı. Buradaki her ad `h-` önekli.
   Hepsi yalnız transform / opacity / filter üzerinden çalışır.
   ========================================================================== */

/* Mürekkep lekesi: kâğıda damlar, yayılır, emilir. */
@keyframes h-leke {
  0%   { opacity: 0;  transform: scale(.4); }
  35%  { opacity: .9; }
  100% { opacity: 0;  transform: scale(1.35); }
}

/* Kalp: tüy kütlesi — kısa, aşmalı, tek atım.
   Aşmayı keyframe DEĞİL --h-yay-tuy üretir; burada yalnız iki uç durur. */
@keyframes h-kalp {
  from { transform: scale(.86); }
  to   { transform: scale(1); }
}

/* Yıldız mührü: seçilen yıldız kâğıda bastırılır. */
@keyframes h-yildiz-muhur {
  0%   { transform: scale(.82) rotate(-4deg); }
  100% { transform: scale(1)   rotate(-4deg); }
}

/* Durum mührü: 0.96'dan 1'e, kâğıt kütlesi, tek aşma. */
@keyframes h-muhur {
  0%   { transform: scale(.96); }
  100% { transform: scale(1); }
}

/* Rozet tiki: sayı değişti — küçük, keskin. */
@keyframes h-rozet-tik {
  0%   { transform: scale(.7); }
  100% { transform: scale(1); }
}

/* Yaprak: yeni içerik masaya konur. Yay yok, pasif giriş. */
@keyframes h-yaprak {
  from { opacity: 0; transform: translateY(var(--h-yol-cilt, 4px)); }
  to   { opacity: 1; transform: none; }
}

/* "Bu senin": kullanıcının kendi yazdığı içeriğin zemin vurgusu.
   Renk değişimi paint'tir ama tek elemanda ve tek seferdir.
   `to` YAZILMAZ — böylece bitiş değeri elemanın KENDİ zemini olur.
   (.ileti'de var(--yuzey), .yorum'da saydam; sabit yazsaydık biri bozulurdu.
   app.css:679'daki `soluk` keyframe'i de aynı tekniği kullanıyor.) */
@keyframes h-benim {
  from { background-color: var(--ana-soluk); }
}

/* Şerit dolumu: width yerine scaleX — compositor'da. */
@keyframes h-serit-dolus {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

/* Marj çizgisi: yukarıdan aşağı, elle çekilmiş gibi. */
@keyframes h-marj {
  from { transform: scaleY(0); }
  to   { transform: scaleY(1); }
}

/* Rafa diziliş: kart derinlikten öne gelir. */
@keyframes h-rafa-dizil {
  from { opacity: 0; transform: translateY(var(--h-yol-tuy, 10px)) scale(.975); }
  to   { opacity: 1; transform: none; }
}

/* Baskı presi: iskelet parlaması, background-position değil translateX. */
@keyframes h-pres {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}
