/* ============================================================
   JESSE PAYNE — Lighting & Production Design
   "TERMINAL" — 1-bit. Pure OLED black, pure white, nothing else.

   The whole system is built from one motif: THE BLOCK. The blinking
   cursor, the section markers, the list bullets and the dithered
   portrait are all the same square pixel at different scales.

   Rules of the house:
     · Two colours. #000 and #fff. No greys as *hues* — only white
       at reduced opacity for hierarchy.
     · No border-radius. No shadows. No gradients. Square corners
       everywhere, or the pixel illusion dies.
     · Interaction = inversion. Hover/focus flips black and white
       instantly. That is the entire interaction vocabulary.
     · Silkscreen (bitmap) for names and labels, always uppercase.
       IBM Plex Mono for anything you actually have to read.
   ============================================================ */

:root{
  /* ── colour: there are only two ─────────────────────────── */
  --bg:        #000000;   /* true black — OLED pixels stay off  */
  --fg:        #ffffff;

  /* white at opacity = the only hierarchy device */
  --fg-2:      rgba(255,255,255,.72);  /* body copy            */
  --fg-3:      rgba(255,255,255,.46);  /* meta, captions       */
  --fg-4:      rgba(255,255,255,.28);  /* placeholders         */
  --line:      rgba(255,255,255,.20);  /* hairlines            */
  --line-2:    rgba(255,255,255,.10);  /* quieter hairlines    */

  /* the checkerboard inside empty image slots — near-black, so
     it reads as "unfilled canvas" without lighting up the panel */
  --void-tile: #0b0b0b;

  /* ── type ───────────────────────────────────────────────── */
  --pixel: 'Silkscreen', 'Courier New', monospace;
  --mono:  'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* ── measure ────────────────────────────────────────────── */
  --pad:    clamp(20px, 5vw, 64px);
  --wide:   1180px;
  --band:   clamp(88px, 13vw, 168px);  /* space between sections */
  --head-h: 52px;
}

/* ============================================================
   RESET
   ============================================================ */

*,*::before,*::after{ box-sizing:border-box; margin:0; padding:0; border-radius:0 }

html{
  background:var(--bg);
  -webkit-text-size-adjust:100%;
  scroll-behavior:smooth;
  scroll-padding-top:calc(var(--head-h) + 24px);
}
@media (prefers-reduced-motion:reduce){ html{ scroll-behavior:auto } }

body{
  background:var(--bg);
  color:var(--fg-2);
  font-family:var(--mono);
  font-size:14px;
  line-height:1.75;
  font-weight:400;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}

img{ display:block; max-width:100%; height:auto }
ul{ list-style:none }
a{ color:inherit; text-decoration:none }
button,input,textarea{ font:inherit; color:inherit; background:none; border:none }

/* 1-bit selection */
::selection{ background:var(--fg); color:var(--bg) }

