/* The site behind the page.
 *
 * The same ambient layer the scaffold estimator carries, so the two products
 * read as one shop: a facade going up over graph paper, drawn in the weight
 * of a pencil elevation, moving slowly enough that nobody notices it twice.
 *
 * What is different here is what the crew is doing. This app exists to find
 * openings in a drawing set and say what happens to them, so the work on this
 * facade is window work: fitters standing in the openings, setting units into
 * reveals that are drawn open until they land. A reader who looks twice
 * should find the picture doing the thing the software does.
 *
 * z-index -1 is the whole clipping strategy. The .sheet cards are opaque, so
 * they cover this completely and it survives only in the gutters. Nothing
 * here needs to know where the content is.
 *
 * The weight hierarchy does as much work as the geometry. The building is
 * faintest, the inner row of standards sits behind the outer, and the people
 * are the darkest thing on the layer -- which is how depth reads at this
 * opacity without a single gradient.
 *
 * One structural rule runs through the whole layer, and getting it wrong is
 * silent: nothing carries an SVG `transform` attribute and a CSS `transform`
 * animation at once. The CSS property wins over the presentation attribute,
 * so anything positioned by `transform="translate(...)"` and then animated on
 * transform loses its place and springs to the origin. Every element is
 * therefore either placed or animated, never both -- which is why a fitter is
 * three groups deep, and why a pane being set is two.
 */
#sitework{
  position:fixed;
  inset:0;
  z-index:-1;
  width:100%;
  height:100%;
  pointer-events:none;
  /* Has to lose to body text at every size. The same alpha reads much
   * stronger on the dark theme, so it is dialled back there. */
  opacity:.52;
  color:var(--ink);
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) #sitework{opacity:.34}
}
:root[data-theme="dark"] #sitework{opacity:.34}

#sitework line,#sitework circle,#sitework rect,#sitework path{
  stroke:currentColor;
  fill:none;
  stroke-linecap:round;
  vector-effect:non-scaling-stroke;
}
/* A hard hat is a filled dome; a ponytail is an open curve and must stay
 * stroked, or it renders as a blob beside the head. */
#sitework .sw-hat{fill:currentColor;stroke:none}
#sitework .sw-hair{fill:none;stroke:currentColor}

/* -- the building. Faintest thing here, and it never animates: a building
 *    does not get built while you watch. -------------------------------- */
.sw-site{opacity:.42}
.sw-wall{stroke-width:1}
.sw-parapet{stroke-width:1.6}
.sw-window{stroke-width:.9;opacity:.7}
.sw-ground{stroke-width:1.2;opacity:.8}

/* -- an opening being worked -------------------------------------------
 *
 * Heavier than a glazed window and drawn as three sides plus a sill rather
 * than a closed rectangle, because that is the difference the whole app is
 * about: a rectangle is a window, and a reveal with nothing in it is an
 * opening somebody still has to price.
 */
.sw-opening{stroke-width:1.5;opacity:.95}
.sw-sill{stroke-width:2.2;opacity:.9}
.sw-reveal{stroke-width:.9;opacity:.45}

/* -- the scaffold ------------------------------------------------------- */
.sw-standard{stroke-width:1.7}
.sw-ledger{stroke-width:1.3}
.sw-transom{stroke-width:1;opacity:.75}
/* The inner row is behind the outer one. Lightening it is the whole depth
 * cue -- there is no perspective here, only weight. */
.sw-inner{opacity:.45;stroke-width:1.1}
.sw-deck{stroke-width:2.8;opacity:.68}
.sw-toeboard{stroke-width:1.8;opacity:.55}
.sw-guardrail{stroke-width:1.1;opacity:.7}
.sw-midrail{opacity:.5}
.sw-post{stroke-width:1.1;opacity:.6}
.sw-brace{stroke-width:.95;opacity:.5}
.sw-tie{stroke-width:1.2;opacity:.65}
.sw-tieplate{stroke-width:.9;opacity:.55}
.sw-baseplate{stroke-width:2.2;opacity:.7}
.sw-soleboard{stroke-width:1.6;opacity:.45}
.sw-stile{stroke-width:1.3;opacity:.7}
.sw-rung{stroke-width:1;opacity:.6}
.sw-jib{stroke-width:1.6}
.sw-wheel{stroke-width:1.3}
.sw-rope{stroke-width:.8;opacity:.55}
.sw-bundle{stroke-width:2.4;opacity:.8}
.sw-sling{stroke-width:.8;opacity:.6}
.sw-tube{stroke-width:1.6;opacity:.75}

/* A unit on its way into an opening: the frame, and the diagonal that says
 * glass on an elevation. */
.sw-unit{stroke-width:1.5;opacity:.85}
.sw-glazing{stroke-width:.8;opacity:.5}

/* -- the people. Darkest on the layer, so they read in front of the steel. */
.sw-crew,.sw-fitters,.sw-walk-1,.sw-walk-2{opacity:1}
#sitework .sw-body line,#sitework .sw-body circle{stroke-width:1.9}
#sitework .sw-vest{stroke-width:1.2;opacity:.55}

/* One long cycle: the structure goes up, stands a while, comes down. The
 * per-member delay is derived in sitework.js from the actual member count, so
 * adding detail can never push the last piece past the end of the loop. */
#sitework .sw-member{
  animation:sw-raise 108s cubic-bezier(.22,.61,.36,1) infinite both;
}
@keyframes sw-raise{
  0%  {opacity:0; transform:translateY(10px)}
  5%  {opacity:1; transform:translateY(0)}
  78% {opacity:1; transform:translateY(0)}
  90% {opacity:0; transform:translateY(10px)}
  100%{opacity:0; transform:translateY(10px)}
}

