/*
Theme Name: Design by Roland
Theme URI: https://designbyroland.co.za
Author: Design by Roland
Description: One-page portfolio theme for Roland Senekal, live events Technical Director. Hero copy, bio and showreel clips are edited in the Customizer; each card is a Selected Work or Renders post with a Gallery block for its images.
Version: 2.4.0
Requires at least: 6.0
Requires PHP: 7.4
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: design-by-roland
*/
:root {
  --bg: #07070e;
  --ink: #2a2a4e;
  --ink-deep: #1a1a30;
  --ink-deeper: #14141f;
  --orange: #ff6200;
  --orange-hi: #ff7d2e;
  --paper: #f4f2ec;
  --muted: #9a9ab8;
  --line: rgba(255, 255, 255, 0.1);
  --maxw: 1180px;
}

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

/* Subtle film grain for cinematic texture.
   This used to use mix-blend-mode: overlay, which forced the browser to
   re-blend the entire viewport on every scroll frame and measured as the single
   largest remaining cost (25 fps with it, 40 fps without). Plain compositing is
   free; the opacity is dialled back from 0.05 because normal blending lays the
   noise on top rather than modulating what is underneath, so the same number
   would read heavier and lift the blacks. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.028;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
@media (prefers-reduced-motion: reduce) {
  .hero-bg, .work-media { transition: none !important; }
}

html { scroll-behavior: smooth; }

body {
  background: #000;
  color: var(--paper);
  font-family: "Inter", system-ui, sans-serif;
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* ---------- Stage lights: fixed volumetric beams behind all content ---------- */
.stage-lights {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}
/* The rig used to be five gradient columns each carrying filter: blur(70px) and
   mix-blend-mode: screen. It is a fixed, full-viewport, entirely static
   decoration, so the browser was re-rendering all of that on every scroll frame
   for a picture that never changes. It is now one baked image of exactly that
   render: identical look, no per-frame cost. The .beam spans stay in the markup
   but are hidden, so nothing depends on them being removed.
   Regenerate with the capture + bake step if the palette ever changes. */
.stage-lights {
  background: #000 url("images/stage-lights.jpg") center top / cover no-repeat;
  animation: rig-breathe 18s ease-in-out infinite;
}
.stage-lights .beam,
.stage-lights::before { display: none; }

/* Opacity only, so this stays on the compositor and costs nothing to animate. */
@keyframes rig-breathe {
  0%, 100% { opacity: 0.82; }
  50%      { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .stage-lights { animation: none; }
}

h1, h2, h3, .wordmark { font-family: "Space Grotesk", sans-serif; line-height: 1.05; letter-spacing: -0.02em; }
h1, h2 { text-wrap: balance; }
h1 { font-size: clamp(2.6rem, 6vw, 5rem); font-weight: 700; }
h2 { font-size: clamp(2rem, 4vw, 3.2rem); font-weight: 600; }
h3 { font-size: 1.4rem; font-weight: 600; }

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

.eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.28em;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--orange);
  margin-bottom: 1.2rem;
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.95rem 1.8rem;
  border-radius: 100px;
  font-weight: 600;
  font-size: 0.95rem;
  transition: transform 0.25s ease, background 0.25s ease, color 0.25s ease;
  cursor: pointer;
  border: 1px solid transparent;
}
.btn-primary { background: var(--orange); color: #fff; }
.btn-primary:hover { background: var(--orange-hi); transform: translateY(-2px); }
.btn-ghost { border-color: var(--line); color: var(--paper); }
.btn-ghost:hover { border-color: var(--orange); color: var(--orange); }
.btn-block { width: 100%; }

/* ---------- Nav ---------- */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.4rem clamp(1.2rem, 5vw, 3.5rem);
  transition: background 0.3s ease, padding 0.3s ease, border-color 0.3s ease;
  border-bottom: 1px solid transparent;
}
.nav.scrolled {
  background: rgba(25, 25, 48, 0.85);
  backdrop-filter: blur(12px);
  border-bottom-color: var(--line);
  padding-top: 1rem;
  padding-bottom: 1rem;
}
.wordmark {
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: -0.01em;
}
.wordmark span {
  color: var(--orange);
  font-weight: 400;
  margin: 0 0.25em;
  font-size: 0.85em;
}
.nav-links { display: flex; align-items: center; gap: 2rem; font-size: 0.95rem; }
.nav-links a { color: var(--muted); transition: color 0.2s ease; }
.nav-links a:hover { color: var(--paper); }
.nav-cta {
  color: var(--paper) !important;
  border: 1px solid var(--line);
  padding: 0.55rem 1.2rem;
  border-radius: 100px;
  /* A pill must never break across two lines. On a 390px phone the wordmark
     and this button together fill the bar exactly, so the button lost the
     flex fight and wrapped. The fluid logo height and the tighter padding
     further down buy back the few pixels it needs. */
  white-space: nowrap;
}
.nav-cta:hover { border-color: var(--orange); color: var(--orange) !important; }

