/* ==========================================================================
   Locksmiths Canada — motion layer

   Loads after lc-character.css and only adds. Delete this file (and
   lc-motion.php) and the site is exactly as still as it was before.

   The rule this layer follows: nothing moves for decoration. Every movement
   either shows where something came from, confirms a press, or puts the eye
   on a number that matters. A printed price card does not bounce, so nothing
   here bounces — short travel, decelerating curve, everything finished
   inside 400ms.

   Everything is gated behind .lc-mo on <html>, which lc-motion.php adds only
   when the browser supports it and the reader has not asked for reduced
   motion. No class, no animation, nothing hidden. That is the safety net.

   --------------------------------------------------------------------------
   EDIT HERE FIRST

   Change a number, save, reload. Nothing below this block needs touching.
   -------------------------------------------------------------------------- */

:root{

  /* Master speed multiplier. 1 is as shipped. .6 makes everything brisker,
     1.6 makes it languid. Sensible: .6 to 1.6. */
  --mo:1;

  /* How far anything travels as it arrives. Past 16px it reads as a
     slideshow rather than a page settling. Sensible: 0 to 14px. */
  --mo-rise:9px;

  /* Arrival curve. Decelerating, no overshoot — deliberately not springy. */
  --mo-ease:cubic-bezier(.22,.7,.24,1);

  /* Gap between one card arriving and the next in the same row.
     0 makes a whole row land at once. Sensible: 0 to 90ms. */
  --mo-step:52ms;

}

/* --- 1. Arrival ------------------------------------------------------------
   Two keyframes carry almost the whole layer.

   lc-rise moves with `translate`, NOT `transform`. That matters: the base
   system already puts transforms on .sv and .fee for hover and tilt, and a
   filled animation on `transform` would win the cascade and kill them.
   `translate` is its own property, so the two never meet. */
@keyframes lc-rise{from{opacity:0;translate:0 var(--mo-rise)}to{opacity:1;translate:0 0}}
@keyframes lc-fade{from{opacity:0}to{opacity:1}}

/* --- 2. The hero lands -----------------------------------------------------
   Read in the order the page is meant to be read: eyebrow, headline, the
   paragraph that makes the promise, the box that acts on it, the counts.
   The fee card comes last because it is the answer, not the question. */
.lc-mo .hero .eyb,
.lc-mo .hero h1,
.lc-mo .hero .lede,
.lc-mo .hero .find,
.lc-mo .hero .find__q,
.lc-mo .hero .stats,
.lc-mo .hero .fee{
  animation:lc-rise calc(430ms * var(--mo)) var(--mo-ease) both;
}
.lc-mo .hero .eyb    {animation-delay:calc(var(--mo-step) * 0)}
.lc-mo .hero h1      {animation-delay:calc(var(--mo-step) * 1)}
.lc-mo .hero .lede   {animation-delay:calc(var(--mo-step) * 2)}
.lc-mo .hero .find   {animation-delay:calc(var(--mo-step) * 3)}
.lc-mo .hero .find__q{animation-delay:calc(var(--mo-step) * 4)}
.lc-mo .hero .stats  {animation-delay:calc(var(--mo-step) * 5)}
.lc-mo .hero .fee    {animation-delay:calc(var(--mo-step) * 3)}

/* The fee card fills itself in, one line at a time, the way a counter clerk
   writes a slip. Starts after the card itself has arrived. */
.lc-mo .hero .fee .fr{animation:lc-fade calc(320ms * var(--mo)) var(--mo-ease) both}
.lc-mo .hero .fee .fr:nth-of-type(1){animation-delay:calc(330ms + var(--mo-step) * 0)}
.lc-mo .hero .fee .fr:nth-of-type(2){animation-delay:calc(330ms + var(--mo-step) * 1)}
.lc-mo .hero .fee .fr:nth-of-type(3){animation-delay:calc(330ms + var(--mo-step) * 2)}
.lc-mo .hero .fee .fr:nth-of-type(4){animation-delay:calc(330ms + var(--mo-step) * 3)}
.lc-mo .hero .fee .fr:nth-of-type(5){animation-delay:calc(330ms + var(--mo-step) * 4)}
.lc-mo .hero .fee .fr:nth-of-type(6){animation-delay:calc(330ms + var(--mo-step) * 5)}
.lc-mo .hero .fee .fee__n{animation:lc-fade calc(320ms * var(--mo)) var(--mo-ease) both;animation-delay:calc(330ms + var(--mo-step) * 6)}

/* --- 3. Cards arrive on scroll --------------------------------------------
   lc-motion.php observes these and adds .is-in once each is in view. Until
   then they sit one notch low. --mo-i is that card's position in its own row,
   so a row lands left to right rather than all at once.

   If the script never runs, .lc-mo is never added and none of this applies. */