/* Placed elements. Opacity only -- see the note at the top. */
#sitework .sw-worker{animation:sw-fade 108s ease-in-out infinite both}
@keyframes sw-fade{
  0%{opacity:0} 6%{opacity:1} 76%{opacity:1} 88%{opacity:0} 100%{opacity:0}
}

/* A unit being set into its opening. It comes up from below the sill, seats,
 * and stays until the scaffold comes down -- which is why the reveal it lands
 * in is drawn open underneath it and simply stops showing.
 *
 * transform-box:fill-box so the translate is in the element's own units and
 * not the whole 1600x900 viewBox.
 */
#sitework .sw-set{
  transform-box:fill-box;
  animation:sw-glaze 108s cubic-bezier(.3,.7,.35,1) infinite both;
}
@keyframes sw-glaze{
  0%  {transform:translateY(26px); opacity:0}
  4%  {transform:translateY(26px); opacity:0}
  9%  {transform:translateY(26px); opacity:.9}
  16% {transform:translateY(0);    opacity:1}
  78% {transform:translateY(0);    opacity:1}
  90% {transform:translateY(0);    opacity:0}
  100%{transform:translateY(0);    opacity:0}
}

/* Material coming up on the gin wheel: rises, hangs at the top lift while it
 * is landed, then the rope goes back down empty. */
#sitework .sw-load{
  transform-box:fill-box;
  animation:sw-hoist 34s cubic-bezier(.4,0,.5,1) infinite both;
}
@keyframes sw-hoist{
  0%  {transform:translateY(0);    opacity:0}
  6%  {transform:translateY(0);    opacity:1}
  46% {transform:translateY(-470px); opacity:1}
  62% {transform:translateY(-470px); opacity:1}
  70% {transform:translateY(-470px); opacity:0}
  100%{transform:translateY(0);    opacity:0}
}

/* Arms. Small, slow, and out of phase between people so they never move in
 * unison -- a row of figures nodding together reads as a machine. */
#sitework .sw-arms{
  transform-box:fill-box;
  transform-origin:0 0;
  animation:sw-work 5.4s ease-in-out infinite alternate;
}
@keyframes sw-work{
  from{transform:rotate(-7deg)} to{transform:rotate(6deg)}
}
/* Walking arms swing further and faster, against the legs. */
#sitework .sw-arms-walk{animation:sw-swing 1.15s ease-in-out infinite alternate}
@keyframes sw-swing{
  from{transform:rotate(-13deg)} to{transform:rotate(13deg)}
}

/* The walk. Legs are drawn as plain verticals and get their stride entirely
 * from this rotation, which is the only way the pivot stays at the hip -- a
 * leg drawn already-angled rotates about the wrong point and skates. The
 * negative delay on the second leg puts it half a stride out. */
#sitework .sw-leg{
  transform-box:fill-box;
  transform-origin:0 0;
  animation:sw-step 1.15s ease-in-out infinite alternate;
}
#sitework .sw-leg-b{animation-delay:-1.15s}
@keyframes sw-step{
  from{transform:rotate(-19deg)} to{transform:rotate(19deg)}
}
/* And the body rises and falls twice per stride, which is what stops a
 * walking figure looking like it is on rails. */
#sitework .sw-walk-1 .sw-body,
#sitework .sw-walk-2 .sw-body{
  animation:sw-bob .575s ease-in-out infinite alternate;
}
@keyframes sw-bob{
  from{transform:translateY(0)} to{transform:translateY(-1.6px)}
}

/* Two crossing the site with tube. Slow enough to be scenery: a full crossing
 * takes over two minutes. Both animations are declared together so the
 * pairing of name to duration cannot drift -- `sw-cross` is last, so it owns
 * `transform` while `sw-fade` owns `opacity`. */
.sw-walk-1{
  animation:sw-fade 108s ease-in-out infinite both,
            sw-cross-1 146s linear infinite both;
}
.sw-walk-2{
  animation:sw-fade 108s ease-in-out infinite both,
            sw-cross-2 168s linear infinite both;
  animation-delay:0s,-58s;
}
@keyframes sw-cross-1{
  from{transform:translateX(-170px)} to{transform:translateX(1780px)}
}
@keyframes sw-cross-2{
  from{transform:translateX(1780px)} to{transform:translateX(-170px)}
}

/* Ambient movement behind text is exactly what this setting is for. The
 * scaffold still stands and the units stay set -- they are part of the page's
 * furniture -- the site simply stops being worked. */
@media (prefers-reduced-motion:reduce){
  #sitework .sw-member,
  #sitework .sw-worker,
  #sitework .sw-arms,
  #sitework .sw-leg,
  #sitework .sw-body,
  #sitework .sw-load,
  #sitework .sw-set,
  #sitework .sw-walk-1,
  #sitework .sw-walk-2{
    animation:none !important;
    opacity:1;
    transform:none;
  }
  #sitework .sw-walk-2{transform:translateX(1180px)}
  #sitework .sw-load{transform:translateY(-470px)}
  #sitework .sw-inner{opacity:.45}
  #sitework .sw-brace{opacity:.5}
  #sitework .sw-deck{opacity:.68}
}

/* Below this the sheet fills the viewport, so there is no grid left to draw
 * on and the layer is a cost with no benefit. */
@media (max-width:900px){#sitework{display:none}}

/* Printing a takeoff must not print scaffolding across it. */
@media print{#sitework{display:none}}
