/* ── MasonryGrid — stable CSS Grid layout ── */ .masonry-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; } /* item */ .masonry-item { position: relative; border-radius: 4px; overflow: hidden; cursor: zoom-in; aspect-ratio: 4 / 3; /* scroll-reveal initial state */ opacity: 0; transform: translateY(20px); transition: opacity 0.45s ease, transform 0.45s ease; } .masonry-item--revealed { opacity: 1; transform: translateY(0); } .masonry-item__img { display: block; width: 100%; height: 100%; object-fit: cover; transition: filter 0.25s ease; } .masonry-item:hover .masonry-item__img { filter: brightness(1.08); } /* hover overlay */ .masonry-item__overlay { position: absolute; inset: 0; display: flex; align-items: flex-end; padding: 8px 10px; background: linear-gradient(transparent 60%, rgba(15, 12, 9, 0.7)); opacity: 0; transition: opacity 0.25s ease; pointer-events: none; } .masonry-item:hover .masonry-item__overlay { opacity: 1; } .masonry-item__label { font: 11px var(--tv-mono); color: var(--tv-text); letter-spacing: 0.04em; } /* sentinel */ .masonry-sentinel { height: 1px; grid-column: 1 / -1; } /* loading dots */ .masonry-loading { grid-column: 1 / -1; display: flex; justify-content: center; gap: 6px; padding: 24px 0; } .masonry-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--tv-muted); animation: masonry-pulse 1.2s ease-in-out infinite; } .masonry-dot:nth-child(2) { animation-delay: 0.15s; } .masonry-dot:nth-child(3) { animation-delay: 0.3s; } @keyframes masonry-pulse { 0%, 80%, 100% { opacity: 0.25; transform: scale(0.8); } 40% { opacity: 1; transform: scale(1); } } /* end message */ .masonry-end { grid-column: 1 / -1; text-align: center; font: 11px var(--tv-mono); letter-spacing: 0.12em; color: var(--tv-dim); padding: 32px 0 16px; margin: 0; } /* responsive */ @media (max-width: 1024px) { .masonry-grid { grid-template-columns: repeat(3, 1fr); } } @media (max-width: 768px) { .masonry-grid { grid-template-columns: repeat(2, 1fr); } } /* reduced motion */ @media (prefers-reduced-motion: reduce) { .masonry-item { opacity: 1; transform: none; transition: none; } .masonry-item__img, .masonry-item__overlay { transition: none; } .masonry-dot { animation: none; } }