/* ============================================================
   Grizzly Beam — SCHEMATIC VISUALS
   The drawing language for the diagrams that sit beside a section's copy.
   Loaded per page through pageCss, not globally: most pages carry no visual.

   Two rules hold the set together:

   1. Structure is currentColor at a set opacity, never a fixed gray. One file
      then works on an ink section and on a paper one, and a drawing can be moved
      between sections without a second copy.
   2. One amber element per drawing — whatever the section is actually claiming.
      Everything else is structure.

   Markup lives in src/components/visuals/*.astro, one component per drawing.
   ============================================================ */

.gbv { width: 100%; height: auto; display: block; overflow: visible; }

/* hairline structure — frames, grids, boxes that only hold a shape */
.gbv .s { fill: none; stroke: currentColor; stroke-opacity: 0.3; stroke-width: 1.5; }
/* a line that carries meaning, not just a boundary */
.gbv .s2 { fill: none; stroke: currentColor; stroke-opacity: 0.58; stroke-width: 1.8; }
/* a block of content: a paragraph, a card, a thumbnail */
.gbv .f, .gbv .f rect { fill: currentColor; fill-opacity: 0.14; stroke: none; }
/* a bar that stands for a measured quantity */
.gbv .s2bar rect { fill: currentColor; fill-opacity: 0.26; stroke: none; }
.gbv .s2arrow, .gbv .s2arrow path { fill: none; stroke: currentColor; stroke-opacity: 0.55; stroke-width: 1.8; }
.gbv .s2pin path { fill: currentColor; fill-opacity: 0.22; stroke: none; }
.gbv .pin--red { fill: var(--danger); stroke: none; }
.gbv .pin--green { fill: var(--success); stroke: none; }
.gbv .s2star path { fill: currentColor; fill-opacity: 0.3; stroke: none; }
.gbv .s2ok path { fill: none; stroke: currentColor; stroke-opacity: 0.5; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.gbv .mark path { fill: currentColor; fill-opacity: 0.5; }
.gbv .box { fill: currentColor; fill-opacity: 0.04; }
.gbv .col rect { fill: currentColor; fill-opacity: 0.03; }

/* the one accent */
/* `color` as well as `stroke`: the arrowhead marker fills itself with currentColor,
   which resolves against the element's color property and not against its stroke.
   Without this an amber path ends in a text-colored head. */
.gbv .a { fill: none; stroke: var(--accent); color: var(--accent); stroke-width: 2; stroke-linecap: round; }
.gbv .af { fill: var(--accent); stroke: none; }
.gbv .dash { stroke-dasharray: 5 5; }
.gbv .strike { stroke-opacity: 0.5; stroke-width: 1.5; }

/* browser window dots — the brand's own status tokens, so "red, amber, green"
   still comes out of the palette instead of borrowing the macOS colors */
.gbv .tl { stroke: none; }
.gbv .tl--r { fill: var(--danger); }
.gbv .tl--y { fill: var(--warning); }
.gbv .tl--g { fill: var(--success); }

/* copy inside a mock — real sentences, not gray bars. A wireframe of gray blocks
   shows the layout; a wireframe with words shows what the layout is FOR, which is
   what the section beside it is arguing. */
.gbv .uih { font-family: var(--f-disp); font-weight: 600; font-size: 16px; fill: currentColor; fill-opacity: 0.88; }
.gbv .uih.sm { font-size: 13px; }
.gbv .uih.xs { font-size: 11.5px; }
.gbv .uih--amber { fill: var(--accent); fill-opacity: 1; }
.gbv .ui { font-family: var(--f-body); font-size: 10.5px; fill: currentColor; fill-opacity: 0.62; }
.gbv .ui.md { font-size: 11.5px; }
.gbv .ui.sm { font-size: 9.5px; }
.gbv .ui.b { font-weight: 600; fill-opacity: 0.85; }
.gbv .ui.dim { fill-opacity: 0.45; }
/* always ink: it sits on the amber button, which is light in both themes */
.gbv .uibtn { font-family: var(--f-disp); font-weight: 600; font-size: 12px; fill: var(--ink); }
.gbv .uibtn.sm { font-size: 10.5px; }
/* a numeral on a colored marker. White, not ink: on --danger it measures 5.17
   against ink's 3.47, and the digit is the only thing in the marker. */
.gbv .uibtn.on-dot { fill: var(--white); }
/* keypad digits and the number on the screen */
.gbv .key { font-family: var(--f-mono); font-size: 13px; fill: currentColor; fill-opacity: 0.7; }
.gbv .key.num { font-size: 11px; fill-opacity: 0.55; letter-spacing: 0.04em; }
/* the handset glyph on the call button, ink on amber in both themes */
.gbv .callglyph { fill: none; stroke: var(--ink); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* a review star in a brand color */
.gbv .star--green path, .gbv path.star--green { fill: var(--success); fill-opacity: 1; }

/* ---------- A map, not graph paper ----------
   #local-map. Four things do the work: two road weights, a river, and the blocks
   between them. Everything is currentColor at a set opacity, so the map inverts
   with the card like the rest of the set, and the river is a stroked band rather
   than a filled shape — one path, and it keeps its width wherever it bends. */
.gbv .road { fill: none; stroke: currentColor; stroke-opacity: 0.24; stroke-width: 2.2; }
.gbv .lane { fill: none; stroke: currentColor; stroke-opacity: 0.12; stroke-width: 1; }
.gbv .water { fill: none; stroke: currentColor; stroke-opacity: 0.1; stroke-width: 15; stroke-linecap: round; stroke-linejoin: round; }
.gbv .blk { fill: currentColor; fill-opacity: 0.07; stroke: none; }

/* ---------- Three pins, three listings, in step ----------
   #local-map. One 6s clock, three beats of 2s, and everything that belongs to a
   beat shares the same keyframes with a 2s and 4s offset. The offsets are safe
   here in a way they were not for the traveling arrowhead: there it was two
   DIFFERENT keyframe sets that would drift against the restart, here it is one
   set staggered against itself, which is exactly what a rota is.

   Nothing changes color in place — an amber copy sits over the gray one and
   fades in, so currentColor keeps working for the gray on either background. */
.gbv .beat { opacity: 0; animation: gbvBeat 6s linear infinite; }
.gbv .beat--2 { animation-delay: 2s; }
.gbv .beat--3 { animation-delay: 4s; }
@keyframes gbvBeat {
  0%      { opacity: 0; }
  3%      { opacity: 1; }
  30%     { opacity: 1; }
  33.33%  { opacity: 0; }
  100%    { opacity: 0; }
}

/* ---------- Two bubbles, taking turns ----------
   #talk-call. The bubble swells, its line appears, it settles, then the other one
   answers. Bubbles run on a 4s clock (each one is up once per 4s, the second
   offset by 2s); the lines run on 8s, so each bubble gets two different lines
   before the conversation repeats.

   transform-box: fill-box makes the group scale about its own middle rather than
   about the SVG origin, which is what an SVG group does by default. */
.gbv .bub { transform-box: fill-box; transform-origin: center; animation: gbvBub 4s ease-in-out infinite; }
.gbv .bub--b { animation-delay: 2s; }
@keyframes gbvBub {
  0%   { transform: scale(1); }
  10%  { transform: scale(1.055); }
  40%  { transform: scale(1.055); }
  50%  { transform: scale(1); }
  100% { transform: scale(1); }
}
.gbv .msg { opacity: 0; animation: gbvMsg 8s linear infinite; }
.gbv .msg--2 { animation-delay: 2s; }
.gbv .msg--3 { animation-delay: 4s; }
.gbv .msg--4 { animation-delay: 6s; }
@keyframes gbvMsg {
  0%   { opacity: 0; }
  4%   { opacity: 1; }
  22%  { opacity: 1; }
  26%  { opacity: 0; }
  100% { opacity: 0; }
}


/* captions — labels, never copy */
.gbv .t {
  font-family: var(--f-mono); font-size: 11px; letter-spacing: 0.08em;
  fill: currentColor; fill-opacity: 0.55; stroke: none;
}
.gbv .num text { font-family: var(--f-mono); font-size: 13px; fill: var(--accent); }
/* A type specimen has to be readable AS a letterform, otherwise it is just a gray
   shape — 0.5 was too faint for the shape of the A to register and it read as a
   default sans. The faces are the real pair: --f-disp is Montserrat, --f-body is
   Hanken Grotesk. */
.gbv .spec { font-family: var(--f-disp); font-weight: 700; font-size: 46px; fill: currentColor; fill-opacity: 0.82; }
.gbv .spec--body { font-family: var(--f-body); font-weight: 400; font-size: 27px; fill-opacity: 0.62; }
.gbv .spec--green { fill: var(--success); fill-opacity: 1; }

/* A solid block in a brand color — a palette swatch, or a stat tile that has been
   asked to carry a color. --danger and --success are brand book §04, where core.css
   marks them "product UI ONLY, never marketing". Shown AS the palette that reading
   holds; used to color a marketing tile it is a stretch. Worth a look before these
   drawings are adopted. */
.gbv .sw { stroke: none; }
.gbv .sw--amber { fill: var(--accent); }
.gbv .sw--red { fill: var(--danger); }
.gbv .sw--green { fill: var(--success); }
.gbv .spec.xs { font-size: 27px; }
.gbv .spec.sm { font-size: 22px; }
.gbv .spec.md { font-size: 34px; }
.gbv .spec.lg { font-size: 48px; }

/* markers inherit the color of the path that uses them */
.gbv .s2arrow { color: inherit; }

/* text in a brand color, for a figure that is the point of its row */
.gbv .tx--red { fill: var(--danger); fill-opacity: 1; }
.gbv .tx--amber { fill: var(--accent); fill-opacity: 1; }
.gbv .uih.lg { font-size: 23px; }
/* a small red cross, the partner of .s2ok */
.gbv .s2no path { fill: none; stroke: var(--danger); stroke-width: 2; stroke-linecap: round; }
/* a dot on a spine that is not the one being pointed at. .f at 0.14 is right for a
   block of content but disappears into the line it sits on, so it gets its own step. */
.gbv .dot--us { fill: currentColor; fill-opacity: 0.34; stroke: none; }

/* a box drawn in the brand red — dead time, a gap, something to avoid */
.gbv .box--red { fill: none; stroke: var(--danger); stroke-width: 1.5; }

/* ---------- Four findings, one at a time ----------
   The sibling of .beat, for a drawing with four steps instead of three: 8s, four
   beats of 2s, same one-set-staggered-against-itself trick. */
.gbv .beat4 { opacity: 0; animation: gbvBeat4 8s linear infinite; }
.gbv .beat4--2 { animation-delay: 2s; }
.gbv .beat4--3 { animation-delay: 4s; }
.gbv .beat4--4 { animation-delay: 6s; }
@keyframes gbvBeat4 {
  0%    { opacity: 0; }
  2%    { opacity: 1; }
  23%   { opacity: 1; }
  25%   { opacity: 0; }
  100%  { opacity: 0; }
}

/* ---------- Traveling arrowhead ----------
   The line stands still and a triangle rides it. Each triangle is drawn around its
   own origin and placed on the route with offset-path, which is declared inside the
   component next to the path it matches; offset-rotate keeps its nose along the curve.

   The two run on one 5s clock with no delay between them, so they stay in phase
   forever. The wait is written into the second one's keyframes instead:

     0.0 - 1.4s   first arrow travels, then stops
     1.6 - 3.0s   second arrow sets off from where the first one stopped
     3.0 - 4.4s   both hold
     4.4 - 5.0s   both fade, then it starts again

   A delay would have drifted the two out of phase against the restart. */
.gbv .travel {
  fill: var(--accent);
  stroke: none;
  offset-rotate: auto;
  offset-distance: 0%;
  opacity: 0;
  animation: gbvTravel1 5s linear infinite, gbvFade1 5s linear infinite;
}
.gbv .travel--2 { animation-name: gbvTravel2, gbvFade2; }

@keyframes gbvTravel1 {
  0%   { offset-distance: 0%; }
  28%  { offset-distance: 100%; }
  100% { offset-distance: 100%; }
}
@keyframes gbvFade1 {
  0%   { opacity: 0; }
  3%   { opacity: 1; }
  88%  { opacity: 1; }
  95%  { opacity: 0; }
  100% { opacity: 0; }
}
@keyframes gbvTravel2 {
  0%   { offset-distance: 0%; }
  32%  { offset-distance: 0%; }
  60%  { offset-distance: 100%; }
  100% { offset-distance: 100%; }
}
@keyframes gbvFade2 {
  0%   { opacity: 0; }
  32%  { opacity: 0; }
  35%  { opacity: 1; }
  88%  { opacity: 1; }
  95%  { opacity: 0; }
  100% { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  /* Nothing travels, nothing pulses, nothing takes turns. Each animated element
     stands in the state it spends most of its time in, which is also the state the
     drawing is making its point with. */
  .gbv .travel { animation: none; opacity: 1; offset-distance: 100%; }
  .gbv .beat, .gbv .beat4, .gbv .bub, .gbv .msg { animation: none; }
  .gbv .beat, .gbv .beat4 { opacity: 0; }
  .gbv .beat--1, .gbv .beat4--1 { opacity: 1; }
  .gbv .msg { opacity: 0; }
  .gbv .msg--1, .gbv .msg--2 { opacity: 1; }
}
