/* =============================================================================
   The orrery, and the night it hangs in.

   Everything that moves here is transform, opacity or stroke-dashoffset, which
   are the three things a browser can animate on the compositor without touching
   layout or style. Nothing animates a custom property that descendants read: it
   is a style level value, so changing it per frame invalidates style on every
   descendant that reads it, on the main thread, and a mid range phone will drop
   frames. --lit-c changes only on interaction.
   ========================================================================== */

@property --lit-c { syntax:"<color>"; inherits:true; initial-value:#968CFF }

/* ------------------------------------------------------------------- night */
/* One field, not a band. The page used to be a dark hero with a light slab of
   cards bolted under it, and the seam between them was the loudest edge on the
   site: it cut the map off from its own index and announced that the pretty
   part had ended. The sky now runs from the masthead to the last line of the
   footer, and everything below the orrery is furniture standing in it. */
.fg-sky{
  position:relative;
  isolation:isolate;
  padding-block:6px 0;
  /* The fade at the foot is not decoration either. Every layer in this stack is
     cut off flat at the section's bottom edge, and a cut gradient draws a hard
     horizontal line across the page -- the exact seam this whole redesign is
     about, reintroduced by the thing meant to remove it. The scene has to end by
     running out, not by stopping.

     THIS STACK USED TO BE THE WHOLE SKY, and it is now the light in the room
     rather than the weather. Three broad radial washes were carrying the entire
     background, and a radial wash has exactly one spatial scale -- so however
     the colours were tuned the result was a blur, which is the single loudest
     thing about a picture that says a machine made it. The cloud moved to the
     canvas, where it can have filaments (see the sky() block in atlas.js). What
     is left here is what a gradient is genuinely the right tool for: a large,
     soft, low-frequency field that has to recolour on interaction without
     costing a repaint.

     So each of these three has one job and is aimed at one place.
       1  the LIT node's colour, wide and low, right behind the ring -- this is
          the layer the hover moves, and the reason it is first (topmost) is
          that it is the only one anybody is meant to notice moving
       2  the WORLD's own hue, tight and high, sitting where the core sits
       3  a cold counter-light in the opposite corner, so the field has two
          temperatures and never reads as one tinted sheet
       4  a deepening at the very top. The masthead sits on this, and a darker
          ground under it is worth more than it costs anywhere else. */
  background:
    radial-gradient(72% 36% at 50% 44%,
      color-mix(in srgb, var(--lit-c) 10%, transparent), transparent 76%),
    radial-gradient(44% 25% at 50% 21%,
      color-mix(in srgb, var(--world-c, var(--lit-c)) 13%, transparent), transparent 74%),
    /* 70% and 40%, not 86% and 42%: a radial centred at 86% of the height with
       a 42% vertical radius is still laying down colour at the element's last
       row, and every layer here has to have finished before that row or it is
       cut flat against whatever is underneath. (100 - 70) / 40 puts the bottom
       edge at 0.75 of the gradient, past the 0.72 where it goes transparent,
       and it stays past it at any sky height because both are percentages. */
    radial-gradient(62% 40% at 10% 70%,
      color-mix(in srgb, var(--d-systems) 6%, transparent), transparent 72%),
    linear-gradient(180deg,
      var(--sky-cap),
      color-mix(in srgb, var(--sky-cap) 40%, transparent) 26%,
      transparent 58%);
  transition:--lit-c .7s cubic-bezier(.22,.7,.28,1);
}
/* THE ONE STATE IN WHICH THAT TRANSITION MUST NOT RUN.
   --lit-c inherits, so a frame of it moving is a style recalc on every element
   under the sky -- the whole scene, the map and the index below it -- whether or
   not that element reads the property. That is the price of the hover, and the
   hover is worth it: it is one gesture, at a moment when nothing else is
   happening.
   The FIRST write is not that. atlas.js lights a body as soon as it boots, to
   say which one the ring is pointing at, and there is no previous colour for
   that to travel from -- it interpolates out of the property's initial value,
   which is a colour the page was never showing. So it buys nothing, and it
   spends 0.7 seconds of main thread recalc during the entrance, which is the
   one second of the visit that has the least to spare. Measured on the 24 body
   ring: 8 to 13 dropped frames with it, 1 to 3 without.
   Held for a single style pass while the value lands. See lightFirst(). */
.fg-sky.is-set{transition:none}
.fg-stars{
  position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:0;
}
/* will-change promotes the canvas to its own compositor layer, and this canvas
   is as tall as the whole scene -- so on a phone, where the parallax is turned
   off anyway, it was reserving a layer the size of the page to hold something
   that never moves. Both the hint and the transform now exist only where there
   is a pointer to drive them. */
@media (hover:hover){
  .fg-stars{
    transform:translate3d(var(--px,0px), var(--py,0px), 0);
    will-change:transform;
  }
}
/* A vignette and a film of grain, in ONE element and one layer. The grain is
   what stops a large flat dark field from banding into visible steps on an 8
   bit panel, and it is most of the difference between a dark background and a
   photographed sky.
   Two things here are performance decisions, not looks. The grain's opacity
   lives inside the SVG rather than on a second pseudo element, so the sky is
   one composited layer instead of two full page ones. And the turbulence is two
   octaves: the third is invisible under 5% opacity and doubles what the browser
   has to rasterise before first paint.

   A third used to be listed here: that the bottom fade was a gradient in this
   same stack rather than a mask, because a mask forces its own layer and an
   extra compositing pass for an effect a gradient does for free. It does not do
   it for free, and it did not do it. See the mask below. */
.fg-veil{
  position:absolute;inset:0;pointer-events:none;z-index:1;
  /* How long the scene takes to run out at the foot. Capped as a share of the
     sky as well as in pixels, because the same 420px is a third of a node
     page's sky and half of a short one on a phone. */
  --foot:min(420px, 34%);
  /* NOTHING IN THIS ELEMENT MAY REACH ITS OWN BOTTOM EDGE.
     The foot used to be a band of --paper painted over the scene, and it could
     not work: what is behind the bottom of the sky is not --paper. body carries
     two large washes over that token (main.css), sized as a share of the whole
     document, so the ground under the sky's last row is a few levels lighter
     than --paper -- and lighter by a different amount on every page, because
     every page is a different length. Measured on a node page at 1440: the
     sky's last row 4,5,13 and the first row below it 6,7,17. Four levels of
     blue arriving in one pixel is a line across the page, drawn by the layer
     whose only purpose is to stop there being one. Lengthening that band and
     easing it changed nothing, and could not: the error is not in how the ramp
     travels, it is in where it ends.

     So it ends nowhere. The veil is masked out over the foot instead, which
     takes the vignette and the grain to zero together and leaves the body's own
     ground running underneath, unbroken, exactly as it does either side of the
     section. It costs this element its own compositing layer, which is what the
     note above says a gradient buys you for free -- and it is worth paying,
     because the free version was wrong. It is a static overlay: no transform,
     no animation, rasterised once.

     Everything else in the scene had to learn to end as well, or the mask would
     simply have exposed a differently cut edge. The stars and the cloud thin out
     across the same distance in the painter, and the .fg-sky washes below are
     shaped so that none of them is still contributing colour at the last row.
     See sky() in atlas.js. */
  /* Smoothstep, in nine stops, and the same curve the painter tapers the stars
     and the cloud with (foot() in atlas.js). A straight ramp is visible at both
     of its ends even when it is going nowhere, because the eye finds the kink in
     the slope rather than the difference in value. */
  -webkit-mask-image:linear-gradient(180deg, rgb(0 0 0 / 1.000) calc(100% - var(--foot) * 1.000), rgb(0 0 0 / 0.957) calc(100% - var(--foot) * 0.875), rgb(0 0 0 / 0.844) calc(100% - var(--foot) * 0.750), rgb(0 0 0 / 0.684) calc(100% - var(--foot) * 0.625), rgb(0 0 0 / 0.500) calc(100% - var(--foot) * 0.500), rgb(0 0 0 / 0.316) calc(100% - var(--foot) * 0.375), rgb(0 0 0 / 0.156) calc(100% - var(--foot) * 0.250), rgb(0 0 0 / 0.043) calc(100% - var(--foot) * 0.125), rgb(0 0 0 / 0.000) calc(100% - var(--foot) * 0.000));
  mask-image:linear-gradient(180deg, rgb(0 0 0 / 1.000) calc(100% - var(--foot) * 1.000), rgb(0 0 0 / 0.957) calc(100% - var(--foot) * 0.875), rgb(0 0 0 / 0.844) calc(100% - var(--foot) * 0.750), rgb(0 0 0 / 0.684) calc(100% - var(--foot) * 0.625), rgb(0 0 0 / 0.500) calc(100% - var(--foot) * 0.500), rgb(0 0 0 / 0.316) calc(100% - var(--foot) * 0.375), rgb(0 0 0 / 0.156) calc(100% - var(--foot) * 0.250), rgb(0 0 0 / 0.043) calc(100% - var(--foot) * 0.125), rgb(0 0 0 / 0.000) calc(100% - var(--foot) * 0.000));
  background:
    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='.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.05'/%3E%3C/svg%3E"),
    radial-gradient(116% 68% at 50% 32%, transparent 46%, rgb(0 0 0 / .58) 100%);
  background-size:140px 140px, auto;
}
.fg-sky > .wrap,
.fg-sky > *:not(.fg-stars):not(.fg-veil){position:relative;z-index:2}

/* ------------------------------------------------------------------- stage */
/* A container, so the stage can be asked how wide it is. It has to be the OUTER
   element: a container query cannot style its own container, and what the query
   decides here is the shape of .fg-atlas__inner. */
.fg-atlas{position:relative;width:100%;container:fg-stage / inline-size}
.fg-atlas__inner{
  position:relative;
  /* 891px, and it is not a taste. The wide frame is now the same box on every
     page (see $fg_frame in atlas.php), and that box is the largest any ring
     produced -- so at the old 760 every drawing in the atlas would have been
     scaled down, and the tightest ring's names are already sitting on their
     10px floor. 891/1050.3 is 0.8483 pixels per viewBox unit, which is exactly
     what the twenty-four body ring had at 760 and more than every other page
     had. Nothing shrinks; most pages are drawn a little larger. */
  width:min(100%, 891px);
  margin-inline:auto;
  aspect-ratio:var(--vb-ratio, 1.07);
  /* Type inside the bodies is sized in container units, so a name is always in
     proportion to the body holding it and no breakpoint has to be invented for
     each ring size. This one line is what makes that possible. */
  container-type:inline-size;
}
.fg-svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
/* The centre, in a box of its own under the ring. Same rectangle as the stage,
   so the two drawings register exactly; see the note in atlas.php for why the
   core cannot simply be a group inside the ring's own <svg>. */
.fg-core-stage{position:absolute;inset:0}
/* Paint servers only. Kept out of both scenes so that hiding one cannot take a
   gradient the other one borrows out of the document with it. */
.fg-defs{position:absolute;width:0;height:0;overflow:hidden}

/* ------------------------------------------------------------- the way up */
/* The parent, as a small satellite above the ring: the one direction the map
   could not take you. A pill rather than a bare arrow because the rule of the
   scene is that a name sits inside the body that carries it -- this is a body,
   it is named, and the name is the destination. In flow above the stage, not
   laid over it: on a 24 child ring the far orbs own the top of the frame.
   Centred, so right to left costs nothing; the arrow points up in every
   script. */
.fg-up{
  position:relative;z-index:3;
  display:flex;align-items:center;gap:9px;
  width:fit-content;min-height:42px;
  margin-inline:auto;margin-bottom:2px;
  padding:9px 18px;border-radius:999px;
  border:1px solid rgb(255 255 255 / .16);
  background:
    radial-gradient(130% 170% at 30% 18%,
      color-mix(in srgb, var(--hue, var(--d-core)) 24%, transparent),
      transparent 72%),
    rgb(9 10 24 / .5);
  color:#fff;
  font-family:var(--f-mono);
  font-size:calc(11px * var(--script-scale));
  letter-spacing:.16em;text-transform:uppercase;
  transition:border-color .25s ease, box-shadow .25s ease,
    transform .25s ease, background-color .25s ease;
}
.fg-up i{
  font-style:normal;line-height:1;
  color:var(--hue, var(--d-core));
  text-shadow:0 0 10px var(--hue, var(--d-core));
  /* The arrow rides slightly high at these sizes; optical, not structural. */
  translate:0 -1px;
}
.fg-up b{font-weight:500}
.fg-up:hover,
.fg-up:focus-visible{
  border-color:color-mix(in srgb, var(--hue, var(--d-core)) 55%, rgb(255 255 255 / .22));
  box-shadow:0 0 26px color-mix(in srgb, var(--hue, var(--d-core)) 30%, transparent);
  transform:translateY(-1px);
}
/* The parent's name is content in the page's own script; see the tail rule. */
[dir="rtl"] .fg-up{letter-spacing:0}
@media (prefers-reduced-motion:reduce){
  .fg-up{transition:none}
  .fg-up:hover,.fg-up:focus-visible{transform:none}
}
/* THE ONE PAGE WITH NOTHING ABOVE IT STILL HAS TO LEAVE ROOM FOR IT.
   The frame is the same box on every page now, so the core is drawn at the same
   size and at the same point of the stage wherever you are -- and then the
   locale home put it 44 pixels higher than everywhere else, because it is the
   one node with no parent and so the only one that draws no chip. 44px is the
   chip: 42 of min-height and 2 of margin. Reserving it costs an empty band at
   the top of one page and buys a centre that does not step up and down as you
   climb out of the tree. */
.fg-atlas:not(:has(.fg-up)) .fg-atlas__inner{margin-block-start:44px}

/* WHICH SHAPE THE STAGE IS.
   The template draws the ring twice -- landscape, and portrait for a column too
   narrow to set a name in a body that ring's size -- and this is the switch.
   It has to live here because it is the one thing CSS knows and PHP does not:
   the template computes geometry once, with no viewport in sight.
   Everything the swap needs is already on the elements as a second custom
   property, so nothing is measured and nothing moves after paint. */
.fg-svg--tall{display:none}


/* --------------------------------------------------------------- the chart */
.fg-bloom{opacity:.5;transition:opacity .7s ease}

.fg-guide{
  fill:none;
  stroke:rgb(255 255 255 / .07);
  stroke-width:1;
}
.fg-guide--far{stroke:rgb(255 255 255 / .045);stroke-dasharray:2 14}
/* Graduations round the track. Instrument, not decoration: they give the ring a
   scale, which is what separates a star chart from five circles on a line. */
.fg-ticks{
  fill:none;
  stroke:rgb(255 255 255 / .16);
  stroke-width:5;
  stroke-dasharray:1 21;
  stroke-linecap:round;
}

.fg-orbit{
  fill:none;
  stroke:rgb(255 255 255 / .24);
  stroke-width:1.3;
  stroke-dasharray:3 9;
}
/* The travelling light. One bright segment run along the same path by dash
   offset, so it follows the ellipse exactly instead of being a rotated chord
   that only touches the orbit at two points. */
.fg-orbit-lit{
  fill:none;
  stroke:var(--lit-c);
  stroke-width:2.4;
  stroke-linecap:round;
  stroke-dasharray:var(--seg, 160) var(--gap, 2000);
  stroke-dashoffset:var(--off, 0);
  opacity:.95;
  transition:stroke-dashoffset .72s cubic-bezier(.22,.7,.28,1);
}

/* ------------------------------------------------------------------- wires */
.fg-wire{
  fill:none;
  stroke-width:1.8;
  stroke-linecap:round;
  opacity:.8;
  transition:opacity .32s ease, stroke-width .32s ease;
}
.fg-wire.is-on{opacity:1;stroke-width:2.6}

/* -------------------------------------------------------------------- core */
.fg-core-halo{opacity:1;transition:opacity .6s ease}
.fg-core-disc,.fg-core-gloss{stroke:none}
/* A dark seat under the translucent fill, so the core reads as a solid body
   sitting in front of the wires rather than a smear of the wash behind it. */
.fg-core-seat{fill:var(--void);opacity:.94}
/* The rim is the reserved hue, not white. Every orb is drawn dark with a lit
   edge, and the core was the one body given a colourless one -- so while the
   ring read as five lamps, the centre read as a grey stone with a wash on it.
   It is the brightest thing in the scene; it should be lit like it. */
.fg-core-rim{
  fill:none;
  stroke:var(--core-c);
  stroke-width:1.8;
  opacity:.85;
}
/* A rim light on part of the circumference, swept round by dash offset so it
   always faces whatever is selected.
   It is the core's own light, not the selected node's. It used to be --lit-c,
   which put a coral or a violet arc across the one body that is supposed to
   wear no pillar colour at all -- and it was saying nothing the travelling
   light on the orbit, the ring on the disc and the lit row in the index were
   not already saying. Which node is selected is carried by where the arc
   points; it does not also need to be carried by what colour the arc is. */
.fg-core-arc{
  fill:none;
  stroke:#FFFFFF;
  stroke-width:2.8;
  stroke-linecap:round;
  stroke-dasharray:var(--cseg, 150) 2000;
  stroke-dashoffset:var(--coff, 0);
  transition:stroke-dashoffset .72s cubic-bezier(.22,.7,.28,1);
}

/* ------------------------------------------------------------------ bodies */
/* Bodies are dark inside and lit at the edge. That is not a style choice made
   after the fact: the name is set in white inside the body, and a saturated
   fill would put white type on a mid tone at about 2:1. An eclipsed planet is
   the only version of this drawing that is also readable. */
.fg-disc__glow{opacity:calc(.5 + .5 * var(--near));transition:opacity .34s ease}
.fg-disc__seat{fill:var(--void);opacity:.95}
/* The lit side and its highlight are the two things that say which way the
   light comes from, so they are also the two things that have to obey the
   depth cue: a body on the far side of the ellipse is turned away from us and
   its highlight should not be as strong as one at the near edge. --near is
   already 1 at the near edge and 0 at the far one. */
.fg-disc__key{opacity:calc(.6 + .4 * var(--near));transition:opacity .32s ease}
.fg-disc__gloss{opacity:calc(.5 + .5 * var(--near));transition:opacity .32s ease}
.fg-disc__rim{
  fill:none;
  stroke:var(--hue);
  stroke-width:1.6;
  opacity:calc(.5 + .45 * var(--near));
  transition:opacity .32s ease, stroke-width .32s ease;
}
.fg-disc__ring{
  fill:none;
  stroke:var(--hue);
  stroke-width:1.1;
  opacity:0;
  transition:opacity .32s ease, r .38s cubic-bezier(.3,1.4,.4,1);
}
/* A leaf is the end of the line: no children to descend into, so its edge is
   drawn rather than solid. You can see which bodies open before you press one. */
.fg-disc.is-leaf .fg-disc__rim{stroke-dasharray:5 6;stroke-width:1.4}

.fg-disc.is-on .fg-disc__glow{opacity:1}
.fg-disc.is-on .fg-disc__key,.fg-disc.is-on .fg-disc__gloss{opacity:1}
.fg-disc.is-on .fg-disc__rim{opacity:1;stroke-width:2.4}
.fg-disc.is-on .fg-disc__ring{opacity:.5}

/* ------------------------------------------------------------------- names */
/* The anchor IS the body: a circle laid over the disc with the name centred in
   it. Nothing sticks out of the drawing, so nothing can walk off the edge of a
   phone; the whole planet is the touch target; and right to left script needs
   no special case, because centred text has no side to be on. */
.fg-label{
  position:absolute;left:var(--x);top:var(--y);
  width:var(--dw);aspect-ratio:1;
  transform:translate(-50%,-50%);
  display:flex;flex-direction:column;
  justify-content:center;
  /* The label carries the name's own size so that `em` below means "a share of
     the name" and not "a share of 16px inherited from the page". The gap was the
     only length in this box that did not scale with the body holding it: on a
     desktop orb it is a third of a 20px name, and in a 51px phone orb it was
     still 5.4px -- half the name -- pushing a three line label far enough off
     centre to put its bottom line through the rim. */
  font-size:clamp(10px, calc(var(--fs, 3) * 1cqw), 21px);
  gap:.34em;
  /* The inset has to be a share of THIS circle, and a bare percentage is not:
     percentage padding resolves against the containing block's width, which here
     is the whole stage. `padding-inline:11%` put 67px of padding inside a 134px
     body and left the name exactly zero pixels to be set in. --dw is already the
     body's width as a share of that same block, so scaling it is the inset.

     5%, not 11%. This number is not margin -- it is the difference between a
     name fitting and being cut in half, and at 11% it was throwing away 22% of
     every disc. That was the single largest cause of mid-word breaks in the
     whole orrery: "Communication" needed 89px in the business ring and was given
     83, "Fundamentals" 95 in 89, and on a 390px phone 144 names were set wider
     than the box holding them. Widening the box to 90% of the diameter fixes
     every one of the desktop breaks by itself.

     It cannot go much further. A name is centred in a CIRCLE, so the width it
     may use is the chord at its own height, not the diameter: the top line of a
     three line name sits far enough off centre that a box any wider than this
     puts its corners outside the body. 5% is what measured clean at both stage
     widths -- see breakcheck.py, which asks the browser where the corners
     actually landed rather than trusting this comment. */
  padding-inline:calc(var(--dw, 20%) * .05);
  border-radius:50%;
  text-align:center;
  opacity:calc(.88 + .12 * var(--near));
  transition:opacity .3s ease;
  -webkit-tap-highlight-color:transparent;
}
.fg-label b{
  display:block;
  font-family:var(--f-display);font-weight:var(--w-name);
  font-size:15px; /* floor for engines without container units */
  font-size:clamp(10px, calc(var(--fs, 3) * 1cqw), 21px);
  line-height:1.08;letter-spacing:-.022em;
  color:rgb(255 255 255 / .95);
  /* break-word, never anywhere. `anywhere` also lowers the element's min-content
     contribution to one glyph, and a flex item sized by its content then resolves
     to that: every name in the ring set itself out one letter per line, vertically,
     like a shop awning. `break-word` breaks the same long words at the same places
     and leaves the intrinsic width alone. */
  /* No hyphenation. It was here as a safety net for a word too wide for its
     body, and it is not needed for that any more -- the template now sizes a
     name from the measured width of its longest word, so the word fits by
     construction. What it did instead was let `text-wrap:balance` buy an evener
     pair of lines by cutting a word in half: "CAD and techni- / cal drawing" in a
     120px body with room to spare. And it did that only where a hyphenation
     dictionary happened to be installed, so the same ring broke differently on
     Chrome, on Safari and on a phone. Balance now has to find its break at a
     space, which is where a name should break. */
  text-wrap:balance;overflow-wrap:break-word;hyphens:manual;
  text-shadow:0 1px 12px rgb(0 0 0 / .85), 0 0 3px rgb(0 0 0 / .7);
  transition:color .26s ease;
}
.fg-label em{
  display:block;font-style:normal;
  font-family:var(--f-mono);
  font-size:9px;
  font-size:clamp(8px, calc(var(--fs, 3) * .5cqw), 11px);
  letter-spacing:.11em;text-transform:uppercase;
  color:rgb(255 255 255 / .58);
  white-space:nowrap;
  text-shadow:0 1px 8px rgb(0 0 0 / .9);
  transition:color .26s ease;
}
.fg-label:hover b,.fg-label:focus-visible b,.fg-label.is-on b{color:#fff}
.fg-label.is-on em,.fg-label:hover em{color:color-mix(in srgb, var(--hue) 62%, #fff)}
.fg-label.is-on,.fg-label:hover,.fg-label:focus-visible{opacity:1}
.fg-label:focus-visible{outline:2px solid #fff;outline-offset:2px}

.fg-core-label{
  position:absolute;left:var(--x);top:var(--y);
  width:var(--dw);aspect-ratio:1;
  transform:translate(-50%,-50%);
  display:flex;flex-direction:column;
  justify-content:center;gap:.5em;
  padding-inline:calc(var(--dw, 28%) * .1);
  text-align:center;
  pointer-events:none;
}
.fg-core-label b{
  display:block;font-family:var(--f-display);font-weight:var(--w-display);
  font-size:24px;
  font-size:clamp(15px, calc(var(--fs, 4) * 1cqw), 34px);
  letter-spacing:-.038em;line-height:1.02;
  color:#fff;text-wrap:balance;overflow-wrap:break-word;
  text-shadow:0 2px 20px rgb(0 0 0 / .85), 0 0 6px rgb(0 0 0 / .6);
}
.fg-core-label span{
  display:block;
  font-family:var(--f-mono);
  font-size:10px;
  font-size:clamp(8px, calc(var(--fs, 4) * .34cqw), 12px);
  letter-spacing:.18em;text-transform:uppercase;
  color:var(--p-core);white-space:nowrap;
  text-shadow:0 1px 10px rgb(0 0 0 / .9);
}

/* 560px is measured, not chosen. The landscape ring sets every name in the tree
   without breaking one down to a 530px stage and starts failing just below it,
   so the switch sits above that with room for the next long name somebody adds.
   Above it the stage is capped at 560px anyway, which is why the number is the
   same one twice.
   Nothing here overrides a custom property: the template writes those inline and
   inline wins. It overrides the PROPERTIES that read them, which the template
   never sets, so the swap is a plain cascade with no !important in it. */
@container fg-stage (max-width: 560px){
  .fg-svg--wide{display:none}
  .fg-svg--tall{display:block}
  .fg-atlas__inner{aspect-ratio:var(--vb-ratio-tall, .78)}

  .fg-label{
    left:var(--x2);top:var(--y2);width:var(--dw2);
    /* 4% here against 5% on a wide stage. The type is sitting on its 10px floor
       all over this band -- that is what the band exists for -- so a name needs
       every pixel of its body, and the count below is out of the flow, which
       leaves the room to give it. */
    padding-inline:calc(var(--dw2, 20%) * .04);
  }
  .fg-label,
  .fg-label b{font-size:clamp(10px, calc(var(--fs2, 3) * 1cqw), 21px)}

  /* THE COUNT IS WHAT GOES, NOT THE NAME -- and here that is a fit rule rather
     than a preference. A name is set in a CIRCLE, so the width a line may use is
     the chord at that line's own height, and stacking a name above a count
     pushes both of its lines off centre, to where the body is narrower than the
     box they were measured against. On a wide stage that costs nothing: every
     part of the label scales with its disc, so the proportions hold at any size.
     Down here they do not. The name is pinned at 10px by its floor and the count
     at 8px by its own, so in a body a third smaller the pair is half again as
     tall in proportion -- which is what set "Vocabulary" 10% outside its own orb,
     and what ran "for children" straight through "4 INSIDE".
     Dropping the count centres the name on the widest chord the body has, and
     the number is not lost with it: the index below the map carries the same
     count for the same node. This supersedes the 340px rule that used to do
     this further down, which is why that one is gone. */
  .fg-label em{display:none}
  .fg-label{gap:0}

  .fg-core-label{
    left:var(--x2);top:var(--y2);width:var(--dw2);
    padding-inline:calc(var(--dw2, 28%) * .1);
  }
  .fg-core-label b{font-size:clamp(15px, calc(var(--fs2, 4) * 1cqw), 34px)}
  .fg-core-label span{font-size:clamp(8px, calc(var(--fs2, 4) * .34cqw), 12px)}
}

/* -------------------------------------------------------------- entrances */
/* THE RING COMES OUT OF THE CORE, AND IT COMES OUT AS ONE THING.
 *
 * It used to arrive in place: every body faded up and scaled from .82 where it
 * already was, which is a slide of the finished drawing rather than the drawing
 * being made. Nothing in it said what the picture MEANS -- that these bodies
 * belong to that centre -- and the page it opens is the same claim: press a
 * body and it becomes the next core.
 *
 * The version between this one and that one flew each body out of the middle
 * separately, on its own vector and its own delay. It said the right thing and
 * it moved badly, in both of the ways it could:
 *
 *   IT STUTTERED. Fourteen elements animating at once, half of them SVG groups
 *   carrying a gradient limb, a key, a specular and a glow drawn at nearly twice
 *   the body's radius. A moving SVG group is rasterised again every frame, and
 *   the keyframes read the per body offset out of a custom property, which is
 *   the one thing that stops an engine lifting an animation off the main thread
 *   at all. Every frame was a style recalc and a repaint of the whole ring.
 *
 *   IT COUNTED. A stagger of one body at a time is a queue, and a queue is what
 *   the eye reports: step, step, step, rather than a thing happening.
 *
 * Both go away together, because the ring is a rigid drawing centred on the
 * core -- so growing it out of the middle is a SCALE about the middle, and one
 * transform on one group does what fourteen were doing. It is smooth because
 * there is almost nothing to be smooth about: five animations on the whole
 * stage where there were more than thirty, none of them reading a custom
 * property, and every one a plain transform or opacity.
 *
 * Three beats, close enough together to be one motion with a grain in it:
 *
 *   .00  the core, alone, with nothing else on the stage to share the moment
 *   .18  the ring: track, graduations, wires and bodies, opening out of it
 *   .30  their names, arriving last and latest of all
 *
 * THE TRACK AND THE BODIES SHARE ONE DELAY, and that is a correctness rule
 * rather than a taste. A wire is drawn from the core's rim to a body's rim: the
 * two are one measurement, and running them on staggered clocks scales them by
 * different amounts at the same instant, so for a third of a second every wire
 * overshot the body it ends at and struck a line across its face. The ring is a
 * rigid drawing and has to be scaled rigidly. The names may lag because a name
 * is not measured against anything -- it is centred in its body, and centres do
 * not drift.
 */
@media (prefers-reduced-motion:no-preference){
  .fg-corebody{animation:fg-pop .66s cubic-bezier(.22,1.1,.36,1) both}
  .fg-core-label{animation:fg-core-in .6s cubic-bezier(.22,1,.36,1) both;animation-delay:.14s}

  /* UNLESS THE PAGE WAS ARRIVED AT RATHER THAN OPENED. atlas.js sets this before
     the first render of a document reached through a transition. The centre was
     on screen a moment ago and is being held exactly where it was, so an
     entrance here is a body breathing inside a photograph -- and it is the one
     element in the drawing whose whole job this hop is to not move. The ring
     keeps its entrance: the ring is the half that changed. */
  .fg-atlas.is-continuing .fg-corebody,
  .fg-atlas.is-continuing .fg-core-label{animation:none}

  .fg-ring,
  .fg-discs{animation:fg-open .92s cubic-bezier(.24,.74,.28,1) both;
    animation-delay:.18s}
  .fg-names{animation:fg-open-names .92s cubic-bezier(.24,.74,.28,1) both;
    animation-delay:.3s}

  /* The travelling light is an affordance, not part of the birth: it says the
     ring answers to a pointer, and there is nothing to point at yet. It is a
     bright segment at full stroke on a track that is still opening, so during
     the bloom it reads as a loose arc lying under the drawing rather than as
     part of it. It waits until the ring has landed and then simply appears. */
  .fg-orbit-lit{animation:fg-fade .55s ease both;animation-delay:.92s}
}
@keyframes fg-fade{from{opacity:0}}
@keyframes fg-pop{from{opacity:0;transform:scale(.82)}}
@keyframes fg-core-in{from{opacity:0;transform:translate(-50%,-50%) scale(.9)}}

/* Constants, not custom properties, and every keyframe states the transform.
 * The first is what lets an engine composite this at all; the second is so the
 * easing is applied to the interval it looks like it is applied to.
 *
 * .5 is the one number here that is measured rather than felt. It is about where
 * a body clears the core's own limb -- the orbs sit at roughly twice the core's
 * radius -- and it is where the fade begins, because anything visible inside
 * that radius is drawn over the core's face and reads as a smudge on the one
 * object in the drawing that should stay clean.
 *
 * The last two frames are a spring: it overshoots a little, gives back slightly
 * less than it took, and settles. That is the entire reason the ring feels
 * pleased to be there rather than merely assembled, and it is two keyframes.
 *
 * No `to`. The finished state is the element's own computed style, which is
 * where the template already put it. */
@keyframes fg-open{
  from{opacity:0;transform:scale(.06)}
  34% {opacity:.42;transform:scale(.5)}
  72% {opacity:1;transform:scale(1.035)}
  88% {opacity:1;transform:scale(.993)}
}
/* The names are held back further still. They are white on a dark ground, so
   they carry at an opacity the bodies under them do not, and a name legible
   while it is still crossing the core reads as a word passing over the hub
   rather than as a body being labelled. */
@keyframes fg-open-names{
  from{opacity:0;transform:scale(.06)}
  34% {opacity:0;transform:scale(.5)}
  72% {opacity:1;transform:scale(1.035)}
  88% {opacity:1;transform:scale(.993)}
}

/* WHERE `center` IS.
   view-box, not fill-box: the reference is the viewBox rather than the group's
   own ink, so the origin does not move when a ring happens to be lopsided --
   an odd child count, or a staggered inner orbit, puts the union of the bodies
   slightly off the centre they orbit, and scaling about THAT is a ring sliding
   as it opens. The frame is cropped symmetrically about the core ($fg_frame
   returns cx - w/2), so 50% of the viewBox is the core exactly, on every ring
   and at both stage shapes. */
.fg-ring,.fg-discs{transform-box:view-box;transform-origin:center}
.fg-corebody{transform-box:fill-box;transform-origin:center}

/* The names overlay is the frame, so its centre is the core for the same
   reason. inset:0 rather than a size, so it cannot disagree with the SVG. */
.fg-names{position:absolute;inset:0;transform-origin:center}

/* The graduations used to creep round the ring on a 140 second loop, and the
   glows used to breathe. Both are gone, and the page is better for it: an
   infinite animation means the main thread never goes idle, so the browser
   repaints a full width scene forever for two effects that, at those speeds,
   nobody could consciously see. Everything that moves here now is either an
   entrance that finishes in under a second or a response to something the
   reader did. */

/* ======================= moving between pages =======================
   The old version held the page for 280ms after a click and faded the scene
   out, then navigated. That is the worst of both: it is not a transition,
   because the next page knows nothing about it, and the 280ms is dead time
   added to every single navigation -- it delays the request, so it delays the
   response, so it delays first paint by the full amount, on every hop through
   the tree.

   This is a real one, and it costs nothing. The browser takes the snapshots and
   runs the morph itself; the click is not intercepted, the request goes out on
   the same tick it always would, and where the API is missing the navigation is
   simply a navigation. The only work the script does is rename two elements
   before leaving.

   What morphs: the body you pressed becomes the core of the page you land on.
   That is the actual claim the site makes -- that you fall into a subject and it
   becomes the centre of the next constellation -- so the transition is not
   decoration on top of the idea, it is the idea, animated once. */
@view-transition{navigation:auto}

/* The crossfade is deliberately the shortest thing here. It is what stands
   between the reader and the text of the next page, so it is over in 200ms and
   the page is fully legible from then on -- only the travelling body is still
   moving, and nothing waits for it. Slowing this down to make the morph feel
   grander would be paying for the animation with the reader's time to content. */
::view-transition-group(root){animation-duration:.2s}
::view-transition-old(root),::view-transition-new(root){
  animation-duration:.2s;
  animation-timing-function:cubic-bezier(.4,0,.2,1);
}
/* The name of the place. It is in the same box on both pages, so the group has
   almost nothing to interpolate and what the reader sees is the crossfade -- one
   name out, the next in, inside a body that has not moved. */
::view-transition-group(fg-core-text){
  animation-duration:.36s;
  animation-timing-function:cubic-bezier(.32,.74,.14,1);
}
::view-transition-old(fg-core-text),
::view-transition-new(fg-core-text){animation-duration:.36s}
/* The masthead and footer do not animate at all -- they are the same object in
   the same place, and a 200ms crossfade of a thing onto itself is just a blink. */
::view-transition-group(fg-mast),
::view-transition-group(fg-foot){animation-duration:0s}
::view-transition-old(fg-mast),::view-transition-new(fg-mast),
::view-transition-old(fg-foot),::view-transition-new(fg-foot){animation:none}
/* The masthead and the footer are the same objects on both pages. Naming them
   holds them still instead of crossfading them against themselves. */
.mast{view-transition-name:fg-mast}
.foot{view-transition-name:fg-foot}
.fg-core-label{view-transition-name:fg-core-text}

/* ------------------------------------------------ the centre does not move
   EVERY PAGE IN THE ATLAS IS THE SAME DRAWING WITH A DIFFERENT RING, so the one
   thing that must not be animated between two of them is the middle. It is
   where the reader is looking, it is the same object either side of the hop,
   and anything it does -- falling away, popping back, being flown into by the
   body that was pressed -- says "you have arrived somewhere else" over a design
   whose whole claim is that you have turned one instrument a notch.

   So the core wears a name that exists on EVERY page. The browser pairs the two
   and holds it: same place, same size, no animation at all. The same treatment
   the masthead and the footer get, for the same reason and with the same note
   -- a crossfade of a thing onto itself is a blink, and a blink is a change.

   What does change is the words in it, and only the words. .fg-core-label is
   paired too, but it is allowed its crossfade, because the name of the place
   you are standing in genuinely is different now. The old page's name fades
   out, the new one's fades in, and the body they are both set inside never
   moves.

   THE ONE THING THIS DOES NOT PROMISE. The core is 138 units of a frame that is
   cropped to fit its own ring, so a two body leaf and the twenty-four body
   language ring do not draw it at quite the same size on screen -- 206px
   against 234px, measured across the tree, with almost every real hop inside
   1% of its neighbour. Held rather than animated, that difference lands as a
   snap on the two or three hops in the atlas wide enough to see it. Making it
   exact means giving the stage a fixed number of pixels per viewBox unit, which
   moves every measured type size in the ring and is not a change to make
   quietly. */
::view-transition-group(fg-core-body){animation-duration:.28s}
::view-transition-old(fg-core-body),
::view-transition-new(fg-core-body){animation-duration:.28s}
.fg-core-stage{view-transition-name:fg-core-body}
/* A crossfade rather than `animation:none`, and the difference is a bug I put
   here and had to be shown. Held with no animation at all, the old core and the
   new one are both painted at full opacity -- which is invisible when they are
   the same size and is a hard crescent of the larger one sticking out from
   behind the smaller when they are not. On a wide stage they are now identical
   and the crossfade is a no-op; on a narrow one, where the frame is still
   cropped to its ring, it dissolves the difference instead of drawing it. */

/* ------------------------------------------------- and what the ring does
   The body you pressed travels to the middle of the next page. Everything ELSE
   on the old ring falls into the middle of the old one -- the bodies you did not
   take, their wires, the track they sat on -- drawn back into the core it came
   out of while the next constellation opens behind it. The entrance, played
   backwards, at the far end of the same hop.

   NOTHING HAS TO BE MEASURED, and that is what makes this four rules instead of
   a stylesheet built at the moment of leaving. In a cross document transition
   the pseudo elements live in the NEW document, which knows none of the old
   page's geometry -- so an implosion written per body would have to carry a
   vector per body across the navigation. It does not have to: the drawing and
   the names are both centred on the core, and `::view-transition-old` takes its
   origin from the middle of the element's own snapshot, so `scale()` alone pulls
   both into the core with no coordinate stated anywhere. The names in these
   rules are fixed, so both documents already have them.

   TWO NAMES, AND THEY ARE THE <svg> AND THE NAMES OVERLAY -- not the groups
   inside the drawing, which cannot carry one. `view-transition-name` is ignored
   on any SVG element that is not itself an <svg>: no snapshot is taken, no group
   is created, and a rule written against the name matches nothing, silently.
   That is measured rather than assumed; see the note in atlas.js, which is also
   where it is explained why the core falling with the rest of the drawing is the
   right answer here rather than a compromise.

   IT COSTS THE READER NOTHING. The names are assigned in `pageswap`, the
   outgoing document's last tick -- the request left long ago, the response is
   in, the next page is built -- so this delays nothing at all and then runs over
   a page that has already arrived. See the note above about the 280ms hold this
   replaced.

   IT DOES NOT ARRIVE. Two thirds of the way in the ring is already gone, and
   what is left is a third of its size and five per cent visible, so nothing ever
   reaches the middle to pile up there. A ring going home should read as being
   absorbed, not as being reeled in on a string: a fall that completes is a
   collision. Fading before the end is what makes it a dissolve, and it is also
   why the whole thing is over in four tenths of a second without looking
   hurried. */
::view-transition-group(fg-orrery),
::view-transition-group(fg-names){animation-duration:.42s}
::view-transition-old(fg-orrery),
::view-transition-old(fg-names){
  animation:fg-fall .42s cubic-bezier(.4,.02,.6,.5) both;
}
/* The names lead the drawing in by a breath, which is the entrance's order kept
   rather than reversed: they arrived last and they leave first, so the ring is
   never a set of bare orbs on an empty sky in either direction. */
::view-transition-old(fg-orrery){animation-delay:.05s}
@keyframes fg-fall{
  26% {opacity:.44;transform:scale(.86)}
  64% {opacity:.05}
  to  {opacity:0;transform:scale(.34)}
}

/* Two locks. `navigation:none` is the right answer and is what a current engine
   will honour; the duration override is there because @view-transition nested in
   a media query is newer than the API itself, and an engine that drops the rule
   it cannot parse would otherwise leave a reader who asked for no motion with
   the full morph. The second rule needs nothing new to work. */
@media (prefers-reduced-motion:reduce){
  @view-transition{navigation:none}
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*){animation:none !important}
}

@media (prefers-reduced-motion:reduce){
  .fg-sky,.fg-orbit-lit,.fg-core-arc,.fg-wire,.fg-disc__rim,
  .fg-disc__glow,.fg-disc__key,.fg-disc__gloss,
  .fg-disc__ring,.fg-label{transition:none !important}
}

/* ============================== the hero ==============================
   Orrery first, words under it, one centred axis. The two column layout this
   replaces made the same claim -- the constellation is the subject -- by giving
   it the wider column; giving it the whole first view says it without a
   competing element on screen at all. The title and the sentence are the
   caption a reader scrolls into, and the DOM agrees: the stage precedes the
   text, so what is seen first is also what is read first. */
.fg-hero{
  display:grid;
  grid-template-columns:minmax(0,1fr);
  grid-template-areas:"stage" "text" "meta";
  justify-items:center;
  align-content:start;
  gap:18px;
  padding-block:12px 30px;
  text-align:center;
}
.fg-hero__text{grid-area:text;min-width:0;max-width:860px}
.fg-hero__meta{grid-area:meta;min-width:0}
.fg-hero__stage{grid-area:stage;min-width:0;width:100%}

/* The stage owns the opening view. The ring is sized by its column everywhere
   else; here it is also capped by the height of the screen it opens on, so the
   whole orbit -- chip to rim -- stands in the first viewport with the top of
   the heading showing below it as the way down. The budget: masthead, hero
   padding, the up chip, and a strip of the caption. Kept off small screens,
   where width is the scarcer axis and the 760px block below frames the ring
   edge to edge instead. */
@media (min-width:761px){
  .fg-hero .fg-atlas__inner{
    /* 660 and 1010, and they are the old 561 and 860 carried through the frame
       change rather than new opinions.

       When this cap binds, the stage is the screen's spare HEIGHT times the
       frame's aspect ratio -- so the scale the ring is drawn at works out as
       height over the frame's HEIGHT, and nothing else. The frame is now one
       fixed box for the whole atlas rather than a crop of each ring (see
       $fg_frame), and a box that has to hold the largest drawing any ring can
       make is taller than the box a small ring used to get. At the old numbers
       every page would therefore have been drawn smaller -- measured, 9% on the
       design ring and 15% on the two body rings, and two names that used to sit
       on one line went to two. That is the exact currency this whole file is
       careful with, so the floor and the ceiling go up in the same proportion
       and give it back. Measured after: nothing in the tree is drawn smaller
       than it was, at any screen height.

       The 561 it replaces was itself measured -- the landscape ring sets every
       name in the tree down to about 530px of stage and starts failing just
       below it (see fitcheck / breakcheck) -- and the trade it encodes is
       unchanged: a short laptop gets a ring that overflows the fold a little
       rather than one that breaks names to honour it. There is simply more ring
       above that line now. The stage container is the hero column, not this
       element, so the scene choice is untouched by the cap either way. */
    width:min(100%, 1010px, max(660px, calc((100svh - var(--head-h, 64px) - 210px) * var(--vb-ratio, 1.07))));
  }
}

/* Centred composition, centred furniture. Scoped off the plain hero, which
   keeps its left axis: a page of text with no map above it is a document, and
   a centred document is a greeting card. */
.fg-hero:not(.fg-hero--plain) .fg-hero__eyebrow{justify-content:center}
.fg-hero:not(.fg-hero--plain) .fg-hero__eyebrow::after{display:none}
.fg-hero:not(.fg-hero--plain) .fg-crumbs{justify-content:center}
.fg-hero:not(.fg-hero--plain) .fg-hero__blurb{margin-inline:auto}
.fg-hero:not(.fg-hero--plain) .fg-hero__meta{justify-content:center}

.fg-hero__eyebrow{
  display:flex;align-items:center;gap:10px;
  font-family:var(--f-mono);font-size:11px;letter-spacing:.2em;
  text-transform:uppercase;color:var(--world-c, var(--d-ai));margin-bottom:18px;
}
.fg-hero__eyebrow::before{
  content:"";width:6px;height:6px;border-radius:50%;flex:0 0 auto;
  background:currentColor;box-shadow:0 0 12px currentColor;
}
.fg-hero__eyebrow::after{
  content:"";flex:1 1 auto;height:1px;max-width:88px;
  background:linear-gradient(90deg, currentColor, transparent);
  opacity:.5;
}
/* The trail, set in the eyebrow slot. Each ancestor is a real link, so the way
   back up the tree is one press from anywhere in it. */
.fg-crumbs{display:flex;align-items:center;flex-wrap:wrap;gap:8px;min-width:0}
.fg-crumbs a{
  color:inherit;opacity:.8;
  transition:opacity .2s ease, color .2s ease;
  border-bottom:1px solid transparent;
}
.fg-crumbs a:hover{opacity:1;border-bottom-color:currentColor}
.fg-crumbs a + a::before{
  content:"/";display:inline-block;margin-inline-end:8px;
  opacity:.42;color:rgb(255 255 255 / .55);
}

/* A hero with nothing to orbit: search results, archives, the 404. It keeps the
   field and the type and drops the two column grid, because a single column of
   text pretending half the page is missing looks like a broken layout. */
.fg-hero--plain{
  grid-template-columns:1fr;
  grid-template-areas:"text" "meta";
  justify-items:start;
  text-align:start;
  align-content:center;
  gap:22px;
  padding-block:52px 34px;
}
.fg-hero--plain .fg-hero__text{max-width:46ch;align-self:center}
.fg-hero--plain .fg-hero__meta{align-self:center}

.fg-hero h1{
  font-family:var(--f-display);font-weight:var(--w-display);
  font-size:clamp(40px,5.4vw,70px);letter-spacing:-.045em;line-height:.96;
  margin-bottom:18px;text-wrap:balance;
  /* A flat white headline on a black field is a sticker. The wash gives the
     type the same falloff as everything else in the scene. */
  color:#fff;
  background:linear-gradient(168deg,#FFFFFF 8%, #EAEBFF 42%, #ACACCF 100%);
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;color:transparent;
}
@supports not ((-webkit-background-clip:text) or (background-clip:text)){
  .fg-hero h1{color:#fff;-webkit-text-fill-color:#fff}
}
/* The supporting line inside the H1: the parent's name and the first children,
   set as an instrument caption under the display phrase. Part of the heading's
   text on purpose -- it is the rest of the query -- but a fifth its size, so
   the head term keeps the voice. It sits inside the same clipped gradient, at
   the bottom of it, which dims it without a colour of its own. */
.fg-hero__h1-tail{
  display:block;
  margin-top:15px;
  font-family:var(--f-mono);
  font-weight:400;
  font-size:calc(12px * var(--script-scale));
  letter-spacing:.15em;
  text-transform:uppercase;
  line-height:1.7;
  max-width:60ch;
  margin-inline:auto;
  text-wrap:balance;
}
/* Node names in the tail are content in the page's own script, and tracking
   pulls a connected script apart mid-word. Chrome strings elsewhere get away
   with it because they are short and ours; a Persian name does not. */
[dir="rtl"] .fg-hero__h1-tail{letter-spacing:0}
.fg-hero__blurb{
  color:rgb(255 255 255 / .62);max-width:44ch;
  font-size:calc(16.5px * var(--script-scale));line-height:1.66;
}
.fg-hero__meta{
  display:flex;flex-wrap:wrap;gap:0;
  border-top:1px solid rgb(255 255 255 / .12);padding-top:16px;
}
.fg-hero__meta div{
  padding-inline-end:24px;margin-inline-end:24px;
  border-inline-end:1px solid rgb(255 255 255 / .12);
}
.fg-hero__meta div:last-child{border-inline-end:0;margin-inline-end:0;padding-inline-end:0}
.fg-hero__meta b{
  display:block;font-family:var(--f-display);font-size:24px;font-weight:var(--w-head);
  letter-spacing:-.03em;line-height:1.15;color:#fff;font-variant-numeric:tabular-nums;
}
.fg-hero__meta s{
  text-decoration:none;font-family:var(--f-mono);font-size:9px;letter-spacing:.14em;
  text-transform:uppercase;color:rgb(255 255 255 / .52);
}

/* ========================= the index below the map =========================
   The list of children used to be a slab of cards on white, which is where the
   page stopped being a map and became a website. It is now set in the same sky:
   an index in the back of an atlas, ruled in hairlines, wired to the drawing
   above it -- hover a row and its body lights up in the constellation. */
.fg-index{padding-block:10px 46px;position:relative}
.fg-index + .fg-index{padding-top:26px}
/* The line that ties the two halves together. Literally: it descends out of the
   orrery and lands on the section rule -- so it is only ever set on an index
   that actually sits under the map, never on one that follows an article. */
.fg-index--tethered::before{
  content:"";position:absolute;top:-58px;left:50%;width:1px;height:58px;
  background:linear-gradient(180deg, transparent, rgb(255 255 255 / .22));
}
/* Siblings are context, not the way forward. Same shape, half the voice: they
   keep the sentence, because that is the part that helps you decide whether to
   go sideways, and drop the count and the contents, which are an invitation. */
.fg-index--quiet .fg-index__label{color:rgb(255 255 255 / .56)}
.fg-index--quiet .fg-idx{padding-block:14px 15px}
/* The rail is kept and only the count column is given up: these numbers have to
   go on lining up with the ones above them, or the page has two left margins. */
.fg-index--quiet .fg-idx{grid-template-columns:var(--rail-w) minmax(150px, 232px) minmax(0, 1fr)}
/* Quieter, and neutral: a sibling is context, and eight rows of lit ordinals
   under a lit index would compete with the thing the reader came down here for. */
.fg-index--quiet .fg-idx__n{
  font-size:17px;padding-top:.3em;
  color:rgb(255 255 255 / .34);text-shadow:none;
}
.fg-index--quiet .fg-idx:hover .fg-idx__n,
.fg-index--quiet .fg-idx.is-here .fg-idx__n{color:color-mix(in srgb, var(--dot) 76%, #fff);text-shadow:none}
.fg-index--quiet .fg-idx b{font-size:16px;color:rgb(255 255 255 / .78)}
.fg-index--quiet .fg-idx__blurb{font-size:calc(13.5px * var(--script-scale));color:rgb(255 255 255 / .58)}
.fg-index__head{
  display:flex;align-items:baseline;justify-content:space-between;gap:16px;
  padding-bottom:14px;margin-bottom:2px;
  border-bottom:1px solid rgb(255 255 255 / .14);
}
.fg-index__label{
  font-family:var(--f-mono);font-size:11px;letter-spacing:.2em;
  text-transform:uppercase;color:rgb(255 255 255 / .72);
}
.fg-index__hint{
  font-family:var(--f-mono);font-size:10px;letter-spacing:.1em;
  text-transform:uppercase;color:rgb(255 255 255 / .48);
}
/* A row answers three questions in the order a reader asks them: what is this
   called, what is it, and what is in it. It used to answer only the first, five
   across in a strip of chips -- which is a legend, not an index, and a legend is
   no use to someone who does not already know what the words mean. Full width
   rows, because a sentence needs a measure and 200px is not one. */
.fg-index__list{
  list-style:none;padding:0;margin:0;
  border-bottom:1px solid rgb(255 255 255 / .1);
}
.fg-idx{
  position:relative;display:grid;
  grid-template-columns:var(--rail-w) minmax(150px, 232px) minmax(0, 1fr) auto;
  grid-template-areas:"n name say count";
  column-gap:var(--rail-gap);align-items:start;
  padding:22px 4px 24px 0;
  border-top:1px solid rgb(255 255 255 / .1);
  transition:background-color .28s ease;
}
.fg-idx__n{grid-area:n}
.fg-idx__name{grid-area:name}
.fg-idx__say{grid-area:say}
.fg-idx__count{grid-area:count}
.fg-index__list > li:first-child .fg-idx{border-top-color:transparent}
.fg-idx::after{
  content:"";position:absolute;inset-inline:0;top:-1px;height:1px;
  background:var(--dot);transform:scaleX(0);transform-origin:0 50%;
  transition:transform .42s cubic-bezier(.22,.8,.3,1);
}
.fg-idx:hover::after,.fg-idx:focus-visible::after,.fg-idx.is-here::after{transform:scaleX(1)}

/* The same ordinal as a branch section's, one size down. It matters that these
   are the same mark: a node page stacks a sectioned index, a list of siblings
   and the way up, and until they shared a rail the numbers down the left edge
   were three different sizes in two different typefaces, restarting at 01 each
   time. One rail, one face, and the sections read as three parts of a document
   rather than three widgets. */
.fg-idx__n{
  font-family:var(--f-display);font-style:normal;font-weight:600;
  font-size:24px;line-height:1;letter-spacing:-.03em;
  justify-self:end;padding-top:.16em;font-variant-numeric:tabular-nums;
  color:color-mix(in srgb, var(--dot) 56%, transparent);
  text-shadow:0 0 18px color-mix(in srgb, var(--dot) 22%, transparent);
  transition:color .28s ease, text-shadow .28s ease;
}
.fg-idx__name{display:block;min-width:0}
.fg-idx b{
  display:flex;align-items:center;gap:11px;
  margin-inline-start:calc(-1 * var(--dot-pull));
  font-family:var(--f-display);font-weight:600;font-size:18.5px;
  letter-spacing:-.02em;line-height:1.24;color:rgb(255 255 255 / .9);
  transition:color .28s ease;
}
.fg-idx b::before{
  content:"";flex:0 0 auto;
  width:var(--dot-size);height:var(--dot-size);border-radius:50%;
  background:var(--dot);
  box-shadow:0 0 0 0 color-mix(in srgb, var(--dot) 60%, transparent);
  transition:box-shadow .34s ease;
}
/* The count sits at the far edge of the row, the way a page number does in a
   table of contents. It was under the name, which left the whole right third of
   every row empty and made a full width row look like a mistake. */
.fg-idx__count{
  display:block;font-style:normal;text-align:end;white-space:nowrap;
  padding-top:.5em;
  font-family:var(--f-mono);font-size:10px;letter-spacing:.12em;
  text-transform:uppercase;color:rgb(255 255 255 / .5);
  transition:color .28s ease;
}

/* The sentence, and under it what the branch actually holds. The second line is
   generated from the tree, so it is right for a node whose sentence has not been
   written yet -- which is the state every new branch starts in. */
.fg-idx__say{display:block;min-width:0;padding-top:1px}
.fg-idx__blurb{
  display:block;max-width:62ch;
  font-size:calc(14.5px * var(--script-scale));line-height:1.55;
  color:rgb(255 255 255 / .7);
  transition:color .28s ease;
  text-wrap:pretty;
}
/* Flex, not an inline rule, so the second line of a long contents list starts
   under the first word rather than under the dash. */
.fg-idx__has{
  display:flex;gap:9px;margin-top:7px;max-width:62ch;
  font-size:calc(12.5px * var(--script-scale));line-height:1.5;
  color:rgb(255 255 255 / .5);
  transition:color .28s ease;
}
.fg-idx__has::before{
  content:"";flex:0 0 auto;
  width:14px;height:1px;margin-top:.72em;
  background:var(--dot);opacity:.5;
}

.fg-idx:hover,.fg-idx:focus-visible,.fg-idx.is-here{
  background:linear-gradient(180deg, color-mix(in srgb, var(--dot) 8%, transparent), transparent 88%);
}
.fg-idx:hover b,.fg-idx:focus-visible b,.fg-idx.is-here b{color:#fff}
.fg-idx:hover b::before,.fg-idx.is-here b::before{box-shadow:0 0 0 4px color-mix(in srgb, var(--dot) 22%, transparent)}
.fg-idx:hover .fg-idx__count,.fg-idx.is-here .fg-idx__count{color:color-mix(in srgb, var(--dot) 78%, #fff)}
.fg-idx:hover .fg-idx__n,.fg-idx.is-here .fg-idx__n{
  color:var(--dot);
  text-shadow:0 0 24px color-mix(in srgb, var(--dot) 42%, transparent);
}
.fg-idx:hover .fg-idx__blurb,.fg-idx.is-here .fg-idx__blurb{color:rgb(255 255 255 / .84)}
.fg-idx:hover .fg-idx__has,.fg-idx.is-here .fg-idx__has{color:rgb(255 255 255 / .62)}
.fg-idx:focus-visible{outline:2px solid #fff;outline-offset:-2px;border-radius:3px}

/* ==================== a branch, and what is inside it ======================
   The same row, with the layer under it shown rather than counted. Everything
   here is the index's own vocabulary at one more level of detail: the ordinal
   rail, the hairline, the dot in the pillar's hue, the line that sweeps in on
   hover. Nothing is boxed. A card would put a lit panel on the night and cut
   the section out of the scene, which is the seam this whole design exists to
   avoid -- so a branch is ruled, not framed, and the cells under it are held
   apart by their own hairlines and the gutter between the columns.

   What was missing was not decoration, it was SCALE. Ten sections set in one
   band of greys between 14 and 20px is a page with no loud note anywhere on
   it: correct, scannable, and completely inert. Two things fix that without
   putting a single box on the sky --

   1. The ordinal is set large, in the pillar's own light. It is the only
      element in the block allowed to be big, so it carries the whole rhythm of
      the page on its own, and because it is coloured it is also the only place
      the index admits which world it is standing in.
   2. A stem descends from it, down the margin, past everything the branch
      holds, and fades out. It is the edge of the tree drawn as a line -- the
      same relation the orrery draws as a wire, at document scale. It is what
      makes the index read as part of the map rather than as the list that
      happens to sit under it.

   The rail is a variable because three things have to agree about it: the head
   grid, the indent on the cells, and the stem that runs between them. They
   drifted apart twice when they were three separate numbers. */
/* ONE RAIL FOR EVERY INDEX ON THE PAGE. Four things have to agree about this
   margin: the head grid, the indent on the cells, the dot that is pulled out of
   the heading into the gutter, and the stem that hangs off that dot. They were three separate numbers twice and
   drifted both times, so they are one set here.

   --dot-pull is the dot's own width plus its gap. Pulling the heading back by
   exactly that puts the dot in the gutter and lands the heading's TEXT on the
   same margin the cells are indented to -- without it the cells start 20px to
   the left of the name they belong to, which is the kind of near-miss that
   reads as carelessness without anyone being able to say why. */
.fg-index{
  --rail-w:3.25rem;--rail-gap:28px;
  --dot-size:9px;
  /* The dot hangs: the heading is pulled back by its own width plus its gap, so
     the dot sits in the gutter and the heading's TEXT lands on the margin. */
  --dot-pull:20px;
  /* Where the cells begin. It is the heading's text margin, which is what the
     pull above just arranged -- stated as its own name because the narrow
     layout puts the dot back in the line and has to say so somewhere. */
  --cell-indent:calc(var(--rail-w) + var(--rail-gap));
  /* The stem is always half a dot plus the dot's gap to the left of that
     margin, at both widths, because that is where the dot's centre is however
     the dot got there. */
  --stem-x:15.5px;
}

.fg-branch{position:relative;padding-bottom:34px}
/* A branch with nothing inside it is a row, and it has to keep a row's rhythm.
   The section's own bottom space is there to separate a block of cells from the
   next heading; under a head with no cells it is just a hole, and seven of them
   in a column -- which is what a pillar of mostly unopened branches looks like
   -- read as a page that has lost its footing. */
.fg-branch--leaf{padding-bottom:8px}
/* The flat list closes on a hairline, and the reader has learned that the line
   means the index has ended. The stack of sections has to end the same way or
   the last branch just runs out of cells and the block stops mid sentence. */
.fg-index--branches > .fg-branch:last-of-type{
  padding-bottom:28px;
  border-bottom:1px solid rgb(255 255 255 / .1);
}

.fg-branch__head{
  position:relative;display:grid;
  grid-template-columns:var(--rail-w) minmax(150px, 232px) minmax(0, 1fr) auto;
  grid-template-areas:"n name say count";
  column-gap:var(--rail-gap);align-items:start;
  padding:24px 4px 20px 0;
  border-top:1px solid rgb(255 255 255 / .1);
  transition:background-color .28s ease;
}
/* The section head sits directly under the index's own rule, and two hairlines
   two pixels apart is a mistake that looks like a rendering bug. */
.fg-index__head + .fg-branch > .fg-branch__head{border-top-color:transparent}
.fg-branch__head::after{
  content:"";position:absolute;inset-inline:0;top:-1px;height:1px;
  background:var(--dot);transform:scaleX(0);transform-origin:0 50%;
  transition:transform .42s cubic-bezier(.22,.8,.3,1);
}
.fg-branch__head:hover::after,
.fg-branch__head:focus-visible::after,
.fg-branch__head.is-here::after{transform:scaleX(1)}

/* The one big thing on the page. Right aligned so its stem hangs off the same
   edge the cells are indented to, which is what turns a number and a line into
   one mark instead of two. Tabular, so 01 and 10 are the same width and the
   column of them down the page is straight -- proportional numerals put a
   visible wobble in the rail and it is the sort of wrongness nobody can name.
   aria-hidden, because "zero one" read before every heading is noise: the
   ordinal is a position, and the heading order already carries it. */
.fg-branch__n{
  grid-area:n;justify-self:end;
  font-family:var(--f-display);font-style:normal;font-weight:600;
  font-size:30px;line-height:1;letter-spacing:-.03em;
  padding-top:.08em;font-variant-numeric:tabular-nums;
  color:color-mix(in srgb, var(--dot) 62%, transparent);
  /* It is the brightest thing in the block and this is a night scene, so it
     glows rather than sits on the surface. A text-shadow and not a filter:
     drop-shadow would cost this element its own compositing layer, ten times
     over on a pillar, for a halo that text-shadow draws for nothing. */
  text-shadow:0 0 20px color-mix(in srgb, var(--dot) 26%, transparent);
  transition:color .3s ease, text-shadow .3s ease;
}
/* A real h2, because a pillar page is a document with sections and its outline
   should say so -- to a screen reader walking the headings, and to a crawler
   deciding what this page is about. Sized back down to index scale: the display
   clamp on h2 is meant for a title, and at 40px it would break the row. */
.fg-branch__name{
  grid-area:name;min-width:0;margin:0;
  margin-inline-start:calc(-1 * var(--dot-pull));
  display:flex;align-items:center;gap:11px;
  font-family:var(--f-display);font-weight:600;font-size:21px;
  letter-spacing:-.022em;line-height:1.22;color:rgb(255 255 255 / .94);
  padding-top:.12em;
  transition:color .28s ease;
}
.fg-branch__name::before{
  content:"";flex:0 0 auto;
  width:var(--dot-size);height:var(--dot-size);border-radius:50%;
  background:var(--dot);
  box-shadow:0 0 0 0 color-mix(in srgb, var(--dot) 60%, transparent);
  transition:box-shadow .34s ease;
}
.fg-branch__say{
  grid-area:say;display:block;min-width:0;max-width:58ch;padding-top:.42em;
  font-size:calc(15px * var(--script-scale));line-height:1.55;
  color:rgb(255 255 255 / .74);text-wrap:pretty;
  transition:color .28s ease;
}
/* Two numbers stacked, the way the hero states its own. The second is the one
   that tells a reader whether this branch is a corridor or a wing. */
.fg-branch__count{
  grid-area:count;display:flex;flex-direction:column;align-items:flex-end;gap:3px;
  font-style:normal;text-align:end;white-space:nowrap;padding-top:.85em;
  font-family:var(--f-mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;
}
.fg-branch__count b{font-weight:400;color:rgb(255 255 255 / .5);transition:color .28s ease}
.fg-branch__count s{text-decoration:none;color:rgb(255 255 255 / .3)}

.fg-branch__head:hover,
.fg-branch__head:focus-visible,
.fg-branch__head.is-here{
  background:linear-gradient(180deg, color-mix(in srgb, var(--dot) 9%, transparent), transparent 88%);
}
.fg-branch__head:hover .fg-branch__name,
.fg-branch__head:focus-visible .fg-branch__name,
.fg-branch__head.is-here .fg-branch__name{color:#fff}
.fg-branch__head:hover .fg-branch__name::before,
.fg-branch__head.is-here .fg-branch__name::before{
  box-shadow:0 0 0 4px color-mix(in srgb, var(--dot) 22%, transparent);
}
.fg-branch__head:hover .fg-branch__n,
.fg-branch__head:focus-visible .fg-branch__n,
.fg-branch__head.is-here .fg-branch__n{
  color:var(--dot);
  text-shadow:0 0 26px color-mix(in srgb, var(--dot) 46%, transparent);
}
.fg-branch__head:hover .fg-branch__say,
.fg-branch__head.is-here .fg-branch__say{color:rgb(255 255 255 / .88)}
.fg-branch__head:hover .fg-branch__count b,
.fg-branch__head.is-here .fg-branch__count b{color:color-mix(in srgb, var(--dot) 78%, #fff)}
.fg-branch__head:focus-visible{outline:2px solid #fff;outline-offset:-2px;border-radius:3px}

/* ---- the cells ----
   Sixty tracks, and each cell spans a share of them set in the markup. The row
   sizes are levelled in PHP so every row is full -- see template-parts/branch.php
   for why a hole is worse than an uneven cell width. Sixty is the number
   because it divides by 1, 2, 3 and 4, so any row we can produce lands on exact
   track boundaries and every gutter in the block is the same 28px whatever
   the row above it did. */
.fg-branch__grid{
  position:relative;
  list-style:none;padding:0;margin:0;
  margin-inline-start:var(--cell-indent);
  /* The gutter is NOT column-gap. A gap applies at every one of the 59
     boundaries in a 60 track grid, not at the four places a cell actually ends,
     so 28px of gutter asks for 1652px of gutter and the tracks collapse to
     nothing. The gutter is carried as end padding on each cell instead, and the
     row's trailing copy of it is pulled back off the block so the right edge
     still lands on the margin the head rules to. */
  margin-inline-end:calc(-1 * var(--rail-gap));
  display:grid;grid-template-columns:repeat(60, minmax(0, 1fr));
  column-gap:0;
}
.fg-branch__grid > li{
  grid-column:span var(--span, 20);min-width:0;
  padding-inline-end:var(--rail-gap);
}

/* The stem. It hangs off the ordinal's edge, runs the height of everything the
   branch holds, and fades out rather than stopping -- a line that ends on a
   hard edge draws attention to its own end, and the one thing this scene is not
   allowed to have is an edge. */
.fg-branch__grid::before{
  content:"";position:absolute;inset-block:-20px 4px;
  inset-inline-start:calc(-1 * var(--stem-x));
  width:1px;
  background:linear-gradient(180deg,
    var(--dot),
    color-mix(in srgb, var(--dot) 34%, transparent) 38%,
    transparent);
}

/* The stem is the branch, so it answers when the branch does. Pointing at the
   head lights the line all the way down past everything it holds, which is the
   one moment the index says out loud what it has been drawing quietly. */
.fg-branch__grid::before{transition:opacity .34s ease}
.fg-branch__head:hover + .fg-branch__grid::before,
.fg-branch__head:focus-visible + .fg-branch__grid::before,
.fg-branch__head.is-here + .fg-branch__grid::before{opacity:1}
.fg-branch__grid::before{opacity:.72}

.fg-cell{
  position:relative;display:block;height:100%;
  padding:13px 12px 16px 0;
  border-top:1px solid rgb(255 255 255 / .08);
  transition:background-color .28s ease;
}
.fg-cell::after{
  content:"";position:absolute;inset-inline:0;top:-1px;height:1px;
  background:var(--dot);transform:scaleX(0);transform-origin:0 50%;
  transition:transform .38s cubic-bezier(.22,.8,.3,1);
}
.fg-cell:hover::after,.fg-cell:focus-visible::after{transform:scaleX(1)}
/* Pointing at a cell puts a node on the line beside it -- literally on the stem
   for the first column, and on the same offset in the gutter for the rest, so
   the mark means the same thing wherever it lands. It is the branch dot at cell
   scale: the smallest possible way of saying "this one", in the vocabulary the
   rest of the page has already taught. */
.fg-cell::before{
  content:"";position:absolute;top:20px;
  inset-inline-start:calc(-1 * var(--stem-x) - 3.5px);
  width:7px;height:7px;border-radius:50%;background:var(--dot);
  opacity:0;transform:scale(.35);
  transition:opacity .28s ease, transform .34s cubic-bezier(.22,.8,.3,1);
}
.fg-cell:hover::before,.fg-cell:focus-visible::before{opacity:1;transform:none}
/* Name and weight on one line, page-number style: the number is the only thing
   separating a cluster from a page, and it belongs at the edge where a reader
   scanning down the column can see all of them at once. */
.fg-cell__top{display:flex;align-items:baseline;justify-content:space-between;gap:10px}
.fg-cell__top b{
  font-family:var(--f-display);font-weight:600;font-size:16px;
  letter-spacing:-.015em;line-height:1.3;color:rgb(255 255 255 / .88);
  transition:color .28s ease;
}
/* The count leans on the pillar hue at rest rather than waiting for a hover.
   At 10px and 40% white it was a smudge; the reader could not tell a cluster of
   six from a page without stopping to look, which is the one question the cell
   grid exists to answer at a glance. */
.fg-cell__top em{
  flex:0 0 auto;font-style:normal;
  font-family:var(--f-mono);font-size:10px;letter-spacing:.06em;
  color:color-mix(in srgb, var(--dot) 62%, transparent);
  font-variant-numeric:tabular-nums;
  transition:color .28s ease;
}
.fg-cell__say{
  display:block;margin-top:5px;
  font-size:calc(13px * var(--script-scale));line-height:1.5;
  color:rgb(255 255 255 / .56);text-wrap:pretty;
  transition:color .28s ease;
}
.fg-cell:hover,.fg-cell:focus-visible{
  background:linear-gradient(180deg, color-mix(in srgb, var(--dot) 8%, transparent), transparent 92%);
}
.fg-cell:hover .fg-cell__top b,.fg-cell:focus-visible .fg-cell__top b{color:#fff}
.fg-cell:hover .fg-cell__top em,.fg-cell:focus-visible .fg-cell__top em{color:color-mix(in srgb, var(--dot) 88%, #fff)}
.fg-cell:hover .fg-cell__say,.fg-cell:focus-visible .fg-cell__say{color:rgb(255 255 255 / .7)}
.fg-cell:focus-visible{outline:2px solid #fff;outline-offset:-2px;border-radius:3px}

/* ---- arrival ----
   The block is long and every section in it looks like the one above. A short
   rise as each branch comes into view is the cheapest way to give a page that
   shape some tempo, and it is doing one honest job as well as a decorative one:
   the cells come in after their heading and in reading order, so the stagger
   says which heading they belong to before anybody reads a word.

   It is armed from JS, per section, and only on sections that are still below
   the fold when the script runs -- so nothing above the fold can flash, and a
   page with no JS at all has simply never been hidden. The transform is on the
   cell rather than the row so it composites, and it is 8px, because this should
   register as the page settling rather than as an entrance. */
.fg-branch.is-armed .fg-branch__head,
.fg-branch.is-armed .fg-branch__grid > li{
  opacity:0;transform:translateY(8px);
}
.fg-branch.is-armed.is-in .fg-branch__head,
.fg-branch.is-armed.is-in .fg-branch__grid > li{
  opacity:1;transform:none;
  transition:opacity .5s ease, transform .5s cubic-bezier(.22,.8,.3,1);
}
.fg-branch.is-armed.is-in .fg-branch__grid > li{
  transition-delay:calc(90ms + var(--i, 0) * 45ms);
}
/* The stem draws itself down as the section lands. One element, one transform,
   and it is the thing that makes the block feel grown rather than faded in. */
.fg-branch.is-armed .fg-branch__grid::before{transform:scaleY(0);transform-origin:50% 0}
.fg-branch.is-armed.is-in .fg-branch__grid::before{
  transform:none;transition:transform .72s cubic-bezier(.22,.8,.3,1) .06s;
}
@media (prefers-reduced-motion:reduce){
  .fg-branch.is-armed .fg-branch__head,
  .fg-branch.is-armed .fg-branch__grid > li{opacity:1;transform:none}
  .fg-branch.is-armed .fg-branch__grid::before{transform:none}
}
/* Paper has no viewport and nothing will ever intersect it, so an armed section
   that the reader never scrolled to would print as a blank space where an index
   should be. */
@media print{
  .fg-branch.is-armed .fg-branch__head,
  .fg-branch.is-armed .fg-branch__grid > li{opacity:1;transform:none}
  .fg-branch.is-armed .fg-branch__grid::before{transform:none}
}

/* --------------------------- the way back up ------------------------------
   The same row as everything else in the index, with the ordinal slot doing a
   different job: there is only one of these, so a number would be answering a
   question nobody asked. The arrow lifts on hover, which is the whole animation
   budget for this block and the only thing on the page that moves in the
   direction it is pointing. */
.fg-index--up{padding-top:2px}
.fg-index--up .fg-idx__n{
  font-size:23px;line-height:1;padding-top:.14em;
  color:var(--dot);opacity:1;text-shadow:0 0 20px color-mix(in srgb, var(--dot) 30%, transparent);
  transition:transform .3s cubic-bezier(.22,.8,.3,1), opacity .24s ease;
}
.fg-index--up .fg-idx:hover .fg-idx__n,
.fg-index--up .fg-idx:focus-visible .fg-idx__n{transform:translateY(-3px);opacity:1}

/* ------------------------------------------------------------ narrow ----- */
@media (max-width:1000px){
  /* The levelled rows are a desktop luxury: below this the widest row we can
     set is two, so the 60 track grid collapses to two real columns and every
     cell takes one of them. The spans stay in the markup and simply stop being
     consulted -- there is no second set of numbers to keep in sync. */
  .fg-branch__grid{
    grid-template-columns:repeat(2, minmax(0, 1fr));
    column-gap:var(--rail-gap);
    margin-inline-end:0;
  }
  .fg-branch__grid > li{grid-column:span 1;padding-inline-end:0}

  .fg-hero{
    gap:4px;
    padding-block:14px 26px;
    text-align:center;
  }
  /* The plain hero centres with everything else at this width -- and it has to
     be said outright now that the wide layout gives it a start axis, or the
     higher specificity of the modifier would carry that axis down here. */
  .fg-hero--plain{justify-items:center;text-align:center}
  .fg-hero__eyebrow{justify-content:center;margin-bottom:14px}
  .fg-hero__eyebrow::after{display:none}
  .fg-crumbs{justify-content:center}
  .fg-hero__blurb{margin-inline:auto}
  .fg-hero__meta{justify-content:center;border-top:0;padding-top:0;margin-top:4px}
}

@media (max-width:760px){
  /* The constellation is the page on a phone, so it gets the whole width of it.
     The 28px gutter that every other element needs is exactly the width the map
     was missing, and edge to edge is also the honest framing: a sky does not
     have a margin. */
  .fg-hero__stage{
    width:100vw;
    margin-inline:calc(50% - 50vw);
  }
  .fg-atlas__inner{width:min(100%, 560px)}
  .fg-hero{padding-block:14px 22px}
  .fg-hero h1{font-size:clamp(33px,10.2vw,46px);letter-spacing:-.04em;margin-bottom:13px}
  .fg-hero__blurb{
    font-size:calc(15px * var(--script-scale));line-height:1.6;
    max-width:34ch;color:rgb(255 255 255 / .58);
  }
  .fg-hero__meta{
    gap:0;margin-top:10px;
    border-top:1px solid rgb(255 255 255 / .12);
    border-bottom:1px solid rgb(255 255 255 / .12);
    padding-block:13px;width:100%;
  }
  .fg-hero__meta div{flex:1 1 0;padding-inline:0;margin-inline:0;text-align:center}
  .fg-hero__meta b{font-size:20px}

  .fg-index{padding-block:6px 34px}
  .fg-index--tethered::before{height:34px;top:-34px}
  .fg-index__hint{display:none}
  /* Three columns will not fit a sentence on a phone, so the row folds: the
     ordinal keeps its rail and everything else stacks against it, which keeps
     the numbers in a line down the page and the prose in one measure. */
  .fg-idx{
    grid-template-columns:var(--rail-w) minmax(0,1fr) auto;
    grid-template-areas:"n name count" ". say say";
    column-gap:var(--rail-gap);row-gap:9px;
    padding:17px 2px 19px 0;
  }
  .fg-index--quiet .fg-idx{
    grid-template-columns:var(--rail-w) minmax(0,1fr);
    grid-template-areas:"n name" ". say";
  }
  /* The section head folds exactly as its row does, so the two shapes of the
     index stay one shape on a phone. The cells lose their indent with it: at
     this width the hierarchy is carried by the rule and the order, and 60px of
     dead gutter costs a whole column of text. */
  /* One place for the rail, so the head grid, the cell indent and the stem
     cannot disagree about where the margin is. */
  /* The gutter is 12px here, which is not enough to hang a dot in without it
     colliding with the ordinal, so the dot returns to the line and the stem
     takes the middle of the gutter instead. */
  .fg-index{
    --rail-w:2.2rem;--rail-gap:12px;
    /* The gutter is 12px here, which is not enough to hang a dot in without it
       colliding with the ordinal. So the dot goes back into the line, and the
       cells are indented past it instead -- the heading's text margin again,
       reached the other way round. The stem is unmoved: the dot's centre is in
       the same place relative to that margin either way. */
    --dot-pull:0px;
    --cell-indent:calc(var(--rail-w) + var(--rail-gap) + 20px);
  }
  .fg-branch__head{
    grid-template-columns:var(--rail-w) minmax(0,1fr) auto;
    grid-template-areas:"n name count" ". say say";
    column-gap:var(--rail-gap);row-gap:9px;
    padding:17px 2px 15px 0;
  }
  /* A 30px ordinal does not fit a phone's rail, and shrinking the rail to fit
     it would eat the measure. It comes down to a size that still reads as the
     loud element next to an 18px heading. */
  .fg-branch__n{font-size:21px;padding-top:.14em}
  .fg-branch__name{font-size:18.5px}
  /* The folded layout drops the sentence onto its own line under the name, and
     with the dot back in that line the name's text no longer starts where its
     own column does. The sentence and the cells both follow the text, not the
     column, or the block has three left edges within one section. */
  .fg-branch__say,
  .fg-idx__say{
    margin-inline-start:calc(var(--cell-indent) - var(--rail-w) - var(--rail-gap));
  }
  .fg-branch__say{padding-top:0}
  .fg-branch__count{padding-top:.5em}
  .fg-branch__grid{grid-template-columns:minmax(0,1fr)}
  .fg-branch{padding-bottom:24px}
  .fg-branch--leaf{padding-bottom:2px}

  .fg-idx__n{font-size:19px;padding-top:.16em}
  .fg-index--quiet .fg-idx__n{font-size:15px;padding-top:.24em}
  .fg-index--up .fg-idx__n{font-size:19px}
  .fg-idx__count{padding-top:.62em}
  .fg-idx b{font-size:17.5px}
  .fg-idx__blurb{font-size:calc(14px * var(--script-scale))}
  .fg-idx__has{font-size:calc(12px * var(--script-scale));margin-top:6px}
}

/* The count line used to be dropped here, at a 340px stage. It is dropped for
   the whole narrow band now -- see the fg-stage query above -- because the
   reason is the same one and it starts biting long before 340px. */

/* -----------------------------------------------------------------------------
   The language ring. Root page only.

   Every language the atlas is written in -- or being written in -- drawn as
   one figure: worlds on a ring around the core, which is the Faragiri mark
   plus the count. The order around the ring is geographic, west to east (see
   front-page.php), which is all the "globe" this site needs: the metaphor
   everywhere else is a sky, and a sky is how you actually see the whole
   planet's languages at once.

   Two states, told apart the way the mark tells them: a published locale is a
   lit body; a drafted one is a dim body inside an unclosed dashed ring -- the
   mark's own "room for the next one", worn per world. Drafted items are spans,
   not links, and none of this is content: the canonical list is the labelled
   cards below the ring.
   -------------------------------------------------------------------------- */
.fg-lring{
  position:relative;
  width:min(860px,94vw);aspect-ratio:1;
  margin-inline:auto;margin-block:0 26px;
  --lr:min(352px, calc(47vw - 56px));
}

/* The orbit itself: the mark's open ring at page scale, turning once every few
   minutes. Decoration, so it asks reduced-motion first and hides from
   assistive tech; the counter-rotation trick is not needed because nothing
   rides on it -- the worlds hold their own positions. */
.fg-lring__orbit{
  position:absolute;left:50%;top:50%;translate:-50% -50%;
  width:calc(var(--lr) * 2);height:calc(var(--lr) * 2);
  border:1px dashed rgb(255 255 255 / .13);border-radius:50%;
  -webkit-mask-image:conic-gradient(#000 0 78%, transparent 82% 86%, #000 90%);
  mask-image:conic-gradient(#000 0 78%, transparent 82% 86%, #000 90%);
}
@media (prefers-reduced-motion:no-preference){
  .fg-lring__orbit{animation:fg-lring-turn 300s linear infinite}
}
@keyframes fg-lring-turn{to{rotate:1turn}}

/* The core: what the languages orbit. The same mark the masthead wears, with
   the one number this page exists to say. */
.fg-lring__core{
  position:absolute;left:50%;top:50%;translate:-50% -50%;
  display:flex;flex-direction:column;align-items:center;gap:6px;
  text-align:center;
}
.fg-lring__core svg{
  width:46px;height:46px;overflow:visible;
  filter:drop-shadow(0 0 12px rgb(255 255 255 / .22));
}
.fg-lring__core b{
  font-family:var(--f-display);font-weight:var(--w-display);
  font-size:34px;letter-spacing:-.03em;color:var(--ink);line-height:1;
  margin-top:4px;
}
.fg-lring__core s{
  font-family:var(--f-mono);font-size:10.5px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-3);text-decoration:none;
}

/* A world on the ring. The chain reads: centre the box, swing it to its hour,
   push it out to the orbit, swing the label level again. The label never
   rotates, so فارسی and 日本語 and Русский all sit upright at every position. */
.fg-lring__item{
  position:absolute;left:50%;top:50%;
  transform:translate(-50%,-50%) rotate(var(--a)) translateX(var(--lr))
            rotate(calc(-1 * var(--a)));
  display:flex;flex-direction:column;align-items:center;gap:7px;
  max-width:104px;text-align:center;
}
.fg-lring__item b{
  font-size:13.5px;line-height:1.3;color:var(--ink-3);font-weight:500;
  transition:color .2s ease;
}

/* Drafted: a dim body inside an open ring. The dash gap is the mark's gap. */
.fg-lring__item:not(.is-live) i{
  width:17px;height:17px;border-radius:50%;
  border:1px dashed rgb(255 255 255 / .26);
  display:grid;place-items:center;opacity:.85;
}
.fg-lring__item:not(.is-live) i::before{
  content:"";width:5px;height:5px;border-radius:50%;background:var(--ink-3);
}

/* Published: a lit body, breathing on its own beat like the footer's arc. */
.fg-lring__item.is-live i{
  width:14px;height:14px;border-radius:50%;
  background:radial-gradient(circle at 32% 28%,
    #fff 0%, var(--d-core) 52%,
    color-mix(in srgb, var(--d-core) 30%, var(--void)) 92%);
  box-shadow:0 0 12px -1px rgb(255 255 255 / .55);
  transition:box-shadow .3s ease, scale .34s cubic-bezier(.22,1.12,.36,1);
}
@media (prefers-reduced-motion:no-preference){
  .fg-lring__item.is-live i{
    animation:fg-lring-breathe 6.5s ease-in-out infinite;
    animation-delay:calc(var(--i,0) * -1.1s);
  }
}
@keyframes fg-lring-breathe{50%{box-shadow:0 0 17px 0 rgb(255 255 255 / .75)}}
.fg-lring__item.is-live b{color:var(--ink-2);font-weight:600;font-size:14.5px}
.fg-lring__item.is-live:hover b,.fg-lring__item.is-live:focus-visible b{color:var(--ink)}
.fg-lring__item.is-live:hover i,.fg-lring__item.is-live:focus-visible i{
  scale:1.3;animation:none;
  box-shadow:0 0 0 4px rgb(255 255 255 / .12), 0 0 19px 1px rgb(255 255 255 / .8);
}

/* Under 760px the geometry has no room to be geometry. The same set becomes a
   cloud of pills -- still one picture, still two states, and the drafted ring
   motif survives as the pill's dashed edge. */
@media (max-width:760px){
  .fg-lring{width:auto;aspect-ratio:auto;--lr:0px;
    display:flex;flex-wrap:wrap;justify-content:center;align-items:center;
    gap:9px 8px;padding-inline:20px;margin-block:4px 30px}
  .fg-lring__orbit{display:none}
  .fg-lring__core{position:static;translate:none;flex-basis:100%;
    margin-bottom:14px}
  .fg-lring__item{position:static;transform:none;
    flex-direction:row;gap:8px;max-width:none;
    padding:7px 13px;border-radius:999px;
    border:1px dashed color-mix(in srgb, var(--rule-2) 80%, transparent)}
  .fg-lring__item.is-live{
    border:1px solid var(--rule-2);
    background:color-mix(in srgb, var(--surface) 55%, transparent)}
  .fg-lring__item:not(.is-live) i{width:13px;height:13px}
  .fg-lring__item.is-live i{width:12px;height:12px}
}

/* The region qualifier under a duplicated name -- see front-page.php. Styled
   as an instrument caption, not a second name. */
.fg-lring__item s{
  font-family:var(--f-mono);font-size:9.5px;letter-spacing:.09em;
  text-transform:uppercase;color:var(--ink-3);text-decoration:none;
  margin-top:-3px;opacity:.85;white-space:nowrap;
}
