/* ==========================================================================
   Quinn — Main Stylesheet
   Dark editorial palette: near-black grounds (with rose-black and navy-
   black section washes), a vivid pop-pink primary accent and an electric
   navy-blue secondary accent, off-white ink. Cormorant Garamond for
   display, Manrope for body/UI.
   ========================================================================== */

/* Cross-document continuity: page navigations cross-fade instead of
   hard-cutting, so moving through the site feels like turning pages in
   one magazine. No-ops silently in browsers without support. */
@view-transition {
  navigation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.4s;
  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}

:root {
  --color-bg: #121014;           /* near-black — primary page background */
  --color-bg-pearl: #1B171C;     /* lifted dark surface — About-teaser, footer, brand-gallery sections */
  --color-bg-blush: #241620;     /* rose-black wash — hero + every page-header masthead */
  --color-bg-sky: #101822;       /* navy-black wash — the Brands page's own blue-region section */

  --color-accent: #FF4F81;       /* vivid pop pink — primary accent, text/links/CTAs, pairs with dark on-accent text when used as a fill */
  --color-accent-soft: #7A4A5C;  /* muted rose — decorative only, deliberately low-contrast (hairline tints, at-rest underlines that solidify on hover) */
  --color-accent-deep: #C9376B;  /* deep saturated pink for gradients */

  --color-accent-blue: #4A90FF;       /* electric blue — secondary accent, the Brands section's own color region + swatches */
  --color-accent-blue-soft: #35547A;  /* muted navy — decorative only, same duty accent-soft holds for the rose family */

  --color-ink: #F5F1F0;          /* primary text — warm off-white, not a literal #FFF */
  --color-ink-soft: #B79AA5;     /* secondary/muted text, tinted from the rose hue rather than gray */
  --color-on-accent: #14101A;    /* dark ink for text/icons on solid accent-fill or light-mat surfaces (buttons, CTA, polaroid caption) */

  --font-heading: 'Cormorant Garamond', Georgia, 'Times New Roman', serif;
  --font-body: 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;

  --rule-soft: 1px solid rgba(245, 241, 240, 0.14);
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);

  --section-pad: 7rem;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: var(--font-body);
  color: var(--color-ink);
  background-color: var(--color-bg);
  line-height: 1.6;
}

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  line-height: 1.1;
  font-weight: 500;
}

p { color: var(--color-ink); }

a {
  color: inherit;
  text-decoration: none;
}

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

.container {
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding-left: 1.5rem;
  padding-right: 1.5rem;
}

/* ==========================================================================
   Navigation
   ========================================================================== */

/* Fixed (desktop) so the header floats over the hero/page-header instead
   of pushing it down — hero content starts at the true top of the
   viewport, with the glass bar overlaid on top of it. Translucent so the
   backdrop-filter blur actually has that content passing underneath it
   to blur — a glass surface over a static background would just look
   tinted, not glassy. Reverted to the original in-flow `sticky` at
   ≤900px (see responsive.css) — the overlay-hero treatment is a desktop
   thing; tablet/mobile keep the header pushing content down normally. */
header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 500;
}

/* Base = fully-grown, resting state (top of page, or scroll position 0).
   initStickyNav() in main.js adds/removes .is-compact / .is-shadow /
   .is-hidden as the page scrolls: .is-compact (shrunk) any time
   scrollY > 0, whether scrolling up or down — so the header stays
   shrunk-but-visible on the way back up rather than growing back first —
   and neither (fully grown again) once scrollY returns to 0. .is-shadow
   lifts it off the page ("hovering" rather than flush/flat) any time
   it's not at that resting top position. .is-hidden (desktop only —
   neutralized at ≤900px in responsive.css) slides the header off-screen
   while actively scrolling down, and is removed the instant the user
   scrolls up, so it doesn't permanently occupy space over page content
   but is always one upward scroll away.

   Deliberate exception to the site's "animate transform/opacity, not
   layout properties" rule (see DESIGN.md Don'ts): this transitions
   `padding`, a genuine layout property, in addition to `transform`
   (used for the hide/show slide, per the rule). A transform: scale()
   can't substitute for the padding change — the requested effect is the
   header's real box height changing (so it actually grows/shrinks)
   while the logo/link text stays crisp at native size, not a
   visually-scaled illusion. Scoped to one small, cheap element (a
   single flex row, not a page-wide grid) transitioning at most once per
   scroll-direction change, not a continuous per-frame animation — the
   layout-thrash risk this rule guards against doesn't really apply at
   this scale. Confirmed smooth in-browser. */
.site-nav {
  position: relative;
  padding: 2.5rem 0;
  background: rgba(27, 23, 28, 0.78);
  backdrop-filter: blur(18px) saturate(160%);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  border-bottom: 1px solid rgba(250, 247, 245, 0.14);
  transition: padding 0.45s var(--ease-out-expo), box-shadow 0.45s var(--ease-out-expo), transform 0.45s var(--ease-out-expo);
}

