/* Monte Lattice - the tool web: the M in the middle, one tile per tool around it. Drawn by the
   tool_grid macro (templates/_tools.html) on /lattice and the homepage, and settled by
   static/js/toolweb.js. One sheet, linked by base.html and profile.html, so the rules cannot
   drift. Tokens only: it relies on tokens.css being loaded before it. */

/* --- Tool grid: the M in the middle, the engine's tools around it ---
   One inline SVG built from tools.py. Colours are tokens only, so light and dark just work. The
   SVG scales by its viewBox, so it never causes horizontal scroll at 375px. */
.tool-grid-wrap { max-width: 560px; margin: 28px auto 0; }
.tool-grid {
  display: block;
  width: 100%;
  height: auto;
}
/* The hairline keeps the tile's silhouette in dark mode, where --teal is close to the page ground. */
.tool-grid__core rect { fill: var(--teal); stroke: var(--brass-warm); stroke-width: 1.5; }
.tool-grid__core path { fill: var(--brass); }
.tool { color: var(--muted); }
/* Every tool is the same 48x48 tile; the mark or monogram sits inside it, the name under it. */
.tool__tile { fill: var(--surface); stroke: var(--rule-strong); stroke-width: 1; }
.tool__mark svg path { fill: currentColor; }
.tool__monogram {
  fill: var(--muted);
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 600;
  text-anchor: middle;
}
.tool__name {
  fill: var(--muted);
  font-family: var(--font-body);
  font-size: 14px;  /* viewBox units: ~8.5px at 375px, the smallest it is allowed to get */
  letter-spacing: 0.02em;
  text-anchor: middle;
}

/* --- Tool grid settle: scattered logos drift into the grid once, when it scrolls into view ---
   The markup is the settled grid. toolweb.js adds .is-scattered (only when motion is allowed),
   then swaps it for .is-settling when the grid is on screen. The animation targets the INNER
   .tool__mark group: a CSS transform on an SVG element replaces its transform attribute, and the
   outer .tool group's attribute is what holds the grid position. */
/* Deterministic scatter per grid position (13 tools; the core <g> is the first <g>, so tool k is
   :nth-of-type(k + 2)). Offsets are in viewBox units and clamped so the START position stays
   inside the 560x300 picture (24 units of margin): nothing is clipped at the edge. */
