/* Johan Rosell — yoga in Copenhagen
   Design tokens taken directly from the Paper file. */

:root {
  --ink:        #16231F;
  --body:       #4A5754;
  --stone:      #6B7975;
  --panel:      #F2EFE8;
  --white:      #FFFFFF;

  /* Placeholder tones for the gallery: one hue, three steps of lightness,
     so the cross-fade is visible before the real photographs land. */
  --photo-placeholder:       #D4977D;   /* hsl(18 50% 66%) */
  --photo-placeholder-light: #E1AE98;   /* hsl(18 55% 74%) */
  --photo-placeholder-deep:  #C48164;   /* hsl(18 45% 58%) */

  --radius-panel: 16px;
  --radius-pill:  999px;
  --radius-focus: 2px;

  /* Two weights of white over the photograph: soft for muted text, veil for
     the surfaces the gallery controls sit on. */
  --white-soft: rgba(255, 255, 255, 0.75);
  --white-veil: rgba(255, 255, 255, 0.2);

  /* Type scale. Sizes are redefined per breakpoint at the bottom of this
     file, so component rules reference a token and never a pixel value. */
  --text-display:     60px;
  --text-sub:         20px;
  --text-action:      17px;
  --text-slide:       21px;
  --text-slide-sub:   16px;
  --text-label:       13px;

  --weight-display: 300;
  --weight-body:    400;
  --weight-label:   500;

  --track-display: -0.045em;
  --track-action:  -0.01em;
  --track-label:    0.14em;
  --track-nav:      0.12em;

  --fade: 0.2s ease;
  --gutter: 16px;

  /* Spring fitted from the reference: exponential approach, tau ~436ms. */
  --spring: linear(0, 0.1895, 0.3445, 0.4713, 0.5751, 0.66, 0.7295, 0.7863, 0.8328, 0.8708, 0.902, 0.9274, 0.9483, 0.9653, 0.9793, 0.9907, 1);

  /* The gallery runs on a second, snappier spring. Sampled frame by frame
     off the reference's own carousel, which is not the curve its page
     transition uses: half the distance is covered in the first 190ms and
     the rest is a long settle. */
  --spring-slide: linear(0, 0.152, 0.354, 0.541, 0.69, 0.795, 0.868, 0.916, 0.945, 0.966, 0.979, 0.987, 0.99, 0.993, 0.994, 0.996, 1);
  --slide-move: 1490ms;
  --slide-dwell: 6000ms;
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--white);
  color: var(--ink);
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-synthesis: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* One small-caps label treatment, shared by the nav and the social link.
   These had drifted to different tracking (0.12em vs 0.14em) for no reason. */
.nav a,
.social {
  font-size: var(--text-label);
  font-weight: var(--weight-label);
  line-height: 16px;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  text-decoration: none;
}

/* ---------- Frame: the two columns ---------- */

.frame {
  display: flex;
  gap: var(--gutter);
  padding: var(--gutter);
  min-height: 100vh;
  min-height: 100dvh;
}

/* Ratio lifted from the design: 882 / 780 */
.panel { flex: 882 1 0; }
.column { flex: 780 1 0; }

/* ---------- Left panel ---------- */

/* The panel IS the card: the photo fills it, and the wordmark and social
   link sit over the image, so the nav falls directly beneath the card. */
.panel {
  position: relative;
  overflow: hidden;
  padding: 0;
  background: var(--panel);
  border-radius: var(--radius-panel);
}

.wordmark-link {
  position: absolute;
  top: 30px;
  left: 30px;
  z-index: 2;
  color: var(--white);
  line-height: 0;
}
.wordmark { display: block; width: 133px; height: 42px; }

/* On the home page this is the stack the gallery slides sit in. On the
   about page it is the card itself, so it keeps the image properties. */
.photo {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
  background-color: var(--photo-placeholder);
  background-position: 50%;
  background-size: cover;
  background-repeat: no-repeat;
}

