/* ==========================================================================
   Fonts
   Loaded via <link> tags in every page's <head> instead of @import here —
   @import forces the browser to fetch and parse this whole file before it
   even discovers the font stylesheet, serializing two requests that could
   otherwise load in parallel.
   ========================================================================== */

/* ==========================================================================
   Design Tokens
   Source of truth: Figma "Foundations" page.
   These are the only place raw values (hex codes, px/rem numbers) should
   appear — every other rule in this file should reference a variable.
   ========================================================================== */
:root {
  /* Colors */
  --color-primary: #000000;
  --color-secondary: #9a9a9a;
  --color-accent: #ed8050;
  --color-white: #ffffff;

  /* Fonts */
  --font-heading: 'Cherry Swash', cursive;
  --font-body: 'Roboto', sans-serif;

  /* Font sizes (Figma type scale) */
  --font-size-h1: 2rem;       /* 32px */
  --font-size-h2: 1.5rem;     /* 24px */
  --font-size-h3: 1.125rem;   /* 18px */
  --font-size-body: 1rem;     /* 16px */
  --font-size-small: 0.75rem; /* 12px */

  /* Font weights */
  --font-weight-regular: 400;
  --font-weight-bold: 700;

  /* Line heights */
  --line-height-heading: 1.2; /* tight, for large/short text */
  --line-height-body: 1.5;    /* looser, for readable paragraphs */

  /* Spacing scale — each step maps to a value actually used in Figma */
  --space-1: 0.25rem;  /* 4px */
  --space-2: 0.5rem;   /* 8px */
  --space-3: 0.625rem; /* 10px — button padding */
  --space-4: 1rem;     /* 16px */
  --space-5: 1.5rem;   /* 24px — mobile layout gap */
  --space-6: 1rem; /* 30px — mobile layout margin */
  --space-7: 2.5rem;   /* 40px — tablet layout gap */
  --space-8: 3.75rem;  /* 60px — tablet layout margin */

  /* Motion */
  --transition-fast: 150ms ease;
}

/* Breakpoints reference (documented here, not usable as variables — plain
   CSS media queries can't read custom properties, so these stay as
   literal px values wherever a @media rule is written):
     Tablet:        768px+
     Desktop:       1024px+
     Large Desktop: 1440px+
*/

/* Smooth-scrolls any in-page anchor jump (the splash arrow, Back to Top).
   Skipped for people who've asked their OS for reduced motion, so this
   never overrides that accessibility preference. */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

/* ==========================================================================
   Entrance animation
   A staggered fade-in for each page's key above-the-fold elements —
   splash logo (homepage only), then the header logo/nav, then the H1,
   then the gallery grid + CTA buttons together — each starting a beat
   after the last so the page reveals itself top to bottom instead of
   popping in all at once. Site-wide: every page shares the same
   .site-header/.hero__heading/.btn markup, so one set of rules covers
   all of them. .splash-hero__logo-link and .gallery-grid simply don't
   match on pages that don't have those elements.

   The header logo and hamburger button are animated directly (not their
   shared .inner parent) — any element targeted by a CSS animation that
   touches opacity gets its own stacking context for as long as the
   animation is attached, which .inner being an ancestor of .site-nav
   would trap: .site-nav depends on z-index: 100 to sit above the entire
   page as a full-screen mobile overlay, and that z-index only competes
   within its nearest stacking context, so it would end up stacking inside
   .inner's context instead of the page's, letting <main>'s content paint
   over the open mobile menu.

   .site-nav itself is only animated at the desktop breakpoint below,
   where it becomes position: static (z-index is a no-op there, so no
   stacking-context risk) — at mobile it's left untouched by this
   animation entirely, since it's hidden behind the hamburger until
   .is-open is toggled and already has its own opacity transition for
   that; animating it here too would fight that and flash the whole
   mobile menu open on load.
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
  .splash-hero__logo-link,
  .site-header__logo-img,
  .site-header__menu-toggle,
  .hero__heading,
  .gallery-grid,
  .card-grid,
  .original-grid,
  .btn {
    animation: fade-in 0.6s ease-out backwards;
  }

  .site-header__logo-img,
  .site-header__menu-toggle {
    animation-delay: 0.15s;
  }

  .hero__heading {
    animation-delay: 0.2s;
  }

  .gallery-grid,
  .card-grid,
  .original-grid,
  .btn {
    animation-delay: 0.3s;
  }

  /* Desktop only: .site-nav becomes inline, static-positioned content here
     (see the 1024px+ override below), so it's safe to fade in directly. */
  @media (min-width: 1024px) {
    .site-nav {
      animation: fade-in 0.6s ease-out backwards;
      animation-delay: 0.15s;
    }
  }

  @keyframes fade-in {
    from {
      opacity: 0;
    }
  }
}