.lc-mo .mo{opacity:0;translate:0 var(--mo-rise)}
.lc-mo .mo.is-in{
  animation:lc-rise calc(430ms * var(--mo)) var(--mo-ease) both;
  animation-delay:calc(var(--mo-step) * var(--mo-i,0));
}

/* --- 4. The gauge fills ----------------------------------------------------
   The city bar is a measurement, so it should be seen being taken. The inline
   width stays exactly as PHP wrote it; only the scale is animated, so the
   number the bar represents is never misreported mid-flight. */
.lc-mo .mo .cty__b i{transform:scaleX(0);transform-origin:left center}
.lc-mo .mo.is-in .cty__b i{
  transform:scaleX(1);
  transition:transform calc(760ms * var(--mo)) var(--mo-ease) 140ms;
}

/* Both of the blocks above are scoped to .mo, the class the script puts on
   the cards it is actually watching. A .dsc or a .cty__b the script never
   reached is left alone and stays visible.

   Disclosure dots fill in one at a time — six points, counted, not shown. */
.lc-mo .mo .dsc i.f{opacity:0;transform:scale(.55)}
.lc-mo .mo.is-in .dsc i.f{
  opacity:1;transform:scale(1);
  transition:opacity 200ms linear, transform 260ms var(--mo-ease);
}
.lc-mo .mo.is-in .dsc i.f:nth-child(1){transition-delay:220ms}
.lc-mo .mo.is-in .dsc i.f:nth-child(2){transition-delay:265ms}
.lc-mo .mo.is-in .dsc i.f:nth-child(3){transition-delay:310ms}
.lc-mo .mo.is-in .dsc i.f:nth-child(4){transition-delay:355ms}
.lc-mo .mo.is-in .dsc i.f:nth-child(5){transition-delay:400ms}
.lc-mo .mo.is-in .dsc i.f:nth-child(6){transition-delay:445ms}

/* --- 5. The cut extends ----------------------------------------------------
   Hovering a service card runs the blade a little further along, as if the
   cut were still being made. The character layer sizes the blade in pixels;
   switching to a percentage lets the width animate without the artwork
   tiling or clipping. */
.sv__n::after{
  background-size:100% 9px;
  transition:width calc(300ms * var(--mo)) var(--mo-ease), opacity 200ms linear;
}
.sv:hover .sv__n::after,
.sv:focus-visible .sv__n::after{width:96px;opacity:1}

.sh::after{background-size:100% 12px;transition:width calc(420ms * var(--mo)) var(--mo-ease)}
.lc-mo .sh::after{width:0}
.lc-mo .sh.is-in::after{width:96px;transition-delay:90ms}

/* --- 6. Press -------------------------------------------------------------
   Every card and button in this design sits on a hard offset shadow. The
   honest response to a press is to move toward that shadow, not away from it.
   1px is enough — this is confirmation, not a bounce. */
.btn,.qp,.shw__i,.sv,.cty,.geo{transition-property:transform,box-shadow,border-color,background-color,color;transition-duration:calc(140ms * var(--mo));transition-timing-function:var(--mo-ease)}
.btn:active,.qp:active,.geo:active{transform:translateY(1px)}
.shw__i:active,.sv:active,.cty:active{transform:translateY(1px) scale(.996)}

.shw__i:hover{box-shadow:4px 4px 0 var(--paper2)}
.cty:hover{box-shadow:4px 4px 0 var(--paper2)}
.qp:hover{transform:translateY(-1px)}

/* --- 7. Navigation --------------------------------------------------------
   A copper rule runs out from the left under the link being considered. Same
   copper, same 1.5px, as the eyebrow rule — it is the house underline, not a
   new device. */
.nv a{position:relative}
.nv a::after{
  content:"";position:absolute;left:0;right:100%;bottom:-6px;height:1.5px;
  background:var(--cu);
  transition:right calc(260ms * var(--mo)) var(--mo-ease);
}
.nv a:hover::after,.nv a:focus-visible::after{right:0}

/* The mark turns a few degrees, the way a key does. */
.mk__g{transition:transform calc(260ms * var(--mo)) var(--mo-ease)}
.mk:hover .mk__g{transform:rotate(-8deg)}

/* The search field lifts its rule to steel when it has the caret. */
.sb{transition:border-color calc(160ms * var(--mo)) linear,box-shadow calc(160ms * var(--mo)) linear}

/* --- 8. The sticky header settles -----------------------------------------
   Flat against the page at the top, lifted off it once you have scrolled.
   Tells you the header is floating over content rather than part of it. */
.hd{transition:box-shadow calc(220ms * var(--mo)) linear}
.lc-scrolled .hd{box-shadow:0 1px 0 var(--rule),0 10px 22px -18px rgba(22,24,27,.55)}