.site-nav.is-shadow {
  box-shadow: 0 20px 40px -18px rgba(0, 0, 0, 0.45);
}

.site-nav.is-hidden {
  transform: translateY(-100%);
}

.site-nav.is-compact {
  padding: 1.75rem 0;
}

/* Content row, capped to the same 1200px column as the rest of the page
   (via the shared .container class) — kept separate from .site-nav so
   the glass background/blur/scroll-shrink padding above can span the
   full viewport width edge-to-edge instead of being capped with it. */
.nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* Ambient "water droplet" rings — decorative only (aria-hidden),
   painted first so the logo/links/toggle (normal DOM flow, no z-index
   needed) stack above them automatically. Kept to a faint pearl
   outline so they read against the dark glass regardless of section. */
.glass-ripples {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

.glass-ripples span {
  position: absolute;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 1px solid rgba(250, 247, 245, 0.35);
  opacity: 0;
  animation: ripple-pulse 4s ease-out infinite;
}

.glass-ripples span:nth-child(1) { left: 8%;  top: 30%; animation-delay: 0s;    width: 12px; height: 12px; }
.glass-ripples span:nth-child(2) { left: 26%; top: 70%; animation-delay: 1.1s;  width: 18px; height: 18px; }
.glass-ripples span:nth-child(3) { left: 42%; top: 25%; animation-delay: 2.2s;  width: 10px; height: 10px; }
.glass-ripples span:nth-child(4) { left: 58%; top: 65%; animation-delay: 0.6s;  width: 16px; height: 16px; }
.glass-ripples span:nth-child(5) { left: 76%; top: 30%; animation-delay: 1.8s;  width: 13px; height: 13px; }
.glass-ripples span:nth-child(6) { left: 92%; top: 60%; animation-delay: 2.9s;  width: 15px; height: 15px; }

@keyframes ripple-pulse {
  0% { transform: scale(0.3); opacity: 0; }
  12% { opacity: 0.55; }
  100% { transform: scale(7); opacity: 0; }
}

.site-nav .logo {
  font-family: var(--font-heading);
  font-style: italic;
  font-weight: 600;
  font-size: 1.7rem;
  color: var(--color-accent);
  letter-spacing: 0.01em;
}

.site-nav ul {
  display: flex;
  gap: 2.25rem;
  list-style: none;
}

/* Light text — the header glass is a lifted-but-still-dark surface
   (rgba(27,23,28,X)), so links use --color-ink (the theme's off-white)
   rather than --color-bg-pearl, which is now itself a dark "section
   surface" tone, not a light one — repointed when the theme flipped
   from light to dark (see PROJECT_NOTES.md). */
.site-nav ul li a {
  position: relative;
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-ink);
  padding-bottom: 0.35rem;
}

.site-nav ul li a::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: var(--color-accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.4s var(--ease-out-expo);
}

.site-nav ul li a:hover::after,
.site-nav ul li a:focus-visible::after {
  transform: scaleX(1);
}

.nav-collapse {
  display: contents;
}

/* Backdrop scrim behind the mobile off-canvas drawer (see .nav-collapse's
   ≤600px override below). Hidden/inert on desktop, where .nav-collapse
   is just `display: contents` and never opens. */
.nav-scrim {
  /* top/left + vw/vh (not `inset: 0`) deliberately — .nav-scrim's
     ancestor .site-nav has `backdrop-filter`, which makes .site-nav
     the containing block for fixed descendants' *percentage* offsets
     (top/right/bottom/left as %, which `inset: 0` shorthand computes
     as). That collapsed this scrim to .site-nav's own ~44px header-bar
     height instead of the full page, since `bottom: 0` resolved against
     .site-nav's bottom edge, not the viewport's. vw/vh units sidestep
     this entirely — they're always viewport-relative regardless of
     containing block, which is also why .nav-collapse's `height: 100vh`
     (below) was unaffected by the same trap. */
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background: rgba(8, 6, 9, 0.35);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.35s var(--ease-out-expo);
  z-index: 55;
}

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

.nav-toggle {
  position: relative;
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 24px;
  padding: 4px;
  background: none;
  border: none;
  cursor: pointer;
}

.nav-toggle-bar {
  display: block;
  width: 100%;
  height: 2px;
  background: var(--color-ink);
  transition: transform 0.35s var(--ease-out-expo), opacity 0.35s var(--ease-out-expo);
}

.nav-toggle[aria-expanded="true"] .nav-toggle-bar:first-child {
  transform: translateY(3.5px) rotate(45deg);
}

.nav-toggle[aria-expanded="true"] .nav-toggle-bar:last-child {
  transform: translateY(-3.5px) rotate(-45deg);
}

.nav-toggle:focus-visible,
a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

/* ==========================================================================
   Buttons / links
   ========================================================================== */

