/* ==========================================================================
   Tom Bruzda — portfolio
   Hand-written replacement for the Webflow build.

   Two aspect ratios carry the whole design: 16:10 for full-width cards,
   4:5 for tall paired cards. Paired videos are 6:5. Everything else is
   the 12px gap and the 8px radius.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Fonts

   Helvetica Now Display (the licensed original, served by Adobe Typekit and
   locked to the webflow domain) is stood in for by Inter Tight until a
   replacement is chosen. To swap it: drop the files in assets/fonts/,
   change the @font-face below and --font-display. Nothing else refers to
   a font by name.

   Supreme is the real thing — it is free from Fontshare.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: "Inter Tight";
  src: url("../fonts/inter-tight-latin.woff2") format("woff2");
  font-weight: 100 900;            /* variable: one file covers every weight */
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}
@font-face {
  font-family: "Inter Tight";
  src: url("../fonts/inter-tight-latin-ext.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
                 U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
                 U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
                 U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Supreme";
  src: url("../fonts/supreme-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Supreme";
  src: url("../fonts/supreme-500.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Supreme";
  src: url("../fonts/supreme-800.woff2") format("woff2");
  font-weight: 800; font-style: normal; font-display: swap;
}

/* --------------------------------------------------------------------------
   2. Tokens
   -------------------------------------------------------------------------- */

:root {
  --bg:            #000;
  --surface:       #1a1a1a;                    /* every rounded dark block   */
  --surface-soft:  rgba(255, 255, 255, .08);   /* the cube's underside       */
  --ink:           #fff;
  --ink-dim:       rgba(255, 255, 255, .5);    /* labels, secondary copy     */

  --gap:     12px;
  --radius:  8px;
  --pad:     24px;                             /* inside a panel             */
  --sidebar: 400px;

  --font-display: "Inter Tight", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-label:   "Supreme", "Helvetica Neue", Helvetica, Arial, sans-serif;

  /* the one easing the whole site moves on */
  --ease:      cubic-bezier(.16, 1, .3, 1);
  --ease-cube: cubic-bezier(0, .9, .5, 1);
}

/* Only the gap shrinks site-wide. --pad stays 24: the footer, the notes, the
   cube faces and the next-project panel all keep it — the design shrinks the
   header and the heading panel only, and those two say so themselves. */
@media (max-width: 479px) {
  :root { --gap: 8px; }
}

/* --------------------------------------------------------------------------
   3. Base
   -------------------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,.25) transparent; }

::-webkit-scrollbar       { width: 0; height: 0; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(255,255,255,.25); border-radius: 10px; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-display);
  font-size: 18px;
  line-height: 26px;
  font-weight: 500;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg, iframe { display: block; max-width: 100%; border: 0; }
img { width: 100%; height: auto; }
a   { color: inherit; text-decoration: none; }
p   { margin: 0; }

h1, h2, h3 { margin: 0; font-weight: 800; }

:focus-visible {
  outline: 2px solid #0050bd;
  outline-offset: 2px;
  border-radius: 4px;
}

/* --------------------------------------------------------------------------
   4. Layout — a sticky sidebar and a scrolling column of work
   -------------------------------------------------------------------------- */

.layout { display: flex; align-items: stretch; }

.sidebar {
  flex: 0 0 var(--sidebar);
  width: var(--sidebar);
  height: 100vh;
  position: sticky;
  top: 0;
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  padding: 12px 0 12px 12px;
}

.main  { flex: 1; min-width: 0; position: relative; }

.stack {
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  padding: var(--gap);
}

/* --------------------------------------------------------------------------
   5. Panel + buttons
   -------------------------------------------------------------------------- */

.panel {
  background: var(--surface);
  border-radius: var(--radius);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 48px;
  padding: 0 16px;
  border-radius: 9999px;
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
  border: 0;
  transition: opacity .2s ease, transform .2s var(--ease);
}
.btn:hover  { opacity: .85; }
.btn:active { transform: scale(.98); }

.btn--solid { background: var(--ink); color: #000; align-self: flex-start; }
.btn--ghost { background: var(--surface-soft); color: var(--ink); }

/* --------------------------------------------------------------------------
   6. Sidebar contents
   -------------------------------------------------------------------------- */

.intro {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: var(--pad);
}
.intro__head { display: flex; flex-direction: column; gap: var(--pad); }
.intro__logo { width: 100%; max-height: 96px; }
.intro__text { color: var(--ink-dim); font-weight: 400; }

.projects-wrap {
  flex: 1;
  display: flex;
  overflow: hidden;
}

.projects {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  padding: 12px 0;
  margin: 0;
  list-style: none;
  overflow: hidden;
}

/* the 3D cube ---------------------------------------------------------------
   The link is the stage; .cube holds two faces 80px apart and turns a
   quarter on hover, so the underside comes up into place.                   */

.project-item { display: block; }

.project-link {
  display: block;
  position: relative;
  width: 100%;
  height: 80px;
  border-radius: var(--radius);
  perspective: 1000px;
}

.cube {
  display: block;
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transform-origin: center center;
  transform: translateZ(-40px);
  transition: transform .5s var(--ease-cube);
  pointer-events: none;
}

.face {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px var(--pad) 8px;
  border-radius: var(--radius);
  backface-visibility: hidden;
}
.face--front { transform: translateZ(40px); }
.face--back  { transform: rotateX(-90deg) translateZ(40px); background: var(--surface-soft); }

.project-link:hover .cube,
.project-link:focus-visible .cube { transform: rotateX(90deg) translateY(-40px); }

.project-meta  { display: flex; }
.project-kind  { flex: 1; }
.project-title {
  font-size: 24px;
  line-height: 1;
  font-weight: 800;
  letter-spacing: .5px;
  text-transform: uppercase;
}

/* small caps labels — the only place Supreme is used */
.label {
  font-family: var(--font-label);
  font-size: 12px;
  line-height: 18px;
  font-weight: 500;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.socials {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: var(--pad);
}
.socials__row  { display: flex; gap: var(--pad); opacity: .5; transition: opacity .2s ease; }
.socials__row:hover { opacity: 1; }
.socials__row a { display: block; }
.socials__row img { width: auto; height: 20px; }

/* --------------------------------------------------------------------------
   7. Frames — every piece of media on the site sits in one

   A frame is a box with a fixed aspect ratio that clips whatever is inside it.
   `--ar` is a NUMBER, not `16/10`, because the cover maths below divides it and
   calc() would read `16/10 / 16/10` as `((16/10)/16)/10`.

       1.6  16:10  full-width cards, the four featured projects, hover loops
       1.2   6:5   the paired videos on avast / caroline / leune
       0.8   4:5   the tall pairs on evangelist and the home concept rows
   -------------------------------------------------------------------------- */

.frame {
  --ar: 1.6;
  position: relative;
  width: 100%;
  aspect-ratio: var(--ar);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);   /* a frame is never a hole while it loads */
}

.frame--6-5 { --ar: 1.2; }
.frame--4-5 { --ar: 0.8; }

/* An image crops itself. */
.frame > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* An iframe cannot. `object-fit` does nothing to one, and Vimeo's player fits
   the video to the iframe box it is given — so if that box is ever a different
   shape than the video, the picture letterboxes or stretches, and it does not
   necessarily re-fit when the window is resized.

   So the iframe is given the VIDEO's own ratio and sized to cover the frame by
   hand: whichever axis would fall short grows past 100% and the frame clips the
   overflow. Where the two ratios agree — which is everywhere on the site today —
   both come out at exactly 100% and nothing moves. Set `--v-ar` on .frame__video
   when a video's shape differs from its frame's. */
.frame__video {
  --v-ar: var(--ar);
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width:  calc(100% * max(1, var(--v-ar) / var(--ar)));
  height: calc(100% * max(1, var(--ar) / var(--v-ar)));
}

.frame__video > iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/* ---- skeleton ------------------------------------------------------------
   Two independent flags, because a poster and a video arrive at different
   moments and a frame can carry both:

     is-loading   nothing to show yet — shimmer on, still image held back.
                  Dropped when the still loads, or when the video is ready
                  in a frame that has no still.
     v-pending    the player has not reported itself playing yet, so the
                  video is held at zero over whatever is underneath it.

   BOTH are added by the script and removed by it, so a page with no
   JavaScript shows everything instead of shimmering for ever. */

.frame__skeleton {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity .45s ease;
  background:
    linear-gradient(100deg,
      var(--surface)            30%,
      rgba(255, 255, 255, .055) 48%,
      rgba(255, 255, 255, .055) 52%,
      var(--surface)            70%);
  background-size: 220% 100%;
}

.frame.is-loading .frame__skeleton {
  opacity: 1;
  animation: frame-shimmer 1.5s linear infinite;
}

.frame > img,
.frame__video { transition: opacity .5s ease; }

.frame.is-loading > img       { opacity: 0; }
.frame.v-pending .frame__video { opacity: 0; }

@keyframes frame-shimmer {
  from { background-position: 170% 0; }
  to   { background-position: -70% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .frame.is-loading .frame__skeleton { animation: none; }
}

/* ---- rows of frames ------------------------------------------------------ */

.pair {
  display: flex;
  gap: var(--gap);
  width: 100%;
  align-items: stretch;
}
.pair > * { flex: 1 1 0; min-width: 0; }

.row { display: flex; gap: var(--gap); align-items: flex-start; position: relative; }
.row > * { flex: 1 1 0; min-width: 0; }

/* --------------------------------------------------------------------------
   8. Text blocks
   -------------------------------------------------------------------------- */

.note {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
  width: 100%;
  min-height: 240px;
  padding: var(--pad);
}
.note__label {
  font-size: 18px;
  line-height: 18px;
  font-weight: 800;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.note__body { font-size: 24px; line-height: 32px; font-weight: 400; }

.section-title {
  padding: var(--pad);
  font-size: 48px;
  line-height: 56px;
  font-weight: 800;
  text-transform: uppercase;
  text-align: center;
}

/* --------------------------------------------------------------------------
   9. Home — the four featured projects

   Each is a frame holding a still, which cross-fades to a looping video on
   hover where one exists. That 100ms fade is the only thing Webflow's whole
   interaction engine was doing on this site.
   -------------------------------------------------------------------------- */

.work {
  display: flex;
  flex-direction: column;
  gap: var(--gap);
}

.work-item { display: block; }

.work-item .frame__video {
  opacity: 0;
  transition: opacity .1s ease-in-out;
}
/* `:not(.v-pending)` rather than a later override: the video must not be
   revealed by a hover it is not ready for, and stating it here keeps the two
   rules from depending on which section of this file comes last. */
.work-item:not(.v-pending):hover .frame__video,
.work-item:not(.v-pending):focus-visible .frame__video { opacity: 1; }

/* --------------------------------------------------------------------------
   10. Next project
   -------------------------------------------------------------------------- */

.next {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  width: 100%;
  padding: var(--pad);
  text-align: center;
  overflow: hidden;
}
.next__label { text-align: center; margin-bottom: 16px; }
.next__logo  { max-height: 96px; margin: 0 auto; }
.next:hover .next__logo { transform: scale(1.02); }
.next__logo  { transition: transform .3s var(--ease); }

/* --------------------------------------------------------------------------
   11. Mobile chrome — hidden until the sidebar goes away
   -------------------------------------------------------------------------- */

.mobile-top,
.mobile-footer,
.menu,
.menu__placeholder { display: none; }

/* --------------------------------------------------------------------------
   12. Responsive
   -------------------------------------------------------------------------- */

@media (max-width: 991px) {
  .sidebar { display: none; }

  .mobile-top,
  .mobile-footer { display: block; }

  /* The menu button expands from its own box to fill the screen. It is
     sticky so it stays reachable, and `position: fixed` is swapped in for
     the flight — see assets/js/site.js.                                     */
  .menu {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    gap: var(--pad);
    padding: var(--pad) 0;
    position: sticky;
    top: 12px;
    z-index: 900;
    cursor: pointer;
    overflow: hidden;
    transition: width .6s var(--ease), height .6s var(--ease),
                top .6s var(--ease), left .6s var(--ease),
                border-radius .6s var(--ease);
    will-change: width, height, top, left;
  }

  .menu.is-fixed {
    position: fixed;
    z-index: 9999;
    margin: 0;
  }
  .menu.is-open {
    top: 0 !important;
    left: 0 !important;
    width: 100vw !important;
    height: 100dvh !important;
    border-radius: 0;
    overflow-y: auto;
    cursor: default;
  }

  /* the two states of the button's own label */
  .menu__states {
    position: relative;
    flex: 0 0 auto;
    display: flex;
    justify-content: center;
    width: 100%;
    pointer-events: none;
  }
  .menu.is-open .menu__states { padding: 16px 0; }

  .menu__state {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--gap);
    transition: opacity .3s ease, transform .3s ease;
  }
  .menu__state--close {
    position: absolute;
    inset: 0;
    opacity: 0;
    transform: scale(.9);
  }
  .menu.is-open .menu__state--open  { opacity: 0; }
  .menu.is-open .menu__state--close {
    opacity: 1;
    transform: scale(1);
    pointer-events: auto;
    cursor: pointer;
  }
  .menu__label { font-size: 18px; font-weight: 500; }
  .menu__icon  { width: auto; height: 20px; }

  /* the project list inside the open menu */
  .menu .projects {
    display: none;
    flex: 0 0 auto;
    width: 100%;
    margin: 0 auto;
    padding: 0 40px;
    opacity: 0;
    pointer-events: none;
  }
  .menu.is-open .projects            { display: flex; }
  .menu.is-open .projects.is-shown   { opacity: 1; pointer-events: auto; }

  /* cubes drop in from above, one after another */
  .menu .cube {
    transform: rotateX(-90deg);
    transition: transform .6s var(--ease);
  }
  .menu.is-open .projects.is-shown .cube { transform: rotateX(0deg); }
  .menu.is-open .projects.is-shown .project-item:nth-child(1) .cube { transition-delay: .1s; }
  .menu.is-open .projects.is-shown .project-item:nth-child(2) .cube { transition-delay: .2s; }
  .menu.is-open .projects.is-shown .project-item:nth-child(3) .cube { transition-delay: .3s; }
  .menu.is-open .projects.is-shown .project-item:nth-child(4) .cube { transition-delay: .4s; }
  .menu.is-open .projects.is-shown .project-item:nth-child(5) .cube { transition-delay: .5s; }

  /* no cube flip on touch — there is no hover to drive it */
  .menu .project-link:hover .cube { transform: rotateX(0deg); }
}

@media (max-width: 767px) {
  .menu .projects { padding: 0 var(--pad); }
}

@media (max-width: 479px) {
  .menu .projects { padding: 0; }
  .menu           { padding: 16px 0; top: 8px; }

  .section-title  { font-size: 32px; line-height: 36px; padding: 16px; }
  .note__body     { font-size: 18px; line-height: 26px; }
  .btn            { width: 100%; }
  .intro          { gap: 14px; padding: 16px; }
  .intro__head    { gap: 16px; }
  .intro__text    { font-size: 16px; line-height: 24px; }

  /* text columns stop being columns */
  .row--text { flex-direction: column; }
}

/* --------------------------------------------------------------------------
   13. Reduced motion
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}