/* --- 9. Locked out now ----------------------------------------------------
   One pill on the page is for somebody standing outside in the cold. It gets
   a slow ring, well under the threshold where a pulse becomes nagging, and it
   stops the moment the pointer arrives. */
@media (prefers-reduced-motion:no-preference){
  .qp--hot{animation:lc-hot 3.4s ease-in-out infinite}
  .qp--hot:hover{animation:none}
}
@keyframes lc-hot{
  0%,100%{box-shadow:0 0 0 0 rgba(200,16,46,0)}
  50%    {box-shadow:0 0 0 5px rgba(200,16,46,.11)}
}

/* --- 10. The stamp lands ---------------------------------------------------
   A stamp is struck, not faded in. It arrives a little large and off-angle
   and settles onto the angle the character layer set. */
.lc-mo .vb::after{opacity:0}
.lc-mo .vb.is-in::after{animation:lc-stamp 380ms var(--mo-ease) 260ms both}
@keyframes lc-stamp{
  from{opacity:0;transform:rotate(-13deg) scale(1.45)}
  to  {opacity:var(--stamp-ink);transform:rotate(-4deg) scale(1)}
}

/* --- 11. Counting ---------------------------------------------------------
   lc-motion.php counts the headline figures up from zero when they come into
   view. Tabular figures are already set by the character layer, so the column
   does not jitter while it counts. This just holds the width steady on the
   trust ticker while it swaps lines. */
.stat__n{font-variant-numeric:tabular-nums slashed-zero}

/* --- 12. Reduced motion ---------------------------------------------------
   The script does not add .lc-mo at all when reduced motion is asked for, so
   sections 2 to 5 and 10 never apply. This turns off the rest: the hover
   transitions and the pill. Nothing is hidden, nothing is delayed. */
@media (prefers-reduced-motion:reduce){
  .btn,.qp,.shw__i,.sv,.cty,.geo,.nv a::after,.mk__g,.sb,.hd,
  .sv__n::after,.sh::after,.cty__b i,.dsc i.f{
    transition:none !important;animation:none !important;
  }
}


/* --- 13. Section rules draw down ------------------------------------------
   The margin running heads in the character layer print a section number in
   the gutter with a hairline under it. The hairline now draws down as the
   section arrives, which is what makes the number read as an index mark
   rather than a stray glyph. */
@media(min-width:981px){
  .lc-mo .sec>.wrap::before{opacity:0;transition:opacity 320ms linear 120ms}
  .lc-mo .sec.is-in>.wrap::before{opacity:1}
  .lc-mo .sec>.wrap::after{height:0;transition:height 480ms var(--mo-ease) 200ms}
  .lc-mo .sec.is-in>.wrap::after{height:60px}
}

/* --- 14. Tags settle after the card ---------------------------------------
   REGISTRY, 24/7, FEATURED land a beat after the name they qualify, left to
   right. They are the fine print, so they arrive like fine print. */
.lc-mo .mo .shw__tags .tg-s,
.lc-mo .mo .lc__tags .tg-s{opacity:0;translate:0 3px}
.lc-mo .mo.is-in .shw__tags .tg-s,
.lc-mo .mo.is-in .lc__tags .tg-s{
  opacity:1;translate:0 0;
  transition:opacity 220ms linear, translate 260ms var(--mo-ease);
}
.lc-mo .mo.is-in .tg-s:nth-child(1){transition-delay:180ms}
.lc-mo .mo.is-in .tg-s:nth-child(2){transition-delay:225ms}
.lc-mo .mo.is-in .tg-s:nth-child(3){transition-delay:270ms}
.lc-mo .mo.is-in .tg-s:nth-child(4){transition-delay:315ms}


/* --- 15. Reading ----------------------------------------------------------
   Everything in this block is for the article template.

   The progress rule is a copper hairline along the bottom edge of the sticky
   header, scaled to how far through the prose you are. It is a measurement,
   not an ornament: on a 9-minute guide it answers how much is left, which is
   the question every reader is actually asking. It only exists on pages that
   have prose, because .lc-reading is only set there. */
.lc-reading .hd::after{
  content:"";
  position:absolute;left:0;right:0;bottom:-1px;height:2px;
  background:var(--cu);
  transform:scaleX(var(--lc-read,0));transform-origin:left center;
  pointer-events:none;
}

/* The contents list marks where you are. Same copper rule the hover state
   already uses, so nothing new is being taught. */
.lc-toc a{transition:color 200ms linear,border-left-color 200ms linear}
.lc-toc a.is-here{color:var(--ink);border-left-color:var(--cu);font-weight:600}

/* Furniture arrives; body copy does not. Animating every paragraph makes a
   long read feel like a slideshow, so only the things you scan for move:
   headings, tables, pull quotes, figures and the two end panels. */
