/* The hero parks its clock under a header of --header-h: room for the nav bar
   (floating, 56px at the top; it was the Framer menu's height here) */
@media (max-width: 1199.98px) { .film { --header-h: 124px; } }
@media (max-width: 809.98px) { .film { --header-h: 96px; } }

/* ------------------------------------------- hero -> Collection transition
   The hero's bottom edge sits on a warm white (~rgb(247 246 244), the mist
   bank) and the Collection starts on pure white. A spill straddles the seam:
   a wash from that warm white to #fff, and a faint copy of the mist drifting
   across it, so the bank thins out instead of stopping on a line. */
.film { z-index: 2; }                  /* above #main, so the spill can overlap it */

.mist--spill {
  --spill-above: min(2.5vh, 20px);     /* reaches just under the hero's buttons, never over them */
  --spill-solid: 12vh;                 /* mist at full (light) strength this far below the seam */
  top: calc(100% - var(--spill-above));
  bottom: auto;
  height: 34vh;
  z-index: 1;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--spill-above), #000 calc(var(--spill-above) + var(--spill-solid)), transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 var(--spill-above), #000 calc(var(--spill-above) + var(--spill-solid)), transparent 100%);
}

.mist--spill .mist__body::before { display: none; }   /* the hero's own legibility wash */

/* very light: the wash below stays at full strength so the seam matches exactly */
.mist--spill .mist__body { opacity: .55; }

/* the colour wash: warm white at the seam -> pure white below */
.mist--spill::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom,
    rgb(247, 246, 244) 0,
    rgb(247, 246, 244) var(--spill-above),
    rgba(255, 255, 255, 0) 100%);
}

/* the mist layers start from the seam, not from the spill's top */
.mist--spill .mist__layer { bottom: 0; height: 100%; }
.mist--spill .mist__layer--wisp { --o: .35; }

@media (max-aspect-ratio: 4/5) {
  .mist--spill { --spill-solid: 7vh; height: 22vh; }
}

/* site typography: text in Satoshi, the title and the clock in Space Grotesk */
.film { --sans: 'Satoshi', system-ui, -apple-system, sans-serif; }
.film .hero__title,
.film .chrono__value,
.film .chrono__unit { font-family: 'Space Grotesk', system-ui, sans-serif; }

/* Hero buttons: the corner of the brand symbol. In the kit the symbol is an
   80-unit square with 14-unit corners: radius = 17.5% of the height. The
   buttons' height is 2 x padding + 1.36em (padding as set in hero.css), so the
   same ratio holds at every size. */
.film .hero__cta {
  border-radius: calc(0.175 * (2 * clamp(10px, 1.6vh, 15px) + 1.36em));
}

/* Hero buttons, hover: the pill turns over, ink <-> paper (the hero's own variables, so the dark
   concept flips by itself). hero.css filled it with a sage green, which has no place in a black and
   white brand, and the cream label was lost on it. The solid pill swells into paper and takes an ink
   ring (on the white ground it would lose its shape); the outlined one swells into ink. The label
   that rolls in is drawn in the opposite colour of the fill, so no colour has to be timed against
   the circle: each label keeps its own for as long as it is seen. */
.film .fx-btn { --fx-fill: var(--paper); --fx-on: var(--ink); }
.film .fx-btn.btn-ghost { --fx-fill: var(--ink); --fx-on: var(--paper); }
.film .fx-btn::before { background: var(--fx-fill); }
.film .fx-btn .fx-roll > span + span { color: var(--fx-on); }
.film .fx-btn::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1.5px var(--ink);
  opacity: 0;
  pointer-events: none;
  transition: opacity .3s var(--ease);
}
@media (hover: hover) {
  .film .fx-btn:hover::after { opacity: 1; }
}
/* keyboard focus fills the pill with no roll: its one label takes the colour of the twin */
.film .fx-btn:focus-visible { color: var(--fx-on); }
.film .fx-btn:focus-visible::after { opacity: 1; }

/* ------------------------------------------- review: concept "Train", dark
   /?concept=train-dark keeps the photo's original blacks: the hero turns light
   on dark. Only the hero's own variables flip; the mist turns to smoke-dark.
   Remove with the concept switch once a choice is made. */