/* ---------- Hero ---------- */
.hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  padding: 8rem clamp(1.2rem, 5vw, 3.5rem) 4rem;
  overflow: hidden;
}
.hero-bg {
  position: absolute; inset: -18% 0;
  z-index: 0; /* own stacking context so the scrim layers stay behind .hero-inner */
  will-change: transform;
  background: #05050c;
}
/* hero.jpg is only ever visible when the showreel is not playing, so it is loaded
   only in those two cases rather than on every visit. It was 227 KB of a page
   nobody sees. */
.hero-bg.no-video { background-image: url("images/hero.jpg"); background-position: center; background-size: cover; }
@media (prefers-reduced-motion: reduce) {
  .hero-bg { background-image: url("images/hero.jpg"); background-position: center; background-size: cover; }
}
/* Showreel clips, cross-faded one into the next by script.js */
.hero-video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 900ms ease;
}
.hero-video.is-active { opacity: 1; }
.hero-bg::before {
  content: "";
  position: absolute; inset: 0; z-index: 1;
  background:
    radial-gradient(1200px 700px at 78% 18%, rgba(255, 98, 0, 0.22), transparent 60%),
    /* heavy on the left so the headline stays legible, light on the right so the footage reads */
    linear-gradient(to right, rgba(5, 5, 12, 0.9) 0%, rgba(5, 5, 12, 0.66) 42%, rgba(5, 5, 12, 0.34) 72%, rgba(5, 5, 12, 0.42) 100%),
    linear-gradient(to top, rgba(5, 5, 12, 0.5) 0%, transparent 45%);
}
.hero-bg::after {
  content: "";
  position: absolute; inset: 0; z-index: 2;
  background-image: linear-gradient(var(--line) 1px, transparent 1px), linear-gradient(90deg, var(--line) 1px, transparent 1px);
  background-size: 70px 70px;
  mask-image: radial-gradient(circle at 70% 30%, #000 0%, transparent 70%);
  opacity: 0.4;
}
.hero-inner { position: relative; max-width: var(--maxw); margin: 0 auto; width: 100%; }
.hero-sub {
  max-width: 620px;
  color: var(--muted);
  font-size: clamp(1.05rem, 1.6vw, 1.3rem);
  margin: 1.8rem 0 2.5rem;
}
.hero-actions { display: flex; gap: 1rem; flex-wrap: wrap; }
/* ---------- Sections ---------- */
.section-head { max-width: var(--maxw); margin: 0 auto 3.5rem; }
.section-head h2 { max-width: 16ch; }
.section-sub { color: var(--muted); margin-top: 1.1rem; max-width: 52ch; }

.work { padding: 7rem clamp(1.2rem, 5vw, 3.5rem); background: transparent; }
/* Renders sit directly under Selected Work, so they share the grid but not a
   second helping of top padding. The other sections carry 7rem of top padding,
   which is what keeps their headings clear of the fixed nav when their anchor is
   clicked; with no padding here, scroll-margin has to do that job instead. */
.work.renders { padding-top: 0; scroll-margin-top: 7rem; }
.work-grid {
  max-width: var(--maxw);
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.4rem;
}
.work-card {
  position: relative;
  display: block;
  cursor: pointer;
  border-radius: 16px;
  overflow: hidden;
  aspect-ratio: 3 / 2;
  background: var(--ink-deeper);
  cursor: pointer;
}
/* First project runs full width as an editorial feature */
.work-card.feature { grid-column: 1 / -1; aspect-ratio: 21 / 9; }
.work-media {
  position: absolute;
  inset: 0;
  transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}
/* The cover is a real <img> so it can lazy-load; object-fit reproduces exactly
   what background-size: cover did. The hover scale still lives on the wrapper,
   which scales the image with it. */
.work-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.work-card:hover .work-media { transform: scale(1.06); }
/* Legibility scrim + a hair of brand warmth, sits above the zooming image */
.work-card::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to top, rgba(15, 15, 32, 0.9) 0%, rgba(15, 15, 32, 0.15) 45%, transparent 70%),
    linear-gradient(120deg, rgba(255, 98, 0, 0.12), transparent 45%);
  transition: opacity 0.4s ease;
}
.work-num {
  position: absolute;
  top: 1rem;
  left: 1.3rem;
  z-index: 2;
  font-family: "Space Grotesk", sans-serif;
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: rgba(255, 255, 255, 0.65);
}
.work-meta {
  position: absolute;
  z-index: 2;
  left: 1.5rem;
  right: 1.5rem;
  bottom: 1.4rem;
  transform: translateY(0);
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.work-card:hover .work-meta { transform: translateY(-4px); }
.work-card:focus-visible { outline: 2px solid var(--orange); outline-offset: 3px; }
/* Tells people the card opens the event's photos. Dim by default so it reads on touch too. */
.work-meta::after {
  content: "View gallery \2192";
  display: block;
  margin-top: 0.7rem;
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--orange);
  opacity: 0.7;
  transition: opacity 260ms ease;
}
.work-card:hover .work-meta::after { opacity: 1; }
.work-meta h3 { margin-bottom: 0.25rem; }
.work-meta p {
  color: rgba(255, 255, 255, 0.72);
  font-size: 0.9rem;
  letter-spacing: 0.02em;
}
/* Who else was on the job. Quieter than the caption above it so the event name
   still leads, but readable against the scrim. */