/* ==========================================================================
   Reset
   A minimal reset — just enough to make box-sizing and default spacing
   predictable across browsers, without fighting sensible defaults.
   ========================================================================== */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

img,
svg {
  display: block;
  max-width: 100%;
}

/* Sticky footer: body is a full-viewport-height column, and main is the
   only child allowed to grow, so on short pages (like Contact) the
   footer still gets pushed to the bottom of the viewport instead of
   floating up under a half-empty page. Pages with enough content to
   scroll are unaffected — this only fills empty space, it doesn't cap
   anything. */
body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  font-family: var(--font-body);
  font-weight: var(--font-weight-regular);
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  color: var(--color-primary);
}

main {
  flex: 1;
}

/* ==========================================================================
   Layout
   .inner is the shared width/gutter wrapper — every section (header,
   footer, and eventually main) puts its content in one of these instead
   of repeating its own side padding. Change the gutter or max-width once
   here and it updates everywhere.

   It only handles horizontal spacing. Each section still sets its own
   vertical padding and its own internal layout (flex direction, gap) via
   a descendant selector like `.site-header > .inner`, since that layout
   differs per section.
   ========================================================================== */
.inner {
  width: 100%;
  max-width: 90rem; /* 1440px — matches the widest Figma desktop frame */
  margin-inline: auto;
  padding-inline: var(--space-6); /* 30px mobile gutter */
}

@media (min-width: 1024px) {
  .inner {
    padding-inline: 10rem; /* 160px — desktop gutter, matches Figma */
  }
}

/* ==========================================================================
   Typography
   Each rule targets both the semantic element and a matching utility
   class (e.g. h2, .text-h2). Use the element by default; reach for the
   class only when a heading needs to look like a different level than
   its place in the document outline (keeps HTML accessible either way).
   ========================================================================== */
h1,
.text-h1 {
  font-family: var(--font-heading);
  font-weight: var(--font-weight-regular);
  font-size: var(--font-size-h1);
  line-height: var(--line-height-heading);
}

/* Figma's "Desktop - H1" style — an explicit size + line-height pair for
   this breakpoint, not just the mobile size scaled by --line-height-heading. */
@media (min-width: 1024px) {
  h1,
  .text-h1 {
    font-size: 3rem;      /* 48px */
    line-height: 3.375rem; /* 54px */
  }
}

h2,
.text-h2 {
  font-family: var(--font-body);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-h2);
  line-height: var(--line-height-heading);
}

h3,
.text-h3 {
  font-family: var(--font-body);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-h3);
  line-height: var(--line-height-heading);
}

p,
.text-body {
  font-family: var(--font-body);
  font-weight: var(--font-weight-regular);
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
}

small,
.text-small {
  font-family: var(--font-body);
  font-weight: var(--font-weight-regular);
  font-size: var(--font-size-small);
  line-height: var(--line-height-body);
}

/* Color utilities — combine with a .text-h* class above to change a
   heading-styled element's color without duplicating its type styles
   (e.g. class="text-h3 text-accent"). */
.text-secondary {
  color: var(--color-secondary);
}

.text-accent {
  text-decoration: none;
  color: var(--color-accent);
}

/* ==========================================================================
   Buttons
   .btn is the shared CTA style. Padding is uniform (--space-3) and the
   button's height is never set directly — it comes from font-size +
   line-height + padding, so it always fits its text instead of clipping
   or leaving odd gaps if the label changes length.
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Centers a second line if the label wraps (e.g. "Commission Artwork" on
     a narrow phone) — justify-content above only centers the button's
     content as a whole, it doesn't affect how wrapped lines align within
     it, so text-align is still needed. */
  text-align: center;
  padding: var(--space-3);
  background-color: var(--color-white);
  color: var(--color-primary);
box-shadow: 1px 1px 4px 0 rgba(0, 0, 0, 0.35);
  font-family: var(--font-body);
  /* Same h3-style label (bold, 18px) at every breakpoint — no separate
     desktop size step-up needed since mobile already matches it. */
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-h3);
  line-height: var(--line-height-heading);
  text-decoration: none;
  border: none;
  cursor: pointer;
  transition: opacity var(--transition-fast);
}

.btn:hover,
.btn:focus-visible {
  opacity: 0.85;
}