/* thin monochrome scrollbar */
html{ scrollbar-width:thin; scrollbar-color:rgba(255,255,255,.25) #000 }
::-webkit-scrollbar{ width:10px; height:10px }
::-webkit-scrollbar-track{ background:#000 }
::-webkit-scrollbar-thumb{ background:rgba(255,255,255,.22) }
::-webkit-scrollbar-thumb:hover{ background:rgba(255,255,255,.45) }

/* every focus ring is the same hard white box — no glow, no radius */
:focus-visible{ outline:2px solid var(--fg); outline-offset:3px }

.skip{
  position:fixed; top:-100px; left:var(--pad); z-index:100;
  background:var(--fg); color:var(--bg);
  font-family:var(--pixel); font-size:10px; text-transform:uppercase;
  padding:10px 14px;
}
.skip:focus{ top:12px }

/* ============================================================
   SHARED ATOMS
   ============================================================ */

/* THE BLOCK — the motif. A solid square pixel. */
.blk{
  display:inline-block;
  width:7px; height:7px;
  background:var(--fg);
  margin-right:10px;
  vertical-align:2px;
}

/* Silkscreen's ampersand is drawn almost identically to its dollar sign —
   "LIGHTS & SOUND" reads as "LIGHTS $ SOUND". Any & inside pixel type must
   be wrapped in this, which sets it in the mono face instead.
   Currently unused (the one & lives inside a logo image now) — kept because
   the next client name or label with an "&" in it will need it. */
.amp{ font-family:var(--mono); font-weight:400; font-size:.92em }

/* section label: pixel type + a hairline running to the right edge */
.label{
  display:flex; align-items:center; gap:16px;
  font-family:var(--pixel); font-weight:400;
  font-size:11px; letter-spacing:.06em; text-transform:uppercase;
  color:var(--fg);
  margin-bottom:clamp(36px, 5vw, 64px);
}
.label::before{
  content:''; flex:none;
  width:9px; height:9px; background:var(--fg);
}
.label::after{
  content:''; flex:1 1 auto; height:1px; background:var(--line);
}

/* ============================================================
   HEADER
   ============================================================ */

/* No wordmark up here — the name is enormous on the page below it, so
   repeating it in 11px type was just noise. The nav sits centred. */
.head{
  position:fixed; inset:0 0 auto 0; z-index:50;
  height:var(--head-h);
  display:flex; align-items:center; justify-content:center;
  padding-inline:var(--pad);
  background:var(--bg);
  border-bottom:1px solid var(--line-2);
}

.head-nav{ display:flex; gap:clamp(18px, 4vw, 44px) }
.head-nav a{
  font-family:var(--pixel);
  font-size:10px; letter-spacing:.06em; text-transform:uppercase;
  color:var(--fg-3);
  padding:5px 4px;
}
/* inversion is the whole interaction language */
.head-nav a:hover{ background:var(--fg); color:var(--bg) }

/* ============================================================
   STAGE — the whole first screen: gallery dominant, identity beside it.

   WHY TWO COLUMNS. Stacking the gallery above the bio made the player
   full-width but short, and that is the worst possible shape for it:
   the photos are landscape and never cropped, so their size is capped
   by the frame's HEIGHT, and every extra pixel of width just added
   black bars. Width the player couldn't use, height it desperately
   needed. Moving the bio alongside trades width the gallery was
   wasting for height it can actually spend — the photo comes out
   roughly twice the area, and the composition reads gallery-first.

   The player then stretches to the full column height (flex:1) and the
   photo is centred inside it, so the gallery is the tallest thing on
   the screen no matter which slide is up.
   ============================================================ */

/* Wider than the rest of the page (--wide is 1180) on purpose. Once the
   photo is width-limited, width is the ONLY thing that makes it bigger,
   so the stage breaks out to near-full-bleed while Clients and Contact
   stay in the narrower measure. Capped at 1640 so the gallery can't
   outgrow the 1600px source images and start upscaling. */
.stage{
  width:100%; max-width:1640px; margin-inline:auto;
  min-height:100svh;
  padding:calc(var(--head-h) + clamp(14px, 2.4vh, 30px)) var(--pad) clamp(22px, 4vh, 48px);
  display:grid;
  grid-template-columns:minmax(0, 2.15fr) minmax(300px, 1fr);
  gap:clamp(30px, 4vw, 56px);
  align-items:stretch;
}

/* Below this the columns get too narrow to be worth it — stack, gallery
   still first, and let the page scroll.

   The player takes a real aspect ratio here instead of absorbing leftover
   height. Stacked, there IS no leftover height to absorb, so flex:1 left
   the photo as a tiny strip in a full-width frame — exactly the problem
   the two-column layout exists to solve. 3/2 matches most of the photos. */
@media (max-width:900px){
  .stage{
    grid-template-columns:1fr;
    align-content:start;
    gap:clamp(24px, 3.5vh, 40px);
  }
  .player{ flex:none; aspect-ratio:3/2; min-height:0 }
}

.deck{
  flex:1 1 auto;
  min-height:0;
  display:flex; flex-direction:column;
}

/* No border. Photos are shown whole (never cropped), so their widths
   differ slide to slide — a fixed frame around them would leave uneven
   margins and read as a broken box. On pure black, an unframed photo
   just floats, the same treatment the portrait and the logos get. */
.player{
  position:relative;
  flex:1 1 auto;
  /* a real floor, NOT a vh-based one. A viewport-scaled minimum grows
     on exactly the short screens that need the player to give way, so
     the one-screen fit fails on a 768px laptop. A fixed floor lets
     flex:1 hand the player whatever is left, down to something still
     worth looking at. */
  min-height:170px;
  overflow:hidden;
}

.slides{ position:absolute; inset:0 }

/* every slide stacked; only .is-on is visible. A short cut, not a
   long dissolve — this is a 1-bit page, transitions stay terse. */
.slide{
  position:absolute; inset:0;
  display:grid; place-items:center;
  opacity:0; visibility:hidden;
  transition:opacity .18s linear, visibility 0s linear .18s;
  background-color:var(--bg);
}
.slide.is-on{
  opacity:1; visibility:visible;
  transition:opacity .18s linear, visibility 0s;
}
/* contain, NOT cover: the whole frame is the work, so nothing is cropped.

   The <img> fills the slide and object-fit does the letterboxing. Sizing
   it with max-height:100% instead does NOT work here — the slide's
   implicit grid row is auto-sized, so the percentage resolves against the
   row (which the image itself grows), and the photo silently overflows
   and gets clipped by the player's overflow:hidden. */
.slide img{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:contain;
}

/* empty slides keep the checkerboard so it's obvious they're unfilled */
.slide:not(:has(img)){
  background-image:
    linear-gradient(45deg, var(--void-tile) 25%, transparent 25% 75%, var(--void-tile) 75%),
    linear-gradient(45deg, var(--void-tile) 25%, transparent 25% 75%, var(--void-tile) 75%);
  background-size:16px 16px;
  background-position:0 0, 8px 8px;
}

/* ── transport controls ────────────────────────────────────── */

.controls{
  flex:none;
  display:flex; align-items:center; gap:10px;
  padding:12px 0 clamp(14px, 2.4vh, 26px);
}

/* these ARE buttons, so unlike the logo wall they get a box */
.ctl{
  flex:none;
  width:34px; height:34px;
  border:1px solid var(--line);
  color:var(--fg);
  display:grid; place-items:center;
  cursor:pointer;
}
.ctl:hover{ background:var(--fg); color:var(--bg); border-color:var(--fg) }

/* glyphs are clip-path shapes on currentColor, not font characters —
   so they stay crisp, need no icon font, and flip with the hover
   inversion for free */
.ico{ display:block; background:currentColor }
.ico-prev,.ico-next{ width:9px; height:11px }
.ico-prev{ clip-path:polygon(100% 0, 100% 100%, 0 50%) }
.ico-next{ clip-path:polygon(0 0, 0 100%, 100% 50%) }
/* two bars, drawn as borders — a single clip-path polygon can't make two
   disjoint shapes, it just connects every point into one bowtie */
.ico-pause{
  width:9px; height:11px;
  background:none;
  border-left:3px solid currentColor;
  border-right:3px solid currentColor;
}
.ico-play { width:9px; height:11px; clip-path:polygon(0 0, 0 100%, 100% 50%) }

/* the current photo's caption, sat right of the transport buttons.
   flex:1 + min-width:0 lets it take the slack and ellipsise rather than
   shoving the dots and counter off the row on a narrow screen. */
.cap{
  flex:1 1 auto; min-width:0;
  margin-left:6px;
  font-size:11px; letter-spacing:.1em; text-transform:uppercase;
  color:var(--fg);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}

/* the dots are the block motif again, at its smallest */
.dots{ display:flex; align-items:center; gap:7px }
.dot{
  width:8px; height:8px;
  background:var(--fg-4);
  cursor:pointer;
  padding:0; border:0;
}
.dot:hover{ background:var(--fg-2) }
.dot[aria-current="true"]{ background:var(--fg) }

.count{
  flex:none;
  margin-left:4px;
  font-size:11px; letter-spacing:.1em; color:var(--fg-3);
}

/* On a phone the row can't carry everything, and the caption is the part
   that's actually content. The dots go — the arrows and the NN / NN
   counter already cover position and navigation — and the caption drops
   to its own full-width line rather than ellipsising to "WAVE CONFEREN…". */
@media (max-width:620px){
  .dots{ display:none }
  .controls{ flex-wrap:wrap }
  .cap{
    order:1;
    flex-basis:100%;
    margin:11px 0 0;
    line-height:1.55;
    white-space:normal; overflow:visible; text-overflow:clip;
  }
}

/* Three things separate a refined bitmap wordmark from a video-game one,
   and the first draft got all three wrong:
     · WEIGHT 400, not 700. Silkscreen Bold at poster scale turns to blobs;
       the regular cut keeps the pixel grid legible as a grid.
     · POSITIVE tracking, not negative. Wide letterspacing is the oldest
       signal of refinement in display type — cramming it reads cheap.
     · ONE line, not a stacked pair. Spanning the measure reads as a
       masthead; two giant stacked words read as a poster.
   The result is still unmistakably 8-bit, just composed rather than loud. */
/* The anchor of the bio block. Big enough to carry the identity on its
   own — the bio sentence no longer repeats the name — while staying
   inside the one-screen height budget. Weight 400 and wide positive
   tracking are what keep bitmap type composed rather than blobby; see
   the note in CLAUDE.md before changing either. */
/* Sized to sit on ONE line inside the narrow right-hand column —
   "JESSE PAYNE" runs about 8.4em wide at this tracking, so the vw term
   is what keeps it from wrapping as the column shrinks. Weight 400 and
   wide positive tracking are what keep bitmap type composed rather than
   blobby; see the note in CLAUDE.md before changing either. */
.name{
  font-family:var(--pixel); font-weight:400;
  font-size:clamp(1.15rem, 2.5vw, 1.95rem);
  line-height:1.15;
  letter-spacing:.155em;
  text-transform:uppercase;
  color:var(--fg);
  margin-bottom:clamp(4px, .8vh, 10px);
}

/* the blinking block cursor — the only motion on the page */
.cursor{
  display:inline-block;
  width:.44em; height:.76em;
  background:var(--fg);
  margin-left:.06em;   /* the tracking already supplies the gap */
  vertical-align:baseline;
  animation:blink 1.1s step-end infinite;
}
@keyframes blink{ 0%,49%{ opacity:1 } 50%,100%{ opacity:0 } }

/* ============================================================
   BANDS
   ============================================================ */

.band{
  width:100%; max-width:var(--wide); margin-inline:auto;
  padding:0 var(--pad) var(--band);
}

/* Empty slots get a near-black checkerboard — it reads as "unfilled
   canvas" without lighting up an OLED panel, and it disappears on its
   own the moment an <img> lands, so a half-filled grid always shows
   exactly what's still missing. */
.logo-cell:not(:has(img)){
  background-image:
    linear-gradient(45deg, var(--void-tile) 25%, transparent 25% 75%, var(--void-tile) 75%),
    linear-gradient(45deg, var(--void-tile) 25%, transparent 25% 75%, var(--void-tile) 75%);
  background-size:16px 16px;
  background-position:0 0, 8px 8px;
}

.frame-tag{
  font-family:var(--pixel); font-size:9px;
  letter-spacing:.1em; color:var(--fg-4);
}

/* ============================================================
   CLIENTS — a logo wall
   ============================================================ */

.logos{
  display:grid;
  grid-template-columns:repeat(auto-fill, minmax(210px, 1fr));
  gap:clamp(26px, 3.5vw, 44px) clamp(18px, 2.5vw, 30px);
}

/* No border and no panel — a boxed logo reads as a button you can click,
   and none of these are links. The marks just sit on the black.

   Sizing is NOT done here. Every file in assets/logos/ is pre-rendered
   onto the same 480x300 transparent canvas, already scaled for equal
   optical weight and centred (see tools/build-logos.py), so the CSS is
   just `width:100%` and all twelve line up by construction. Doing it in
   CSS instead would need a dozen per-logo overrides. */
.logo-cell{
  aspect-ratio:8/5;
  margin-bottom:16px;
}

.logo-cell img{
  width:100%; height:100%;
  object-fit:contain;
  /* brightness(0) crushes the artwork to solid black, invert(1) flips it
     to white — any brand colour becomes a clean 1-bit silhouette */
  filter:brightness(0) invert(1);
}

/* the stand-in until a logo exists */
.logo-name{
  font-family:var(--pixel); font-size:11px; line-height:1.6;
  letter-spacing:.02em; text-transform:uppercase;
  text-align:center; text-wrap:balance;
  color:var(--fg);
}

/* Captions vary — some clients have a production line, some don't, and
   some wrap to two lines. Make each card fill its row and push the role
   to the bottom, so every role in a row sits on one baseline instead of
   ragging against its neighbours. */
.client{ display:flex; flex-direction:column; height:100% }

/* Every client gets its name in text. Roughly half these logos are
   symbols rather than wordmarks — Wave's W, the AVS mark, Breakaway's
   monogram, Celebration, Victory City, Life Family — so without this the
   cell identified nobody. The <img> is alt="" (decorative) because the
   name is right here; otherwise a screen reader says it twice. */
.client-name{
  font-family:var(--pixel); font-weight:400;
  font-size:11px; line-height:1.55; letter-spacing:.02em;
  text-transform:uppercase; color:var(--fg);
  margin-bottom:9px;
}

.client-prod{ font-size:13px; line-height:1.5; color:var(--fg-2) }
.client-role{
  font-size:11px; letter-spacing:.05em; text-transform:uppercase;
  color:var(--fg-3);
  margin-top:auto; padding-top:5px;
}

/* ============================================================
   ABOUT
   ============================================================ */

/* The secondary column: portrait, name, statement, competencies, stacked.
   Centred against the gallery so the two columns read as one composition
   rather than two things that happen to start at the same y. */
.about{
  display:flex; flex-direction:column;
  align-self:center;
  gap:clamp(16px, 2.2vh, 26px);
}

@media (max-width:900px){
  .about{
    align-self:start;
    border-top:1px solid var(--line-2);
    padding-top:clamp(22px, 3.2vh, 34px);
  }
}

/* the dithered portrait sits on the page with no frame — the vignette
   baked into the PNG is pure black, so it dissolves into the background.
   Centred against the text column rather than top-aligned: the head is
   the optical centre of the image, so hanging it from the top left it
   sitting high next to the block of copy. */
.portrait{ flex:none; width:120px }
.portrait img{
  width:100%; height:auto;
  image-rendering:pixelated;
}

/* the bio statement, in his own all-caps voice */
.lede{
  font-size:13px; line-height:1.75;
  letter-spacing:.04em; text-transform:uppercase;
  color:var(--fg);
  max-width:64ch;
}

.sub{
  font-family:var(--pixel); font-weight:400;
  font-size:10px; letter-spacing:.08em; text-transform:uppercase;
  color:var(--fg-3);
  padding-bottom:11px;
  border-bottom:1px solid var(--line-2);
}

.comps{
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(260px, 1fr));
  gap:2px clamp(20px, 3vw, 40px);
  margin-top:-6px;
}
.comps li{
  display:flex; align-items:flex-start;
  font-size:11.5px; line-height:1.6;
  letter-spacing:.03em; text-transform:uppercase;
  color:var(--fg-2);
  padding:4px 0;
}
.comps .blk{
  flex:none; width:5px; height:5px;
  margin:7px 12px 0 0;   /* optical: sits on the first line's x-height */
  vertical-align:baseline;
}

@media (max-width:680px){
  .portrait{ width:110px }
}

/* ============================================================
   CONTACT
   ============================================================ */

.contact{
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(300px, 1fr));
  gap:clamp(44px, 7vw, 96px);
  align-items:start;
}

