/* The Enterprise Console tile.

   Typography is set here rather than in typekit.css, which is the one documented exception to that
   rule and worth saying out loud: this is not the site's type. It is a picture of a product's
   interface, and its sizes belong to that product the way the pixels of a screenshot do. Routing
   them through the site's scale would make the thumbnail a drawing of the Console in Novia's
   typeface, which is a different and less true thing. Everything here is namespaced .ch- and
   reaches nothing outside the tile.

   The card is a fixed 1440x810 stage scaled down to whatever the tile is, so every coordinate below
   is a real coordinate from the design rather than a percentage that has to be re-derived each time
   the grid changes. */

/* Squint Camera Plain, variable, weights 400-900. Lifted out of the 209KB base64 data URI it
   arrived in: inline, it is re-parsed on every page load and cannot be cached on its own. */
@font-face {
  font-family: "Squint Camera Plain";
  src: url("fonts/SquintCameraPlainVariable.woff2") format("woff2");
  font-weight: 400 900;
  font-style: normal;
  font-display: swap;
}

/* The frame the card lives in. The tile's other shots are 16/10 images; this one is a stage, so it
   gets a box of the same proportion and the card fills it. */
.tile .shot.is-card { position: relative; overflow: hidden; background: #e8effc }

.ch-card {
  --ease: cubic-bezier(.2,.7,.2,1);
  --s: .5;                         /* set by console.js from the tile's width */
  position: absolute; inset: 0; overflow: hidden;
  background: radial-gradient(90% 80% at 50% 45%, #f1f6ff, #dbe8ff);
}

/* 16/9 inside a 16/10 frame, so console.js centres it vertically and the gradient fills the rest.
   Cropping to cover instead would cut the ends off the site network, which is the thing the pull
   back exists to reveal. */
.ch-stage { position: absolute; left: 0; top: 0; width: 1440px; height: 810px;
  transform: scale(var(--s)); transform-origin: 0 0;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif }

.ch-win { position: absolute; left: 348px; top: 40px; width: 1200px; height: 750px;
  border-radius: 18px; background: #171717; overflow: hidden;
  box-shadow: 0 30px 70px rgba(21,52,120,.22), 0 3px 8px rgba(21,52,120,.12);
  transform: scale(.62); transform-origin: 0 0; transition: transform 700ms var(--ease) }
.ch-card.is-hot .ch-win { transform: translateY(-6px) scale(.62) }

/* Camera: rests zoomed in on the console. On hover the cursor heads for search while the camera
   pushes in for the click, holds a beat, then pulls all the way back. */
.ch-cam { position: absolute; left: 0; top: 0; width: 1440px; height: 810px; transform-origin: 0 0;
  transform: translate(-446px,-36px) scale(1.62);
  transition: transform 600ms cubic-bezier(.4,0,.2,1) }
@keyframes ch-camk {
  0%, 3.8%     { transform: translate(-446px,-36px) scale(1.62); animation-timing-function: cubic-bezier(.45,0,.25,1) }
  23.1%, 53.8% { transform: translate(-1517px,-71px) scale(3.4); animation-timing-function: cubic-bezier(.65,0,.35,1) }
  100%         { transform: translate(101px,48px) scale(.86) }
}
.ch-card.is-on .ch-cam { animation: ch-camk 3900ms linear both }

/* The network: many sites below, linked back to the console.

   Hidden until the card is hot. The camera at rest is zoomed in far enough that the field itself
   is off the bottom of the frame — but the hub and the top of the trunk are not, and they landed
   just inside the edge as a stray blue tick under the console with nothing to explain it. The
   network is the payoff of the pull back; before the pull back it should not exist. */
.ch-net, .ch-sites, .ch-hub { opacity: 0; transition: opacity 220ms ease }
.ch-card.is-hot .ch-net, .ch-card.is-hot .ch-sites, .ch-card.is-hot .ch-hub { opacity: 1 }
.ch-net, .ch-sites { position: absolute; inset: 0;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 14%, #000 86%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 14%, #000 86%, transparent) }
.ch-net { width: 1440px; height: 810px; overflow: visible }
.ch-net path { fill: none; stroke: #2f63e0; stroke-width: 1.5; stroke-opacity: var(--o);
  stroke-dasharray: 1; stroke-dashoffset: 0 }
.ch-hub { position: absolute; left: 713px; top: 508px; width: 14px; height: 14px; border-radius: 2px;
  background: #154eca; box-shadow: 0 0 0 5px rgba(21,78,202,.14) }
.ch-site { position: absolute; box-sizing: border-box; border-radius: 7px;
  background: rgba(255,255,255,.85); border: 1px solid rgba(47,99,224,.28);
  box-shadow: 0 4px 10px rgba(21,78,202,.08); opacity: var(--o) }
@keyframes ch-line  { from { stroke-dashoffset: 1 } }
@keyframes ch-pop   { from { opacity: 0; transform: translateY(10px) scale(.9) } }
@keyframes ch-hubin { 0% { transform: scale(0) } 70% { transform: scale(1.25) } }
.ch-card.is-hot .ch-net path { animation: ch-line 700ms cubic-bezier(.4,0,.2,1) both; animation-delay: var(--d) }
.ch-card.is-hot .ch-site    { animation: ch-pop 560ms var(--ease) both; animation-delay: var(--d) }
.ch-card.is-hot .ch-hub     { animation: ch-hubin 560ms var(--ease) 3300ms both }

/* ------------------------------------------------------------------------ sidebar */
.ch-side { position: absolute; left: 0; top: 0; bottom: 0; width: 220px; box-sizing: border-box;
  padding: 20px 12px 0; display: flex; flex-direction: column; gap: 3px; font-size: 15px }
.ch-brand { display: flex; align-items: center; gap: 10px; padding: 0 10px 16px }
.ch-logo { flex: none; width: 28px; height: 28px; border-radius: 8px; border: 1px solid #404040;
  display: flex; align-items: center; justify-content: center; background: #2a2a2a }
.ch-bt { min-width: 0; font-size: 14px; display: flex; flex-direction: column; align-items: flex-start;
  gap: 3px; white-space: nowrap }
/* Truncated rather than sliced. The sidebar is 220px and leaves about 138 for the name, so an org
   called anything longer than "Davis Manufacturing" runs under .ch-main, which is opaque and sits
   over it — at thumbnail size that was invisible, and at full width it cuts the word mid-letter
   with no ellipsis to say it was cut. */
.ch-name, .ch-sub { max-width: 100%; overflow: hidden; text-overflow: ellipsis }
.ch-name { color: #fff; font-weight: 600 }
.ch-sub { color: #3b82f6; font-size: 12px; font-weight: 500 }
.ch-nav { color: #a3a3a3; padding: 8px 10px; white-space: nowrap; align-self: stretch }
.ch-nav.on { color: #fff; background: #2a2a2a; font-weight: 500; border-radius: 5px }
.ch-lab { color: #737373; font-size: 12px; margin: 14px 10px 4px; align-self: flex-start }
.ch-prof { margin: auto -12px 0; padding: 16px 22px; border-top: 1px solid #2a2a2a;
  display: flex; align-items: center; gap: 10px; white-space: nowrap }
.ch-av { color: #fff; background: #2a6fc8; flex: none; width: 30px; height: 30px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 600 }
.ch-pn { display: flex; flex-direction: column; align-items: flex-start; gap: 2px }
.ch-pn .a { color: #fff; font-size: 13px; font-weight: 500 }
.ch-pn .b { color: #737373; font-size: 11px }

/* --------------------------------------------------------------------------- main */
.ch-main { position: absolute; left: 220px; top: 10px; right: 10px; bottom: 10px; border-radius: 14px;
  overflow: hidden; background: #fff; color: #171717 }
.ch-hero { position: relative; height: 330px; box-sizing: border-box; padding: 120px 40px 0;
  display: flex; flex-direction: column; align-items: flex-start; gap: 14px }
.ch-glass { position: absolute; inset: 0; overflow: hidden;
  background: linear-gradient(100deg, #fff 45%, #f3f6fb) }
/* Frosted tiles: 2D rotations only, soft shadow down and to the left, fine grain.

   Only one of these used to be glass and the other three were flat fills, which is why the stack
   read as coloured paper. Frosted is four things at once and it needs all four: you can see
   through it, what you see is blurred, the lit edges catch a highlight, and the surface has grain.
   The grain was already here; the rest is below.

   There is no backdrop-filter here and that is the finding, not an omission. A live blur was the
   obvious move and it was measured: seven of them cost 23% of the frame rate on the card's camera
   move, and bought nothing visible. Blur samples what is behind, and what is behind these is the
   hero's near-white gradient — a blurred near-white gradient is a near-white gradient. It showed
   only where two tiles crossed, and these barely cross. Worth recording that the blur RADIUS was
   nearly free once the readback was being paid for at all (6px and 14px measured the same), so
   the choice was never "cheaper blur", it was blur or no blur.

   What actually reads as frost costs nothing: a sheen across the face, a lit edge, a fill you can
   see through, and grain. All four are below, and all four are paint. */
.ch-cluster { position: absolute; inset: 0 }
.gc { position: absolute; border-radius: 22%; transform: rotate(var(--r));
  display: flex; align-items: center; justify-content: center;
  /* Grain over sheen. The sheen runs from the top-right corner, which is where the light is, and
     falls off before the middle — a highlight that reaches the far edge is a gradient, and a
     gradient reads as plastic. */
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.1' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .16 0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E"),
    linear-gradient(215deg, rgba(255,255,255,.46) 0%, rgba(255,255,255,.10) 38%, rgba(255,255,255,0) 62%);
  /* Edges before shadow. The first two are the lit sides — top and right — and they rotate with
     the tile, which is what a tilted pane of glass actually does. The third is the shaded
     underside. A hairline border would have drawn all four sides the same and turned the tiles
     back into stickers. */
  box-shadow:
    inset 0 1.5px 0 rgba(255,255,255,.58),
    inset -1.5px 0 0 rgba(255,255,255,.34),
    inset 1.5px -1.5px 0 rgba(20,52,120,.10),
    -8px 18px 30px -10px rgba(21,78,202,.30) }
.gc svg { width: 40%; height: 40% }
/* Alpha rather than solid, and the darker the tile the more of it there is: a deep glass holds
   more colour before it stops being glass, a pale one goes milky almost immediately. */
.gc.navy  { background-color: rgba(15,63,214,.74) }
.gc.mid   { background-color: rgba(37,99,245,.60) }
.gc.light { background-color: rgba(95,155,255,.48) }
.gc.pale  { background-color: rgba(205,222,255,.52);
  box-shadow:
    inset 0 1.5px 0 rgba(255,255,255,.75),
    inset -1.5px 0 0 rgba(255,255,255,.45),
    inset 1.5px -1.5px 0 rgba(20,52,120,.07),
    -6px 14px 26px -10px rgba(20,52,120,.14) }
.ch-title { color: #171717; position: relative; margin: 0;
  font: 500 28px/34px "Squint Camera Plain", system-ui, sans-serif }
.ch-btn { color: #fff; background: #154eca; position: relative; display: inline-flex; align-items: center;
  gap: 8px; padding: 8px 12px; border-radius: 8px; font-size: 13px; font-weight: 500 }
.ch-stats { display: grid; grid-template-columns: repeat(3,1fr); padding: 0 14px;
  border-top: 1px solid #e5e5e5 }
.ch-tile { padding: 22px 28px 36px; display: flex; flex-direction: column; align-items: flex-start; gap: 4px }
.ch-tile + .ch-tile { border-left: 1px solid #e5e5e5 }
.ch-num { color: #171717; font: 500 28px/34px "Squint Camera Plain", system-ui, sans-serif;
  font-variant-numeric: tabular-nums }
.ch-tl { color: #737373; font-size: 15px }
.ch-gsh { border-top: 1px solid #e5e5e5; border-bottom: 1px solid #e5e5e5; background: #fafafa;
  padding: 9px 40px; display: flex; justify-content: space-between; font-size: 13px; font-weight: 500 }
.ch-gsh .g { color: #737373 }
.ch-step { border-bottom: 1px solid #e5e5e5; padding: 20px 40px; display: flex; align-items: center;
  gap: 14px; font-size: 15px; font-weight: 500 }
.ch-step .n { color: #3b82f6; background: #eff6ff; width: 20px; height: 20px; display: flex;
  align-items: center; justify-content: center; font-size: 12px }
.ch-step .t { color: #171717 }

/* The .sk class and the --c/--bg indirection are gone. They were the hooks for a skeleton
   cascade the card's header comment described and nobody built: every element declared its colour
   as a variable so one rule could blank them all at once, and 29 stagger delays pointed at
   keyframes that did not exist. Colours are declared on the elements now, which is where a reader
   looks for them. */
.ch-title, .ch-btn { z-index: 1 }

/* Ghost cursor: drifts to the search button, clicks, and the click sets off the pull back. */
.ch-cur { position: absolute; left: 0; top: 0; width: 30px; height: 30px; opacity: 0;
  pointer-events: none; z-index: 5; transform-origin: 0 0;
  filter: drop-shadow(0 2px 3px rgba(0,0,0,.25)) }
.ch-ring { position: absolute; left: 444px; top: 206px; width: 40px; height: 40px; margin: -20px;
  border-radius: 50%; border: 3px solid #154eca; opacity: 0; pointer-events: none; z-index: 4 }
@keyframes ch-cur {
  0%    { opacity: 0; transform: translate(760px,500px) }
  10%   { opacity: 1; transform: translate(730px,470px); animation-timing-function: cubic-bezier(.45,0,.2,1) }
  40%   { opacity: 1; transform: translate(444px,206px) scale(1) }
  44.5% { transform: translate(444px,206px) scale(.86) }
  48.6% { transform: translate(444px,206px) scale(1) }
  82%   { opacity: 1; transform: translate(444px,206px) }
  100%  { opacity: 0; transform: translate(444px,206px) }
}
@keyframes ch-ring  { 0%, 43.6% { opacity: 0; transform: scale(.4) } 45.5% { opacity: 1; transform: scale(.5) } 59%, 100% { opacity: 0; transform: scale(1.6) } }
@keyframes ch-press { 0%, 43.6% { filter: none } 45.5% { filter: brightness(.78) } 52%, 100% { filter: none } }
@keyframes ch-ar    { 0%, 37% { opacity: 1 } 37.7%, 100% { opacity: 0 } }
@keyframes ch-hd    { 0%, 37% { opacity: 0 } 37.7%, 100% { opacity: 1 } }
.ch-card.is-hot .ch-cur      { animation: ch-cur 2200ms linear both }
.ch-card.is-hot .ch-cur .ar  { animation: ch-ar 2200ms linear both }
.ch-card.is-hot .ch-cur .hd  { animation: ch-hd 2200ms linear both }
.ch-card.is-hot .ch-ring     { animation: ch-ring 2200ms ease-out both }
.ch-card.is-hot .ch-btn      { animation: ch-press 2200ms linear both }
.ch-cur svg { position: absolute; width: 30px; height: 30px }
.ch-cur .ar { left: 0; top: 0 }
.ch-cur .hd { left: -13px; top: -1px; opacity: 0 }

/* The rest frame is the finished screen, so with motion off there is still something true to look
   at — it just never moves. */
@media (prefers-reduced-motion: reduce) {
  .ch-card, .ch-card * { animation: none !important; transition: none !important }
}
