/* ---------------------------------------------
   Obra Studio — V2
   Base styles shared by every page

   Typography/layout constants below are measured
   directly off the reference (christianlopez.xyz)
   at 1920px (desktop) and 390px (mobile) viewports,
   so header/brand/nav sit at identical sizes and
   offsets, just with Obra's own content.
--------------------------------------------- */

:root {
  --color-fg: #fefefe;
  --color-bg: #000000;
  --color-intro-bg: #fefefe;
  --color-intro-fg: #0a0a0a;
  --pad: .75rem;       /* header/clock inset, mobile  = 12px  */
  --ease: cubic-bezier(.4, 0, .2, 1);
}

* {
  box-sizing: border-box;
}

html, body {
  height: 100%;
  margin: 0;
}

html {
  font-size: 100%; /* 16px */
}

@media (min-width: 960px) {
  html {
    font-size: 87.5%; /* 14px — matches reference desktop scale */
  }
  :root {
    --pad: 2rem;        /* = 28px at 14px root, matches reference */
  }
}

body {
  background: var(--color-bg);
  color: var(--color-fg);
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

body.home {
  overflow: hidden;
  height: 100vh;
}

a {
  color: inherit;
  text-decoration: none;
  transition: opacity .3s var(--ease);
}

a:hover,
a:focus-visible {
  opacity: .55;
}

h1 {
  font-weight: 400;
  margin: 0;
}

/* ---------------------------------------------
   Full-bleed background video (home only)
--------------------------------------------- */

.bg-video {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  z-index: 0;
  background: #000;
  cursor: pointer;
}

.bg-video video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* click the video, or hit spacebar, to pause/resume — see
   js/home-video.js. Pausing brings up a dark scrim with the
   studio's statement text over it (matching obra-studio.fr);
   clicking anywhere on it (other than the email link) resumes. */
.pause-overlay {
  position: fixed;
  inset: 0;
  z-index: 6;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(0, 0, 0, .78);
  opacity: 0;
  pointer-events: none;
  transform: scale(.995);
  filter: blur(2px);
  transition: opacity .28s, transform .28s, filter .28s;
  user-select: none;
  cursor: pointer;
}

.pause-overlay.is-visible {
  opacity: 1;
  pointer-events: auto;
  transform: scale(1);
  filter: blur(0);
}

.pause-overlay__copy {
  color: #fff;
  text-transform: uppercase;
  font-size: clamp(14px, 1.6vw, 20px);
  line-height: 1.25;
  letter-spacing: .02em;
  max-width: min(1100px, 92vw);
  text-align: left;
}

.pause-overlay__copy strong {
  font-weight: 700;
  color: #fff;
}

.pause-overlay__email {
  color: #fff;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .14em;
  transition: opacity .15s;
  cursor: pointer;
}

.pause-overlay__email:hover,
.pause-overlay__email:focus {
  opacity: .85;
}

/* ---------------------------------------------
   Header: brand (name + tagline) and nav.
   Mobile: stacked, both left-aligned, in normal flow.
   Desktop (>=960px): nav becomes a full-width flex row
   (first link at the left edge, last at the right edge)
   and brand is absolutely centered — left edge pinned to
   the horizontal center, text itself stays left-aligned.
--------------------------------------------- */

.site-header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 10;
  padding: var(--pad);
  display: block;
  /* the header/nav boxes span edge-to-edge even where no
     text is visible (e.g. the hidden "current page" link) —
     let clicks/hovers pass through to what's underneath, and
     re-enable them only on the actual links. */
  pointer-events: none;
}

.site-header a {
  pointer-events: auto;
}

.brand {
  font-size: 1rem;
  line-height: 1.4286; /* 20/14 — reference's lead-3 ratio */
  text-align: left;
  color: var(--color-brand, var(--color-fg));
}

/* while a work-page hover preview (image or video) is showing,
   the brand text blends against it instead of sitting flat on
   top — see js/work.js's showMedia()/hideAll(). */
body.is-previewing .brand {
  mix-blend-mode: difference;
}

