/* ===========================================================================
   komal khandelwal — growth marketer

   A warm near-black ground, not a neutral one: the base carries a little red
   so it sits under her brand colour rather than fighting it. Anton, condensed
   and heavy, carries the name and the section titles; everything readable is
   Inter Tight. Nothing is boxed: rules and space do the separating, so the
   stills are the only images on the page.

   Structure is a left rail of labels against one measured column of content.
   The rail is what you read to navigate, the column is what you read to know.
   ========================================================================= */

@font-face {
  font-family: "Anton";
  src: url("assets/fonts/anton.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Inter Tight";
  src: url("assets/fonts/inter-tight.woff2") format("woff2");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}

:root {
  --display: "Anton", "Haettenschweiler", "Arial Narrow", sans-serif;
  --body:    "Inter Tight", "Helvetica Neue", Arial, sans-serif;

  --paper:  #120f10;   /* the ground. warm, not neutral black */
  --ink:    #f0eae3;   /* type */
  --soft:   #8b807a;
  --rule:   #2b2325;
  --red:    #e8404f;   /* lifted off the brand card's red, which goes muddy on black */
  --well:   #1c1719;   /* behind a still before it loads */

  --rail:   170px;
  --gap:    56px;
  --page:   1120px;
  --edge:   40px;
}

* { box-sizing: border-box; }

html { background: var(--paper); }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 16.5px/1.6 var(--body);
  letter-spacing: -0.008em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.wrap {
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 var(--edge);
}

a { color: var(--red); text-decoration: none; }

/* Prose links carry a rule that lifts off on hover. On paper this is enough
   signal without underlining every line into stripes. */
.prose a,
.posts a,
.contact a {
  box-shadow: inset 0 -1px 0 rgba(232, 64, 79, .5);
  transition: box-shadow .2s ease, color .2s ease;
}

.prose a:hover,
.posts a:hover,
.contact a:hover { box-shadow: inset 0 -1px 0 transparent; color: var(--ink); }

/* the small label that names a section, in the rail on desktop */
.label {
  font: 500 11px/1.5 var(--body);
  letter-spacing: .15em;
  text-transform: uppercase;
  color: var(--soft);
}

/* --- masthead ------------------------------------------------------------- */

.masthead { padding: 44px 0 0; }

.top {
  display: flex;
  justify-content: space-between;
  gap: 20px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--rule);
}

.top .now { color: var(--red); }

h1 {
  font: 400 clamp(52px, 10.6vw, 148px)/0.88 var(--display);
  letter-spacing: -0.012em;
  text-transform: lowercase;
  margin: 30px 0 0;
}

/* the surname carries the accent, so the eye lands on the name twice */
h1 span { color: var(--red); }

.deck {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 24px;
  margin-top: 24px;
  padding-bottom: 30px;
  border-bottom: 1px solid var(--rule);
}

.deck p {
  margin: 0;
  max-width: 26ch;
  font-size: clamp(19px, 2vw, 24px);
  line-height: 1.34;
  letter-spacing: -0.016em;
}

/* The portrait isn't a headshot, it's four frames of her actually on camera,
   from four different shoots, cross-fading on a twelve second loop. It says
   what a static avatar can't: she is the one in front of the lens. Hovering
   holds whichever frame you're on. */
.portrait {
  position: relative;
  flex: 0 0 auto;
  width: 132px;
  height: 165px;
  overflow: hidden;
  background: var(--well);
}

.portrait img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 26%;
  opacity: 0;
  animation: cycle 12s linear infinite;
}

/* four frames, three seconds each, quarter of the loop apart */
.portrait img:nth-child(1) { animation-delay: 0s; }
.portrait img:nth-child(2) { animation-delay: 3s; }
.portrait img:nth-child(3) { animation-delay: 6s; }
.portrait img:nth-child(4) { animation-delay: 9s; }

.portrait:hover img { animation-play-state: paused; }

@keyframes cycle {
  0%           { opacity: 0; }
  2%, 23%      { opacity: 1; }
  26%, 100%    { opacity: 0; }
}

/* the counter under the frame, so the change reads as deliberate */
.portrait::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 25%;
  background: var(--red);
  animation: sweep 12s linear infinite;
}

@keyframes sweep {
  from { transform: translateX(0); }
  to   { transform: translateX(300%); }
}

.portrait:hover::after { animation-play-state: paused; }

/* --- the rail layout ------------------------------------------------------ */

/* One grid used by every block on the page: a narrow rail of labels on the
   left, a single content column on the right. Everything lines up because
   nothing opts out of it. */
.row {
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr);
  gap: var(--gap);
  align-items: start;
}

.row + .row { margin-top: 0; }

section { padding: 74px 0 0; }

.row > .rail { position: sticky; top: 34px; }

.rail h2 {
  font: 400 clamp(26px, 2.5vw, 34px)/0.94 var(--display);
  text-transform: lowercase;
  letter-spacing: -0.008em;
  margin: 0 0 8px;
}

.rail .label { display: block; }

.rail .n {
  display: block;
  margin-bottom: 10px;
  font: 400 13px/1 var(--display);
  color: var(--red);
  letter-spacing: .06em;
}