.btn-primary {
  position: relative;
  display: inline-block;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.82rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-on-accent);
  background: var(--color-accent);
  padding: 1.05rem 2.1rem;
  border-radius: 2px;
  box-shadow: 0 14px 28px -14px rgba(8, 6, 9, 0.55);
  transition: transform 0.4s var(--ease-out-expo), box-shadow 0.4s var(--ease-out-expo);
}

.btn-primary:hover {
  transform: translateY(-3px);
  box-shadow: 0 20px 34px -12px rgba(8, 6, 9, 0.6);
}

.btn-primary-inverse {
  position: relative;
  display: inline-block;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.82rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent);
  background: var(--color-bg-pearl);
  padding: 1.05rem 2.1rem;
  border-radius: 2px;
  box-shadow: 0 14px 28px -14px rgba(0, 0, 0, 0.35);
  transition: transform 0.4s var(--ease-out-expo), box-shadow 0.4s var(--ease-out-expo);
}

.btn-primary-inverse:hover {
  transform: translateY(-3px);
  background: #272129;
}

.btn-secondary {
  position: relative;
  display: inline-block;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.82rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent);
  padding-bottom: 0.35rem;
  transition: color 0.3s var(--ease-out-expo);
}

.btn-secondary::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: var(--color-accent-soft);
  transition: background 0.3s var(--ease-out-expo);
}

.btn-secondary:hover {
  color: var(--color-ink);
}

.btn-secondary:hover::after {
  background: var(--color-accent);
}

.text-link {
  color: var(--color-accent);
  font-weight: 600;
  border-bottom: 1px solid var(--color-accent-soft);
  padding-bottom: 2px;
  transition: border-color 0.3s var(--ease-out-expo), color 0.3s var(--ease-out-expo);
}

.text-link:hover {
  color: var(--color-ink);
  border-color: var(--color-accent);
}

/* ==========================================================================
   Section labels
   ========================================================================== */

.kicker {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin-bottom: 1.25rem;
}

.kicker::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--color-accent-soft);
  opacity: 0.6;
}

.section-kicker {
  font-family: var(--font-heading);
  font-style: italic;
  font-weight: 600;
  font-size: 1.5rem;
  color: var(--color-accent);
  margin-bottom: 0.75rem;
}

/* ==========================================================================
   Hero
   ========================================================================== */

.hero {
  background: var(--color-bg-blush);
  padding: var(--section-pad) 0;
  overflow: hidden;
}

.hero-grid {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: 1.5rem;
  align-items: center;
}

.hero-copy {
  position: relative;
  z-index: 2;
}

.hero-title {
  /* Oversized past the usual 6rem display cap on purpose: the brief's own
     cover-line-bleeding-behind-the-panel concept only reads at masthead
     scale, so the type has to reach toward the visual to sell it. */
  font-family: var(--font-heading);
  font-style: italic;
  font-weight: 600;
  font-size: clamp(4.5rem, 13vw, 9rem);
  line-height: 0.9;
  letter-spacing: -0.02em;
  color: var(--color-ink);
  position: relative;
  z-index: 2;
}

.hero-title .accent {
  color: var(--color-accent);
}

.hero-tagline {
  font-family: var(--font-body);
  font-size: 1.05rem;
  line-height: 1.75;
  color: var(--color-ink-soft);
  max-width: 42ch;
  margin-top: 1.75rem;
}

.hero-actions {
  display: flex;
  align-items: center;
  gap: 2rem;
  flex-wrap: wrap;
  margin-top: 2.75rem;
}

.hero-visual {
  position: relative;
  z-index: 1;
  margin-left: -8rem;
  margin-right: calc(-1 * max(0px, (100vw - 1200px) / 2 + 1.5rem));
}

/* ==========================================================================
   Polaroid stack (hero — real photos, replaces the placeholder silhouette
   in the same slot/aspect-ratio/entrance animation)
   ========================================================================== */

/* Same footprint as the old .silhouette-frame so the hero layout doesn't
   shift: js/main.js's initPolaroidStack() drives the scatter/cycle/reshuffle
   via the --tx/--ty/--rot/--drag custom properties below. Fallback (no JS)
   is the fixed nth-child fan so the hero never shows a dead stack of
   identical, unrotated photos. */
.polaroid-stack {
  position: relative;
  aspect-ratio: 3 / 4;
  touch-action: pan-y;
}

.polaroid-stack:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 6px;
}

.polaroid-card {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 72%;
  aspect-ratio: 4 / 5;
  background: var(--color-ink);
  padding: 0.6rem 0.6rem 1.6rem;
  border-radius: 2px;
  box-shadow: 0 32px 60px -22px rgba(8, 6, 9, 0.5);
  cursor: grab;
  user-select: none;
  transform: translate(-50%, -50%) translate(var(--tx, 0%), var(--ty, 0%)) translate(var(--drag, 0px), 0) rotate(var(--rot, 0deg));
  transition: transform 0.6s var(--ease-out-expo), opacity 0.5s var(--ease-out-expo);
}