.brand h1,
.brand .tagline {
  font-size: 1em;
  line-height: inherit;
  margin: 0;
}

.brand h1 {
  font-weight: 500;
}

.main-nav {
  margin-top: .2em;
  display: block;
  font-size: 2rem;           /* 32px mobile */
  line-height: .875;         /* reference's lead-1 ratio */
  text-transform: uppercase;
  letter-spacing: normal;
}

.main-nav > a + a,
.main-nav > .nav-group {
  margin-left: 1rem;
}

.nav-group > a + a {
  margin-left: .5em;
}

/* ---------------------------------------------
   Live clock / location
--------------------------------------------- */

.clock {
  position: fixed;
  right: var(--pad);
  bottom: var(--pad);
  z-index: 10;
  display: flex;
  gap: .5em;
  align-items: baseline;
  text-transform: uppercase;
  letter-spacing: .06em;
  font-size: .75rem;
}

#clock-time {
  font-variant-numeric: tabular-nums;
  opacity: .85;
}

.clock-place {
  font-weight: 700;
}

.clock-temp {
  font-variant-numeric: tabular-nums;
  opacity: .55;
}

.clock-temp:empty {
  display: none;
}

/* About page — Instagram link, mirrored bottom-left of the clock */
.social-link {
  position: fixed;
  left: var(--pad);
  bottom: var(--pad);
  z-index: 10;
  text-transform: uppercase;
  letter-spacing: .06em;
  font-size: .75rem;
  font-weight: 700;
  color: inherit;
}

.social-link:hover,
.social-link:focus {
  opacity: .7;
}

/* ---------------------------------------------
   Desktop layout
--------------------------------------------- */

@media (min-width: 960px) {
  /* raised well clear of the standard --pad inset — closes up
     the big empty band above the header/work list — while the
     work list's own top padding (below) is nudged down to the
     same reduced height, so its first item stays level with the
     header instead of drifting apart. */
  .site-header {
    padding-top: calc(var(--pad) - 1rem);
  }

  .brand {
    position: absolute;
    top: calc(var(--pad) - 1rem);
    left: 50%;
    /* no transform: left edge sits at the horizontal
       center, text itself stays left-aligned — same as
       the reference, not centered on the midpoint. */
  }

  .main-nav {
    margin-top: 0;
    display: flex;
    justify-content: space-between;
    font-size: 4rem;         /* 56px desktop */
  }

  .main-nav > a + a,
  .main-nav > .nav-group {
    margin-left: 0;
  }

  .nav-group {
    display: flex;
  }

  .clock {
    font-size: .85rem;
  }

  .social-link {
    font-size: .85rem;
  }

  /* current page's own nav link is redundant on desktop —
     visibility:hidden (not display:none) keeps its layout
     space, so with 3 links the remaining two (e.g. Archive/
     About) stay correctly spread by justify-content:space-between
     instead of the row collapsing */
  .main-nav a.current {
    visibility: hidden;
  }
}

/* ---------------------------------------------
   Simple inner pages (About / Archive placeholders)
--------------------------------------------- */

.page-content {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  text-align: center;
}

.page-content p {
  opacity: .7;
  max-width: 40ch;
  margin: 0 auto;
}

/* About statement — same treatment as the homepage's paused-video
   overlay copy (obra-studio.fr): uppercase, left-aligned block
   centered on the page. */
.page-content--about {
  text-align: left;
}

.page-content--about .about-copy {
  color: #fff;
  text-transform: uppercase;
  font-size: clamp(14px, 1.6vw, 20px);
  line-height: 1.25;
  letter-spacing: .02em;
  max-width: min(1100px, 92vw);
  text-align: left;
}

.page-content--about .about-copy strong {
  font-weight: 700;
  color: #fff;
}

.page-content--about .about-copy a {
  color: #fff;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .14em;
}

.page-content--about .about-copy a:hover,
.page-content--about .about-copy a:focus {
  opacity: .85;
}

/* ---------------------------------------------
   Work page — light theme + project list
--------------------------------------------- */