/* --- prose ---------------------------------------------------------------- */

.prose { max-width: 66ch; }
.prose p { margin: 0 0 15px; }
.prose p:last-child { margin-bottom: 0; }
.prose b { font-weight: 600; }

/* --- figures -------------------------------------------------------------- */

.figures {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 28px;
  margin-top: 34px;
  padding-top: 22px;
  border-top: 1px solid var(--rule);
}

.fig b {
  display: block;
  font: 400 clamp(38px, 4.2vw, 54px)/0.92 var(--display);
  letter-spacing: -0.008em;
  font-variant-numeric: tabular-nums;
}

.fig span {
  display: block;
  margin-top: 8px;
  font-size: 14px;
  line-height: 1.42;
  color: var(--soft);
  max-width: 24ch;
}

/* --- work ----------------------------------------------------------------- */

.job { padding: 26px 0; border-top: 1px solid var(--rule); }
.job:first-child { border-top: 0; padding-top: 0; }

.job-head {
  display: flex;
  align-items: center;
  gap: 13px;
  margin-bottom: 12px;
}

/* Client marks arrive as dark artwork meant for white backgrounds, so each one
   sits on its own small light chip rather than disappearing into the page. */
.job-head img {
  width: 28px;
  height: 28px;
  flex: 0 0 auto;
  object-fit: contain;
  padding: 3px;
  background: #e7e0d8;
  filter: grayscale(1);
  opacity: .65;
  transition: filter .3s ease, opacity .3s ease;
}

.job:hover .job-head img { filter: none; opacity: 1; }

.job-head h3 {
  margin: 0;
  font: 400 clamp(26px, 2.6vw, 34px)/1 var(--display);
  text-transform: lowercase;
  letter-spacing: -0.008em;
}

.job-head .when {
  margin-left: auto;
  flex: 0 0 auto;
  font: 500 11px/1.5 var(--body);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--soft);
  text-align: right;
}

.job p { margin: 0 0 12px; max-width: 66ch; }
.job p:last-child { margin-bottom: 0; }
.job b { font-weight: 600; }

/* the selected posts under a client */
.posts { margin: 12px 0 0; padding: 0; list-style: none; max-width: 66ch; }
.posts li { margin-bottom: 5px; color: var(--soft); }
.posts li b { color: var(--ink); }

/* --- stills --------------------------------------------------------------- */

/* No frames, no borders. The stills sit on the paper and the caption hangs
   under them like a photo credit.

   Two columns of equal width, and because a vertical clip is twice the height
   of a widescreen one, the rows are packed rather than aligned: main.js gives
   every tile a row span measured off its own height, so the next tile starts
   where this one ended instead of where the tallest in the row ended. Without
   JS the .masonry class is never added and this stays an ordinary grid. */
.grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  gap: 38px 22px;
}

.grid.masonry {
  grid-auto-rows: 8px;   /* the unit a span is counted in, must match main.js */
  row-gap: 0;
}

.tile[data-href] { cursor: pointer; }

.frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--well);
}

.tile[data-aspect="9/16"] .frame { aspect-ratio: 9 / 16; }

.frame img,
.frame video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(1) contrast(1.02);
  transition: filter .4s ease, transform 1s cubic-bezier(.16, .84, .28, 1);
}

/* a screenshot is read, not watched: whole frame, always in colour */
.tile[data-kind="image"] .frame { background: transparent; }
.tile[data-kind="image"] .frame img { object-fit: contain; filter: none; }

.tile:hover .frame img,
.tile:focus-within .frame img { filter: none; transform: scale(1.02); }

.frame video { opacity: 0; filter: none; transition: opacity .28s ease; }
.frame video.on { opacity: 1; }

/* the only hover chrome: a red mark that says this one plays */
.play {
  position: absolute;
  left: 10px;
  bottom: 10px;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 6px;
  font: 500 10px/1 var(--body);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: #fff;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity .25s ease, transform .25s ease;
  text-shadow: 0 1px 6px rgba(0, 0, 0, .5);
}

.play::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--red);
}

.tile:hover .play,
.tile:focus-within .play { opacity: 1; transform: none; }

.cap { margin-top: 11px; }

.cap h4 {
  margin: 0;
  font-size: 15.5px;
  font-weight: 600;
  line-height: 1.32;
  letter-spacing: -0.012em;
}

.cap h4 a { color: var(--ink); }
.tile:hover .cap h4 a { color: var(--red); }

.cap .meta,
.cap .note { margin-top: 2px; font-size: 13.5px; line-height: 1.45; color: var(--soft); }

.cap .src { margin-top: 6px; }

.cap .src a {
  font: 500 10px/1 var(--body);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--soft);
  margin-right: 10px;
  transition: color .2s ease;
}

.cap .src a:hover { color: var(--red); }

/* the heading above one group of stills */
.group-row {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin: 44px 0 20px;
}

.group-row:first-child { margin-top: 0; }

.group-row h3 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.008em;
}

.group-row::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--rule);
}

.group-row .label { flex: 0 0 auto; }