.polaroid-card:active {
  cursor: grabbing;
}

.polaroid-card img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.polaroid-caption {
  position: absolute;
  left: 0.6rem;
  right: 0.6rem;
  bottom: 0.5rem;
  font-family: var(--font-body);
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-on-accent);
  text-align: center;
}

/* No-JS / pre-JS fallback fan — fixed, not random, so the layout is stable
   before initPolaroidStack() applies its own scatter values. */
.polaroid-card:nth-child(1) { --rot: -6deg; --tx: -6%;  --ty: 2%;  z-index: 6; }
.polaroid-card:nth-child(2) { --rot: 4deg;  --tx: 6%;   --ty: -4%; z-index: 5; }
.polaroid-card:nth-child(3) { --rot: -4deg; --tx: -3%;  --ty: 6%;  z-index: 4; }
.polaroid-card:nth-child(4) { --rot: 7deg;  --tx: 5%;   --ty: 5%;  z-index: 3; }
.polaroid-card:nth-child(5) { --rot: -8deg; --tx: -5%;  --ty: -5%; z-index: 2; }
.polaroid-card:nth-child(6) { --rot: 9deg;  --tx: 3%;   --ty: -7%; z-index: 1; }

/* ==========================================================================
   Page header (About / Portfolio / Brands / Contact masthead-lite)
   ========================================================================== */

.page-header {
  background: var(--color-bg-blush);
  padding: calc(var(--section-pad) * 0.85) 0 var(--section-pad);
}

.page-header-grid {
  display: grid;
  grid-template-columns: 1.2fr 0.8fr;
  gap: 3rem;
  align-items: center;
}

.page-header .kicker {
  animation: fade-up 0.6s var(--ease-out-expo) both;
}

.page-header h1 {
  font-style: italic;
  font-size: clamp(2.75rem, 6vw, 4.5rem);
  margin: 0.5rem 0 1.25rem;
  color: var(--color-ink);
  animation: ink-reveal 0.8s var(--ease-out-expo) both 0.2s;
}

.page-header-intro {
  font-size: 1.05rem;
  line-height: 1.75;
  color: var(--color-ink-soft);
  max-width: 52ch;
  animation: fade-up 0.7s var(--ease-out-expo) both 0.4s;
}

.page-header-visual {
  animation: fade-up 0.8s var(--ease-out-expo) both 0.3s;
}

/* Real photo used in every non-hero page-header (About/Portfolio/Brands/
   Contact) — replaces what used to be a smaller .silhouette-frame rose
   panel. js/main.js's initHeaderPhoto() randomly picks one of a fixed set
   of photos into the .page-header-photo img on each load (fresh pairing
   per visit, not a fixed per-page assignment). Two-layer composition
   echoes the hero's silhouette-back/front bleed: a rose clipped shape
   peeks out from behind a slightly rotated, larger-than-the-box photo
   card, rather than a flat single rectangle. Neither layer clips to the
   .page-header-photo-frame box (no overflow: hidden there) so the back
   shape's offset is actually visible, same as the hero's silhouette-frame
   never clipping its own children. */
.page-header-photo-frame {
  position: relative;
  aspect-ratio: 3 / 4;
}

.page-header-photo-back {
  position: absolute;
  inset: 0;
  border-radius: 2px;
  background: linear-gradient(155deg, var(--color-accent) 0%, var(--color-accent-deep) 85%);
  clip-path: polygon(20% 0%, 100% 8%, 86% 100%, 2% 90%);
  opacity: 0.7;
  transform: translate(6%, 5%);
}

.page-header-photo {
  position: absolute;
  inset: 0;
  border-radius: 2px;
  overflow: hidden;
  box-shadow: 0 32px 60px -22px rgba(8, 6, 9, 0.5);
  transform: rotate(-4deg) translate(-4%, -3%) scale(1.06);
}

.page-header-photo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.page-header-photo::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 75% 20%, rgba(255, 255, 255, 0.22), transparent 60%);
  mix-blend-mode: soft-light;
  pointer-events: none;
}

/* ==========================================================================
   Bio
   ========================================================================== */

.bio-section {
  background: var(--color-bg);
  padding: var(--section-pad) 0;
}

.bio-grid {
  display: grid;
  grid-template-columns: 0.6fr 1.4fr;
  gap: 3rem;
}

.bio-copy p {
  color: var(--color-ink-soft);
  line-height: 1.8;
  max-width: 65ch;
  margin-bottom: 1.25rem;
}

.glance-list {
  margin-top: 1.5rem;
  border-top: var(--rule-soft);
}

.glance-item {
  padding: 1.25rem 0;
  border-bottom: var(--rule-soft);
}

.glance-item dt {
  font-family: var(--font-body);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-ink-soft);
  margin-bottom: 0.4rem;
}

.glance-item dd {
  font-family: var(--font-body);
  font-size: 0.95rem;
  color: var(--color-ink);
  margin: 0;
}