body.theme-light {
  --color-fg: #0a0a0a;
  --color-bg: #ffffff;
  --color-brand: #0000ff;   /* light theme = blue RGB */
}

.work-list {
  position: relative;
  z-index: 1;
  list-style: none;
  margin: 9rem 0 0;
  padding: 0 var(--pad) var(--pad);
  font-size: 2rem;
  line-height: .875;
  text-transform: uppercase;
}

.work-list li {
  margin-top: .75rem;
}

.work-list li:first-child {
  margin-top: 0;
}

.work-list a {
  position: relative;
  cursor: default;
}

.mobile-media {
  margin-top: .5rem;
}

.mobile-media img,
.mobile-media video {
  display: block;
  width: 100%;
  height: auto;
}

/* ---------------------------------------------
   Hover preview — desktop only, single media item
   (image or video), reproducing christianlopez.xyz's
   ".floating" mechanism: fixed full-viewport box,
   padded inset, object-fit:contain so the media sits
   centered without cropping, no pointer events, no
   fade — an instant show/hide exactly like the reference.
--------------------------------------------- */

.work-preview {
  display: none;
}

@media (min-width: 960px) {
  .mobile-media {
    display: none;
  }

  .work-list {
    margin-top: 0;
    padding: var(--pad);
    /* the list's own type (4rem / line-height .875) sits ~8.5px
       higher than its padding-top implies, unlike the nav's flex
       layout — compensated here so the first item's glyph still
       lands exactly level with the header text above. */
    padding-top: calc(var(--pad) - 5.5px);
    /* enough clearance at the bottom that the last project name
       never runs into the fixed clock/temperature — its own
       height plus a clear gap above it. */
    padding-bottom: calc(var(--pad) * 2);
    width: 50%;
    font-size: 4rem;
  }

  .work-list li {
    margin-top: 0;
  }

  .work-preview {
    display: block;
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    pointer-events: none;
    z-index: 5;
  }

  /* position/size are set inline per hover by js/work.js — every
     appearance gets a fresh random spot and size, never the same
     placement twice, reproducing createdbyus.org's effect */
  .work-preview img,
  .work-preview video {
    display: none;
    position: fixed;
    /* contain, never cover — the box only ever scales the media,
       it never crops it — full frame, original aspect ratio kept */
    object-fit: contain;
    background: transparent;
  }

  /* images: one random position + size per page load (js/work.js
     sets top/left/width/height inline), reproducing the scale of
     createdbyus.org's mosaic — always a large, legible size, up to
     nearly the full viewport */
  .work-preview img[data-active] {
    display: block;
  }

  /* videos: always the same fixed, centered format as
     christianlopez.xyz/work's floating box — never randomized */
  .work-preview video {
    inset: 0;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    padding: 4rem;
    box-sizing: border-box;
  }

  @media (min-height: 60em) {
    .work-preview video {
      padding: 9rem;
    }
  }

  .work-preview video[data-active] {
    display: block;
  }
}


/* ---------------------------------------------
   Archive — Obra Playground
   A bounded, dark "viewport" panel holding an
   infinite panning mosaic of packshots (drag or
   two-finger trackpad scroll in any direction —
   tiles recycle so there is always more to see).
   Clicking a tile zooms it into a full-screen
   viewer; arrow keys then slide a filmstrip left/
   right so the *camera* feels like it is moving,
   not the picture just changing in place.
--------------------------------------------- */

.playground-page {
  position: relative;
  z-index: 1;
}

.playground-frame {
  position: fixed;
  inset: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
  background: var(--color-bg);
  overflow: hidden;
  cursor: grab;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}

.playground-frame.is-grabbing {
  cursor: grabbing;
}

.playground-canvas {
  position: absolute;
  inset: 0;
}

.tile {
  position: absolute;
  box-sizing: border-box;
  padding: .15rem;
  will-change: transform;
}

.tile img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  transition: opacity .4s var(--ease), transform .2s var(--ease);
  pointer-events: none;
}

.tile img.is-loaded {
  opacity: 1;
}

