/* Hover-disclosure for measured figures.
 *
 * The visible number stays scannable; the margin of error, the harness and the
 * competing result sit one hover, focus or tap away. This is for CAVEATS AND
 * SOURCES ONLY. An unflattering headline never goes in here, because a reader
 * who does not hover must still come away with a true impression.
 *
 * Self-contained palette on purpose: the home page loads home.css (--ink/--body)
 * and /models/ loads app.css (--txt/--dim2), so this component cannot assume
 * either set. It prefers whichever exists and falls back to its own.
 */
.fct{
  --fct-surface:#FFFFFF;
  --fct-border:#D2CEC4;
  --fct-shadow:0 6px 22px rgba(25,25,23,.13);
  --fct-fg:var(--txt, var(--ink, #191917));
  --fct-dim:var(--dim2, var(--body, #6B6860));
  --fct-acc:var(--mint, #0E7A62);
  position:relative;
  display:inline-block;
}
:root[data-theme="dusk"] .fct{ --fct-surface:#F3F0E7; --fct-border:#B6B0A0 }
:root[data-theme="night"] .fct{
  --fct-surface:#1B2734; --fct-border:#33445A;
  --fct-shadow:0 6px 22px rgba(0,0,0,.5);
}

/* The trigger is a real button so keyboard and assistive tech get it for free.
   All the inherited-look resets are deliberate: it must read as text, not a control. */
.fct-t{
  font:inherit;color:inherit;letter-spacing:inherit;
  background:none;border:0;padding:0;margin:0;
  cursor:help;
  text-decoration:underline;
  text-decoration-style:dotted;
  text-decoration-thickness:1px;
  text-underline-offset:3px;
  text-decoration-color:color-mix(in srgb, var(--fct-acc) 55%, transparent);
  transition:text-decoration-color .18s, color .18s;
}
.fct-t:hover, .fct-t[aria-expanded="true"]{
  color:var(--fct-acc);
  text-decoration-color:var(--fct-acc);
}
.fct-t:focus-visible{
  outline:2px solid var(--fct-acc);
  outline-offset:3px;
  border-radius:2px;
}

.fct-n{
  position:absolute;
  z-index:60;
  top:calc(100% + 9px);
  left:0;
  width:max-content;
  max-width:min(330px, 78vw);
  background:var(--fct-surface);
  color:var(--fct-dim);
  border:1px solid var(--fct-border);
  border-radius:10px;
  box-shadow:var(--fct-shadow);
  padding:11px 13px;
  font-size:calc((13.5px) * var(--ts));
  line-height:1.5;
  text-align:left;
  font-weight:400;
  letter-spacing:normal;
  opacity:0;
  visibility:hidden;
  pointer-events:none;
  transform:translateY(-3px);
  /* A closed note is invisible but still laid out, and a hidden box still counts toward the
     page's scrollable width. Anchored at left:0 and up to 330px wide, the note of a trigger
     within 330px of the right edge therefore widened the page with nothing to see: on
     /research/what-compression-inherits/ it scrolled sideways by up to about 220px at widths
     between 701px (where the narrow rule below stops applying) and 1292px. So a closed note
     is parked wholly to the LEFT of its trigger. Overflow to the left is never scrollable, and
     -100% is of the note's own width, so it holds at any width or type scale with no magic
     number. An OPEN note is put back by the open rules below; one whose trigger is close to
     the right edge is then flipped to open leftward by fact.js (data-flip). */
  translate:-100% 0;
  /* visibility must NOT be transitioned as a duration: doing so leaves the computed
     value stuck at `hidden` in some engines, so the note never appears. Instead it
     flips instantly on open (see below) and is DELAYED on close by the fade length,
     which keeps the fade-out visible without ever animating visibility itself.
     translate is parked the same way, so the note holds its place while it fades out. */
  transition:opacity .18s, transform .18s, visibility 0s linear .18s, translate 0s linear .18s;
}
.fct-n b{ color:var(--fct-fg) }

/* Right-hand facts open leftward so the note cannot run off the page. data-align="right" is
   set by hand (one place does). data-flip is set by fact.js as a note opens, when the note
   would run off the viewport on its usual side and has room on the other: it swaps the two,
   so a hand-set note opens rightward again. A closed note is parked to the left of either
   anchor just as it is of the default one. */
.fct[data-align="right"] .fct-n,
.fct[data-flip] .fct-n{ left:auto; right:0 }
.fct[data-align="right"][data-flip] .fct-n{ left:0; right:auto }

/* Each selector is listed as its own rule rather than one comma group: if an engine
   cannot parse :focus-visible it drops the WHOLE group, taking the hover and the
   aria-expanded cases down with it. Separate rules fail independently. */
.fct-t:hover + .fct-n{
  opacity:1; visibility:visible; pointer-events:auto; transform:translateY(0); translate:none;
  transition:opacity .18s, transform .18s, visibility 0s;
}
.fct-t:focus-visible + .fct-n{
  opacity:1; visibility:visible; pointer-events:auto; transform:translateY(0); translate:none;
  transition:opacity .18s, transform .18s, visibility 0s;
}
.fct-t[aria-expanded="true"] + .fct-n{
  opacity:1; visibility:visible; pointer-events:auto; transform:translateY(0); translate:none;
  transition:opacity .18s, transform .18s, visibility 0s;
}

/* Touch: no hover, so fact.js toggles aria-expanded on tap. Widen the tap target. */
@media (hover:none){
  .fct-t{ cursor:pointer; padding:2px 0 }
}

:where(html[data-motion="reduced"],html[data-motion="off"]) .fct-t, :where(html[data-motion="reduced"],html[data-motion="off"]) .fct-n{ transition:none }

/* A figure inside a .figs stack: keep the note clear of the next figbox. */
.figbox .fct-n{ font-weight:400 }

/* ---- Narrow widths: the note stops being a popover ---------------------------------------
   THE BUG. The panel is anchored to the INLINE trigger (left:0 on a position:relative .fct)
   and is up to 330px wide. On a 375px phone a trigger sitting mid-line has neither 330px to
   its right nor 330px to its left, so the note opens partly off-screen and the reader cannot
   reach the rest of it -- nothing scrolls horizontally to it. Measured on /models/ at 375px:
   "a failure to separate" opened at left 215 and ran to 508, 133px past the edge.

   WHY data-align="right" IS NOT THE FIX. That attribute already exists for exactly this, and
   it does rescue some triggers -- but it only moves the problem to the other edge. On the
   homepage at 375px the two notes want opposite answers: "the FP8 build it replaces" fits
   right-aligned (9..301) and "68.6 GB to 24.5 GB" then spills LEFT instead (-29..264). No
   single horizontal anchor is correct for an inline trigger in a viewport this narrow, so
   the anchoring itself has to go rather than be flipped per instance.

   THE FIX. Below 700px the note leaves the popover layer and becomes a block in normal flow
   directly under the paragraph, so its width is the column's width and it cannot overflow
   from any anchor. display:none while closed rather than the desktop's
   visibility/opacity pair, because a static box would otherwise hold its space open when
   shut -- and that also stops the closed notes inflating scrollWidth, which is what made the
   page report 375 -> 508 with nothing visibly wrong.

   The transform, the parking offset and the fade go with it: there is nothing to slide or park
   when the box is in flow, and animating height here would fight the motion levels for no gain. */
@media (max-width:700px){
  .fct-n{
    display:none;
    position:static;
    width:auto;
    max-width:none;
    margin-top:9px;
    transform:none;
    translate:none;
    transition:none;
  }
  /* Right-alignment is meaningless once the note spans the column. */
  .fct[data-align="right"] .fct-n,
  .fct[data-flip] .fct-n{ left:auto; right:auto }

  /* Listed separately for the same reason the desktop rules above are: an engine that cannot
     parse :focus-visible must not drop the tap case with it. */
  .fct-t:hover + .fct-n{ display:block; opacity:1; visibility:visible; pointer-events:auto; transform:none }
  .fct-t:focus-visible + .fct-n{ display:block; opacity:1; visibility:visible; pointer-events:auto; transform:none }
  .fct-t[aria-expanded="true"] + .fct-n{ display:block; opacity:1; visibility:visible; pointer-events:auto; transform:none }
}