/* About page only. The home card's bottom left belongs to the gallery
   caption, so the link lives on the one card that has the corner free. */
.social {
  position: absolute;
  left: 30px;
  bottom: 30px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--white-soft);
  transition: color var(--fade);
}
.social svg { flex-shrink: 0; }
.social:hover { color: var(--white); }
.social:focus-visible { outline: 2px solid var(--white); outline-offset: 4px; border-radius: var(--radius-focus); }

/* ---------- Gallery ---------- */
/* Slides are stacked, one on top of the other. The incoming slide fades up
   from nothing while it shrinks back to its resting size; the outgoing one
   stays fully opaque underneath and keeps growing, then cuts away once the
   new slide has covered it. Hence the zero-second opacity transition with a
   delay: the leaving slide holds, so there is never a gap to see through. */

.slide {
  position: absolute;
  inset: 0;
  background-color: var(--photo-placeholder);
  background-position: 50%;
  background-size: cover;
  background-repeat: no-repeat;
  opacity: 0;
  transform: scale(1.178);
  transition:
    opacity 0s linear var(--slide-move),
    transform var(--slide-move) var(--spring-slide);
}

.slide:nth-child(2) { background-color: var(--photo-placeholder-light); }
.slide:nth-child(3) { background-color: var(--photo-placeholder-deep); }

.slide.is-active {
  z-index: 1;
  opacity: 1;
  transform: scale(1);
  transition:
    opacity var(--slide-move) var(--spring-slide),
    transform var(--slide-move) var(--spring-slide);
}

.gallery {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 24px;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 48px;
  padding: 0 32px;
}

.gallery__row { display: flex; align-items: center; }

.gallery__caption {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.gallery__title,
.gallery__sub {
  margin: 0;
  line-height: 1.4;
}

.gallery__title {
  font-size: var(--text-slide);
  font-weight: var(--weight-label);
  color: var(--white);
}

/* The reference sets this at 70% white; reusing --white-soft keeps one
   muted-white token instead of two that differ by five hundredths. */
.gallery__sub {
  font-size: var(--text-slide-sub);
  font-weight: var(--weight-body);
  color: var(--white-soft);
}

.gallery__next {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--white-veil);
  color: var(--white);
  cursor: pointer;
  transition: background-color var(--fade);
}
.gallery__next:focus-visible { outline: 2px solid var(--white); outline-offset: 3px; }

.gallery__dots { display: flex; justify-content: center; gap: 8px; }

/* Each dot is the track; the active one fills white across the dwell, so
   the pagination doubles as a countdown to the next slide. */
.dot {
  position: relative;
  width: 28px;
  height: 6px;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: var(--white-veil);
  backdrop-filter: blur(10px);
}

.dot__fill {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 100%;
  background: var(--white);
}

@keyframes dot-fill {
  from { width: 0; }
  to   { width: 100%; }
}

.dot.is-active .dot__fill { animation: dot-fill var(--slide-dwell) linear forwards; }

/* Once the arrow is used, the carousel stops advancing on its own and the
   countdown has nothing left to count: the active dot simply reads full. */
.gallery[data-autoplay="off"] .dot.is-active .dot__fill {
  animation: none;
  width: 100%;
}

@media (prefers-reduced-motion: reduce) {
  .slide, .slide.is-active { transition: none; }
}

/* ---------- Right column ---------- */

.column {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: 48px;
  padding: 16px 32px 40px 48px;
}

/* Statement and button travel together, sitting in the free space below the
   nav rather than being pinned to the bottom of the column. */
.column__body {
  display: flex;
  flex-direction: column;
  gap: 40px;
  margin-block: auto;
}
/* No align-items here: it would shrink-wrap the headline to its longest
   line instead of letting it use the column. The button sizes itself,
   since .contact already aligns its own children to the start. */