.tile:hover img.is-loaded {
  opacity: 1;
  transform: scale(1.04);
}

.playground-label {
  position: absolute;
  bottom: var(--pad);
  left: var(--pad);
  font-size: .65rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: rgba(0, 0, 0, .45);
  pointer-events: none;
}

/* ---------------------------------------------
   Viewer — full-screen filmstrip
--------------------------------------------- */

.viewer {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: #fff;
  opacity: 0;
  pointer-events: none;
  transition: opacity .35s var(--ease);
  overflow: hidden;
}

.viewer.is-open {
  opacity: 1;
  pointer-events: auto;
}

.viewer-track {
  position: absolute;
  top: 0;
  left: 0;
  width: 300vw;
  height: 300vh;
  display: grid;
  grid-template-columns: repeat(3, 100vw);
  grid-template-rows: repeat(3, 100vh);
  transform: translate(-100vw, -100vh);
}

.viewer-track.no-transition {
  transition: none !important;
}

.viewer.is-animating .viewer-track {
  transition: transform .6s cubic-bezier(.65, 0, .35, 1);
}

.viewer-panel {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.viewer-panel img {
  max-width: min(82vw, 82vh);
  max-height: min(82vw, 82vh);
  object-fit: contain;
  transform: scale(.82);
  transition: transform .45s var(--ease);
}

.viewer.is-open .viewer-panel img {
  transform: scale(1);
}

.viewer-close {
  position: absolute;
  top: var(--pad);
  right: var(--pad);
  z-index: 2;
  font-size: 2rem;
  line-height: 1;
  background: none;
  border: 0;
  color: #0a0a0a;
  cursor: pointer;
  opacity: 0;
  transition: opacity .3s var(--ease) .15s;
}

.viewer.is-open .viewer-close {
  opacity: .7;
}

.viewer-close:hover {
  opacity: 1 !important;
}

.viewer-hint {
  position: absolute;
  right: var(--pad);
  bottom: var(--pad);
  z-index: 2;
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: #0a0a0a;
  opacity: 0;
  transition: opacity .3s var(--ease) .15s;
}

.viewer.is-open .viewer-hint {
  opacity: .5;
}

@media (max-width: 599px) {
  .viewer-hint {
    display: none;
  }
}

/* ---------------------------------------------
   Project page — Work detail
   One single full-bleed horizontal carousel:
   the first slide is the "related" row (links
   toward other projects), every slide after it
   is one of the project's own photos/videos at
   near-full viewport height — exactly the same
   sequence, so paging past the related row with
   an arrow or a swipe is what reveals the shoot,
   each frame filling almost the whole screen.
--------------------------------------------- */

.project-page main {
  position: relative;
  z-index: 1;
}

/* the header sits on its own solid strip above the gray
   gallery stage, like a printed masthead — everything
   below it goes gray, the header never does. */
.project-page .site-header {
  background: var(--color-bg);
  padding-bottom: 1.25rem;
}

.project-gallery {
  position: fixed;
  inset: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  background: var(--color-bg);
  cursor: pointer;
}

.project-gallery[data-arrow="left"] {
  cursor: w-resize;
}

.project-gallery[data-arrow="right"] {
  cursor: e-resize;
}


.gallery-track {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  display: flex;
  transform: translateX(-100%);
}

.gallery-track.no-transition {
  transition: none !important;
}

.project-gallery.is-animating .gallery-track {
  transition: transform .6s cubic-bezier(.65, 0, .35, 1);
}

.gallery-panel {
  width: 100vw;
  height: 100%;
  flex: 0 0 100vw;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6.5rem 3vw 3rem;
}

.gallery-panel img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.gallery-panel video {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: 100%;
  object-fit: contain;
}

/* slide 0 — the related row: the project you're
   leaving stays large and centered behind it, like a
   held frame, while the other projects float in front
   as a staggered shelf of thumbnails — tall / short /
   short / short / tall — held well clear of the screen
   edges, near the bottom of the frame. */
.gallery-panel--related {
  position: relative;
  display: block;
  padding: 0;
}

.related-anchor {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6.5rem 3vw 3rem;
}

.related-anchor img,
.related-anchor video {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  /* this is the project you're leaving — it fades back to
     make room for what's next, which is why only this image
     (never the related thumbnails) loses opacity here. */
  opacity: .4;
}

.related-rail {
  /* sized to its own content (capped at max-width) rather than
     stretched to fill it — a stretched box needs
     justify-content to visually center a shorter row, and that
     breaks the scroll starting position the moment the row is
     wider than the box (the browser starts scrolled to the
     middle of the overflow instead of at the first item). */
  position: absolute;
  left: 50%;
  bottom: 30vh;
  transform: translateX(-50%);
  z-index: 2;
  display: flex;
  align-items: flex-end;
  gap: .6rem;
  /* 5 fixed-width columns + 4 gaps add up to a known total,
     so this only needs to be a safety ceiling, never the
     thing actually shaping the row. */
  max-width: 92vw;
  /* the row's own height comes from its tallest card, not a
     forced rail height — each card keeps its own picture's
     real proportions instead of being cropped to fit one. */
  height: auto;
  max-height: 46vh;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}

.related-rail::-webkit-scrollbar {
  display: none;
}

.related-item {
  flex: 0 0 auto;
  display: block;
  cursor: pointer !important;
  /* held back until JS reveals it — each one arrives in turn
     rather than the whole row popping in at once. */
  opacity: 0;
  transform: translateY(18px);
}

.related-item.is-in {
  opacity: 1;
  transform: translateY(0);
  transition: opacity .55s var(--ease), transform .55s var(--ease);
}

.related-item:nth-child(1).is-in { transition-delay: 0ms; }
.related-item:nth-child(2).is-in { transition-delay: 70ms; }
.related-item:nth-child(3).is-in { transition-delay: 140ms; }
.related-item:nth-child(4).is-in { transition-delay: 210ms; }
.related-item:nth-child(5).is-in { transition-delay: 280ms; }

.related-thumb {
  /* fixed, even column width — like the reference, height is
     whatever each picture's own aspect ratio gives at that
     width, which is what actually produces the taller/shorter
     rhythm across the row (not a forced crop). */
  width: 16vw;
  max-height: 46vh;
  overflow: hidden;
  background: #ececec;
}

.related-thumb img,
.related-thumb video {
  display: block;
  width: 100%;
  height: auto;
  max-height: 46vh;
  object-fit: cover;
  opacity: 1;
}

.related-label {
  display: inline-block;
  margin-top: .6rem;
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--color-fg);
  background: var(--color-bg);
  padding: .2em .4em;
  white-space: nowrap;
}