.work-meta p.work-credit {
  margin-top: 0.45rem;
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.45);
}
.work-credit span { color: rgba(255, 255, 255, 0.8); }

/* ---------- About ---------- */
.about { padding: 7rem clamp(1.2rem, 5vw, 3.5rem); }
.about-grid {
  max-width: var(--maxw);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: 4rem;
  align-items: center;
}
.about-portrait {
  aspect-ratio: 4 / 5;
  border-radius: 18px;
  border: 1px solid var(--line);
  background: linear-gradient(160deg, var(--ink-deep), var(--ink-deeper));
  overflow: hidden;
}
.about-portrait img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.about-copy h2 { margin-bottom: 1.4rem; }
.about-copy p { color: var(--muted); margin-bottom: 1.1rem; max-width: 54ch; }
.services {
  list-style: none;
  margin-top: 2rem;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.8rem 1.5rem;
}
.services li {
  padding-left: 1.4rem;
  position: relative;
  font-weight: 500;
  color: var(--paper);
}
.services li::before {
  content: "";
  position: absolute; left: 0; top: 0.55em;
  width: 7px; height: 7px;
  background: var(--orange);
  border-radius: 2px;
}

/* ---------- Contact ---------- */
.contact { padding: 7rem clamp(1.2rem, 5vw, 3.5rem); background: transparent; }
.contact-inner { max-width: 840px; margin: 0 auto; }
.contact-head { text-align: center; margin-bottom: 3rem; }
.contact-head h2 { margin: 0 auto; }
.contact-sub { color: var(--muted); margin-top: 1rem; }
.contact-direct { margin-top: 0.9rem; color: var(--muted); font-size: 0.95rem; }
.contact-direct a { color: var(--paper); border-bottom: 1px solid var(--orange); padding-bottom: 1px; }
.contact-direct a:hover { color: var(--orange); }
.contact-direct .dot { margin: 0 0.6rem; color: var(--line); }
.lead-form {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.4rem;
}
.field { display: flex; flex-direction: column; }
.field-full { grid-column: 1 / -1; }
.field label {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
  margin-bottom: 0.5rem;
}
.field input, .field textarea {
  background: var(--ink-deeper);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 0.9rem 1rem;
  color: var(--paper);
  font-family: inherit;
  font-size: 1rem;
  transition: border-color 0.2s ease;
}
.field input:focus, .field textarea:focus { outline: none; border-color: var(--orange); }
.field textarea { resize: vertical; }
.hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; font: inherit; }
.form-status { margin-top: 1rem; text-align: center; font-size: 0.95rem; color: var(--orange); min-height: 1.2em; }

/* ---------- Footer ---------- */
/* ---------- Event gallery lightbox ---------- */
.lightbox {
  position: fixed; inset: 0; z-index: 100;
  /* No backdrop-filter here: the colour above is 97% opaque, so a full-viewport
     blur cost a lot of compositing for something nobody can actually see. */
  background: rgba(3, 3, 8, 0.97);
  display: grid;
  place-items: center;
  padding: clamp(1rem, 4vw, 3rem);
  opacity: 0;
  transition: opacity 260ms ease;
}
/* display:grid above beats the UA's [hidden]{display:none}, so restate it or the
   invisible overlay keeps covering the page and eats every click and hover */
