{% extends 'base.html' %}
{% load i18n %}

{% block extra_css %}
<style>
  /* ============================================================
     Apple-inspired design system — Home page
     Light & airy: #f5f5f7 canvas, SF typography, frosted glass,
     soft shadows, generous whitespace, physics-like easing.
     ============================================================ */
  :root {
    --apple-bg: #f5f5f7;
    --apple-text: #1d1d1f;
    --apple-gray: #6e6e73;
    --apple-blue: #0071e3;
    --apple-blue-hover: #0077ed;
    --apple-red: #ff3b30;
    --apple-separator: rgba(0, 0, 0, 0.06);
    --apple-ease: cubic-bezier(0.28, 0.11, 0.32, 1);
    --apple-radius-lg: 28px;
    --apple-radius-md: 22px;
    --apple-shadow-card: 0 4px 24px rgba(0, 0, 0, 0.05);
    --apple-shadow-lift: 0 18px 44px rgba(0, 0, 0, 0.10);
  }

  body {
    background-color: var(--apple-bg) !important;
    font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text",
      "Helvetica Neue", "Segoe UI", Roboto, Arial, sans-serif;
    color: var(--apple-text);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
  }

  .apple-wrap { max-width: 72rem; margin: 0 auto; }

  /* ---------- Entrance & scroll-reveal animations ---------- */
  @keyframes hero-rise {
    from { opacity: 0; transform: translateY(34px); }
    to   { opacity: 1; transform: translateY(0); }
  }
  .hero-anim { animation: hero-rise 1s var(--apple-ease) both; }

  .reveal {
    opacity: 0;
    transform: translateY(26px);
    transition: opacity 0.85s var(--apple-ease), transform 0.85s var(--apple-ease);
    transition-delay: calc(var(--d, 0) * 70ms);
  }
  .reveal.in-view { opacity: 1; transform: translateY(0); }

  @media (prefers-reduced-motion: reduce) {
    .hero-anim, .reveal {
      animation: none !important;
      transition: none !important;
      opacity: 1 !important;
      transform: none !important;
    }
    .app-chip, .post-row, .fan-card, .fan-card img, .fan-card-frame, .fan-lightbox { transition: none !important; animation: none !important; }
  }

  /* ---------- Hero (compact: search + stats share one row on desktop) ---------- */
  .apple-hero {
    position: relative;
    text-align: center;
    padding: clamp(1.5rem, 4vh, 2.5rem) 1rem clamp(1.25rem, 3vh, 1.75rem);
  }
  @media (min-width: 768px) {
    .apple-hero { display: flex; align-items: center; gap: 2.25rem; text-align: left; }
  }
  .hero-glow {
    position: absolute;
    inset: -12% -6% auto;
    height: 340px;
    z-index: 0;
    pointer-events: none;
    background:
      radial-gradient(38% 46% at 26% 42%, rgba(0, 144, 255, 0.15), transparent 70%),
      radial-gradient(34% 42% at 70% 30%, rgba(191, 90, 242, 0.13), transparent 70%),
      radial-gradient(30% 40% at 52% 72%, rgba(255, 94, 98, 0.10), transparent 70%);
    filter: blur(8px);
  }
  .apple-hero > *:not(.hero-glow) { position: relative; z-index: 1; }

  .hero-title {
    font-size: clamp(3.1rem, 9vw, 5.9rem);
    font-weight: 700;
    letter-spacing: -0.035em;
    line-height: 1.04;
    background: linear-gradient(97deg, #0090ff 0%, #6a5cff 38%, #c850c0 70%, #ff5e62 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
  .hero-subtitle {
    margin: 1.1rem auto 0;
    max-width: 37rem;
    font-size: clamp(1.05rem, 2.3vw, 1.375rem);
    line-height: 1.45;
    font-weight: 400;
    letter-spacing: -0.01em;
    color: var(--apple-gray);
  }

  /* ---------- Hero search (frosted glass pill) ---------- */
  .hero-search { position: relative; width: 100%; max-width: 44rem; margin: 0 auto; text-align: left; }
  @media (min-width: 768px) {
    .hero-search { flex: 1 1 auto; margin: 0; }
  }
  /* Must stack above .hero-stats (which shares the same z-index: 1 from the
     ">*:not(.hero-glow)" rule above) so the search results dropdown never
     renders underneath the stats row. */
  .apple-hero > .hero-search { z-index: 20; }
  .hero-search .search-icon {
    position: absolute; left: 1.4rem; top: 50%; transform: translateY(-50%);
    width: 1.25rem; height: 1.25rem; color: var(--apple-gray); pointer-events: none;
  }
  .hero-search input {
    width: 100%; height: 3.4rem; padding: 0 1.6rem 0 3.5rem;
    font-size: 1.08rem; color: var(--apple-text);
    background: rgba(255, 255, 255, 0.82);
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    backdrop-filter: blur(20px) saturate(180%);
    border: 1px solid rgba(0, 0, 0, 0.07);
    border-radius: 980px;
    box-shadow: 0 12px 36px rgba(0, 0, 0, 0.08);
    transition: box-shadow 0.35s var(--apple-ease), border-color 0.35s var(--apple-ease);
  }
  .hero-search input::placeholder { color: #86868b; }
  .hero-search input:focus {
    outline: none;
    border-color: rgba(0, 113, 227, 0.55);
    box-shadow: 0 0 0 4px rgba(0, 113, 227, 0.16), 0 16px 48px rgba(0, 0, 0, 0.11);
  }

  .search-results {
    position: absolute; top: calc(100% + 0.65rem); left: 0; right: 0; z-index: 60;
    background: rgba(255, 255, 255, 0.94);
    -webkit-backdrop-filter: blur(24px) saturate(180%);
    backdrop-filter: blur(24px) saturate(180%);
    border: 1px solid rgba(0, 0, 0, 0.06);
    border-radius: var(--apple-radius-md);
    box-shadow: 0 26px 68px rgba(0, 0, 0, 0.17);
    max-height: 22rem; overflow-y: auto; padding: 0.4rem;
  }
  .search-result-item {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    padding: 0.85rem 1.05rem; border-radius: 16px; cursor: pointer;
  }
  .search-result-item:hover, .search-result-item.active { background: rgba(0, 0, 0, 0.05); }
  .search-result-item a {
    flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-weight: 600; font-size: 1.02rem; color: var(--apple-text); text-decoration: none;
  }
  .search-result-item mark {
    background: rgba(0, 113, 227, 0.13); color: var(--apple-blue);
    border-radius: 4px; padding: 0 1px;
  }
  .search-result-item .chev { width: 0.85rem; height: 0.85rem; color: #c7c7cc; flex: none; }
  .search-empty { padding: 1.1rem; color: var(--apple-gray); text-align: center; font-size: 0.95rem; }

  /* ---------- Hero stats ---------- */
  .hero-stats { display: flex; align-items: center; justify-content: center; gap: 1.4rem; margin-top: 1.1rem; }
  @media (min-width: 768px) {
    .hero-stats { margin-top: 0; flex: none; padding-right: 0.35rem; }
  }
  .hero-stat { display: flex; flex-direction: column; align-items: center; gap: 0.1rem; }
  .hero-stat-number { font-size: 1.35rem; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
  .hero-stat-label { font-size: 0.7rem; color: var(--apple-gray); text-transform: uppercase; letter-spacing: 0.09em; font-weight: 500; }
  .hero-stat-divider { width: 1px; height: 2rem; background: rgba(0, 0, 0, 0.10); }

  /* ---------- Sections, cards, buttons ---------- */
  .home-section { margin-top: clamp(1.5rem, 3.5vh, 2.5rem); }
  .apple-card {
    background: #fff;
    border-radius: var(--apple-radius-lg);
    border: 1px solid rgba(0, 0, 0, 0.04);
    box-shadow: var(--apple-shadow-card);
  }
  .section-header { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: 1.15rem; padding: 0 0.35rem; }
  .section-title { font-size: clamp(1.45rem, 3vw, 1.8rem); font-weight: 700; letter-spacing: -0.022em; }

  .apple-link {
    color: var(--apple-blue); font-weight: 500; font-size: 0.98rem;
    display: inline-flex; align-items: center; gap: 0.3rem; text-decoration: none;
  }
  .apple-link:hover { text-decoration: underline; }
  .apple-link svg { width: 0.8em; height: 0.8em; transition: transform 0.3s var(--apple-ease); }
  .apple-link:hover svg { transform: translateX(2px); }

  .apple-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 0.45rem;
    min-height: 44px; padding: 0.6rem 1.4rem; border-radius: 980px;
    font-weight: 600; font-size: 0.95rem; border: none; cursor: pointer; text-decoration: none;
    transition: background 0.3s var(--apple-ease), transform 0.3s var(--apple-ease), box-shadow 0.3s var(--apple-ease);
  }
  .apple-btn:active { transform: scale(0.97); }
  .apple-btn-primary { background: var(--apple-blue); color: #fff; box-shadow: 0 6px 18px rgba(0, 113, 227, 0.28); }
  .apple-btn-primary:hover { background: var(--apple-blue-hover); color: #fff; }
  .apple-btn-danger { background: transparent; color: var(--apple-red); border: 1px solid rgba(255, 59, 48, 0.4); }
  .apple-btn-danger:hover { background: rgba(255, 59, 48, 0.08); }

  /* ---------- Link library (table) ---------- */
  .library-header {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
    gap: 1rem; padding: 1.7rem 1.8rem 1.25rem;
  }
  .library-title-group { display: flex; flex-direction: column; gap: 0.15rem; }
  .library-count { font-size: 0.88rem; color: var(--apple-gray); }
  .library-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; }
  .library-filter { position: relative; }
  .library-filter svg {
    position: absolute; left: 0.95rem; top: 50%; transform: translateY(-50%);
    width: 1rem; height: 1rem; color: var(--apple-gray); pointer-events: none;
  }
  .library-filter input {
    height: 44px; width: min(16rem, 64vw); padding: 0 1rem 0 2.5rem;
    border-radius: 980px; border: 1px solid rgba(0, 0, 0, 0.10);
    background: var(--apple-bg); font-size: 0.95rem; color: var(--apple-text);
    transition: border-color 0.3s ease, box-shadow 0.3s ease, background 0.3s ease;
  }
  .library-filter input::placeholder { color: #86868b; }
  .library-filter input:focus {
    outline: none; background: #fff;
    border-color: rgba(0, 113, 227, 0.5);
    box-shadow: 0 0 0 4px rgba(0, 113, 227, 0.14);
  }

  .table-scroll { overflow-x: auto; }
  .apple-table { width: 100%; min-width: 620px; border-collapse: collapse; }
  .apple-table thead th {
    text-align: left; font-size: 0.72rem; font-weight: 600; letter-spacing: 0.09em;
    text-transform: uppercase; color: var(--apple-gray);
    padding: 0.85rem 1.1rem; border-bottom: 1px solid var(--apple-separator);
    background: rgba(245, 245, 247, 0.6);
  }
  .apple-table thead th a { color: inherit; text-decoration: none; display: inline-flex; align-items: center; gap: 0.25rem; }
  .apple-table thead th a:hover { color: var(--apple-text); }
  .apple-table tbody td {
    padding: 0.95rem 1.1rem; border-bottom: 1px solid var(--apple-separator);
    font-size: 0.95rem; vertical-align: middle;
  }
  .apple-table tbody tr { transition: background 0.25s ease; }
  .apple-table tbody tr:hover { background: rgba(0, 0, 0, 0.025); }
  .apple-table tbody tr:last-child td { border-bottom: none; }

  .alias-link { color: var(--apple-blue); font-weight: 600; text-decoration: none; }
  .alias-link:hover { text-decoration: underline; }
  .url-cell { max-width: 20rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--apple-gray); }
  .url-cell a { color: inherit; text-decoration: none; }
  .url-cell a:hover { color: var(--apple-blue); }
  .clicks-cell { font-variant-numeric: tabular-nums; font-weight: 500; }

  .badge {
    display: inline-flex; align-items: center; gap: 0.4rem;
    padding: 0.28rem 0.72rem; border-radius: 980px;
    font-size: 0.78rem; font-weight: 600;
  }
  .badge-template { background: rgba(191, 90, 242, 0.12); color: #8944ab; }
  .badge-link { background: rgba(0, 113, 227, 0.10); color: var(--apple-blue); }
  .badge-custom { background: rgba(48, 209, 88, 0.15); color: #1e7b34; }

  .icon-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; border-radius: 50%; color: #8e8e93;
    transition: background 0.25s ease, color 0.25s ease, transform 0.25s ease;
  }
  .icon-btn:hover { background: rgba(0, 0, 0, 0.05); transform: translateY(-1px); }
  .icon-btn.blue:hover { color: var(--apple-blue); }
  .icon-btn.green:hover { color: #1e7b34; }
  .icon-btn.red:hover { color: var(--apple-red); }
  .icon-btn svg { width: 1.2rem; height: 1.2rem; }

  .library-footer {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem; padding: 1.1rem 1.8rem 1.5rem;
  }
  .selection-hint { font-size: 0.85rem; color: var(--apple-gray); margin: 0; }

  .library-load-more { display: flex; justify-content: center; padding: 0.4rem 1.8rem 1.2rem; }
  .apple-btn-secondary {
    background: var(--apple-bg); color: var(--apple-text);
    border: 1px solid rgba(0, 0, 0, 0.10);
  }
  .apple-btn-secondary:hover { background: #ececef; }
  .apple-btn-secondary[disabled] { opacity: 0.55; cursor: default; pointer-events: none; }

  /* ---------- First-screen dashboard (posts + images side by side) ---------- */
  .dash-grid {
    display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.25rem;
    align-items: start;
  }
  @media (min-width: 1024px) {
    .dash-grid { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); }
  }
  .dash-rail { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }

  /* ---------- Latest Posts (dense list) ---------- */
  .posts-panel { overflow: hidden; }
  .posts-panel-header {
    display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
    padding: 1.35rem 1.5rem 0.8rem;
  }
  .post-list { list-style: none; margin: 0; padding: 0.15rem 0.5rem 0.6rem; }
  .post-row {
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto;
    align-items: center; gap: 0.9rem;
    padding: 0.72rem 1rem; border-radius: 16px;
    color: inherit; text-decoration: none;
    transition: background 0.25s ease;
  }
  .post-row:hover { background: rgba(0, 0, 0, 0.035); }
  .post-row-date {
    font-size: 0.78rem; font-weight: 600; color: var(--apple-gray);
    text-transform: uppercase; letter-spacing: 0.04em; white-space: nowrap;
  }
  .post-row-main { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
  .post-row-title {
    font-size: 0.98rem; font-weight: 600; letter-spacing: -0.01em;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .post-row-summary {
    font-size: 0.82rem; color: var(--apple-gray);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .post-row-tags { display: flex; gap: 0.35rem; }
  .post-row-chev { width: 0.9rem; height: 0.9rem; color: #c7c7cc; flex: none; transition: transform 0.3s var(--apple-ease); }
  .post-row:hover .post-row-chev { transform: translateX(2px); }
  .tag-pill { font-size: 0.75rem; font-weight: 500; color: var(--apple-gray); background: rgba(0, 0, 0, 0.05); padding: 0.22rem 0.65rem; border-radius: 980px; white-space: nowrap; }

  .empty-state { padding: 2.75rem 1.5rem; text-align: center; color: var(--apple-gray); display: flex; flex-direction: column; align-items: center; gap: 1rem; }
  .empty-state svg { width: 2.6rem; height: 2.6rem; color: #c7c7cc; }

  /* ---------- Mini app chips (compact gradients under the photo fan) ---------- */
  .app-chips { display: grid; grid-template-columns: 1fr 1fr; gap: 0.9rem; }
  .app-chip {
    position: relative; isolation: isolate; overflow: hidden;
    display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
    min-height: 68px; padding: 0.9rem 1.15rem; border-radius: var(--apple-radius-md);
    color: #fff; text-decoration: none; box-shadow: var(--apple-shadow-card);
    transition: transform 0.45s var(--apple-ease), box-shadow 0.45s var(--apple-ease);
  }
  .app-chip:hover { transform: translateY(-3px); box-shadow: var(--apple-shadow-lift); color: #fff; }
  .app-chip::before {
    content: ""; position: absolute; inset: 0; z-index: -1;
    background: radial-gradient(120% 120% at 85% 0%, rgba(255, 255, 255, 0.30) 0%, rgba(255, 255, 255, 0) 46%);
  }
  .app-chip > svg { width: 1rem; height: 1rem; flex: none; opacity: 0.9; transition: transform 0.35s var(--apple-ease); }
  .app-chip:hover > svg { transform: translate(2px, -2px); }
  .app-chip-text { display: flex; flex-direction: column; gap: 0.05rem; min-width: 0; }
  .app-chip-name { font-size: 1.02rem; font-weight: 700; letter-spacing: -0.01em; }
  .app-chip-desc {
    font-size: 0.8rem; color: rgba(255, 255, 255, 0.85);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .gradient-1 { background: linear-gradient(135deg, #0a84ff 0%, #5e5ce6 100%); }
  .gradient-2 { background: linear-gradient(135deg, #ff9f0a 0%, #ff375f 100%); }
  .gradient-3 { background: linear-gradient(135deg, #30d158 0%, #0a84ff 100%); }
  .gradient-4 { background: linear-gradient(135deg, #bf5af2 0%, #ff375f 100%); }

  /* ---------- Photo fan (creative image deck) ---------- */
  .fan-actions { display: flex; align-items: center; gap: 1.1rem; }
  .photo-fan {
    display: flex; justify-content: center; align-items: flex-end;
    padding: clamp(1.25rem, 3vw, 1.75rem) 0.5rem 0.75rem;
  }
  /* Fan lives in the dashboard's side rail on desktop: shrink the cards so
     all five fit the narrower column. When the grid stacks (< 1024px) the
     fan is full-width again and falls back to the original roomy sizes. */
  .rail-header { flex-wrap: wrap; margin-bottom: 0; padding: 0; }
  .dash-rail .fan-card { --w: clamp(120px, 11vw, 170px); }
  @media (max-width: 1023px) {
    .dash-rail .fan-card { --w: clamp(96px, 15.5vw, 172px); }
    .rail-header { padding: 0 0.35rem; margin-bottom: 0.5rem; }
  }
  @media (max-width: 640px) {
    .dash-rail .fan-card { --w: clamp(84px, 21vw, 120px); }
  }
  .fan-card {
    --w: clamp(96px, 15.5vw, 172px);
    position: relative; z-index: var(--z, 1);
    width: var(--w); flex: none;
    margin: 0 calc(var(--w) * -0.17);
    padding: 0; border: none; background: none; cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transform-origin: 50% 115%;
    transform: translateY(var(--fy, 0px)) rotate(var(--r, 0deg));
    animation: fan-in 0.95s var(--apple-ease) both;
    animation-delay: calc(0.55s + var(--d, 0) * 95ms);
    transition: transform 0.5s var(--apple-ease), opacity 0.35s ease;
  }
  @keyframes fan-in {
    from { opacity: 0; transform: translateY(56px) rotate(0deg) scale(0.86); }
    to   { opacity: 1; transform: translateY(var(--fy, 0px)) rotate(var(--r, 0deg)) scale(1); }
  }
  .fan-card:hover, .fan-card:focus-visible {
    z-index: 30;
    transform: translateY(calc(var(--fy, 0px) - 24px)) rotate(0deg) scale(1.18);
  }
  .photo-fan:hover .fan-card:not(:hover):not(:focus-visible) { opacity: 0.85; }
  .fan-card-frame {
    position: relative; display: block;
    background: #fff; border-radius: 18px; padding: 8px 8px 26px;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.13), 0 2px 6px rgba(0, 0, 0, 0.06);
    transition: box-shadow 0.45s var(--apple-ease);
  }
  .fan-card:hover .fan-card-frame, .fan-card:focus-visible .fan-card-frame {
    box-shadow: 0 30px 62px rgba(0, 0, 0, 0.22), 0 4px 10px rgba(0, 0, 0, 0.08);
  }
  /* Washi tape strip on top of each card */
  .fan-card-frame::before {
    content: ""; position: absolute; top: -11px; left: 50%;
    width: 44%; height: 22px; border-radius: 4px;
    transform: translateX(-50%) rotate(var(--tape-r, -3deg));
    background: var(--tape, rgba(0, 113, 227, 0.20));
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
    -webkit-backdrop-filter: blur(1.5px); backdrop-filter: blur(1.5px);
  }
  .fan-card img {
    display: block; width: 100%; aspect-ratio: 3 / 4; object-fit: cover;
    border-radius: 12px; background: #e8e8ed;
    transition: opacity 0.3s ease;
  }
  .fan-card-hint {
    position: absolute; inset: 8px 8px 26px; border-radius: 12px;
    display: flex; align-items: center; justify-content: center;
    background: rgba(0, 0, 0, 0.22); color: #fff;
    opacity: 0; transition: opacity 0.3s ease;
  }
  .fan-card-hint svg { width: 1.35rem; height: 1.35rem; }
  .fan-card:hover .fan-card-hint, .fan-card:focus-visible .fan-card-hint { opacity: 1; }
  @media (max-width: 640px) {
    .fan-card { --w: clamp(84px, 21vw, 120px); margin: 0 calc(var(--w) * -0.14); }
    .fan-card-frame { padding: 6px 6px 20px; border-radius: 15px; }
    .fan-card img { border-radius: 10px; }
    .fan-card-hint { inset: 6px 6px 20px; border-radius: 10px; }
    .fan-actions { gap: 0.8rem; }
  }

  /* ---------- Fan lightbox (full-size preview on card click) ---------- */
  .fan-lightbox {
    position: fixed; inset: 0; z-index: 1000;
    display: flex; align-items: center; justify-content: center;
    padding: 2rem;
    background: rgba(0, 0, 0, 0.82);
    -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
    animation: fan-lightbox-fade 0.25s ease;
    cursor: zoom-out;
  }
  .fan-lightbox[hidden] { display: none; }
  @keyframes fan-lightbox-fade { from { opacity: 0; } to { opacity: 1; } }
  .fan-lightbox-img {
    max-width: min(92vw, 900px); max-height: 88vh;
    display: block; object-fit: contain; cursor: default;
    border-radius: 14px;
    background: #e8e8ed;
    box-shadow: 0 30px 80px rgba(0, 0, 0, 0.45);
  }
  .fan-lightbox-close {
    position: absolute; top: 1.1rem; right: 1.1rem;
    width: 2.5rem; height: 2.5rem; border-radius: 50%; border: none;
    display: flex; align-items: center; justify-content: center;
    background: rgba(255, 255, 255, 0.14); color: #fff; cursor: pointer;
    transition: background 0.2s ease;
  }
  .fan-lightbox-close:hover, .fan-lightbox-close:focus-visible { background: rgba(255, 255, 255, 0.28); }
  .fan-lightbox-close svg { width: 1.2rem; height: 1.2rem; }

  /* ---------- Fan lightbox: prev / next navigation ---------- */
  .fan-lightbox-nav {
    position: absolute; top: 50%; transform: translateY(-50%);
    width: 3rem; height: 3rem; border-radius: 50%; border: none;
    display: flex; align-items: center; justify-content: center;
    background: rgba(255, 255, 255, 0.14); color: #fff; cursor: pointer;
    transition: background 0.2s ease, opacity 0.2s ease;
    -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  }
  .fan-lightbox-nav:hover, .fan-lightbox-nav:focus-visible { background: rgba(255, 255, 255, 0.28); }
  .fan-lightbox-nav svg { width: 1.6rem; height: 1.6rem; }
  .fan-lightbox-nav.prev { left: 1.1rem; }
  .fan-lightbox-nav.next { right: 1.1rem; }
  .fan-lightbox-counter {
    position: absolute; bottom: 1.3rem; left: 50%; transform: translateX(-50%);
    padding: 0.35rem 1rem; border-radius: 980px;
    background: rgba(255, 255, 255, 0.14); color: #fff;
    font-size: 0.85rem; font-weight: 500; letter-spacing: 0.01em;
    -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
    pointer-events: none;
  }
  @media (max-width: 640px) {
    .fan-lightbox-nav { width: 2.6rem; height: 2.6rem; }
    .fan-lightbox-nav.prev { left: 0.6rem; }
    .fan-lightbox-nav.next { right: 0.6rem; }
  }

  /* ---------- Accessibility: visible keyboard focus ---------- */
  a:focus-visible, button:focus-visible {
    outline: 2px solid var(--apple-blue);
    outline-offset: 3px;
    border-radius: 12px;
  }
  .apple-btn:focus-visible, .icon-btn:focus-visible { border-radius: 980px; }
  .fan-card:focus-visible { border-radius: 18px; outline-offset: 5px; }

  /* ---------- Small screens ---------- */
  @media (max-width: 640px) {
    .library-header { padding: 1.35rem 1.1rem 1rem; }
    .library-footer { padding: 1rem 1.1rem 1.25rem; }
    .apple-table thead th, .apple-table tbody td { padding-left: 0.8rem; padding-right: 0.8rem; }
    .hero-stats { gap: 1.25rem; }
    .posts-panel-header { padding: 1.15rem 1.1rem 0.6rem; }
    .post-row { gap: 0.65rem; padding: 0.68rem 0.75rem; }
    .post-row-tags { display: none; }
    .app-chip-desc { display: none; }
    .app-chip { min-height: 52px; }
  }
</style>
{% endblock %}

{% block extra_head %}
<script>
  'use strict';

  /* Random image helpers — defined in <head> because inline onerror/onclick
     handlers on the <img> elements can fire while the body is still parsing.
     The random-image API picks from random subfolders and can 404 occasionally;
     retry with a fresh cache-buster a couple of times before giving up. */
  window.handleRandomImageError = function (img) {
    const retries = parseInt(img.dataset.retries || '0', 10);
    if (retries >= 2) return;
    img.dataset.retries = String(retries + 1);
    setTimeout(() => window.refreshRandomImage(img), 400 * (retries + 1));
  };

  window.refreshRandomImage = function (img) {
    if (!img) return;
    const box = img.getAttribute('data-box');
    const timestamp = new Date().getTime();
    // Request the same fixed high-resolution size used for the initial page
    // load (600x800), regardless of the card's small on-screen CSS size —
    // the .fan-card is only ~100-170px wide, so using img.offsetWidth/Height
    // here fetched a much smaller/blurrier image than the initial fetch did.
    const width = 600;
    const height = 800;
    const newUrl = `/api/images/random/${width}/${height}/?fit=crop&v=${timestamp}&box=${box}`;

    const newImg = new Image();
    newImg.onload = function () {
      img.style.opacity = '0.4';
      setTimeout(() => {
        img.src = newUrl;
        img.dataset.retries = '0';
        img.style.opacity = '1';
      }, 160);
    };
    newImg.onerror = function () {
      console.warn('Random image request failed (the picker can 404 intermittently); retrying via onerror handler.');
      const currentUrl = new URL(img.src);
      currentUrl.searchParams.set('v', timestamp);
      img.src = currentUrl.toString();
      img.style.opacity = '1';
    };
    newImg.src = newUrl;
  };
</script>
{% endblock %}

{% block content %}
<div class="apple-wrap">

  <!-- ============ Hero ============ -->
  <section class="apple-hero" aria-labelledby="hero-title">
    <div class="hero-glow" aria-hidden="true"></div>

    <!-- <h1 id="hero-title" class="hero-title hero-anim">{% trans "GoLinks" %}.</h1> -->

    <div class="hero-search hero-anim" style="animation-delay: 0.24s;" role="search">
      <svg class="search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
        <path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd" />
      </svg>
      <label for="main-search-input" class="sr-only">{% trans "Search aliases" %}</label>
      <input
        type="search"
        id="main-search-input"
        placeholder="{% trans 'Search aliases...' %}"
        autocomplete="off"
        autofocus
        role="combobox"
        aria-expanded="false"
        aria-controls="main-search-results"
        aria-autocomplete="list"
      >
      <div id="main-search-results" class="search-results" role="listbox" aria-label="{% trans 'Matching aliases' %}" hidden></div>
    </div>

    <div class="hero-stats hero-anim" style="animation-delay: 0.36s;">
      <div class="hero-stat">
        <span class="hero-stat-number">{{ total_links_count }}</span>
        <span class="hero-stat-label">{% trans "Links" %}</span>
      </div>
      <span class="hero-stat-divider" aria-hidden="true"></span>
      <div class="hero-stat">
        <span class="hero-stat-number">{{ total_clicks }}</span>
        <span class="hero-stat-label">{% trans "Total Clicks" %}</span>
      </div>
    </div>
  </section>

  <!-- ============ First-screen dashboard: Latest Posts + Random Images ============ -->
  <section class="dash-grid home-section">
    <!-- Latest Posts (dense list) -->
    <div class="apple-card posts-panel reveal" aria-labelledby="posts-heading">
      <div class="posts-panel-header">
        <h2 id="posts-heading" class="section-title">{% trans "Latest Posts" %}</h2>
        <a href="{% url 'post-list' %}" class="apple-link">
          {% trans "View all posts" %}
          <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5" aria-hidden="true">
            <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/>
          </svg>
        </a>
      </div>
      {% if latest_posts %}
        <ul class="post-list" role="list">
          {% for post in latest_posts %}
            <li>
              <a href="{{ post.get_absolute_url }}" class="post-row">
                <span class="post-row-date">{{ post.created_at|date:"M d" }}</span>
                <span class="post-row-main">
                  <span class="post-row-title">{{ post.title }}</span>
                </span>
                <svg class="post-row-chev" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5" aria-hidden="true">
                  <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/>
                </svg>
              </a>
            </li>
          {% endfor %}
        </ul>
      {% else %}
        <div class="empty-state">
          <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9.5a2 2 0 00-2-2h-2"/>
          </svg>
          <p>{% trans "No posts available yet" %}</p>
          <a href="{% url 'post-create' %}" class="apple-btn apple-btn-primary">{% trans "Create your first post" %}</a>
        </div>
      {% endif %}
    </div>

    <!-- Side rail: photo fan + mini app chips -->
    <div class="dash-rail">
      <section aria-labelledby="images-heading">
        <div class="section-header rail-header">
          <h2 id="images-heading" class="section-title">{% trans "Random Images" %}</h2>
          <div class="fan-actions">
            <button type="button" id="shuffle-all-images" class="apple-btn apple-btn-secondary">
              <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" aria-hidden="true">
                <path stroke-linecap="round" stroke-linejoin="round" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
              </svg>
              {% trans "Shuffle all" %}
            </button>
            <a href="{% url 'mini-apps-image-gallery' %}" class="apple-link">
              {% trans "Open gallery" %}
              <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5" aria-hidden="true">
                <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/>
              </svg>
            </a>
          </div>
        </div>
        <div class="photo-fan">
      <button type="button" class="fan-card" style="--r: -13deg; --fy: 18px; --z: 1; --d: 0; --tape: rgba(0, 113, 227, 0.22); --tape-r: -4deg;" onclick="openFanLightbox(this.querySelector('img'))" aria-label="{% trans 'View Full Size' %}">
        <span class="fan-card-frame">
          <img src="/api/images/random/600/800/?fit=crop&v={{ timestamp }}&box=fan1" data-box="fan1" alt="{% trans 'Random Image' %}" loading="lazy" onerror="handleRandomImageError(this)">
          <span class="fan-card-hint" aria-hidden="true">
            <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
              <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-4.35-4.35M11 19a8 8 0 100-16 8 8 0 000 16zM11 8v6M8 11h6"/>
            </svg>
          </span>
        </span>
      </button>
      <button type="button" class="fan-card" style="--r: -6.5deg; --fy: 7px; --z: 2; --d: 1; --tape: rgba(255, 55, 95, 0.20); --tape-r: 3deg;" onclick="openFanLightbox(this.querySelector('img'))" aria-label="{% trans 'View Full Size' %}">
        <span class="fan-card-frame">
          <img src="/api/images/random/600/800/?fit=crop&v={{ timestamp }}&box=fan2" data-box="fan2" alt="{% trans 'Random Image' %}" loading="lazy" onerror="handleRandomImageError(this)">
          <span class="fan-card-hint" aria-hidden="true">
            <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
              <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-4.35-4.35M11 19a8 8 0 100-16 8 8 0 000 16zM11 8v6M8 11h6"/>
            </svg>
          </span>
        </span>
      </button>
      <button type="button" class="fan-card" style="--r: 0deg; --fy: 0px; --z: 3; --d: 2; --tape: rgba(48, 209, 88, 0.22); --tape-r: -2deg;" onclick="openFanLightbox(this.querySelector('img'))" aria-label="{% trans 'View Full Size' %}">
        <span class="fan-card-frame">
          <img src="/api/images/random/600/800/?fit=crop&v={{ timestamp }}&box=fan3" data-box="fan3" alt="{% trans 'Random Image' %}" loading="lazy" onerror="handleRandomImageError(this)">
          <span class="fan-card-hint" aria-hidden="true">
            <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
              <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-4.35-4.35M11 19a8 8 0 100-16 8 8 0 000 16zM11 8v6M8 11h6"/>
            </svg>
          </span>
        </span>
      </button>
      <button type="button" class="fan-card" style="--r: 6.5deg; --fy: 7px; --z: 2; --d: 3; --tape: rgba(255, 159, 10, 0.22); --tape-r: 4deg;" onclick="openFanLightbox(this.querySelector('img'))" aria-label="{% trans 'View Full Size' %}">
        <span class="fan-card-frame">
          <img src="/api/images/random/600/800/?fit=crop&v={{ timestamp }}&box=fan4" data-box="fan4" alt="{% trans 'Random Image' %}" loading="lazy" onerror="handleRandomImageError(this)">
          <span class="fan-card-hint" aria-hidden="true">
            <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
              <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-4.35-4.35M11 19a8 8 0 100-16 8 8 0 000 16zM11 8v6M8 11h6"/>
            </svg>
          </span>
        </span>
      </button>
      <button type="button" class="fan-card" style="--r: 13deg; --fy: 18px; --z: 1; --d: 4; --tape: rgba(191, 90, 242, 0.22); --tape-r: -3deg;" onclick="openFanLightbox(this.querySelector('img'))" aria-label="{% trans 'View Full Size' %}">
        <span class="fan-card-frame">
          <img src="/api/images/random/600/800/?fit=crop&v={{ timestamp }}&box=fan5" data-box="fan5" alt="{% trans 'Random Image' %}" loading="lazy" onerror="handleRandomImageError(this)">
          <span class="fan-card-hint" aria-hidden="true">
            <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
              <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-4.35-4.35M11 19a8 8 0 100-16 8 8 0 000 16zM11 8v6M8 11h6"/>
            </svg>
          </span>
        </span>
      </button>
        </div>
      </section>

      <!-- Mini app chips -->
      {% if mini_apps %}
        <div class="app-chips">
          {% for app in mini_apps %}
            {% if app.url != '#' %}
              <a href="{% url app.url %}" class="app-chip gradient-{% cycle '1' '2' '3' '4' %}" aria-label="{{ app.name }} — {{ app.description }}">
                <span class="app-chip-text">
                  <span class="app-chip-name">{{ app.name }}</span>
                  <span class="app-chip-desc">{{ app.description }}</span>
                </span>
                <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5" aria-hidden="true">
                  <path stroke-linecap="round" stroke-linejoin="round" d="M7 17L17 7M17 7H8M17 7v9"/>
                </svg>
              </a>
            {% else %}
              <div class="app-chip gradient-{% cycle '1' '2' '3' '4' %}">
                <span class="app-chip-text">
                  <span class="app-chip-name">{{ app.name }}</span>
                  <span class="app-chip-desc">{{ app.description }}</span>
                </span>
              </div>
            {% endif %}
          {% endfor %}
        </div>
      {% endif %}
    </div><!-- /.dash-rail -->
  </section><!-- /.dash-grid -->

  <!-- ============ Fan Lightbox (full-size preview) ============ -->
  <div id="fan-lightbox" class="fan-lightbox" hidden role="dialog" aria-modal="true" aria-label="{% trans 'Random Image' %}">
    <button type="button" class="fan-lightbox-close" id="fan-lightbox-close" aria-label="{% trans 'Close' %}">
      <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5" aria-hidden="true">
        <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
      </svg>
    </button>
    <img id="fan-lightbox-img" class="fan-lightbox-img" src="" alt="{% trans 'Random Image' %}">
    <button type="button" class="fan-lightbox-nav prev" id="fan-lightbox-prev" aria-label="{% trans 'Previous image' %}">
      <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5" aria-hidden="true">
        <path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/>
      </svg>
    </button>
    <button type="button" class="fan-lightbox-nav next" id="fan-lightbox-next" aria-label="{% trans 'Next image' %}">
      <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5" aria-hidden="true">
        <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/>
      </svg>
    </button>
    <span class="fan-lightbox-counter" id="fan-lightbox-counter" aria-hidden="true"></span>
  </div>

  <!-- ============ Link Library ============ -->
  <section class="apple-card home-section" aria-labelledby="links-heading">
    <div class="library-header">
      <div class="library-title-group">
        <h2 id="links-heading" class="section-title">{% trans "Link List" %}</h2>
        <p class="library-count">
          {% blocktrans count counter=total_links_count %}{{ counter }} link{% plural %}{{ counter }} links{% endblocktrans %}
        </p>
      </div>
      <div class="library-actions">
        <div class="library-filter">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
            <path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd" />
          </svg>
          <label for="search-alias-input" class="sr-only">{% trans "Search links" %}</label>
          <input type="search" id="search-alias-input" placeholder="{% trans 'Search links...' %}" autocomplete="off">
        </div>
        <a href="{% url 'link_create' %}" class="apple-btn apple-btn-primary">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true">
            <path d="M12 5v14m-7-7h14"></path>
          </svg>
          {% trans "New Link" %}
        </a>
      </div>
    </div>

    <form method="post" action="{% url 'delete_selected' %}">
      {% csrf_token %}
      <div class="table-scroll">
        <table class="apple-table">
          <thead>
            <tr>
              <th scope="col" style="width: 3rem;">
                <input type="checkbox" id="select-all" aria-label="{% trans 'Select all links' %}" class="rounded border-gray-300 text-blue-600 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50">
              </th>
              <th scope="col">
                <a href="?sort=alias&order={% if current_sort == 'alias' and current_order == 'asc' %}desc{% else %}asc{% endif %}">
                  {% trans "Alias" %}
                  {% if current_sort == 'alias' %}
                    <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
                      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M{% if current_order == 'asc' %}19 9l-7 7-7-7{% else %}5 15l7-7 7 7{% endif %}"></path>
                    </svg>
                  {% endif %}
                </a>
              </th>
              <th scope="col" class="hidden sm:table-cell">
                <a href="?sort=link_type&order={% if current_sort == 'link_type' and current_order == 'asc' %}desc{% else %}asc{% endif %}">
                  {% trans "Type" %}
                  {% if current_sort == 'link_type' %}
                    <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
                      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M{% if current_order == 'asc' %}19 9l-7 7-7-7{% else %}5 15l7-7 7 7{% endif %}"></path>
                    </svg>
                  {% endif %}
                </a>
              </th>
              <th scope="col" class="hidden md:table-cell">
                <a href="?sort=original_url&order={% if current_sort == 'original_url' and current_order == 'asc' %}desc{% else %}asc{% endif %}">
                  {% trans "Original URL" %}
                  {% if current_sort == 'original_url' %}
                    <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
                      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M{% if current_order == 'asc' %}19 9l-7 7-7-7{% else %}5 15l7-7 7 7{% endif %}"></path>
                    </svg>
                  {% endif %}
                </a>
              </th>
              <th scope="col" class="hidden sm:table-cell">
                <a href="?sort=clicks&order={% if current_sort == 'clicks' and current_order == 'asc' %}desc{% else %}asc{% endif %}">
                  {% trans "Clicks" %}
                  {% if current_sort == 'clicks' %}
                    <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
                      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M{% if current_order == 'asc' %}19 9l-7 7-7-7{% else %}5 15l7-7 7 7{% endif %}"></path>
                    </svg>
                  {% endif %}
                </a>
              </th>
              <th scope="col">{% trans "Actions" %}</th>
            </tr>
          </thead>
          <tbody id="links-table-body">
            {% for link in links %}
              {% include 'links/_link_row.html' %}
            {% empty %}
              <tr>
                <td colspan="6" style="text-align: center; padding: 3rem 1rem; color: var(--apple-gray);">{% trans "No links available" %}</td>
              </tr>
            {% endfor %}
          </tbody>
        </table>
      </div>
      {% if has_more_links %}
        <div class="library-load-more">
          <button
            type="button"
            id="load-more-links"
            class="apple-btn apple-btn-secondary"
            data-offset="{{ links_page_size }}"
            data-sort="{{ current_sort }}"
            data-order="{{ current_order }}"
          >
            {% trans "Load more" %}
          </button>
        </div>
      {% endif %}
      <div class="library-footer">
        <p class="selection-hint" id="selection-hint" aria-live="polite"></p>
        <button type="submit" class="apple-btn apple-btn-danger">{% trans "Delete Selected" %}</button>
      </div>
    </form>
  </section>

  <div style="height: 2.5rem;" aria-hidden="true"></div>

</div>
{% endblock %}

{% block extra_js %}
<script>
  'use strict';

  document.addEventListener('DOMContentLoaded', function () {

    /* ---------- Photo fan: shuffle all cards ---------- */
    const shuffleAllBtn = document.getElementById('shuffle-all-images');
    if (shuffleAllBtn) {
      shuffleAllBtn.addEventListener('click', function () {
        document.querySelectorAll('.fan-card img').forEach((img, i) => {
          setTimeout(() => window.refreshRandomImage(img), i * 120);
        });
      });
    }

    /* ---------- Photo fan: release entrance animation so hover transforms work ---------- */
    document.querySelectorAll('.fan-card').forEach(card => {
      card.addEventListener('animationend', () => { card.style.animation = 'none'; }, { once: true });
    });

    /* ---------- Photo fan: lightbox (click a card to view it full-size) ---------- */
    const fanLightbox = document.getElementById('fan-lightbox');
    const fanLightboxImg = document.getElementById('fan-lightbox-img');
    const fanLightboxClose = document.getElementById('fan-lightbox-close');
    const fanLightboxPrev = document.getElementById('fan-lightbox-prev');
    const fanLightboxNext = document.getElementById('fan-lightbox-next');
    const fanLightboxCounter = document.getElementById('fan-lightbox-counter');

    // Snapshot of the five fan-card images, captured once so navigation works
    // even if the DOM shuffles later. We store the live <img> elements and
    // read their current src each time, so refreshed (re-shuffled) images are
    // reflected without rebuilding this list.
    const fanImages = Array.from(document.querySelectorAll('.fan-card img'));
    let fanIndex = -1;

    function showFanImage(index) {
      if (!fanImages.length || !fanLightboxImg) return;
      // Wrap around the ends so left/right never dead-end.
      fanIndex = ((index % fanImages.length) + fanImages.length) % fanImages.length;
      const src = fanImages[fanIndex].currentSrc || fanImages[fanIndex].src;
      // Preserve any inline opacity transition state from the source image.
      fanLightboxImg.src = src;
      fanLightboxImg.alt = fanImages[fanIndex].alt;
      if (fanLightboxCounter) {
        fanLightboxCounter.textContent = (fanIndex + 1) + ' / ' + fanImages.length;
      }
    }

    window.openFanLightbox = function (img) {
      if (!img || !fanLightbox || !fanLightboxImg) return;
      // Track which of the five images was clicked so navigation starts there.
      fanIndex = fanImages.indexOf(img);
      if (fanIndex < 0) {
        // Fallback: match by current src if the element reference was replaced.
        const src = img.currentSrc || img.src;
        fanIndex = fanImages.findIndex(i => (i.currentSrc || i.src) === src);
        if (fanIndex < 0) fanIndex = 0;
      }
      showFanImage(fanIndex);
      fanLightbox.hidden = false;
      document.body.style.overflow = 'hidden';
      fanLightboxClose.focus();
    };

    function closeFanLightbox() {
      if (!fanLightbox) return;
      fanLightbox.hidden = true;
      fanLightboxImg.src = '';
      fanIndex = -1;
      document.body.style.overflow = '';
    }

    if (fanLightbox) {
      fanLightboxClose.addEventListener('click', closeFanLightbox);
      if (fanLightboxPrev) {
        fanLightboxPrev.addEventListener('click', function (event) {
          event.stopPropagation();
          showFanImage(fanIndex - 1);
          fanLightboxPrev.focus();
        });
      }
      if (fanLightboxNext) {
        fanLightboxNext.addEventListener('click', function (event) {
          event.stopPropagation();
          showFanImage(fanIndex + 1);
          fanLightboxNext.focus();
        });
      }
      // Click the dimmed backdrop (not the image or nav buttons) to dismiss.
      fanLightbox.addEventListener('click', function (event) {
        if (event.target === fanLightbox) closeFanLightbox();
      });
      document.addEventListener('keydown', function (event) {
        if (fanLightbox.hidden) return;
        if (event.key === 'Escape') closeFanLightbox();
        else if (event.key === 'ArrowLeft') { event.preventDefault(); showFanImage(fanIndex - 1); }
        else if (event.key === 'ArrowRight') { event.preventDefault(); showFanImage(fanIndex + 1); }
      });
    }

    /* ---------- Scroll-reveal animations (respects reduced motion) ---------- */
    const revealEls = document.querySelectorAll('.reveal');
    const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reducedMotion || !('IntersectionObserver' in window)) {
      revealEls.forEach(el => el.classList.add('in-view'));
    } else {
      // threshold: 0 fires as soon as any part of the target intersects the
      // root, regardless of the target's own size. Using a ratio-based
      // threshold (e.g. 0.08) breaks for tall elements (like a long table)
      // whose intersection ratio can never reach that fraction even when
      // most of it is on screen, leaving it permanently hidden.
      const observer = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            entry.target.classList.add('in-view');
            observer.unobserve(entry.target);
          }
        });
      }, { threshold: 0, rootMargin: '0px 0px -6% 0px' });
      revealEls.forEach(el => observer.observe(el));
    }

    /* ---------- Hero search: alias autocomplete ---------- */
    setupMainSearch('main-search-input', 'main-search-results');

    function setupMainSearch(inputId, resultsId) {
      const searchInput = document.getElementById(inputId);
      const searchResults = document.getElementById(resultsId);
      if (!searchInput || !searchResults) return;

      let currentFocus = -1;

      function debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
          clearTimeout(timeout);
          timeout = setTimeout(() => func(...args), wait);
        };
      }

      const escapeRegExp = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
      const escapeHtml = s => s.replace(/[&<>"']/g, c => ({
        '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'
      }[c]));

      function highlightMatch(text, query) {
        const safe = escapeHtml(text);
        if (!query) return safe;
        const regex = new RegExp('(' + escapeRegExp(escapeHtml(query)) + ')', 'gi');
        return safe.replace(regex, '<mark>$1</mark>');
      }

      function closeResults() {
        searchResults.hidden = true;
        searchResults.innerHTML = '';
        searchInput.setAttribute('aria-expanded', 'false');
        searchInput.removeAttribute('aria-activedescendant');
        currentFocus = -1;
      }

      const chevronSvg = '<svg class="chev" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/></svg>';

      searchInput.addEventListener('input', debounce(function (e) {
        const query = e.target.value.trim();

        if (query.length === 0) {
          closeResults();
          return;
        }

        fetch(`/search/aliases/?q=${encodeURIComponent(query)}`)
          .then(response => response.json())
          .then(data => {
            searchResults.innerHTML = '';
            currentFocus = -1;

            if (Array.isArray(data) && data.length > 0) {
              data.forEach((item, index) => {
                if (item && typeof item === 'object' && 'alias' in item && 'url' in item) {
                  const div = document.createElement('div');
                  div.className = 'search-result-item';
                  div.setAttribute('role', 'option');
                  div.setAttribute('aria-selected', 'false');
                  div.id = `${resultsId}-opt-${index}`;

                  const aliasLink = document.createElement('a');
                  aliasLink.href = item.url;
                  aliasLink.innerHTML = highlightMatch(item.alias, query);

                  div.innerHTML = '';
                  div.appendChild(aliasLink);
                  div.insertAdjacentHTML('beforeend', chevronSvg);
                  div.addEventListener('click', function (e) {
                    if (e.target.tagName !== 'A') window.location.href = item.url;
                  });

                  searchResults.appendChild(div);
                }
              });
            } else {
              const empty = document.createElement('div');
              empty.className = 'search-empty';
              empty.textContent = '{% trans "No results found" %}';
              searchResults.appendChild(empty);
            }

            searchResults.hidden = false;
            searchInput.setAttribute('aria-expanded', 'true');
          })
          .catch(error => {
            console.error('Error:', error);
            closeResults();
          });
      }, 250));

      searchInput.addEventListener('keydown', function (e) {
        const items = searchResults.getElementsByClassName('search-result-item');

        if (e.key === 'ArrowDown') {
          e.preventDefault();
          setActive(items, currentFocus + 1);
        } else if (e.key === 'ArrowUp') {
          e.preventDefault();
          setActive(items, currentFocus - 1);
        } else if (e.key === 'Enter') {
          if (currentFocus > -1 && items[currentFocus]) {
            e.preventDefault();
            items[currentFocus].querySelector('a').click();
          } else if (items.length > 0) {
            e.preventDefault();
            items[0].querySelector('a').click();
          }
        } else if (e.key === 'Escape') {
          closeResults();
          searchInput.value = '';
        }
      });

      function setActive(items, index) {
        if (!items || items.length === 0) return;
        Array.from(items).forEach(el => {
          el.classList.remove('active');
          el.setAttribute('aria-selected', 'false');
        });
        currentFocus = ((index % items.length) + items.length) % items.length;
        const active = items[currentFocus];
        active.classList.add('active');
        active.setAttribute('aria-selected', 'true');
        active.scrollIntoView({ block: 'nearest' });
        searchInput.setAttribute('aria-activedescendant', active.id);
      }

      document.addEventListener('click', function (e) {
        if (e.target !== searchInput && !searchResults.contains(e.target)) {
          closeResults();
        }
      });
    }

    /* ---------- Link table client-side filter ---------- */
    const filterInput = document.getElementById('search-alias-input');
    const tbody = document.getElementById('links-table-body');
    if (filterInput && tbody) {
      filterInput.addEventListener('keyup', function () {
        const searchTerm = filterInput.value.toLowerCase().trim();
        let visibleCount = 0;

        tbody.querySelectorAll('.link-row').forEach(row => {
          const aliasElement = row.querySelector('td:nth-child(2) a');
          if (!aliasElement) return;
          const shouldShow = aliasElement.textContent.toLowerCase().includes(searchTerm);
          row.style.display = shouldShow ? '' : 'none';
          if (shouldShow) visibleCount++;
        });

        const noResultsRow = document.getElementById('no-results-row');
        if (visibleCount === 0) {
          if (!noResultsRow) {
            const newRow = document.createElement('tr');
            newRow.id = 'no-results-row';
            newRow.innerHTML = '<td colspan="6" style="text-align:center; padding: 2.5rem 1rem; color: var(--apple-gray);">{% trans "No matching links found" %}</td>';
            tbody.appendChild(newRow);
          }
        } else if (noResultsRow) {
          noResultsRow.remove();
        }
      });
    }

    /* ---------- Load more links ---------- */
    const loadMoreBtn = document.getElementById('load-more-links');
    if (loadMoreBtn && tbody) {
      loadMoreBtn.addEventListener('click', function () {
        const offset = loadMoreBtn.dataset.offset;
        const sort = loadMoreBtn.dataset.sort;
        const order = loadMoreBtn.dataset.order;

        loadMoreBtn.disabled = true;
        loadMoreBtn.textContent = '{% trans "Loading…" %}';

        const params = new URLSearchParams({ offset, sort, order });
        fetch(`{% url 'load_more_links' %}?${params.toString()}`)
          .then(response => response.json())
          .then(data => {
            tbody.insertAdjacentHTML('beforeend', data.html);
            if (data.has_more) {
              loadMoreBtn.dataset.offset = data.next_offset;
              loadMoreBtn.disabled = false;
              loadMoreBtn.textContent = '{% trans "Load more" %}';
            } else {
              loadMoreBtn.remove();
            }
          })
          .catch(error => {
            console.error('Error loading more links:', error);
            loadMoreBtn.disabled = false;
            loadMoreBtn.textContent = '{% trans "Load more" %}';
          });
      });
    }

    /* ---------- Select-all + selection hint ---------- */
    const selectAll = document.getElementById('select-all');
    const selectionHint = document.getElementById('selection-hint');

    function updateSelectionHint() {
      if (!selectionHint) return;
      const count = document.querySelectorAll('input[name="selected_links"]:checked').length;
      selectionHint.textContent = count > 0 ? count + ' {% trans "selected" %}' : '';
    }

    if (selectAll) {
      selectAll.addEventListener('change', function () {
        document.querySelectorAll('input[name="selected_links"]').forEach(checkbox => {
          checkbox.checked = selectAll.checked;
        });
        updateSelectionHint();
      });
    }
    document.addEventListener('change', function (e) {
      if (e.target.name === 'selected_links') updateSelectionHint();
    });
  });
</script>
{% endblock %}