.related-label .n {
  font-variant-numeric: tabular-nums;
  margin-right: .5em;
}

/* the project you're on: title, position, and the
   placeholder credits, all held in one vertical column
   pinned to the right edge — same type and color as the
   rest of the page, just stacked instead of run in a row. */
.project-info-rail {
  position: fixed;
  top: 8.5rem;
  right: var(--pad);
  bottom: 4.5rem;
  z-index: 6;
  width: min(19vw, 220px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-end;
  gap: 2rem;
  text-align: right;
  pointer-events: none;
  opacity: 1;
  transition: opacity .35s var(--ease);
}

.project-info-rail.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.project-info-rail > * {
  pointer-events: auto;
}

.project-info-head {
  display: flex;
  flex-direction: column;
  gap: .35em;
}

.project-info-title {
  font-size: 1rem;
  text-transform: uppercase;
  letter-spacing: .02em;
}

.project-info-count {
  font-variant-numeric: tabular-nums;
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  opacity: .55;
}

.project-info-credits {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  margin: 0;
}

.project-info-credits dt {
  font-size: .65rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  opacity: .5;
}

.project-info-credits dd {
  margin: .3em 0 0;
  font-size: .9rem;
  text-transform: uppercase;
}

/* exception: a project's own flowing bio/description text (used in
   place of a normal short credit value, e.g. instead of Client) stays
   in normal case for readability instead of shouting a whole paragraph */
.project-info-credits dd.credit-freetext {
  text-transform: none;
}

@media (max-width: 599px) {
  .gallery-panel {
    padding: 5.5rem 4vw 4.5rem;
  }

  .related-anchor {
    padding: 5.5rem 4vw 4.5rem;
  }

  .related-rail {
    bottom: 17vh;
    max-height: 28vh;
    max-width: 94vw;
    gap: .5rem;
  }

  .related-thumb {
    width: 38vw;
    max-height: 28vh;
  }

  .related-thumb img,
  .related-thumb video {
    max-height: 28vh;
  }

  .project-info-rail {
    top: 6rem;
    bottom: auto;
    right: var(--pad);
    width: 48vw;
    justify-content: flex-start;
    gap: 1.25rem;
  }

  .project-info-title {
    font-size: .85rem;
  }
}

/* ---------------------------------------------
   Home intro — sequence (slow, deliberate pacing):
   1. "Loading" label, plain, small.
   2. swaps to "[ ... ]" — the brackets appear
      immediately (sharp), the wordmark text starts
      as random scrambling letters and resolves,
      left to right, into "OBRA STUDIO™" (the
      trailing mark itself cycles through similar
      marks — © ℗ ℠ ® — before locking to ™; see
      js/intro.js for the mechanism/timing).
   3. holds, then the word + brackets slowly spread
      apart (letter-spacing + gap widen).
   4. they slowly recombine — and as they do, the
      word's text fades out, leaving only the
      brackets behind, closed almost touching.
   5. the whole loader fades away to reveal the
      background video already playing underneath.
   Stays in the DOM afterwards (inert, opacity 0)
   rather than being removed. Once per browser
   session — see js/intro.js.
--------------------------------------------- */

.intro-loader {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-intro-bg);
  opacity: 1;
  transition: opacity .7s ease;
  user-select: none;
}

