/* ==========================================================================
   Foundryline — motion, third pass
   --------------------------------------------------------------------------
   WHAT WAS WRONG WITH THE FIRST TWO PASSES

   Pass one removed the things that cost frames (a full-viewport blur, an
   animated filter, box-shadow animation, `transition: all`). That made the
   site smooth and also made it feel mechanical: every block faded up 14px over
   the same duration with the same curve. Nothing had weight, nothing had
   hierarchy, and the page read as a list of identical boxes appearing.

   This pass keeps every performance rule and adds the thing that was missing:
   variation. Different elements now move differently and at different speeds,
   because that is what makes motion feel designed rather than applied.

   THE THREE LAYERS

     1. Entrance, on load — the header settles, the hero lifts and scales very
        slightly, and the headline's underline draws itself. Finished by 0.9s.

     2. Scroll, as you travel — blocks rise further and slower than they did
        (28px over 0.75s), stagger their children, and the images settle with a
        touch of scale. Once only: nothing re-animates on the way back up.

     3. Ambient, small and constant — the availability dot pulses, the reading
        progress bar tracks. Nothing else. Two infinite animations in total.

   EVERYTHING ANIMATES OPACITY AND TRANSFORM ONLY.
   Both composite on the GPU. There is no filter, no box-shadow, no layout
   property, and no `will-change` left switched on after an animation ends.
   All of it is disabled under prefers-reduced-motion and on a device
   main.js has graded as slow.
   ========================================================================== */

/* ==========================================================================
   1. Entrance
   ========================================================================== */