/* Back to Top is navigation (jumps to an anchor on the same page), so it's
   a plain accent-colored link, not a filled button. */
.back-to-top {
  color: var(--color-accent);
  font-family: var(--font-body);
  font-weight: var(--font-weight-regular);
  font-size: var(--font-size-body);
  text-decoration: none;
}

.back-to-top:hover,
.back-to-top:focus-visible {
  text-decoration: underline;
}

/* ==========================================================================
   Site Header & Navigation
   Three states from Figma: mobile closed (logo + hamburger), mobile open
   (full-screen link overlay), and desktop (logo + inline links).

   Mobile-first: the overlay behavior below is the default. It switches to
   the always-visible inline desktop layout at 1024px, matching the
   "Navigation - desktop" frame name and the same breakpoint used above
   for .btn.

   A few spacing values here (43px, 196px, 29px) are specific to this one
   component, so they're written as local rem values with px comments
   instead of being added to the --space-* scale above, which is reserved
   for values reused across multiple components.

   Horizontal gutters come from the shared .inner wrapper (see Layout,
   above); .site-header only sets its own vertical padding.
   ========================================================================== */
.site-header {
  padding-block: var(--space-6); /* 30px top/bottom, same at every breakpoint */
}

.site-header > .inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.site-header__logo-img {
  height: 4.4375rem; /* 71px — mobile logo height */
  width: auto;
}

.site-header__menu-toggle {
  background: none;
  border: none;
  padding: 0;
  width: 2.75rem;  /* 44px hit target, matches Figma's Menu instance */
  height: 2.75rem;
  color: var(--color-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

/* Mobile nav overlay — hidden until .is-open is toggled by nav.js */
/* display stays flex at all times — it can't be transitioned (no partial
   state between none and flex), so open/closed is instead driven by
   opacity + visibility below, which fade smoothly. visibility's own
   default behavior handles the timing: it only flips to hidden at the
   very end of the fade-out (so the menu is still there to fade, but
   un-clickable/unfocusable once actually gone), and flips to visible
   instantly on fade-in. */
.site-nav {
  display: flex;
  position: fixed;
  inset: 0;
  z-index: 100;
  flex-direction: column;
  align-items: center;
  background-color: var(--color-white);
  padding: var(--space-6); /* 30px */
  overflow-y: auto; /* scroll instead of clipping the close button if content is taller than the viewport */
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--transition-fast), visibility var(--transition-fast);
}

.site-nav.is-open {
  opacity: 1;
  visibility: visible;
}

.site-nav__close {
  background: none;
  border: none;
  padding: 0;
  align-self: flex-end;
  flex-shrink: 0; /* stay full size even if the list below is tall enough to need scrolling */
  color: var(--color-primary);
  cursor: pointer;
}

/* flex: 1 + justify-content: center (instead of a fixed gap on .site-nav) lets
   the link list center itself in whatever space is left below the close
   button, so it works at any viewport height instead of only the one
   Figma mobile frame (874px) the original 196px gap was measured from. */
.site-nav__list {
  list-style: none;
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.5rem; /* 24px — tightened from 43px now that the socials row sits
    below; local to this list, not the shared --space scale, so it can be
    retuned without shifting spacing anywhere else on the site */
}

.site-nav__list a {
  font-family: var(--font-body);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-h3); /* 18px — shrunk for mobile now that the
    socials row adds another row below; desktop restores the original H2
    size below */
  color: var(--color-primary);
  text-decoration: none;
}

/* Mobile: no positioning rule needed here beyond the gap/size below —
   .site-nav is already flex-direction: column + align-items: center, and
   .site-nav__list above already takes flex: 1 (filling all remaining
   height and centering its own links within that space). Adding this row
   as a third, non-growing flex child means it just falls out at the
   bottom of the column, already centered, for free. */
.site-nav__socials {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 1.4375rem; /* 23px — Figma's Socials-row spacing, kept local to this
    component rather than the shared --space scale, so changing it can't
    shift spacing anywhere else on the site */
}

.site-nav__socials img,
.site-footer__socials img {
  width: 2.25rem;  /* 36px */
  height: 2.25rem; /* 36px */
}

/* Desktop-only — mobile keeps its socials in the hamburger overlay
   (.site-nav__socials) instead of also duplicating them into the mobile
   footer. Hidden here by default (mobile-first) since this rule must come
   *before* the desktop media query below that turns it into a row — same
   selector, same specificity, so whichever one is later in the file wins
   at every screen size, media query or not. Sits as a direct sibling of
   the footer nav within .site-footer > .inner, so it picks up that
   container's existing 18px gap for free — no extra spacing rule needed. */