/* ==========================================================================
   Focus areas (editorial contributor-list, not icon cards)
   ========================================================================== */

.focus-section {
  background: var(--color-bg-pearl);
  padding: var(--section-pad) 0;
}

.focus-list {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  margin-top: 2.5rem;
  border-top: var(--rule-soft);
}

.focus-item {
  padding: 2rem 1.75rem 2rem 0;
  border-bottom: var(--rule-soft);
  border-right: var(--rule-soft);
}

.focus-item:last-child {
  border-right: none;
}

.focus-item h3 {
  font-style: italic;
  font-size: 1.5rem;
  color: var(--color-accent);
  margin-bottom: 0.75rem;
}

.focus-item p {
  color: var(--color-ink-soft);
  line-height: 1.7;
}

/* ==========================================================================
   About teaser
   ========================================================================== */

.about-teaser {
  background: var(--color-bg-pearl);
  padding: var(--section-pad) 0;
}

.about-grid {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: 4rem;
  align-items: start;
}

.about-grid h2 {
  font-size: clamp(2rem, 4vw, 2.75rem);
  margin-bottom: 1.5rem;
  max-width: 14ch;
}

.about-copy p {
  color: var(--color-ink-soft);
  line-height: 1.8;
  max-width: 62ch;
  margin-bottom: 1.25rem;
}

/* ==========================================================================
   Portfolio teaser
   ========================================================================== */

.work-teaser {
  background: var(--color-bg);
  padding: var(--section-pad) 0;
}

.work-intro {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: 4rem;
  align-items: end;
  margin-bottom: 3.5rem;
}

.work-intro h2 {
  font-size: clamp(2rem, 4vw, 2.75rem);
  max-width: 14ch;
}

.work-intro p {
  color: var(--color-ink-soft);
  line-height: 1.8;
  max-width: 58ch;
}

.work-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-auto-rows: minmax(140px, 1fr);
  gap: 1.25rem;
}

.work-panel {
  position: relative;
  border-radius: 2px;
  overflow: hidden;
  min-height: 200px;
}

.work-panel--large {
  grid-column: span 4;
  grid-row: span 2;
}

.work-panel--a {
  grid-column: span 2;
  grid-row: span 1;
}

.work-panel--b {
  grid-column: span 2;
  grid-row: span 1;
}

.work-panel .swatch,
.work-panel img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.work-panel--large .swatch {
  background: linear-gradient(135deg, var(--color-accent) 0%, var(--color-accent-deep) 70%);
}

.work-panel--a .swatch {
  background: linear-gradient(200deg, var(--color-accent-blue-soft) 0%, var(--color-accent-blue) 100%);
}

.work-panel--b .swatch {
  background: linear-gradient(20deg, var(--color-bg-blush) 0%, var(--color-accent-blue-soft) 100%);
}

.work-panel::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 75% 20%, rgba(255, 255, 255, 0.22), transparent 60%);
  mix-blend-mode: soft-light;
  pointer-events: none;
}

.work-tag {
  position: absolute;
  top: 1.1rem;
  left: 1.1rem;
  font-family: var(--font-body);
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-on-accent);
  background: rgba(250, 247, 245, 0.88);
  padding: 0.4rem 0.85rem;
  border-radius: 100px;
}

.work-panel--large .work-tag {
  color: var(--color-ink);
  background: rgba(8, 6, 9, 0.35);
}

.work-note {
  margin-top: 1.25rem;
  font-size: 0.8rem;
  color: var(--color-ink-soft);
}

.work-cta {
  margin-top: 1rem;
  text-align: right;
}

/* ==========================================================================
   Portfolio (contact-sheet grid)
   ========================================================================== */

.portfolio-section {
  background: var(--color-bg);
  padding: var(--section-pad) 0;
}

.portfolio-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-bottom: 2.5rem;
}

.portfolio-filters button {
  position: relative;
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-ink-soft);
  background: transparent;
  border: 1px solid var(--color-ink-soft);
  border-radius: 100px;
  padding: 0.6rem 1.25rem;
  cursor: pointer;
  transition: background 0.3s var(--ease-out-expo), color 0.3s var(--ease-out-expo), border-color 0.3s var(--ease-out-expo);
}

.portfolio-filters button:hover {
  color: var(--color-ink);
  border-color: var(--color-accent);
}

.portfolio-filters button.active {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-on-accent);
}

.portfolio-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.25rem;
}

.portfolio-item {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  border-radius: 2px;
  overflow: hidden;
  border: none;
  padding: 0;
  cursor: pointer;
  text-align: left;
}

.portfolio-item[hidden] {
  /* Author styles always beat the UA [hidden] rule regardless of
     specificity, so `.portfolio-item { display: block }` above silently
     defeats `item.hidden = true` in main.js unless restated here with
     higher specificity — otherwise filtered-out tiles stay in the grid
     as invisible blank boxes instead of closing up. */
  display: none;
}