.lc-mo .lc-prose h2,
.lc-mo .lc-prose figure,
.lc-mo .lc-prose blockquote,
.lc-mo .lc-tw,
.lc-mo .lc-art__note,
.lc-mo .lc-art__cta,
.lc-mo .lc-guide{opacity:0;translate:0 var(--mo-rise)}
.lc-mo .lc-prose h2.is-in,
.lc-mo .lc-prose figure.is-in,
.lc-mo .lc-prose blockquote.is-in,
.lc-mo .lc-tw.is-in,
.lc-mo .lc-art__note.is-in,
.lc-mo .lc-art__cta.is-in,
.lc-mo .lc-guide.is-in{
  animation:lc-rise calc(430ms * var(--mo)) var(--mo-ease) both;
  animation-delay:calc(var(--mo-step) * var(--mo-i,0));
}

/* A heading you can link to. The mark only appears on hover or keyboard
   focus, and it is a real anchor, not a decoration. */
.lc-anch{
  font-family:IBM Plex Mono,monospace;font-weight:500;
  color:var(--ink30);text-decoration:none;
  margin-left:.4em;opacity:0;
  transition:opacity 160ms linear,color 160ms linear;
}
.lc-prose h2:hover .lc-anch,.lc-anch:focus-visible{opacity:1}
.lc-anch:hover{color:var(--cu)}

/* A table wider than its column shows a shadow on the edge that still has
   content behind it, and only while it has. Pure CSS: the paper-coloured
   layer scrolls with the content and covers the shadow at the end. */
.lc-tw{
  background-image:
    linear-gradient(to right,var(--card),var(--card)),
    linear-gradient(to right,rgba(22,24,27,.13),rgba(22,24,27,0));
  background-position:right center,right center;
  background-size:100% 100%,24px 100%;
  background-repeat:no-repeat;
  background-attachment:local,scroll;
}


/* --- 16. Pins drop --------------------------------------------------------
   Leaflet inserts its markers after the tiles resolve, so the animation runs
   the moment each pin exists. Staggered, because they are being placed one
   at a time, not stamped as a set. */
@media (prefers-reduced-motion:no-preference){
  .map--live .leaflet-marker-icon{animation:lc-drop 380ms var(--mo-ease) both}
  .map--live .leaflet-marker-icon:nth-child(2){animation-delay:60ms}
  .map--live .leaflet-marker-icon:nth-child(3){animation-delay:120ms}
  .map--live .leaflet-marker-icon:nth-child(4){animation-delay:180ms}
  .map--live .leaflet-marker-icon:nth-child(5){animation-delay:240ms}
  .map--live .leaflet-marker-icon:nth-child(n+6){animation-delay:300ms}
}
@keyframes lc-drop{
  from{opacity:0;translate:0 -14px}
  to  {opacity:1;translate:0 0}
}


/* --- 17. Small controls ---------------------------------------------------
   The city caret turns over when its list is open. The primary button grows
   the same hard offset shadow everything else in this design uses. */
.geo .cr{transition:transform calc(220ms * var(--mo)) var(--mo-ease)}
.geo[aria-expanded=true] .cr{transform:rotate(180deg)}

.btn--p,.btn--c{transition:transform calc(140ms * var(--mo)) var(--mo-ease),box-shadow calc(140ms * var(--mo)) var(--mo-ease),background-color 140ms linear}
.btn--p:hover,.btn--c:hover{box-shadow:3px 3px 0 var(--cu-lt)}
.btn--p:active,.btn--c:active{box-shadow:0 0 0 var(--cu-lt)}


/* --- 18. The receipt prints ----------------------------------------------
   The 404 is set as a torn slip. It should arrive the way one comes out of a
   machine: from under the top edge, not fading up from nowhere. */
@media (prefers-reduced-motion:no-preference){
  .lc-doc--404 .empty{animation:lc-print 520ms cubic-bezier(.25,.8,.3,1) both;transform-origin:top center}
}
@keyframes lc-print{
  from{opacity:0;transform:scaleY(.72);translate:0 -10px}
  to  {opacity:1;transform:scaleY(1);translate:0 0}
}


/* --- 19. Reduced motion, second pass --------------------------------------
   .lc-mo is never set when reduced motion is asked for, so most of the above
   is already inert. This covers the few rules that are not gated on it.
   The reading rule and the contents marker stay: both are driven by the
   reader own scrolling and report position rather than perform. */
@media (prefers-reduced-motion:reduce){
  .geo .cr,.btn--p,.btn--c,.lc-anch,.lc-toc a,
  .map--live .leaflet-marker-icon,.lc-doc--404 .empty{
    transition:none !important;animation:none !important;
  }
}