.intro-stage {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-intro-fg);
  font-family: Arial, Helvetica, sans-serif;
  font-size: clamp(.68rem, 1.35vw, .88rem);
  font-weight: 500;
}

.intro-label {
  position: absolute;
  font-size: .95em;
  letter-spacing: .12em;
  text-transform: uppercase;
  opacity: 1;
  transition: opacity .3s ease;
}

.intro-label.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.intro-word {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  letter-spacing: .18em;
  text-transform: uppercase;
  opacity: 0;
  transition: gap 1.1s cubic-bezier(.77, 0, .18, 1), opacity .3s ease;
}

.intro-word.is-visible {
  opacity: 1;
}

.intro-word.is-spread {
  gap: 46px;
}

.intro-word.is-closed {
  gap: 3px;
}

.intro-text {
  display: inline-block;
  letter-spacing: .18em;
  opacity: 1;
  transition:
    letter-spacing 1.1s cubic-bezier(.77, 0, .18, 1),
    opacity 1s ease,
    font-size 1.1s cubic-bezier(.77, 0, .18, 1);
}

.intro-word.is-spread .intro-text {
  letter-spacing: .4em;
}

/* higher specificity than ".intro-word.is-visible .intro-text"
   (which pins opacity:1/filter:blur(0)) so this actually wins once
   the brackets are closing back in. font-size collapses to 0 too —
   opacity alone doesn't remove the text from the flex layout, so
   without this the (invisible) text would keep holding the
   brackets apart instead of letting them close in together. */
.intro-word.is-visible .intro-text.is-fading {
  opacity: 0;
  font-size: 0;
}

.intro-bracket {
  display: inline-block;
}

/* returning visitor this session — data-intro="skip" is set on
   <html> synchronously before paint (see index.html), so the
   loader is hidden from the very first frame, no flash. */
html[data-intro="skip"] .intro-loader {
  display: none;
}

.intro-loader.hidden {
  opacity: 0;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .intro-loader {
    transition: opacity .3s linear;
  }
  .intro-word,
  .intro-word.is-spread,
  .intro-word.is-closed,
  .intro-text,
  .intro-word.is-spread .intro-text {
    transition-duration: .01s;
  }
}