.portfolio-item--wide {
  grid-column: span 2;
  aspect-ratio: 16 / 9;
}

.portfolio-item .swatch,
.portfolio-item img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.5s var(--ease-out-expo);
}

.portfolio-item:hover .swatch,
.portfolio-item:focus-visible .swatch,
.portfolio-item:hover img,
.portfolio-item:focus-visible img {
  transform: scale(1.04);
}

.portfolio-item::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 75% 20%, rgba(255, 255, 255, 0.22), transparent 60%);
  mix-blend-mode: soft-light;
  pointer-events: none;
}

.swatch-1 { background: linear-gradient(135deg, var(--color-accent) 0%, var(--color-accent-deep) 70%); }
.swatch-2 { background: linear-gradient(200deg, var(--color-accent-blue-soft) 0%, var(--color-accent-blue) 100%); }
.swatch-3 { background: linear-gradient(20deg, var(--color-bg-blush) 0%, var(--color-accent-soft) 100%); }
.swatch-4 { background: linear-gradient(160deg, var(--color-accent-blue) 0%, var(--color-accent-blue-soft) 100%); }
.swatch-5 { background: linear-gradient(210deg, var(--color-bg-blush) 0%, var(--color-accent) 90%); }
.swatch-6 { background: linear-gradient(45deg, var(--color-accent-blue) 0%, var(--color-bg-sky) 100%); }

.portfolio-item .work-tag {
  z-index: 1;
}

.portfolio-item-view {
  position: absolute;
  right: 1.1rem;
  bottom: 1.1rem;
  z-index: 1;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-ink);
  background: rgba(8, 6, 9, 0.55);
  padding: 0.4rem 0.85rem;
  border-radius: 100px;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.3s var(--ease-out-expo), transform 0.3s var(--ease-out-expo);
}

.portfolio-item:hover .portfolio-item-view,
.portfolio-item:focus-visible .portfolio-item-view {
  opacity: 1;
  transform: translateY(0);
}

/* ==========================================================================
   Brand strip
   ========================================================================== */

.brand-strip {
  background: var(--color-bg-sky);
  padding: var(--section-pad) 0;
  text-align: center;
}

/* The Brands section is the one deliberate "blue region" of the site
   (its own --color-bg-sky wash) — kicker and brand-name links use
   --color-accent-blue here instead of the site-wide pink --color-accent,
   so blue actually appears as a real, legible brand color rather than
   only showing up in placeholder swatch gradients. */
.brand-strip .kicker {
  justify-content: center;
  color: var(--color-accent-blue);
}

.brand-strip .kicker::after {
  display: none;
}

.brand-row {
  display: flex;
  justify-content: center;
  align-items: baseline;
  gap: 3.5rem;
  flex-wrap: wrap;
  margin-top: 2.75rem;
  padding-top: 2.5rem;
  border-top: 1px solid rgba(74, 144, 255, 0.25);
}

.brand-row a {
  font-family: var(--font-heading);
  font-style: italic;
  font-weight: 600;
  font-size: clamp(1.85rem, 2.6vw, 2.25rem);
  color: var(--color-accent-blue);
  border-bottom: 1px solid transparent;
  padding-bottom: 0.3rem;
  transition: color 0.3s var(--ease-out-expo), border-color 0.3s var(--ease-out-expo);
}

.brand-row a:hover {
  color: var(--color-ink);
  border-color: var(--color-accent-blue-soft);
}

.brand-strip .work-cta {
  text-align: center;
  margin-top: 2.75rem;
}

/* ==========================================================================
   Brand campaign-preview gallery (static, non-lightbox)
   ========================================================================== */

.brand-gallery {
  background: var(--color-bg-pearl);
  padding: var(--section-pad) 0;
}

.brand-gallery-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.25rem;
  margin-top: 2rem;
}

.gallery-tile {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: 2px;
  overflow: hidden;
}

.gallery-tile .swatch {
  position: absolute;
  inset: 0;
}

.gallery-tile::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 75% 20%, rgba(255, 255, 255, 0.22), transparent 60%);
  mix-blend-mode: soft-light;
  pointer-events: none;
}

/* ==========================================================================
   Brand index (partner-credits rows)
   ========================================================================== */

.brand-index-section {
  background: var(--color-bg);
  padding: var(--section-pad) 0;
}

.brand-index-list {
  border-top: var(--rule-soft);
}

.brand-index-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  padding: 2rem 0;
  border-bottom: var(--rule-soft);
  transition: transform 0.3s var(--ease-out-expo);
}

.brand-index-row:hover,
.brand-index-row:focus-visible {
  transform: translateX(0.5rem);
}

.brand-index-name {
  flex: 0 0 auto;
  min-width: 220px;
  font-family: var(--font-heading);
  font-style: italic;
  font-weight: 600;
  font-size: 2rem;
  color: var(--color-accent);
}

.brand-index-desc {
  flex: 1;
  color: var(--color-ink-soft);
}