.site-footer__socials {
  display: none;
  list-style: none;
}

/* Desktop (1024px+): logo + inline link row, no hamburger/overlay
   (the horizontal gutter change to 160px comes from .inner, above —
   .site-header's own vertical padding doesn't change at this breakpoint) */
@media (min-width: 1024px) {

  .site-header {
  padding-block: var(--space-7); /* 30px top/bottom, same at every breakpoint */
  }
  
  .site-header__logo-img {
    height: 6.25rem; /* 100px — desktop logo height */
  }

  .site-header__menu-toggle {
    display: none;
  }

  .site-nav {
    position: static;
    display: flex;
    background: none;
    padding: 0;
    /* Desktop nav is always shown, regardless of .is-open (that class only
       ever gets toggled by the mobile menu button, which is hidden here) —
       so it needs to override the mobile fade-out state above. */
    opacity: 1;
    visibility: visible;
  }

  .site-nav__close {
    display: none;
  }

  .site-nav__list {
    flex-direction: row;
    align-items: center;
    gap: 1.8125rem; /* 29px */
  }

  .site-nav__list a {
    font-size: var(--font-size-h3); /* 24px — restores the original size;
      only mobile shrank to make room for the socials row */
  }

  .site-nav__socials {
    display: none; /* desktop shows socials in the footer instead
      (.site-footer__socials, below) — kept here for mobile's hamburger
      overlay only */
  }

  .site-footer__socials {
    display: flex;
    align-items: center;
    gap: 1.4375rem; /* 23px — same icon spacing as .site-nav__socials, local
      to this rule rather than the shared --space scale */
  }
}

/* ==========================================================================
   Splash Hero
   Full-viewport image + logo intro. Homepage only, first thing in <body>,
   before .site-header. No JS: it's an ordinary block taking up exactly one
   viewport, so the rest of the page is simply below it in normal flow.
   ========================================================================== */
.splash-hero {
  position: relative; /* positioning context for .splash-hero__bg below */
  height: 100vh;
  height: 100dvh; /* progressive enhancement: dvh tracks the *real* visible
    viewport as mobile browser chrome (address bar) shows/hides, so this
    section doesn't overshoot or undershoot on phones. Browsers that don't
    support dvh just keep using the 100vh line above. */
  flex-shrink: 0; /* body is a flex column (see Sticky footer, above) —
    this keeps the splash at full height even if the rest of the page is
    ever short, same defensive reasoning as .site-nav__close, above */
}

.splash-hero__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover; /* fills the section and crops top/bottom instead of
    stretching or leaving gaps, at every viewport size. Since the box
    itself stays exactly 100% x 100% of .splash-hero, nothing overflows
    the section — no overflow: hidden needed here. */
  object-position: center;
}

/* Links out to a separate graphic design portfolio site (href is a
   placeholder until that site exists). One shared visual style, used in
   two places: pinned to the splash-hero's top-left corner on the
   homepage (.splash-hero__gd-port-button, below), and as a plain flowing
   item at the bottom of the mobile hamburger overlay on every page — no
   extra rules needed for the second spot, since .site-nav's existing
   flex-direction: column + align-items: center already centers it like
   everything else in that overlay. */
.gd-port-button {
  display: block;
  max-width: 19.25rem; /* 308px, Figma's cap — lets it shrink on narrower
    screens rather than forcing a fixed width */
  padding: var(--space-3); /* 10px — same token already used for button padding */
  background-color: var(--color-white);
  border-radius: 0 0 1rem 0; /* 16px on the bottom-right corner only — the
    "tab hanging off the corner" shape from the Figma spec */
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
  color: var(--color-primary);
  text-decoration: none;
}

/* Pins the homepage instance to the splash-hero's own top-left corner —
   .splash-hero already has position: relative, so no new positioning
   context is needed here. Scrolls away with the section (not fixed to the
   viewport) per the confirmed design. */
.splash-hero__gd-port-button {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1; /* above .splash-hero__bg and .splash-hero__content's own
    stacking context, so it's never hidden behind either */
}

.splash-hero__content {
  position: relative;
  z-index: 0; /* position: relative alone does NOT create a new stacking
    context — an explicit z-index is what actually does. Without this,
    the glass panel's z-index: -1 below would escape past this wrapper
    and sink behind .splash-hero__bg too, instead of just behind the
    logo/arrow the way it's supposed to. */
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3); /* 10px — Figma's mobile logo-to-arrow gap */
  color: var(--color-white); /* the arrow's fill="currentColor" picks this up */
}