.tool-grid .tool:nth-of-type(2) .tool__mark { --sx: -32px; --sy: -26px; --sr: -34deg; --i: 0; }
.tool-grid .tool:nth-of-type(3) .tool__mark { --sx: 8px; --sy: -2px; --sr: 19deg; --i: 1; }
.tool-grid .tool:nth-of-type(4) .tool__mark { --sx: 105px; --sy: 59px; --sr: -19deg; --i: 2; }
.tool-grid .tool:nth-of-type(5) .tool__mark { --sx: -39px; --sy: -26px; --sr: 34deg; --i: 3; }
.tool-grid .tool:nth-of-type(6) .tool__mark { --sx: 32px; --sy: 20px; --sr: -4deg; --i: 4; }
.tool-grid .tool:nth-of-type(7) .tool__mark { --sx: -32px; --sy: -80px; --sr: -42deg; --i: 5; }
.tool-grid .tool:nth-of-type(8) .tool__mark { --sx: 11px; --sy: -19px; --sr: 11deg; --i: 6; }
.tool-grid .tool:nth-of-type(9) .tool__mark { --sx: 108px; --sy: 42px; --sr: -27deg; --i: 7; }
.tool-grid .tool:nth-of-type(10) .tool__mark { --sx: -36px; --sy: -58px; --sr: 26deg; --i: 8; }
.tool-grid .tool:nth-of-type(11) .tool__mark { --sx: 61px; --sy: 3px; --sr: -12deg; --i: 9; }
.tool-grid .tool:nth-of-type(12) .tool__mark { --sx: -83px; --sy: 26px; --sr: 41deg; --i: 10; }
.tool-grid .tool:nth-of-type(13) .tool__mark { --sx: 14px; --sy: -36px; --sr: 3deg; --i: 11; }
.tool-grid .tool:nth-of-type(14) .tool__mark { --sx: 88px; --sy: 25px; --sr: -35deg; --i: 12; }
.tool-grid.is-scattered .tool__mark {
  transform: translate(var(--sx), var(--sy)) rotate(var(--sr));
  opacity: 0;
}
.tool-grid.is-settling .tool__mark {
  animation: tool-settle 1s cubic-bezier(0.2, 0.7, 0.2, 1) both;
  animation-delay: calc(var(--i) * 45ms);
}
@keyframes tool-settle {
  from { transform: translate(var(--sx), var(--sy)) rotate(var(--sr)); opacity: 0; }
  to { transform: none; opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .tool-grid .tool__mark { animation: none !important; transform: none !important; opacity: 1 !important; }
}
/* Printed or exported before it ever scrolled into view: show the finished grid. */
@media print {
  .tool-grid .tool__mark { animation: none !important; transform: none !important; opacity: 1 !important; }
}

/* --- Web backdrop: the drifting constellation behind a whole tool web ---
   One class for /lattice (around the wordmark, the line and the web) and the homepage (around the
   web). The layer fills the box and the box carries bottom padding, so it runs from above the web
   to past the last row of labels (they hang below the SVG's own box); nothing sticks out sideways,
   so it cannot add a horizontal scroll. The art is sized by HEIGHT (120% of the box): these boxes
   are close to the art's own 2:1, so `cover` would leave almost no overflow to drift across and
   the lines would sit still. At 120% it always overflows by 20% of the height, and the box is
   covered while its width is at most 2.4 times its height (a test pins that). Same svg, same keyframes (topbar.css) and the same
   mid-cycle start as the other bands. Opacity 0.2 is the most that keeps the muted tile names at
   4.5:1 (tests recompute it). Decorative: no pointer events, behind the content. */
.web-backdrop {
  position: relative;
  isolation: isolate;
  padding-bottom: 40px;
}
.web-backdrop::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-color: var(--brass-warm);
  opacity: 0.2;
  -webkit-mask: url("../img/constellation.svg") no-repeat center top / auto 120%;
  mask: url("../img/constellation.svg") no-repeat center top / auto 120%;
  animation: constellation-drift 30s ease-in-out -15s infinite alternate;
}
@media (prefers-reduced-motion: reduce) {
  .web-backdrop::before { animation: none; }
}

/* --- Repo web (/code): the same macro, one tile per public repo with its stack under the name ---
   The sub-lines use the name's size so the smallest text stays above the 8px floor at 375px. The
   scatter starts are re-set for these slots: the tool web's offsets would start the first tile
   past the left edge. Tokens only. */
.tool__sub {
  fill: var(--muted);
  font-family: var(--font-body);
  font-size: 14px;
  letter-spacing: 0.02em;
  text-anchor: middle;
}
.tool-grid[data-web="repos"] .tool__name { fill: var(--ink); }  /* the repo name above its stack */
.tool-grid[data-web="repos"] .tool:nth-of-type(2) .tool__mark { --sx: 30px; --sy: -10px; --sr: -34deg; --i: 0; }
.tool-grid[data-web="repos"] .tool:nth-of-type(3) .tool__mark { --sx: -30px; --sy: 20px; --sr: 19deg; --i: 1; }
.tool-grid[data-web="repos"] .tool:nth-of-type(4) .tool__mark { --sx: 35px; --sy: -20px; --sr: -19deg; --i: 2; }
.tool-grid[data-web="repos"] .tool:nth-of-type(5) .tool__mark { --sx: -30px; --sy: -30px; --sr: 34deg; --i: 3; }
.tool-grid[data-web="repos"] .tool:nth-of-type(6) .tool__mark { --sx: 30px; --sy: -30px; --sr: -4deg; --i: 4; }