.brand-index-arrow {
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-ink-soft);
  white-space: nowrap;
  transition: color 0.3s var(--ease-out-expo);
}

.brand-index-row:hover .brand-index-arrow,
.brand-index-row:focus-visible .brand-index-arrow {
  color: var(--color-accent);
}

.brand-back-link {
  display: inline-block;
  margin-bottom: 1.5rem;
  font-size: 0.85rem;
}

/* ==========================================================================
   Closing CTA
   ========================================================================== */

.cta-close {
  background: var(--color-accent);
  color: var(--color-on-accent);
  padding: var(--section-pad) 0;
  text-align: center;
}

.cta-close h2 {
  font-size: clamp(2.25rem, 5vw, 3.5rem);
  color: var(--color-on-accent);
  margin-bottom: 1.5rem;
}

.cta-close p {
  color: rgba(20, 16, 26, 0.82);
  max-width: 46ch;
  margin: 0 auto 2.5rem;
  line-height: 1.75;
}

/* ==========================================================================
   Contact
   ========================================================================== */

.contact-section {
  background: var(--color-bg);
  padding: var(--section-pad) 0 calc(var(--section-pad) * 1.3);
}

.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4rem;
  padding-top: 3rem;
  border-top: var(--rule-soft);
}

.contact-email {
  display: inline-block;
  font-family: var(--font-heading);
  font-style: italic;
  font-weight: 600;
  font-size: clamp(1.75rem, 3.5vw, 2.5rem);
  color: var(--color-accent);
  word-break: break-word;
  transition: color 0.3s var(--ease-out-expo);
}

.contact-email:hover {
  color: var(--color-ink);
}

.contact-socials {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-top: 0.5rem;
}

.contact-social {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  font-family: var(--font-body);
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--color-ink);
  transition: color 0.3s var(--ease-out-expo);
}

.contact-social span {
  font-weight: 400;
  color: var(--color-ink-soft);
}

.contact-social:hover {
  color: var(--color-accent);
}

/* ==========================================================================
   Footer
   ========================================================================== */

.site-footer {
  padding: 2.5rem 1.5rem;
  text-align: center;
  color: var(--color-ink-soft);
  background: var(--color-bg-pearl);
  border-top: var(--rule-soft);
  font-size: 0.85rem;
}

/* ==========================================================================
   Entrance motion (single authored moment, CSS-only so it never depends
   on JS/observer timing and never leaves content stuck invisible)
   ========================================================================== */

@keyframes fade-up {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Scroll-triggered, not a load-time animation: below-the-fold .reveal
   elements used to play their fade-up on page load, off-screen, before
   the user ever scrolled to see them — a no-op in practice. main.js's
   initScrollReveal() adds .js-reveal to <body> only when it can actually
   observe intersections, so the pre-hidden state below only applies once
   JS is confirmed running; no-JS/unsupported browsers see the content
   fully visible immediately, same guarantee the rest of the site's
   motion (typewriter, polaroid stack) already makes. */
.reveal {
  transition: opacity 0.9s var(--ease-out-expo), transform 0.9s var(--ease-out-expo);
}

body.js-reveal .reveal {
  opacity: 0;
  transform: translateY(20px);
}

body.js-reveal .reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.reveal-delay-1 {
  transition-delay: 0.15s;
}

/* ==========================================================================
   Hero focal moment: the masthead opens like a lookbook cover — the
   silhouette panel swings open on its left edge like a page turning,
   while the wordmark inks in with a left-to-right wipe. This is the one
   authored sequence the THESIS asks for; every other section keeps the
   quieter shared fade-up above.
   ========================================================================== */

@keyframes ink-reveal {
  from { clip-path: inset(0 100% 0 0); }
  to { clip-path: inset(0 0 0 0); }
}

@keyframes page-open {
  from {
    opacity: 0;
    transform: perspective(1600px) rotateY(-26deg) scale(0.94);
  }
  to {
    opacity: 1;
    transform: perspective(1600px) rotateY(0) scale(1);
  }
}

.hero .kicker {
  animation: fade-up 0.6s var(--ease-out-expo) both;
}

/* Typewriter: JS (initTypewriter in main.js) wraps each character of
   .hero-title .typewriter in its own .letter span with a computed
   animation-delay, so every glyph fades/settles in whole rather than
   being clipped mid-character by a fixed-step width mask — that
   fixed-width-per-step approach reads as jerky on a proportional
   italic face where letters vary widely in width. If JS never runs,
   the plain text node underneath is already fully visible, so nothing
   here can leave the wordmark permanently hidden. */
.hero-title .letter {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.12em);
  transition: opacity 0.22s var(--ease-out-expo), transform 0.22s var(--ease-out-expo);
}

.hero-title .letter.is-in {
  opacity: 1;
  transform: translateY(0);
}

.hero-title .caret {
  display: inline-block;
  width: 2px;
  height: 0.78em;
  margin-left: 0.05em;
  background: var(--color-accent);
  vertical-align: -0.05em;
  animation: caret-blink 0.9s step-end infinite;
}