/* Frosted-glass backdrop behind the logo/arrow. A ::before instead of an
   empty <div> since it has no content or meaning of its own. */
.splash-hero__content::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: -1; /* behind the logo/arrow, in front of the background photo */
  width: 12.375rem;  /* 198px */
  height: 10.4375rem; /* 167px */
  background-color: rgba(163, 40, 40, 0.4); /* #a34029, tinted glass */
  -webkit-backdrop-filter: blur(4px); /* Safari still needs the prefix */
  backdrop-filter: blur(4px);
}

.splash-hero__logo {
  width: 9.375rem; /* 150px — 70% of the 214px desktop size below, rather
    than the 140px straight from the mobile Figma spec */
  height: auto; /* scales automatically from the SVG's own aspect ratio,
    same technique as .site-header__logo-img */
}

/* Same "click to scroll down" behavior and hover feedback as
   .splash-hero__arrow below, just wrapping the logo instead of the arrow
   icon — two separate links with the same destination and purpose. */
.splash-hero__logo-link {
  transition: opacity var(--transition-fast);
}

.splash-hero__logo-link:hover,
.splash-hero__logo-link:focus-visible {
  opacity: 0.7;
}

/* Scrolls to #site-header — animated by the global scroll-behavior: smooth
   set near the top of this file. Same hover/focus feedback pattern as
   .btn, kept tight to the icon's own size rather than padded out to a
   44px hit target, since it's a supplementary cue rather than a primary
   action. */
.splash-hero__arrow,
.splash-hero__arrow:visited {
  display: flex;
  color: inherit; /* links get their own blue/visited-purple color by
    default instead of inheriting — this restores the white from
    .splash-hero__content so fill="currentColor" on the icon stays white */
  transition: opacity var(--transition-fast);
}

.splash-hero__arrow:hover,
.splash-hero__arrow:focus-visible {
  opacity: 0.7;
}

/* Desktop (1024px+): glass panel and logo step up to their Figma desktop
   sizes (from the live file's Desktop page, not the pasted mobile spec).
   The arrow is NOT included here — Figma's desktop arrow (19x12.75px) is
   essentially the same size as mobile (19x13px), so it just stays as-is. */
@media (min-width: 1024px) {
  .splash-hero__content::before {
    width: 17.5rem;    /* 280px */
    height: 14.8125rem; /* 237px */
  }

  .splash-hero__logo {
    width: 13.375rem; /* 214px */
  }
}

/* ==========================================================================
   Site Footer
   Same content and layout at every breakpoint — a centered row of links,
   then a row with the copyright on the left and "Back to Top" on the
   right. Only the .inner gutter changes at desktop, so this component
   doesn't need its own media query.
   ========================================================================== */
.site-footer {
  padding-top: 4.375rem; /* 70px — more breathing room above the footer than below it */
  padding-bottom: var(--space-6); /* 30px */
}

.site-footer > .inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.125rem; /* 18px — space between the links row and the meta row */
}

.site-footer__links {
  list-style: none;
  display: flex;
  justify-content: center;
  gap: 1.8125rem; /* 29px — same link spacing as the desktop site nav */
}

.site-footer__links a {
  font-family: var(--font-body);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-h3); /* 18px */
  color: var(--color-accent);
  text-decoration: none;
}

.site-footer__meta {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
}

.site-footer__copyright {
  color: var(--color-secondary);
}

/* ==========================================================================
   Hero
   The page's main heading + subhead. Same content, layout, and padding at
   every breakpoint (only the .inner gutter changes), so no media query
   is needed here.
   ========================================================================== */
.hero {
  padding-block: var(--space-6); /* 30px */
}

.hero > .inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-2); /* 8px */
}

.hero__subhead {
  color: var(--color-secondary);
}

/* ==========================================================================
   Page sections that hold a card grid
   Each page's section keeps its own heading (or lack of one) and its own
   padding/gap, since that content differs per page — only the card grid
   itself (below) is shared between them.
   ========================================================================== */
.services,
.featured-cards,
.originals,
.gallery {
  padding-block: var(--space-6); /* 30px */
}

.services > .inner,
.featured-cards > .inner,
.originals > .inner,
.gallery > .inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-5); /* 24px */
}