.lightbox[hidden] { display: none; }
.lightbox.is-open { opacity: 1; }
.lb-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  max-width: 100%;
  max-height: 100%;
}
.lb-img {
  max-width: 100%;
  max-height: calc(100vh - 9rem);
  border-radius: 10px;
  object-fit: contain;
}
.lb-caption {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  font-size: 0.85rem;
  letter-spacing: 0.04em;
}
.lb-title { font-family: "Space Grotesk", sans-serif; text-transform: uppercase; }
.lb-count { color: var(--muted); font-variant-numeric: tabular-nums; }
.lb-btn {
  position: absolute;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--line);
  color: var(--paper);
  border-radius: 50%;
  width: 48px; height: 48px;
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  transition: background 200ms ease, border-color 200ms ease;
}
.lb-btn:hover { background: rgba(255, 98, 0, 0.18); border-color: var(--orange); }
.lb-close { top: clamp(1rem, 3vw, 2rem); right: clamp(1rem, 3vw, 2rem); }
.lb-prev { left: clamp(0.6rem, 3vw, 2rem); top: 50%; transform: translateY(-50%); }
.lb-next { right: clamp(0.6rem, 3vw, 2rem); top: 50%; transform: translateY(-50%); }
body.lb-locked { overflow: hidden; }
@media (max-width: 620px) {
  .lb-btn { width: 42px; height: 42px; font-size: 1.3rem; }
  .lb-prev { left: 0.4rem; }
  .lb-next { right: 0.4rem; }
}

.footer { background: rgba(5, 5, 10, 0.55); padding: 3.5rem clamp(1.2rem, 5vw, 3.5rem); border-top: 1px solid var(--line); }
.footer-inner {
  max-width: var(--maxw);
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1.5rem;
}
.footer-cta {
  color: var(--paper);
  border: 1px solid var(--line);
  padding: 0.6rem 1.4rem;
  border-radius: 100px;
  font-weight: 600;
  font-size: 0.9rem;
  transition: border-color 0.2s ease, color 0.2s ease;
}
.footer-cta:hover { border-color: var(--orange); color: var(--orange); }
.footer-note { color: var(--muted); font-size: 0.85rem; width: 100%; }

/* ---------- Reveal animation baseline ---------- */
.reveal { opacity: 0; transform: translateY(24px); }

/* ---------- Responsive ---------- */
@media (max-width: 820px) {
  .nav-links { gap: 1.1rem; }
  .nav-links a:not(.nav-cta) { display: none; }
  /* narrow screens: text runs full width, so darken vertically instead of left-to-right */
  .hero-bg::before {
    background:
      radial-gradient(900px 600px at 70% 12%, rgba(255, 98, 0, 0.2), transparent 60%),
      linear-gradient(to bottom, rgba(5, 5, 12, 0.55) 0%, rgba(5, 5, 12, 0.78) 45%, rgba(5, 5, 12, 0.86) 100%);
  }
  .work-grid { grid-template-columns: 1fr; }
  .work-card, .work-card.feature { aspect-ratio: 4 / 3; }
  .about-grid { grid-template-columns: 1fr; gap: 2.5rem; }
  .about-portrait { max-width: 320px; }
  .lead-form { grid-template-columns: 1fr; }
  .services { grid-template-columns: 1fr; }
}


/* ---------- WordPress additions ---------- */
/* A card with no Gallery block should not advertise photos that do not exist. */
.work-meta.no-gallery::after { content: none; }
.work-card:not([data-gallery]) { cursor: default; }

/* ---------- Logo ---------- */
/* When a logo image is set it replaces the text wordmark. The artwork is white
   on transparent, so it needs no colour treatment, only sizing. */
.wordmark-img { line-height: 0; }
.wordmark-img img { display: block; width: auto; }
.nav .wordmark-img img { height: 56px; transition: height 0.3s ease; }
.footer .wordmark-img img { height: 112px; }
/* The bar tightens on scroll, so give the logo a little back or it drives the
   whole header taller than the scrolled state is meant to be. */
.nav.scrolled .wordmark-img img { height: 46px; }
@media (max-width: 820px) {
  /* Fluid rather than a flat 40px: the logo is the widest thing in the bar,
     so on a narrow phone it has to give up a little height to leave room for
     the CTA. Caps at the old 40px, so nothing changes on a tablet. */
  .nav .wordmark-img img,
  .nav.scrolled .wordmark-img img { height: clamp(28px, 9vw, 40px); }
  .footer .wordmark-img img { height: 84px; }
}
@media (max-width: 460px) {
  .nav-cta { font-size: 0.85rem; padding: 0.5rem 1rem; }
}