.email{
  display:inline-block;
  font-size:clamp(15px, 2.6vw, 21px);
  color:var(--fg);
  border-bottom:1px solid var(--line);
  padding:2px 4px 4px;
  margin-left:-4px;
  word-break:break-all;
}
.email:hover{ background:var(--fg); color:var(--bg); border-bottom-color:var(--fg) }

.socials{ margin-top:40px }
.socials li{
  display:flex; gap:16px; align-items:baseline;
  padding:12px 0;
  border-top:1px solid var(--line-2);
  font-size:13px;
}
.socials .k{
  flex:none; width:88px;
  font-family:var(--pixel); font-size:9px;
  letter-spacing:.08em; text-transform:uppercase; color:var(--fg-3);
}
.socials a{ color:var(--fg); border-bottom:1px solid var(--line-2); padding-bottom:1px }
.socials a:hover{ background:var(--fg); color:var(--bg); border-bottom-color:var(--fg) }
.socials .v{ color:var(--fg-2) }

/* ── form: terminal input lines, no boxes ──────────────────── */

.hp{ position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden }

.field{ margin-bottom:26px }
.field label{
  display:block;
  font-family:var(--pixel); font-size:9px;
  letter-spacing:.1em; text-transform:uppercase; color:var(--fg-3);
  margin-bottom:10px;
}
.field input,
.field textarea{
  display:block; width:100%;
  font-family:var(--mono); font-size:14px; color:var(--fg);
  padding:8px 2px;
  border-bottom:1px solid var(--line);
  resize:vertical;
}
.field input:focus,
.field textarea:focus{
  outline:none;
  border-bottom-color:var(--fg);
}
.field textarea::placeholder{ color:var(--fg-4) }
/* keep autofill inside the 1-bit palette */
.field input:-webkit-autofill{
  -webkit-text-fill-color:var(--fg);
  -webkit-box-shadow:0 0 0 1000px #000 inset;
}

.send{
  font-family:var(--pixel); font-size:11px;
  letter-spacing:.1em; text-transform:uppercase;
  color:var(--fg);
  border:1px solid var(--fg);
  padding:14px 32px;
  cursor:pointer;
  margin-top:8px;
}
.send:hover{ background:var(--fg); color:var(--bg) }

.ok{
  grid-column:1 / -1;
  font-family:var(--pixel); font-size:10px;
  letter-spacing:.08em; text-transform:uppercase;
  color:var(--bg); background:var(--fg);
  padding:14px 18px;
}

/* ============================================================
   FOOTER
   ============================================================ */

/* mono, not pixel — Silkscreen has no legible © glyph at this size */
.foot{
  width:100%; max-width:var(--wide); margin-inline:auto;
  padding:26px var(--pad) 40px;
  border-top:1px solid var(--line-2);
  display:flex; justify-content:space-between; flex-wrap:wrap; gap:12px;
  font-size:11px; letter-spacing:.08em;
  text-transform:uppercase; color:var(--fg-3);
}

/* ============================================================
   REDUCED MOTION — the cursor stops blinking and simply stays on
   ============================================================ */

@media (prefers-reduced-motion:reduce){
  .cursor{ animation:none; opacity:1 }
}