html.hs-train-dark .film {
  --paper: #0a0a0a;
  --ink: #f4f3ef;
  --ink-2: rgba(244, 243, 239, .74);
  --ink-3: rgba(244, 243, 239, .64);
  --line: rgba(244, 243, 239, .18);
  --cream: #0a0a0a;
}
html.hs-train-dark .film .mist { filter: invert(1); }          /* white mist -> dark smoke */
html.hs-train-dark .hero__cta.btn-ghost {
  background: rgba(10, 10, 10, .35);
  box-shadow: inset 0 0 0 1.5px rgba(244, 243, 239, .6);
}
/* tall screens: the footage is a band in the middle of a dark stage. Its edges
   dissolve into it */
@media (max-aspect-ratio: 4/5) {
  html.hs-train-dark .film__video,
  html.hs-train-dark .film__still {
    -webkit-mask-image: linear-gradient(to bottom,
      transparent calc((100svh - 56.25vw) * .44), #000 calc((100svh - 56.25vw) * .44 + 9vw),
      #000 calc((100svh - 56.25vw) * .44 + 47.25vw), transparent calc((100svh - 56.25vw) * .44 + 56.25vw));
            mask-image: linear-gradient(to bottom,
      transparent calc((100svh - 56.25vw) * .44), #000 calc((100svh - 56.25vw) * .44 + 9vw),
      #000 calc((100svh - 56.25vw) * .44 + 47.25vw), transparent calc((100svh - 56.25vw) * .44 + 56.25vw));
  }
}

/* ------------------------------------------- review: concept "Train"
   The train is filmed head-on: no slide from the right, the footage is in place
   from the first frame.
   !important beats the inline transform GSAP writes. */
html.hs-train .film__media { transform: none !important; animation: none; }

/* The steam that engulfs the frame as the train reaches the camera: switched on
   by the concept switch at 0.430 on the clock, so that at 0.500 the frozen frame
   is white steam (the film's last frames, under the train, are dark). A white
   veil with grey billows (fractal noise), opening from the train, then drifting
   slowly. Under the mist and the copy (DOM order). */
.film__steam { display: none; }
html.hs-train .film__steam {
  display: block;
  position: absolute;
  inset: -12%;
  z-index: 4;
  pointer-events: none;
  opacity: 0;
  transform: scale(.45);
  transform-origin: 64% 60%;
  transition: opacity .8s var(--ease), transform 1.4s var(--ease-expo);
  background: radial-gradient(ellipse at 64% 60%, #fff 0, rgba(255, 255, 255, .98) 35%, rgba(250, 250, 248, .96) 70%, rgba(246, 245, 242, .94) 100%);
}
html.hs-train .film__steam.is-on { opacity: 1; transform: none; }
.film__steam i {
  position: absolute;
  inset: 0;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1600' height='900' viewBox='0 0 1600 900' preserveAspectRatio='none'%3E%3Cfilter id='n' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.0032 0.0055' numOctaves='5' seed='11'/%3E%3CfeColorMatrix values='0 0 0 0 .5  0 0 0 0 .5  0 0 0 0 .49  0 0 0 1.5 -.62'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E") 50% 50% / 100% 100% no-repeat;
  opacity: .5;
  animation: hs-steam-drift 36s linear infinite alternate;
}
.film__steam i + i {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1600' height='900' viewBox='0 0 1600 900' preserveAspectRatio='none'%3E%3Cfilter id='n' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.006 0.009' numOctaves='4' seed='29'/%3E%3CfeColorMatrix values='0 0 0 0 .62  0 0 0 0 .62  0 0 0 0 .61  0 0 0 1.7 -.78'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: .42;
  animation-duration: 23s;
  animation-direction: alternate-reverse;
}
@keyframes hs-steam-drift { to { transform: translate3d(-5%, -3%, 0) scale(1.1); } }
.is-reduced .film__steam i { animation: none; }

/* The smoke that takes over from the film at the impact (0.500): the user's cloud, in WebGL2
   (assets/hero/halfsecond-smoke.js). Where the steam was: under the mist and the copy. The cursor is read on the window, so the hero's buttons stay clickable. */
.film__smoke { display: none; }
html.hs-train .film__smoke {
  display: block;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 4;
  pointer-events: none;
}
html.is-reduced .film__smoke { display: none; }
/* As the steam bursts, the film's last frames keep rushing at the camera and melt into it,
   instead of freezing on the impact (halfsecond-smoke.js adds .is-hit and puts the origin on
   the train). They fade to white under the steam, so the train never lingers as a dark shape
   where the steam is still thin (on phones it is a small shape in the middle of the screen). */
html.hs-train .film__video--intro.is-hit {
  transform: scale(1.22);
  filter: blur(8px) brightness(1.3);
  opacity: 0;
  transition: transform 1.1s cubic-bezier(.2, .75, .25, 1), filter .9s cubic-bezier(.2, .75, .25, 1),
              opacity .7s cubic-bezier(.4, 0, .6, 1) .1s;
}
/* Without WebGL2: the same cloud as a CSS layer, opening from the train. It thins to white at
   the top and into the mist at the bottom, like the film. */
html.hs-train .film__steam--cloud {
  inset: 0;
  transform-origin: 64% 50%;
  background:
    linear-gradient(to bottom, #fff 0, rgba(255, 255, 255, 0) 14%, rgba(255, 255, 255, 0) 70%, rgba(255, 255, 255, .8) 88%, #fff 97%),
    var(--hs-cloud) 50% 50% / cover no-repeat,
    #fff;
}
html.hs-train .film__steam--cloud i { display: none; }

/* ------------------------------------------- review: the train hero without the bank of mist
   The bank along the bottom of the frame (#mistLayer, the wisps over the copy, the spill below the
   seam) is off: the concept switch (plain "train" mode, no /?mist=on) sets hs-no-mist, and the film,
   its poster, its still (made with --no-bottom-veil) and the smoke run to the bottom edge instead of
   thinning into it. Remove with the switch once a choice is made. */
html.hs-no-mist .film .mist { display: none; }
html.hs-no-mist.hs-train .film__steam--cloud {
  background:
    linear-gradient(to bottom, #fff 0, rgba(255, 255, 255, 0) 14%),
    var(--hs-cloud) 50% 50% / cover no-repeat,
    #fff;
}
/* The copy now sits on the footage and on the cloud, not on the bank: its two greys go a notch darker,
   so that the small lines keep AA over grey cloud (hero.css's values are 5:1 on white). */
html.hs-no-mist.hs-train .film { --ink-2: rgba(20, 19, 15, .86); --ink-3: rgba(20, 19, 15, .8); }
/* The still (reduced motion) keeps the rails under the clock, the two small lines and the end of the
   lede for good (in the film they only cross them for its 1.6 s, and the smoke then covers them): a
   hair of paper around the letters keeps them readable. It only shows where letters meet rails. */
html.is-reduced.hs-no-mist.hs-train .hero__lede,
html.is-reduced.hs-no-mist.hs-train .chrono__micro,
html.is-reduced.hs-no-mist.hs-train .chrono__unit {
  text-shadow: 0 0 1px #fff, 0 0 3px #fff, 0 0 6px rgba(255, 255, 255, .85),
               0 1px 2px rgba(20, 19, 15, .12), 0 6px 16px rgba(20, 19, 15, .16);
}
html.is-reduced.hs-no-mist.hs-train .chrono__value {
  text-shadow: 0 0 2px #fff, 0 0 5px #fff, 0 0 10px rgba(255, 255, 255, .8),
               0 2px 3px rgba(20, 19, 15, .10), 0 14px 30px rgba(20, 19, 15, .22), 0 30px 60px rgba(20, 19, 15, .12);
}
/* Tall screens: the footage is a band in the middle of the page (hero.css: contain at 50% 44%). Its top
   and its left edge melt into the white inside the film itself; the bottom edge, which the bank used to
   cover, would end on a ruler-straight line. A short dissolve (the last 10vw of the band), nothing more. */
@media (max-aspect-ratio: 4/5) {
  html.hs-no-mist.hs-train .film__video,
  html.hs-no-mist.hs-train .film__still {
    -webkit-mask-image: linear-gradient(to bottom,
      #000 calc((100svh - 56.25vw) * .44 + 46.25vw), transparent calc((100svh - 56.25vw) * .44 + 56.25vw));
            mask-image: linear-gradient(to bottom,
      #000 calc((100svh - 56.25vw) * .44 + 46.25vw), transparent calc((100svh - 56.25vw) * .44 + 56.25vw));
  }
}

/* ------------------------------------------- hero -> Collection: the smoke goes into the night
   halfsecond-transition.js sets html.hs-runway (the train with its film, with motion; reduced
   motion and the still concepts keep the one-screen hero). The stage stays pinned for a runway
   after the hero's first screen. Over it the copy goes (--hs-out), the ground goes black under the
   cloud (--hs-night: .film__night; the cloud itself is redrawn white on black by
   halfsecond-smoke.js), the smoke thins away (--hs-gone, in the shader) as the Collection's title
   comes in, in the middle (--hs-word), and the stage clears (--hs-reveal) onto the Collection,
   which has slid in under it. Then the Collection's first card comes up to the title, and the stage
   lets go as it reaches it. The phases are in halfsecond-transition.js, in screens scrolled. */
html.hs-runway .film { --hs-runway: 170svh; height: calc(100svh + var(--hs-runway)); background: transparent; }

/* The Collection's title, on the stage: alone in the middle of the black from the moment the smoke
   goes; it stays there while the first card comes up to it, then goes up with the stage, pushed by
   the card (see --hs-drop). Framer's own title, in the Collection, would be a second one following
   it: it stays (read out, and in its place), unseen.
   The same face and sizes as Framer's (its text style: 8rem, 5.8rem, 4.8rem; on phones it shrinks
   to fit the column, the screen less 72px, which "Collection" fills at 4.651 times its size). */
html.hs-runway { --hs-coll-size: 8rem; --hs-coll-photo: 100vh; }
@media (max-width: 1199.98px) { html.hs-runway { --hs-coll-size: 5.8rem; --hs-coll-photo: 60vh; } }
@media (max-width: 809.98px)  { html.hs-runway { --hs-coll-size: min(4.8rem, (100vw - 72px) / 4.651); } }
.film__next { display: none; }
html.hs-runway .film__next {
  display: flex;
  align-items: center;
  justify-content: center;
  position: absolute;
  inset: 0;
  z-index: 4;                          /* over the night; the smoke, after it in the page, passes over it */
  margin: 0;
  font-family: 'Space Grotesk', system-ui, sans-serif;
  font-size: var(--hs-coll-size);
  font-weight: 600;
  line-height: .9;
  letter-spacing: -.04em;
  color: #fafafa;
  white-space: nowrap;
  opacity: var(--hs-word, 0);
  transform: translate3d(0, calc((1 - var(--hs-word, 0)) * 4vh), 0);
  pointer-events: none;
}
html.hs-runway #proejcts [data-framer-name="Projects"] { opacity: 0 !important; }

/* The Collection opens on a black screen (Framer's sticky "Backphoto"), which slides in under the
   pinned stage (the hero is above #main): when the stage clears there is only black under it. Its
   cards come under that photo less 320px (the Collection's offset, halfsecond.css) plus Framer's
   title (.9 of its size). --hs-drop places the Collection so that the first card's top is
   --hs-push-gap under the title (its lower half: .45 of its size, under the middle of the stage)
   when the stage lets go: the card comes up to the title while the stage is pinned, then both go
   up together, the card pushing the title, all with the page's own scroll. The runway (170svh, the
   stage lets go at 1.7 screens) keeps that card below the screen until the stage has cleared (1.3
   screens): from 1.32 on every screen size. If the gap, the title or the runway change, check that
   the card still comes in after 1.3. Between the stage and the Collection the body is black. */
html.hs-runway { --hs-push-gap: clamp(40px, 8svh, 88px); }
html.hs-runway { --hs-drop: calc(100vh - 50svh - var(--hs-coll-photo) + 320px - .45 * var(--hs-coll-size) + var(--hs-push-gap)); }
html.hs-runway .film { margin-bottom: calc(var(--hs-drop) - 100vh); }
/* under the stage there is only black (the body, then the Collection): the paper can go with the light.
   Every layer also goes with the clearing (--hs-reveal), which follows the scroll alone: after a jump
   (the "See the work" link, a reload half way down) the night and the smoke are still catching up,
   and nothing may hide the Collection meanwhile. */
html.hs-runway .film__stage { background-color: rgb(255 255 255 / calc((1 - var(--hs-night, 0)) * (1 - var(--hs-reveal, 0)))); }
html.hs-runway .film__smoke { opacity: calc(1 - var(--hs-reveal, 0)); }
.film__night { display: none; }
html.hs-runway .film__night {
  display: block;
  position: absolute;
  inset: 0;
  z-index: 3;                          /* over the film (DOM order), under the smoke and the copy */
  background: #000;
  opacity: calc(var(--hs-night, 0) * (1 - var(--hs-reveal, 0)));
  pointer-events: none;
}
/* the film's last frame is already gone into the steam (.is-hit); without WebGL2 it is not */
html.hs-runway .film__media { opacity: calc(1 - var(--hs-reveal, 0)); }
html.hs-runway.hs-stage-clear .film { pointer-events: none; }
/* once the stage has cleared (hs-stage-gone, halfsecond-transition.js: --hs-reveal at 1), its
   layers are all at opacity 0: out of the way altogether, so the browser stops drawing them over
   the Collection while the stage stays on screen (the word push). The smoke also stops its own
   loop then (halfsecond-smoke.js). The title (.film__next) stays. */
html.hs-runway.hs-stage-gone .film__media,
html.hs-runway.hs-stage-gone .film__night,
html.hs-runway.hs-stage-gone .film__steam,
html.hs-runway.hs-stage-gone .film__smoke,
html.hs-runway.hs-stage-gone .film .mist { visibility: hidden; }
html.hs-runway .film .hero {
  opacity: calc(1 - var(--hs-out, 0));
  transform: translate3d(0, calc(var(--hs-out, 0) * -4vh), 0);
}
html.hs-runway.hs-copy-out .film .hero { visibility: hidden; }
/* without WebGL2 the cloud is a CSS layer: it sinks into the black */
html.hs-runway .film__steam.is-on { opacity: calc((1 - var(--hs-night, 0)) * (1 - var(--hs-reveal, 0))); }
/* /?mist=on: the bank goes with the light */
html.hs-runway .film .mist { opacity: calc((1 - var(--hs-night, 0)) * (1 - var(--hs-reveal, 0))); }

/* The Collection rises out of that black: in the train concept its ink photo (white smoke in black
   water) is covered with black, so the smoke that just thinned away does not come back. A layer
   over the photo rather than its opacity: Framer animates that photo's appearance itself. */
html.hs-train #proejcts [data-framer-name="Backphoto (place your image)"]::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background: #000;
  pointer-events: none;
}

/* ------------------------------------------- the home's sections (the user's list, 2026-10-13)
   Hero, Collection, Services, Process, Studio, Proof, FAQ, footer: nothing else. The template's
   other sections (About, its white Services, Testimonials, Dashboard, Team, Insights) are out of
   the page. Not deleted: Framer renders them (React would put back any markup taken from the
   server's HTML), so they stay in the files, layouts ready to reuse, and are simply not shown.
   display:none, so they take no room and their images are not fetched (lazy ones). "What wins"
   went to the Insights (insights/what-wins-the-half-second/). */
#about, #services, #tetimonials, #dashboard, #team, #insights { display: none !important; }
/* The Projects pages are gone (2026-10-14): the Collection's "All Projects" button goes with them, and
   its three demo cards no longer lead anywhere (halfsecond-nav.js keeps their link from loading a page
   that is not there), so they stop looking like links. */
#proejcts [data-framer-name="CTA block"] { display: none !important; }
#proejcts a[href*="projects/"] { cursor: default; }
/* and the template's founder quote at the top of the footer ("Michael Rosenberg"), which slid into
   view over the FAQ as the footer comes up: its place stays, the footer's smoke shows there */
#main footer [data-framer-name="Message and description"] { visibility: hidden !important; }

/* ------------------------------------------- the template's Pricing, end of the Collection
   Replaced by our own "Services" section (index.html, assets/site/halfsecond-services.*), which has
   the three offers of the copy where the template had room for two. */
#proejcts [data-framer-name="Pricing"] { display: none !important; }

/* The clock, on the title's line (2026-10-17): it stays at the right, as it was, but rises from the
   hero's bottom line to the line of "That's all you get." (the title's last line). The lift is the
   distance between the hero's bottom and the title's bottom, measured by halfsecond-clock.js
   (--clock-lift on .hero); without script it stays at the bottom as before. The phone keeps the clock
   at the top. */
@media (min-width: 821px) {
  html.hs-train .hero__chrono { margin-bottom: var(--clock-lift, 0px); }
}