/* Nav — the current page sits on a fully-rounded pill, matching the
   reference's 12px/6px padding. Every item carries the padding so the
   spacing stays even; only the current one is painted. The asymmetric
   horizontal padding compensates for the trailing letter-space, which
   would otherwise push the text right of centre inside the pill. */
.nav { display: flex; align-items: center; gap: 8px; }
.nav a {
  padding: 6px 11px 6px 13px;
  border-radius: var(--radius-pill);
  color: var(--stone);
  /* Tighter than --label-track. Deliberate: the nav sits on white, so it
     doesn't need the extra air the label over the photograph does. */
  letter-spacing: var(--track-nav);
  transition: color var(--fade), background-color var(--fade);
}
.nav a[aria-current="page"] {
  color: var(--ink);
  background-color: var(--panel);
}
.nav a:hover { color: var(--ink); }
.nav a:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; border-radius: var(--radius-focus); }

/* Statement */
.statement { display: flex; flex-direction: column; gap: 24px; }

.headline {
  margin: 0;
  font-size: var(--text-display);
  font-weight: var(--weight-display);
  line-height: 1.167;
  letter-spacing: var(--track-display);
  color: var(--ink);
}

.sub {
  margin: 0;
  font-size: var(--text-sub);
  font-weight: var(--weight-body);
  line-height: 1.6;
  color: var(--body);
}

/* ---------- Contact ---------- */

.contact { display: flex; flex-direction: column; align-items: flex-start; gap: 18px; }

.copy {
  display: inline-flex;
  align-items: center;
  border: 0;
  margin: 0;
  padding: 18px 30px;
  border-radius: var(--radius-pill);
  background: var(--ink);
  color: var(--white);
  font-family: inherit;
  font-size: var(--text-action);
  font-weight: var(--weight-label);
  line-height: 22px;
  letter-spacing: var(--track-action);
  cursor: pointer;
  transition: transform var(--fade), opacity var(--fade);
}
.copy:hover { opacity: 0.88; }
.copy:active { transform: scale(0.985); }
.copy:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

/* Both states share one grid cell, so the pill is always as wide as the
   widest label and never resizes when it flips to "Copied". */
.copy__stack { display: grid; }
.copy__state {
  grid-area: 1 / 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  transition: opacity var(--fade), visibility var(--fade);
}
.copy__state svg { flex-shrink: 0; }
.copy__state--copied { opacity: 0; visibility: hidden; }

.copy.is-copied .copy__state--default { opacity: 0; visibility: hidden; }
.copy.is-copied .copy__state--copied  { opacity: 1; visibility: visible; }

/* ---------- Responsive ---------- */
/* Boundaries measured on the reference: two columns at 1024 and above,
   stacked below; a second step below 810. Card height is a fixed value at
   each tier (463 / 216), not a share of the viewport. The gutter is the
   frame padding, so the card and the text below it share one left edge. */

@media (max-width: 1023px) {
  :root { --text-display: 50px; --text-sub: 18px; }

  .frame {
    flex-direction: column;
    min-height: 0;
    padding: 30px;
    gap: 0;
  }
  .panel  { flex: none; height: 463px; }
  .column { flex: none; gap: 48px; padding: 48px 0 40px; }
}

/* Below 810 the reference shrinks exactly two things in the gallery: the
   slide title, and the gap between the caption and the pagination. The
   padding, the arrow and the dots hold their size all the way down. */
@media (max-width: 809px) {
  :root {
    --text-display: 40px;
    --text-sub:     17px;
    --text-action:  16px;
    --text-slide:   16px;
  }

  .frame  { padding: 20px; }
  .panel  { height: 216px; }
  .column { gap: 40px; padding: 36px 0 28px; }
  .copy { padding: 16px 24px; }
  .wordmark { width: 108px; height: 34px; }
  .wordmark-link { top: 20px; left: 20px; }
  .social { left: 20px; bottom: 20px; }
  .gallery { gap: 24px; }
}