/* A folded group of stills. The summary is styled as the same heading an open
   group gets, so the only thing marking it as a fold is the sign on the right
   and the count of what's inside. */
details.fold { margin: 44px 0 0; }

details.fold > summary {
  display: flex;
  align-items: baseline;
  gap: 14px;
  cursor: pointer;
  list-style: none;
  padding-bottom: 2px;
}

details.fold > summary::-webkit-details-marker { display: none; }

details.fold > summary h3 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.008em;
  transition: color .2s ease;
}

details.fold > summary:hover h3 { color: var(--red); }

/* the rule runs between the heading and the count, as it does on open groups */
details.fold > summary::before {
  content: "";
  order: 1;
  flex: 1;
  height: 1px;
  background: var(--rule);
}

details.fold > summary .label { order: 2; flex: 0 0 auto; }

details.fold > summary::after {
  content: "show";
  order: 3;
  flex: 0 0 auto;
  font: 500 11px/1.5 var(--body);
  letter-spacing: .15em;
  text-transform: uppercase;
  color: var(--red);
}

details.fold[open] > summary::after { content: "hide"; }

details.fold > .grid { margin-top: 20px; }

/* --- questions ------------------------------------------------------------ */

details { border-top: 1px solid var(--rule); padding: 16px 0; }
details:first-of-type { border-top: 0; padding-top: 0; }

summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 18px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

summary::-webkit-details-marker { display: none; }

summary::after { content: "+"; flex: 0 0 auto; color: var(--red); font-weight: 400; }
details[open] summary::after { content: "–"; }

details p { margin: 9px 0 0; max-width: 64ch; color: var(--soft); }

/* --- contact -------------------------------------------------------------- */

.contact-lines a {
  display: block;
  width: fit-content;
  margin-bottom: 6px;
  font: 400 clamp(24px, 3.4vw, 42px)/1.24 var(--display);
  text-transform: lowercase;
  letter-spacing: -0.006em;
  color: var(--ink);
  box-shadow: none;
  transition: color .2s ease;
}

.contact-lines a:hover { color: var(--red); }

footer {
  margin-top: 74px;
  padding: 22px 0 60px;
  border-top: 1px solid var(--rule);
}

footer .wrap { display: flex; justify-content: space-between; gap: 16px; }

/* --- lightbox ------------------------------------------------------------- */

#lb {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(10, 8, 9, .965);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 66px 24px 26px;
  animation: fade .22s ease;
}

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

#lb[hidden] { display: none; }

#lb .box { width: 100%; max-width: 1040px; }

#lb video,
#lb img {
  display: block;
  width: 100%;
  max-height: calc(100dvh - 116px);
  object-fit: contain;
  background: var(--well);
}

#lb[data-aspect="9/16"] .box { max-width: min(440px, 54vh); }

#lb .cap-lb { margin-top: 10px; font-size: 14px; color: var(--soft); }

#lb .x {
  position: absolute;
  top: 22px;
  right: 26px;
  padding: 0;
  background: none;
  border: 0;
  color: var(--ink);
  font: 500 11px/1 var(--body);
  letter-spacing: .14em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color .2s ease;
}

#lb .x:hover { color: var(--red); }

/* --- motion --------------------------------------------------------------- */

/* One idea, used everywhere: things arrive from slightly below, slightly
   dimmed. No wipes, no slides, nothing that competes with the stills. */
.rise { opacity: 0; animation: rise .7s cubic-bezier(.16, .84, .28, 1) forwards; }

@keyframes rise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

/* --- tablet --------------------------------------------------------------- */

@media (max-width: 940px) {
  :root { --gap: 30px; --rail: 128px; --edge: 30px; }
}

@media (max-width: 760px) {
  .row { grid-template-columns: 1fr; gap: 18px; }
  .row > .rail { position: static; }
  .rail h2 { font-size: clamp(30px, 8vw, 40px); }
  .figures { grid-template-columns: 1fr 1fr; }
}

/* --- phone ---------------------------------------------------------------- */

@media (max-width: 560px) {
  :root { --edge: 20px; }

  .masthead { padding-top: 28px; }
  h1 { font-size: clamp(46px, 15vw, 74px); }
  .portrait { width: 92px; height: 116px; }
  section { padding-top: 52px; }
  .figures { grid-template-columns: 1fr; gap: 20px; }

  /* one still per row, verticals included: a 9/16 clip at half a phone's
     width is smaller than the phone it was shot on */
  .grid { grid-template-columns: minmax(0, 1fr); gap: 30px 0; }

  .job-head { flex-wrap: wrap; }
  .job-head .when { margin-left: 41px; width: 100%; text-align: left; }

  footer .wrap { flex-direction: column; gap: 6px; }
  #lb { padding: 58px 14px 16px; }
  #lb[data-aspect="9/16"] .box { max-width: min(100%, 50vh); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
  }
  .rise { opacity: 1; }

  /* the cycle's last keyframe is opacity 0, so collapsing it would leave the
     frame empty. hold the first shot instead. */
  .portrait img { animation: none !important; opacity: 0; }
  .portrait img:first-child { opacity: 1; }
  .portrait::after { display: none; }
}
