/* * MasonryGallery – scoped CSS * * Grid column definitions (activated when React adds .is-enhanced to the root). * Mirrors the blade @push('styles') blocks so the same rules apply whether the * page is rendered server-side or by the React component. */ /* ── Masonry grid ─────────────────────────────────────────────────────────── */ [data-nova-gallery].is-enhanced [data-gallery-grid] { display: grid; grid-template-columns: repeat(1, minmax(0, 1fr)); grid-auto-rows: 8px; gap: 1rem; } @media (min-width: 768px) { [data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(2, minmax(0, 1fr)); } } /* Spec §5: 4 columns desktop, scaling up for very wide screens */ @media (min-width: 1024px) { [data-nova-gallery] [data-gallery-grid] { grid-template-columns: repeat(4, minmax(0, 1fr)); } [data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(4, minmax(0, 1fr)); } } @media (min-width: 1600px) { [data-nova-gallery] [data-gallery-grid] { grid-template-columns: repeat(5, minmax(0, 1fr)); } [data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(5, minmax(0, 1fr)); } } @media (min-width: 2200px) { [data-nova-gallery] [data-gallery-grid] { grid-template-columns: repeat(6, minmax(0, 1fr)); } [data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(6, minmax(0, 1fr)); } } [data-nova-gallery].is-enhanced [data-gallery-grid] > .nova-card { margin: 0 !important; } /* ── Fallback aspect-ratio for cards without stored dimensions ───────────── */ /* * When ArtworkCard has no width/height data it renders the img as h-auto, * meaning the container height is 0 until the image loads. Setting a * default aspect-ratio here reserves approximate space immediately and * prevents applyMasonry from calculating span=1 → then jumping on load. * Cards with an inline aspect-ratio style (from real dimensions) override this. */ [data-nova-gallery] [data-gallery-grid] .nova-card-media { aspect-ratio: 3 / 2; width: 100%; /* prevent aspect-ratio + max-height from shrinking the column width */ } /* Override: when an inline aspect-ratio is set by ArtworkCard those values */ /* take precedence naturally (inline style > class). No extra selector needed. */ /* ── Card max-height cap ──────────────────────────────────────────────────── */ /* * Limits any single card to the height of 2 stacked 16:9 images in its column. * Formula: 2 × (col_width × 9/16) = col_width × 9/8 * * 5-col (lg+): col_width = (100vw - 80px_padding - 4×24px_gaps) / 5 * = (100vw - 176px) / 5 * max-height = (100vw - 176px) / 5 × 9/8 * = (100vw - 176px) × 0.225 * * 2-col (md): col_width = (100vw - 80px - 1×24px) / 2 * = (100vw - 104px) / 2 * max-height = (100vw - 104px) / 2 × 9/8 * = (100vw - 104px) × 0.5625 * * 1-col mobile: uncapped – portrait images are fine filling the full width. */ /* Global selector covers both the React-rendered gallery and the blade fallback */ [data-nova-gallery] [data-gallery-grid] .nova-card-media { overflow: hidden; /* ensure img is clipped at max-height */ } @media (min-width: 1024px) { [data-nova-gallery] [data-gallery-grid] .nova-card-media { /* 5-column layout: 2 × (col_width × 9/16) = col_width × 9/8 */ max-height: calc((100vw - 176px) * 9 / 40); } /* Wide (2-col spanning) cards get double the column width */ [data-nova-gallery] [data-gallery-grid] .nova-card--wide .nova-card-media { max-height: calc((100vw - 176px) * 9 / 20); } } @media (min-width: 768px) and (max-width: 1023px) { [data-nova-gallery] [data-gallery-grid] .nova-card-media { /* 2-column layout */ max-height: calc((100vw - 104px) * 9 / 16); } [data-nova-gallery] [data-gallery-grid] .nova-card--wide .nova-card-media { /* 2-col span fills full width on md breakpoint */ max-height: calc((100vw - 104px) * 9 / 8); } } /* Image is positioned absolutely inside the container so it always fills the capped box (max-height), cropping top/bottom via object-fit: cover. */ [data-nova-gallery] [data-gallery-grid] .nova-card-media > .nova-card-main-image { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; } /* ── Skeleton ─────────────────────────────────────────────────────────────── */ .nova-skeleton-card { border-radius: 1rem; min-height: 180px; background: linear-gradient( 110deg, rgba(255, 255, 255, 0.06) 8%, rgba(255, 255, 255, 0.12) 18%, rgba(255, 255, 255, 0.06) 33% ); background-size: 200% 100%; animation: novaShimmer 1.2s linear infinite; } @keyframes novaShimmer { to { background-position-x: -200%; } } /* ── Card enter animation (appended by infinite scroll) ───────────────────── */ .nova-card-enter { opacity: 0; transform: translateY(8px); } .nova-card-enter-active { opacity: 1; transform: translateY(0); transition: opacity 200ms ease-out, transform 200ms ease-out; } /* ── Card hover: bottom glow pulse ───────────────────────────────────────── */ .nova-card > a { will-change: transform, box-shadow; } .nova-card:hover > a { box-shadow: 0 8px 30px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.08), 0 0 20px rgba(224, 122, 33, 0.07); } /* ── Quick action buttons ─────────────────────────────────────────────────── */ /* * .nb-card-actions – absolutely positioned at top-right of .nova-card. * Fades in + slides down slightly when the card is hovered. * Requires .nova-card to have position:relative (set inline by ArtworkCard.jsx). */ .nb-card-actions { position: absolute; top: 0.5rem; right: 0.5rem; z-index: 30; display: flex; align-items: center; gap: 0.25rem; opacity: 0; transform: translateY(-4px); transition: opacity 200ms ease-out, transform 200ms ease-out; pointer-events: none; } .nova-card:hover .nb-card-actions, .nova-card:focus-within .nb-card-actions { opacity: 1; transform: translateY(0); pointer-events: auto; } .nb-card-action-btn { display: inline-flex; align-items: center; justify-content: center; width: 2rem; height: 2rem; border-radius: 0.5rem; background: rgba(10, 14, 20, 0.75); backdrop-filter: blur(6px); border: 1px solid rgba(255, 255, 255, 0.12); color: rgba(255, 255, 255, 0.85); font-size: 0.875rem; line-height: 1; cursor: pointer; text-decoration: none; transition: background 150ms ease, transform 150ms ease, color 150ms ease; -webkit-tap-highlight-color: transparent; } .nb-card-action-btn:hover { background: rgba(224, 122, 33, 0.85); color: #fff; transform: scale(1.1); }