/* ---------- Page transitions ---------- */
/* Cross-document View Transitions. Chrome, Edge and Safari animate;
   Firefox simply navigates, which is a clean fallback.

   Deliberately few named groups. Every name is a separate snapshot that
   the browser rasterises and moves on its own clock, so naming each text
   element makes the type shimmer and splits one movement into several.
   Two things move: the photo, and the column content as a single block. */

@view-transition { navigation: auto; }

/* Held still. Named so they are lifted out of the moving groups. */
.panel         { view-transition-name: panel; }
.wordmark-link { view-transition-name: wordmark; }
.social        { view-transition-name: social; }
.nav           { view-transition-name: nav; }
/* Only on the home page. A name that exists on one side of a navigation
   gets no pairing, so the gallery simply appears and disappears with the
   page rather than trying to morph into something. */
.gallery       { view-transition-name: gallery; }

/* The two things that move. */
.photo  { view-transition-name: photo; }
.column { view-transition-name: content; }

/* Held still. The outgoing snapshot is removed rather than merely frozen:
   when an old and a new snapshot both exist, the browser composites them
   with plus-lighter, which ADDS the two images together. Identical artwork
   added to itself blows out bright — visible as a white flash on fine dark
   strokes like the wordmark and the Instagram link. */
::view-transition-old(root),
::view-transition-old(panel),
::view-transition-old(wordmark),
::view-transition-old(social),
::view-transition-old(nav),
::view-transition-old(gallery) {
  display: none;
}

::view-transition-new(root),
::view-transition-new(panel),
::view-transition-new(wordmark),
::view-transition-new(social),
::view-transition-new(nav),
::view-transition-new(gallery) {
  animation: none;
  mix-blend-mode: normal;
}

@keyframes vt-out {
  to { opacity: 0; }
}
@keyframes vt-rise {
  from { opacity: 0.001; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes vt-swell {
  from { opacity: 0.001; transform: scale(1.1); }
  to   { opacity: 1; transform: scale(1); }
}

/* Outgoing leaves; incoming starts at 100ms while it is still going, so
   something is on screen throughout and it reads as one movement. */
::view-transition-old(photo),
::view-transition-old(content) {
  animation: vt-out 160ms linear both;
}

::view-transition-old(photo),
::view-transition-old(content),
::view-transition-new(photo),
::view-transition-new(content) {
  mix-blend-mode: normal;
}

::view-transition-new(photo),
::view-transition-new(content) {
  animation-duration: 1400ms;
  animation-delay: 0ms;
  animation-timing-function: var(--spring);
  animation-fill-mode: both;
}

::view-transition-new(photo)   { animation-name: vt-swell; }
::view-transition-new(content) { animation-name: vt-rise; }

/* Container morph on the same clock as its contents. */
::view-transition-group(photo),
::view-transition-group(content) {
  animation-duration: 1400ms;
  animation-timing-function: var(--spring);
}

::view-transition-group(photo) {
  overflow: clip;
  border-radius: var(--radius-panel);
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

/* ---------- First-load animation ---------- */
/* Real elements rather than snapshots, so this one can afford to animate
   the pieces; kept on the same clock so load and navigation feel alike.
   The attribute is removed before first paint when a view transition is
   running, so the two never fight. */

:root[data-load] .photo,
:root[data-load] .statement,
:root[data-load] .contact {
  animation-duration: 1400ms;
  animation-delay: 40ms;
  animation-timing-function: var(--spring);
  animation-fill-mode: both;
}

:root[data-load] .photo     { animation-name: vt-swell; overflow: clip; }
:root[data-load] .statement { animation-name: vt-rise; }
:root[data-load] .contact   { animation-name: vt-rise; }

@media (prefers-reduced-motion: reduce) {
  :root[data-load] .photo,
  :root[data-load] .statement,
  :root[data-load] .contact { animation: none !important; }
}