/* ==========================================================================
   Card grid
   The reusable image + title card, used by both the Services page (2
   cards, no description) and the homepage's featured-links section (3
   cards, with a description). Shared here as one component instead of
   being defined separately in each place, since they're the same design.

   Grid rather than Flexbox: with an odd number of cards (the homepage
   has 3), a flex row that wraps will stretch the lone leftover card to
   fill the row. Grid's fixed column tracks just leave that space empty
   instead, matching the Figma desktop layout exactly — this is the case
   the project's "flexbox by default" rule calls out as an exception,
   since Grid is clearly simpler here than the flex workarounds would be.

   Mobile: single column. Tablet+ (768px): two columns — chosen at the
   tablet breakpoint, not desktop, since two ~340px cards already fit
   comfortably at that width.
   ========================================================================== */
.card-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.8125rem; /* 13px — specific to this component's card spacing */
}

.card {
  display: flex;
  flex-direction: column;
  gap: 0.75rem; /* 12px — space between the image and the text block below it */
}

/* Wraps the title + paragraph so they can have their own, tighter gap than
   the image-to-text gap above (gap only takes one value per flex
   container, so a pair that needs a different spacing needs its own
   nested container — same trick as .about-story__content). Local literal,
   not the shared --space scale, so retuning it can't shift spacing
   elsewhere on the site. */
.card__body {
  display: flex;
  flex-direction: column;
  gap: 0.375rem; /* 6px */
}

/* Always fills the card's column width, no cropping — every .card image
   (landing page and Services page alike) keeps its own natural aspect
   ratio, so height just falls out of the photo itself. Transition is here
   (rather than scoped to .card__image-link) since every .card__image gets
   it for free, but it only ever fires where a hover/focus rule below
   actually changes the opacity. */
.card__image {
  width: 100%;
  height: auto;
  transition: opacity var(--transition-fast);
}

.card__link {
  color: var(--color-accent);
  text-decoration: none;
}

/* Wraps a card's <h3> text on the landing page so the whole title links
   out (Commission Artwork -> Services, Browse Prints -> Prints, About Me
   -> About) — color: inherit keeps it the same black as plain heading
   text instead of picking up the browser's default link blue. */
.card__title-link {
  color: inherit;
  text-decoration: none;
}

@media (min-width: 768px) {
  .card-grid {
    grid-template-columns: repeat(2, 1fr);
    column-gap: 1.625rem; /* 26px */
    row-gap: var(--space-5); /* 24px */
  }
}

/* ==========================================================================
   Original grid
   A different card type from .card-grid above: a real photo instead of a
   fixed-height placeholder, sized by its own aspect ratio (portrait,
   landscape, whatever the actual artwork is) instead of being forced into
   a uniform box. Same "1 column mobile, 2 columns desktop" grid mechanic
   as .card-grid, but its own gap value, so it's a separate class rather
   than a modifier on .card-grid.
   ========================================================================== */
.original-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5); /* 24px, every direction, every breakpoint */
}

.original-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem; /* 12px — specific to this component, same reasoning as .card */
}

.original-card__image {
  width: 100%;
  max-width: 25rem; /* 400px — caps how large a single image can get, but
    never crops or forces its shape: height is left to scale automatically
    from the photo's own aspect ratio, so a landscape photo and a portrait
    photo both display correctly without any per-card CSS. */
  height: auto;
}

.original-card__meta {
  text-align: center;
}

@media (min-width: 1024px) {
  .original-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* ==========================================================================
   Print grid
   Identical to .original-grid above — same "1 column mobile, repeat(N, 1fr)
   desktop" grid mechanic, same per-card layout — except this page uses 3
   desktop columns instead of 2, and a smaller 356px image cap instead of
   400px. Kept as its own class rather than a modifier for the same reason
   .original-grid is separate from .card-grid: different content, own set
   of values.
   ========================================================================== */
.print-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5); /* 24px, every direction, every breakpoint */
}

.print-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem; /* 12px — same reasoning as .original-card */
}

.print-card__image {
  width: 100%;
  max-width: 22.25rem; /* 356px — same "cap size, never crop" reasoning as
    .original-card__image, just a smaller cap to fit 3 desktop columns */
  height: auto;
}

.print-card__meta {
  text-align: center;
}

@media (min-width: 1024px) {
  .print-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* ==========================================================================
   Gallery grid
   Homepage only. 2 columns on mobile, 3 from the tablet breakpoint up —
   tablet and desktop want the same column count, so one media query
   covers both; no separate 1024px override needed. Every thumbnail is
   cropped to the same fixed 8:10 shape regardless of the source photo's
   own orientation (unlike .print-card__image/.original-card__image, which
   cap width but never crop) — and unlike those two, there's no max-width
   here, so the images fill their grid column all the way up.
   ========================================================================== */
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-5); /* 24px, every direction, every breakpoint */
}