@keyframes caret-blink {
  50% {
    opacity: 0;
  }
}

.hero-tagline {
  animation: fade-up 0.7s var(--ease-out-expo) both 0.55s;
}

.hero-actions {
  animation: fade-up 0.7s var(--ease-out-expo) both 0.7s;
}

.hero-visual .silhouette-frame,
.hero-visual .polaroid-stack {
  transform-origin: left center;
  animation: page-open 0.9s var(--ease-out-expo) both 0.15s;
}

/* ==========================================================================
   Portfolio filter transitions
   ========================================================================== */

.portfolio-grid [data-category] {
  transition: opacity 0.35s var(--ease-out-expo), transform 0.35s var(--ease-out-expo);
}

.portfolio-grid [data-category].is-hidden {
  opacity: 0;
  transform: scale(0.96);
  pointer-events: none;
}

/* ==========================================================================
   Lightbox
   ========================================================================== */

.lightbox-overlay {
  position: fixed;
  inset: 0;
  z-index: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 3.5rem 2rem 2rem;
  background: rgba(8, 6, 9, 0.82);
  opacity: 0;
  visibility: hidden;
  overflow-y: auto;
  transition: opacity 0.35s var(--ease-out-expo), visibility 0s linear 0.35s;
}

.lightbox-overlay.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.35s var(--ease-out-expo);
}

.lightbox-content {
  /* Width is capped by viewport height too (converted through the 4:5
     panel ratio), not just viewport width, so the panel plus its close
     button and caption never overflow a short viewport. */
  position: relative;
  width: min(90vw, 720px, calc((100vh - 6rem) * 4 / 5));
  margin: auto;
  transform: scale(0.94);
  transition: transform 0.4s var(--ease-out-expo);
}

.lightbox-overlay.is-open .lightbox-content {
  transform: scale(1);
}

.lightbox-panel {
  width: 100%;
  aspect-ratio: 4 / 5;
  border-radius: 2px;
  position: relative;
  overflow: hidden;
  box-shadow: 0 50px 90px -30px rgba(0, 0, 0, 0.65);
}

.lightbox-panel::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 75% 20%, rgba(255, 255, 255, 0.22), transparent 60%);
  mix-blend-mode: soft-light;
  pointer-events: none;
}

.lightbox-panel img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.lightbox-panel .work-tag {
  color: var(--color-ink);
  background: rgba(8, 6, 9, 0.4);
}

.lightbox-caption {
  margin-top: 1rem;
  font-size: 0.8rem;
  color: var(--color-ink);
  text-align: center;
}

.lightbox-close {
  position: absolute;
  top: -2.75rem;
  right: 0;
  background: none;
  border: none;
  color: var(--color-ink);
  font-size: 1.9rem;
  line-height: 1;
  cursor: pointer;
  transition: transform 0.3s var(--ease-out-expo);
}

.lightbox-close:hover {
  transform: rotate(90deg);
}

/* ==========================================================================
   Ripple (tap/click feedback on solid buttons, filter pills, and tiles)
   ========================================================================== */

.ripple-surface {
  /* No `position` here on purpose: `.lightbox-close` already declares
     `position: absolute` elsewhere in this file, and this rule's later
     source order would otherwise win the specificity tie and break it.
     Each ripple-eligible selector sets its own `position` below instead. */
  overflow: hidden;
}

.ripple {
  position: absolute;
  border-radius: 50%;
  background: var(--ripple-color, rgba(255, 79, 129, 0.25));
  transform: scale(0);
  pointer-events: none;
  animation: ripple-expand 0.6s var(--ease-out-expo);
}

@keyframes ripple-expand {
  to {
    transform: scale(1);
    opacity: 0;
  }
}

.btn-primary {
  --ripple-color: rgba(20, 16, 26, 0.35);
}

.btn-primary-inverse,
.portfolio-filters button {
  --ripple-color: rgba(255, 79, 129, 0.25);
}

.portfolio-item,
.lightbox-close {
  --ripple-color: rgba(245, 241, 240, 0.35);
}

.nav-toggle {
  --ripple-color: rgba(255, 79, 129, 0.3);
}

@media (prefers-reduced-motion: reduce) {
  .ripple {
    display: none;
  }

  .glass-ripples {
    display: none;
  }

  .nav-collapse {
    transition: none;
  }

  .hero .kicker,
  .hero-title,
  .hero-tagline,
  .hero-actions,
  .hero-visual .polaroid-stack {
    animation: none;
  }

  .reveal {
    transition: none;
  }

  body.js-reveal .reveal {
    opacity: 1;
    transform: none;
  }

  .hero-title .letter {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .polaroid-card {
    transition: none;
  }

  .hero-title .caret {
    display: none;
  }

  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation: none !important;
  }

  .lightbox-content,
  .lightbox-close:hover {
    transition: none;
    transform: none;
  }
}