/* the header drops a little further and lands softly */
@keyframes m-head-settle {
  from { opacity: 0; transform: translate3d(0, -10px, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

.site-head {
  animation: m-head-settle 0.52s cubic-bezier(0.16, 0.84, 0.28, 1) both;
}

/* the page itself lifts and un-scales a hair. The scale is 0.994 - small
   enough that nobody consciously sees it, large enough that the page feels
   like it arrives rather than switches on. */
@keyframes m-page-in {
  from { opacity: 0; transform: translate3d(0, 16px, 0) scale(0.994); }
  to   { opacity: 1; transform: translate3d(0, 0, 0) scale(1); }
}

.wrap {
  animation: m-page-in 0.66s cubic-bezier(0.16, 0.84, 0.28, 1) both;
}

/* the hero goes first and takes slightly longer, so it has weight */
.hero .wrap {
  animation-duration: 0.78s;
  animation-delay: 0.06s;
}

/* the small print under the hero arrives last */
.hero .price-line,
.hero .mock-cap {
  animation: m-page-in 0.6s cubic-bezier(0.16, 0.84, 0.28, 1) both;
  animation-delay: 0.34s;
}

/* the underline draws itself, later and slower than before so the eye has
   already read the sentence by the time it finishes */
.hl::after {
  animation: hl-in 0.82s 0.46s cubic-bezier(0.16, 0.84, 0.28, 1) both;
}

/* the browser mock-up settles rather than pops */
.mock {
  animation: m-mock-in 0.86s 0.16s cubic-bezier(0.16, 0.84, 0.28, 1) both;
}

@keyframes m-mock-in {
  from { opacity: 0; transform: translate3d(0, 22px, 0) scale(0.985); }
  to   { opacity: 1; transform: translate3d(0, 0, 0) scale(1); }
}

/* the skeleton bars inside it fill in one after another, which reads as the
   page loading - which is the whole point of the illustration.
   These delays are deliberately short. A first version ran them out to 0.8s,
   which meant the illustration was still filling itself in while the visitor
   had already started reading the paragraph below it. Everything here is
   finished inside 0.65s. */
.mock .sk {
  animation: m-sk-in 0.4s cubic-bezier(0.16, 0.84, 0.28, 1) both;
}
.mock .sk-hero      { animation-delay: 0.30s; }
.mock .sk-row .sk:nth-child(1) { animation-delay: 0.36s; }
.mock .sk-row .sk:nth-child(2) { animation-delay: 0.41s; }
.mock .sk-row .sk:nth-child(3) { animation-delay: 0.46s; }
.mock .w70          { animation-delay: 0.50s; }
.mock .w45          { animation-delay: 0.54s; }
.mock .sk-btn       { animation-delay: 0.58s; }

@keyframes m-sk-in {
  from { opacity: 0; transform: translate3d(0, 6px, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

/* ==========================================================================
   2. Scroll
   ========================================================================== */

/* Blocks travel 28px over 0.75s instead of 20px over 0.5s. Slower entrances
   read as more expensive; faster ones read as twitchy. The curve is a soft
   ease-out that decelerates hard at the end, so nothing drifts. */
.reveal {
  opacity: 0;
  transform: translate3d(0, 28px, 0);
  transition: opacity 0.75s cubic-bezier(0.16, 0.84, 0.28, 1),
              transform 0.75s cubic-bezier(0.16, 0.84, 0.28, 1);
}

.reveal.in {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* A block that contains a grid staggers its direct children. The delay rises
   in steps rather than evenly, which groups them instead of marching them. */
.stagger > * {
  opacity: 0;
  transform: translate3d(0, 22px, 0);
}

.stagger.in > * {
  animation: m-item-in 0.62s cubic-bezier(0.16, 0.84, 0.28, 1) both;
}

@keyframes m-item-in {
  from { opacity: 0; transform: translate3d(0, 22px, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

.stagger.in > *:nth-child(1) { animation-delay: 0.05s; }
.stagger.in > *:nth-child(2) { animation-delay: 0.13s; }
.stagger.in > *:nth-child(3) { animation-delay: 0.21s; }
.stagger.in > *:nth-child(4) { animation-delay: 0.28s; }
.stagger.in > *:nth-child(5) { animation-delay: 0.35s; }
.stagger.in > *:nth-child(6) { animation-delay: 0.41s; }
.stagger.in > *:nth-child(n+7) { animation-delay: 0.46s; }

/* Cards settle inward. A scale of 0.99 that resolves to 1 gives a card
   somewhere to arrive from without looking like it zoomed. */
.card,
.tpl-card,
.price-card,
.tile {
  transition: transform 0.3s cubic-bezier(0.16, 0.84, 0.28, 1),
              border-color 0.3s var(--ease-out),
              box-shadow 0.3s var(--ease-out);
}

.card:hover,
.tpl-card:hover,
.price-card:hover,
.tile:hover {
  transform: translate3d(0, -5px, 0);
}

/* but a card inside a scroll stagger must not also animate on hover while it
   is still arriving, so the two are kept on separate properties */
.stagger.in > .card { animation-fill-mode: both; }

/* The phone mock-up drifts a fraction as you scroll past. 14px over the whole
   viewport, transform only, one shared handler in main.js. Deliberately tiny:
   parallax that is visible is parallax that is annoying. */
.mock {
  transition: transform 0.1s linear;
  will-change: auto;
}

.mock.drifting { will-change: transform; }

/* ==========================================================================
   3. Ambient
   ========================================================================== */

/* the availability dot. Eight pixels. The only thing on the site that runs
   forever apart from the progress bar. */
@keyframes m-dot {
  0%   { transform: scale(1);    opacity: 1;   }
  60%  { transform: scale(1.55); opacity: 0.5; }
  100% { transform: scale(1);    opacity: 1;   }
}

.dot {
  animation: m-dot 2.8s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}

/* the reading progress bar: transform only, already throttled to one rAF */
.scroll-bar {
  transform-origin: 0 50%;
}

/* ==========================================================================
   4. Buttons and links
   ========================================================================== */

.btn {
  transition: background-color 0.2s var(--ease-out),
              color 0.2s var(--ease-out),
              border-color 0.2s var(--ease-out),
              transform 0.2s cubic-bezier(0.16, 0.84, 0.28, 1);
}

.btn:hover { transform: translate3d(0, -2px, 0); }
.btn:active { transform: translate3d(0, 0, 0) scale(0.98); }

/* the nav underline grows from the left rather than fading */
.navlink {
  position: relative;
}

.navlink::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -3px;
  height: 1.5px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.26s cubic-bezier(0.16, 0.84, 0.28, 1);
}

.navlink:hover::after,
.navlink[aria-current="page"]::after { transform: scaleX(1); }

/* ==========================================================================
   5. When to do none of this
   ========================================================================== */

/* ==========================================================================
   6. Type and rhythm in the hero
   --------------------------------------------------------------------------
   THE ODD LINE BREAKS

   `.hl` carried `white-space: nowrap`, which forbade the highlighted phrase
   from ever breaking across lines. A two-word highlight coped; the Bulgarian
   headline highlights five words, so the browser had to wrap the rest of the
   sentence around an unbreakable block and produced ragged, uneven lines with
   a short orphaned word underneath. That is what looked wrong.

   Allowing it to wrap needs the underline to survive the break, which is what
   `box-decoration-break: clone` does: the background is drawn on each line
   fragment instead of only the first.
   ========================================================================== */

.hl {
  white-space: normal;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

/* The heading breathes more. Line height 1.04 is tight enough to look
   deliberate and loose enough that descenders do not touch the line below;
   1.0 makes Cyrillic collide because its ascenders are taller. */
h1 {
  line-height: 1.06;
  letter-spacing: -0.032em;
  text-wrap: balance;
  margin-bottom: 0.42em;
}

@supports not (text-wrap: balance) {
  h1 { max-width: 20ch; }
}

/* the lede sits clearly apart from the heading rather than crowding it */
.lede {
  line-height: 1.62;
  max-width: 58ch;
}

/* the availability pill and the price line get room to be separate things */
.avail { margin-bottom: 26px; }

.price-line {
  margin-top: 26px;
  gap: 12px;
}

.price-line b {
  font-size: 1.5rem;
  letter-spacing: -0.03em;
}

.price-line span {
  max-width: 46ch;
  line-height: 1.5;
}

/* the caption under the mock-up is closer to the thing it describes, so it
   reads as a caption rather than floating text */
.mock-cap {
  margin-top: 10px;
  line-height: 1.5;
}

/* Windows never breaks a line before the last word of a heading, but it does
   leave one word alone on the final line. balance above handles the general
   case; this guard handles the short headings it cannot help. */
h1, h2 { orphans: 2; widows: 2; }

/* On a phone the hero stacks, and the mock-up is the first thing to go: it is
   decoration and the headline is the message. */
@media (max-width: 860px) {
  .hero { padding: 46px 0 44px; }
  .hero .mock-cap { display: none; }
  h1 { letter-spacing: -0.026em; }
}

@media (prefers-reduced-motion: reduce) {
  .site-head,
  .wrap,
  .hero .wrap,
  .hero .price-line,
  .hero .mock-cap,
  .hl::after,
  .mock,
  .mock .sk,
  .stagger.in > *,
  .dot {
    animation: none !important;
    animation-duration: 0.001ms !important;
    animation-delay: 0ms !important;
  }
  .reveal,
  .stagger > * {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .site-head.at-top { backdrop-filter: none; }
  .card:hover,
  .tpl-card:hover,
  .price-card:hover,
  .tile:hover,
  .btn:hover { transform: none; }
  .navlink::after { transition: none; }
}

/* A device main.js has graded as slow gets the finished state immediately.
   A stuttering animation reads as slower than no animation. */
.light-motion .site-head,
.light-motion .wrap,
.light-motion .hero .wrap,
.light-motion .hero .price-line,
.light-motion .hero .mock-cap,
.light-motion .hl::after,
.light-motion .mock,
.light-motion .mock .sk,
.light-motion .stagger.in > *,
.light-motion .dot,
.light-motion body {
  animation: none !important;
  opacity: 1 !important;
  transform: none !important;
  transition: none !important;
}

.light-motion .site-head.at-top {
  backdrop-filter: none !important;
  background: var(--paper) !important;
}

.light-motion .reveal,
.light-motion .stagger > * {
  opacity: 1 !important;
  transform: none !important;
}

@media print {
  .site-head,
  .wrap,
  .reveal,
  .stagger > *,
  .hl::after,
  .mock,
  .mock .sk {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}