.gallery-card {
  display: flex;
  flex-direction: column;
  gap: 0.75rem; /* 12px — same reasoning as .card/.original-card/.print-card */
}

/* Resets the <button> back down to "just a clickable image" — the
   thumbnail is a button rather than a link because opening the lightbox
   is an action, not navigation. */
.gallery-card__trigger {
  display: block;
  width: 100%;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}

.gallery-card__image {
  width: 100%;
  aspect-ratio: 8 / 10;
  object-fit: cover;
}

@media (min-width: 768px) {
  .gallery-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* ==========================================================================
   Lightbox
   A single <dialog>, shared by every .gallery-card__trigger (see
   lightbox.js). Native showModal() already centers the dialog and makes
   the rest of the page inert, so no custom positioning is needed here —
   these rules only style the box itself, its backdrop, and its contents.
   ========================================================================== */
.lightbox {
  border: none;
  padding: 0;
  margin: auto; /* the site's global reset (top of this file) zeroes every
    element's margin, which overrides the browser's own default centering
    for an open dialog — restoring it here is what actually centers the
    lightbox in the viewport */
  max-width: 40rem; /* 640px, matches the Figma popup's width */
  width: calc(100% - 2rem);
  background-color: var(--color-white);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
}

/* Deliberately dark, not the light gray shown in the Figma mock — that
   gray is almost certainly just Figma reusing its placeholder-rectangle
   fill for the whole frame, not an intended overlay color, and a dark
   backdrop is the standard, accessible convention for a lightbox anyway. */
.lightbox::backdrop {
  background-color: rgba(0, 0, 0, 0.6);
}

.lightbox__card {
  position: relative; /* positioning context for the prev/next buttons below */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  padding-block: var(--space-6); /* top/bottom, from the site's own space
    scale rather than the Figma spec's raw 40/20px, which was desktop-only
    anyway */
  padding-inline: 2rem; /* left/right — doubled from the old 1rem, local to
    this rule rather than the shared scale, so the prev/next buttons
    (pinned to this padding's outer edge, regardless of its size) clear
    the image with room to spare instead of running into it */
}

.lightbox__close {
  align-self: flex-end;
  padding: 0;
  border: none;
  background: none;
  color: var(--color-primary);
  cursor: pointer;
}

/* Prev/next flank the card, vertically centered — same icon-button reset
   as .lightbox__close, just positioned absolutely instead of placed in
   the normal flex flow. */
.lightbox__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  padding: var(--space-2);
  border: none;
  background: none;
  color: var(--color-primary);
  cursor: pointer;
  transition: opacity var(--transition-fast);
}

.lightbox__nav:hover,
.lightbox__nav:focus-visible {
  opacity: 0.6;
}

.lightbox__nav--prev {
  left: 0;
}

.lightbox__nav--next {
  right: 0;
}

/* Full, uncropped photo — deliberately not the same 8:10 crop as the
   thumbnail, since the point of a lightbox is seeing the whole image. */
/* Groups the image and its caption so the caption's left edge lines up
   with the image's own left edge, not just the card's — inline-flex
   shrinks this wrapper to the image's rendered width (its widest child in
   the normal case), then align-items: flex-start flushes both children
   against that same left edge. Without this wrapper, a narrow portrait
   photo would leave the caption sitting to the left of the actual photo,
   since .lightbox__card centers the image independently of the caption. */
.lightbox__media {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.75rem;
  max-width: 100%;
}

.lightbox__image {
  max-width: 100%;
  max-height: 70vh; /* keeps a tall portrait photo from overflowing the viewport */
  width: auto;
  height: auto;
  object-fit: contain;
}

/* Toggled on <body> by lightbox.js while the dialog is open. */
.has-lightbox-open {
  overflow: hidden;
}

/* ==========================================================================
   CTA buttons
   Two .btn instances (defined above) in a row — no new button styling
   needed here, just the layout around them.
   ========================================================================== */
.cta-buttons {
  padding-block: var(--space-6); /* 30px */
}

.cta-buttons__row {
  display: flex;
  gap: 0.6875rem; /* 11px */
}

.cta-buttons__row .btn {
  flex: 1; /* both buttons share the row equally, regardless of label length */
  max-width: 15.625rem; /* 250px cap per button, not the row — so a single
    button (Prints) caps at 250px instead of stretching to fill the whole
    row, while two buttons each cap at 250px too, landing at roughly the
    same total width the row's own 500px max-width used to allow */
}

/* ==========================================================================
   Commission note
   Short paragraph + email, below the featured cards. Homepage only.
   ========================================================================== */
.commission-note {
  padding-block: var(--space-6); /* 30px */
}

.commission-note > .inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-3); /* 10px */
}

/* Desktop only (not tablet): cap the paragraph at one card-grid column's
   width (547px) so it lines up with the cards above it instead of
   stretching across the full row. */
@media (min-width: 1024px) {
  .commission-note p {
    max-width: 34.1875rem; /* 547px */
  }
}

/* ==========================================================================
   Info block
   A tightly-grouped heading + short text cluster (10px gap) — the
   "Process" pattern (heading, description, email), reused as-is on both
   the Services and About pages.
   ========================================================================== */
.info-block {
  display: flex;
  flex-direction: column;
  gap: var(--space-3); /* 10px */
}

/* ==========================================================================
   Services details (Process / Pricing)
   Two .info-blocks that share the page's usual 24px gap between each
   other. Services page only.
   ========================================================================== */
.services__details {
  display: flex;
  flex-direction: column;
  gap: var(--space-5); /* 24px */
}

/* Desktop only (not tablet): same reasoning as .commission-note — cap at
   one card-grid column's width instead of stretching full width. */
@media (min-width: 1024px) {
  .services__details {
    max-width: 34.1875rem; /* 547px */
  }
}

/* ==========================================================================
   About story
   Normally two rows — an image and some text — in document order: image,
   text, image, .info-block, which a 2-column desktop grid pairs up
   automatically. Right now the second photo is commented out in the HTML,
   so .about-story__text and .info-block are wrapped together in
   .about-story__content — that keeps the grid at exactly 2 items (photo,
   content) so desktop lands as one row: photo | text-then-info-block,
   instead of leaving an empty cell where the second photo used to be.
   See the HTML comment above the commented-out <img> for how to restore
   the original 4-item layout.

   The photo uses a fixed 8:10 portrait aspect-ratio — a fixed shape that
   still scales responsively with the column, at every breakpoint, rather
   than stretching to match the text column's height like before.
   ========================================================================== */
.about-story {
  padding-block: var(--space-6); /* 30px */
}

.about-story__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5); /* 24px */
}

.about-story__image {
  width: 100%;
  aspect-ratio: 8 / 10;
  object-fit: cover; /* crops to fill the shape, same reasoning as .card__image */
}

.about-story__content {
  display: flex;
  flex-direction: column;
  gap: var(--space-5); /* 24px — same spacing .info-block used to get from
    being its own grid row, now that it's stacked under the text instead */
}

.about-story__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-4); /* 16px between paragraphs — not in the Figma spec (it's one text block there), just a reasonable paragraph gap */
}

@media (min-width: 1024px) {
  .about-story__grid {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-5); /* 24px, both directions */
  }
}

/* ==========================================================================
   Contact info
   A short paragraph + two contact lines. Same content, layout, and
   padding at every breakpoint — no width cap like the Services/About
   pages use, since the Figma desktop spec just lets this fill the full
   content width instead of matching a card-grid column.
   ========================================================================== */
.contact-info {
  padding-block: var(--space-6); /* 30px */
}

.contact-info > .inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-4); /* 16px */
}

/* Mobile: a real tel: link, so tapping it dials — styled as a plain link
   rather than a button, same treatment as .card__title-link (the "Browse
   Prints"/"About Me" card titles on the landing page): color: inherit
   keeps it plain black instead of the browser's default link blue. Text
   size/weight comes from the .text-h3 class on the element itself, since
   this isn't wrapped in an actual heading tag the way the card titles are.
   Desktop: hidden — .contact-info__phone-text (below) takes over there,
   since a single <a> can't stop being a real, clickable link at a
   different breakpoint, only look different; two elements are needed to
   make desktop plain, non-clickable, copyable text instead of a link. */
.contact-info__phone-link {
  color: inherit;
  text-decoration: none;
}

@media (min-width: 1024px) {
  .contact-info__phone-link {
    display: none;
  }
}

/* Desktop-only plain text version of the phone number — see the comment
   above .contact-info__phone-link for why this needs to be a separate
   element rather than just restyling the link. */
.contact-info__phone-text {
  display: none;
}

@media (min-width: 1024px) {
  .contact-info__phone-text {
    display: block;
  }
}
