/* geigercapital.us · studio index
   ---------------------------------------------------------------------------
   Achromatic by rule: black on white, no accent hue anywhere. Hierarchy is
   carried by scale, weight and whitespace only, so the two children (play's
   near-black atmosphere, finplatform's warm institutional orange) each stay
   distinct against a parent that claims no colour of its own.

   Type is a system grotesque stack rather than a hosted webfont, following the
   same no-third-party-CDN rule finplatform already holds itself to.
   --------------------------------------------------------------------------- */

:root{
  /* Declares that both palettes below are real, so the browser paints its own
     surfaces (scrollbars, form controls, the canvas behind the page) to match
     rather than assuming light. */
  color-scheme:light dark;

  --ink:#000;
  --paper:#fff;
  --mute:#6b6b6b;
  --faint:#9a9a9a;
  --rule:#e0e0e0;
  --rule-strong:#c9c9c9;

  --sans:"Helvetica Neue",Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",Arial,sans-serif;
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;

  --pad:clamp(20px,5vw,64px);
  --measure:1560px;

  --ease:cubic-bezier(.22,1,.36,1);
  --dur:.7s;
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{
  margin:0;background:var(--paper);color:var(--ink);
  font:400 16px/1.6 var(--sans);
  letter-spacing:-.005em;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  overflow-x:hidden;
}
a{color:inherit}
img{max-width:100%;display:block}
::selection{background:var(--ink);color:var(--paper)}

.wrap{max-width:var(--measure);margin:0 auto;padding:0 var(--pad)}

.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
         clip:rect(0 0 0 0);white-space:nowrap;border:0}
.skip{position:absolute;left:-9999px;top:0;background:var(--ink);color:var(--paper);
      padding:12px 18px;z-index:100;font-size:13px}
.skip:focus{left:0}

:focus-visible{outline:2px solid var(--ink);outline-offset:3px}

/* eyebrow ------------------------------------------------------------- */
.eyebrow{
  display:flex;gap:14px;align-items:baseline;flex-wrap:wrap;
  margin:0 0 clamp(28px,4vw,52px);
  font-size:11px;font-weight:500;letter-spacing:.18em;text-transform:uppercase;
  color:var(--mute);
}
/* The separator between an eyebrow's label and sublabel. It was an em dash at
   --rule-strong, which had the width to read at that weight. A middle dot does
   not, so it sits one step brighter. */
.eyebrow span:nth-child(even){color:var(--faint)}
/* Group headings inside the index. They sit above a list rather than a section,
   so they need air above them that the hero's eyebrow does not. */
.eyebrow.group{margin-top:clamp(44px,5.5vw,84px)}
.eyebrow.group:first-of-type{margin-top:0}

/* nav ----------------------------------------------------------------- */
.nav{
  position:sticky;top:0;z-index:40;
  background:color-mix(in srgb,var(--paper) 88%,transparent);
  backdrop-filter:saturate(180%) blur(14px);
  -webkit-backdrop-filter:saturate(180%) blur(14px);
  border-bottom:1px solid transparent;
  transition:border-color .3s linear;
}
.nav.is-stuck{border-bottom-color:var(--rule)}
.nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:24px;
           min-height:72px}
.mark{
  display:flex;align-items:center;gap:11px;
  text-decoration:none;white-space:nowrap;
}
.mark .glyph{width:30px;height:30px;flex:0 0 auto;color:var(--ink)}

/* Sweep: each row thickens in turn, top to bottom, like a reading being taken.
   The disc is symmetric now, so the 180-degree rotation this replaces would
   have been invisible. Rows scale from their own centre, which needs fill-box:
   the default stroke-box would scale each rect about the SVG's origin and throw
   the raster apart. */
.mark .glyph rect{transform-box:fill-box;transform-origin:center}
.mark:hover .glyph rect,
.mark:focus-visible .glyph rect{animation:sweepRow .5s ease-out both}
@keyframes sweepRow{
  0%{transform:scaleY(1)}
  40%{transform:scaleY(2.1)}
  100%{transform:scaleY(1)}
}
.mark:hover .glyph rect:nth-child(1),
.mark:focus-visible .glyph rect:nth-child(1){animation-delay:0ms}
.mark:hover .glyph rect:nth-child(2),
.mark:focus-visible .glyph rect:nth-child(2){animation-delay:48ms}
.mark:hover .glyph rect:nth-child(3),
.mark:focus-visible .glyph rect:nth-child(3){animation-delay:95ms}
.mark:hover .glyph rect:nth-child(4),
.mark:focus-visible .glyph rect:nth-child(4){animation-delay:142ms}
.mark:hover .glyph rect:nth-child(5),
.mark:focus-visible .glyph rect:nth-child(5){animation-delay:190ms}
.mark:hover .glyph rect:nth-child(6),
.mark:focus-visible .glyph rect:nth-child(6){animation-delay:238ms}
.mark:hover .glyph rect:nth-child(7),
.mark:focus-visible .glyph rect:nth-child(7){animation-delay:285ms}
.mark:hover .glyph rect:nth-child(8),
.mark:focus-visible .glyph rect:nth-child(8){animation-delay:332ms}
.mark .wm{
  font-size:11px;font-weight:500;letter-spacing:.17em;text-transform:uppercase;
  line-height:1.32;
}
@media (max-width:420px){
  .mark .glyph{width:26px;height:26px}
  .mark .wm{font-size:10px;letter-spacing:.14em}
}
.nav nav{display:flex;gap:clamp(18px,3vw,40px)}
.nav nav a{
  font-size:11px;font-weight:500;letter-spacing:.16em;text-transform:uppercase;
  color:var(--mute);text-decoration:none;padding:4px 0;position:relative;
  transition:color .25s linear;
}
.nav nav a::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:1px;
  background:var(--ink);transform:scaleX(0);transform-origin:right;
  transition:transform .45s var(--ease);
}
.nav nav a:hover{color:var(--ink)}
.nav nav a:hover::after{transform:scaleX(1);transform-origin:left}

/* hero ---------------------------------------------------------------- */
.hero .wrap{
  padding-top:clamp(72px,13vw,190px);
  padding-bottom:clamp(64px,9vw,128px);
}
h1{
  margin:0;
  font-weight:400;
  font-size:clamp(38px,8.2vw,124px);
  line-height:.98;
  letter-spacing:-.038em;
  max-width:14ch;
}
/* line-by-line mask reveal */
.ln{display:block;overflow:hidden}
.ln>span{display:block;transform:translateY(105%);
         transition:transform 1.05s var(--ease)}
.ln:nth-child(2)>span{transition-delay:.09s}
.ln:nth-child(3)>span{transition-delay:.18s}
body.ready .ln>span{transform:none}

.lede{
  margin:clamp(34px,5vw,60px) 0 0;max-width:62ch;
  font-size:clamp(15px,1.35vw,18px);line-height:1.62;color:var(--mute);
  opacity:0;transform:translateY(14px);
  transition:opacity .9s linear .45s,transform .9s var(--ease) .45s;
}
body.ready .lede{opacity:1;transform:none}

/* index --------------------------------------------------------------- */
.index{position:relative;padding-bottom:clamp(72px,10vw,140px)}
.works{list-style:none;margin:0;padding:0;border-top:1px solid var(--rule)}
.work{border-bottom:1px solid var(--rule)}

/* .inert is the same row without a destination. See the comment on 04 in the
   markup. It shares the grid so an unlinked row stays in column with linked ones,
   including the empty fourth column where the arrow would be. */
.work>a,
.work>.inert{
  display:grid;
  grid-template-columns:clamp(46px,5vw,88px) minmax(0,1fr) minmax(0,22rem) 32px;
  gap:clamp(16px,3vw,48px);
  align-items:start;
  padding:clamp(30px,4.4vw,62px) 0;
  text-decoration:none;
  position:relative;
  transition:opacity .4s linear;
}

.idx{
  font:500 11px/1.9 var(--mono);letter-spacing:.14em;color:var(--faint);
  padding-top:.55em;
}
.ttl{
  display:block;
  font-weight:400;font-size:clamp(26px,4.4vw,64px);line-height:1.02;
  letter-spacing:-.03em;
  transition:transform .6s var(--ease);
}
.work>a:hover .ttl,
.work>a:focus-visible .ttl{transform:translateX(clamp(6px,1vw,18px))}

.dsc{
  display:block;margin-top:16px;max-width:52ch;
  font-size:15px;line-height:1.66;color:var(--mute);
}

.meta{display:flex;flex-direction:column;gap:12px;padding-top:.7em}
.host{font:400 12px/1.5 var(--mono);letter-spacing:0;color:var(--mute);
      overflow-wrap:anywhere}
.state{display:flex;align-items:center;gap:9px;
       font-size:11px;font-weight:500;letter-spacing:.14em;text-transform:uppercase;
       color:var(--faint)}
.dot{width:6px;height:6px;border-radius:50%;background:var(--rule-strong);flex:0 0 auto}
.state.is-up .dot{background:var(--ink)}
.state.is-up .dot{animation:pulse 2.6s var(--ease) infinite}
.state.is-up .txt{color:var(--ink)}
.state.is-static .dot{background:var(--ink);opacity:.28}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.28}}

/* Figures under the state: measured numbers, not status. Collapses entirely when
   empty so a row whose numbers never arrive shows no orphaned gap. */
.figs{font:400 12px/1.5 var(--mono);letter-spacing:0;color:var(--faint)}
.figs:empty{display:none}
.work>.inert{cursor:default}

.arrow{
  justify-self:end;padding-top:.5em;font-size:17px;color:var(--faint);
  transition:transform .6s var(--ease),color .3s linear;
}
.work>a:hover .arrow,
.work>a:focus-visible .arrow{transform:translate(4px,-4px);color:var(--ink)}
/* An internal destination gets a horizontal arrow, and moves horizontally. The
   diagonal is doing real work on the other rows: it means this leaves the site. */
.work>a:hover .arrow--in,
.work>a:focus-visible .arrow--in{transform:translate(5px,0)}



/* studio -------------------------------------------------------------- */
.studio{border-top:1px solid var(--ink)}
.studio .wrap{padding-top:clamp(64px,8vw,120px);padding-bottom:clamp(64px,8vw,120px)}
.cols{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
      gap:clamp(28px,5vw,90px);align-items:start}
.say{
  margin:0;font-weight:400;font-size:clamp(22px,2.9vw,40px);line-height:1.14;
  letter-spacing:-.026em;max-width:20ch;
}
.prose{margin:0;max-width:56ch}
.prose p{margin:0 0 20px;font-size:15px;line-height:1.72;color:var(--mute)}
.prose p:last-child{margin-bottom:0}

/* writeup ------------------------------------------------------------- */
/* A stack of .cols blocks: a short claim on the left, the detail on the right.
   The same figure the studio section uses on the index, repeated down a page.
   Only the spacing between blocks is new; everything else is already carried by
   .cols, .say and .prose. */
.writeup .cols + .cols{margin-top:clamp(40px,5.5vw,84px)}
.prose code{font:400 13px/1.6 var(--mono);color:var(--ink)}
.prose sup{font-size:.72em;line-height:0}
.prose a{color:inherit;text-decoration:none;
         border-bottom:1px solid var(--rule-strong);padding-bottom:1px;
         transition:border-color .25s linear}
.prose a:hover{color:var(--ink);border-bottom-color:var(--ink)}
.lede code{font:400 .88em/1.6 var(--mono)}
.colophon a{color:inherit;text-decoration:none;
            border-bottom:1px solid var(--rule);padding-bottom:1px}
.colophon a:hover{color:var(--ink);border-bottom-color:var(--ink)}

/* plate ---------------------------------------------------------------- */
/* Full-bleed: the plate is the one element allowed outside the measure, so it
   reads as an interruption rather than as an illustration inside the column. */
.plate{margin:0;position:relative;border-top:1px solid var(--ink)}
.plate img{
  width:100%;height:clamp(240px,44vh,520px);object-fit:cover;object-position:center 42%;
  display:block;
}
.plate figcaption{
  display:flex;gap:clamp(14px,2.4vw,34px);flex-wrap:wrap;align-items:baseline;
  max-width:var(--measure);margin:0 auto;padding:16px var(--pad) 22px;
  font-size:11px;letter-spacing:.11em;text-transform:uppercase;color:var(--faint);
}
.plate figcaption span:first-child{color:var(--ink);font-weight:500;letter-spacing:.17em}
.plate figcaption i{font-style:italic;text-transform:none;letter-spacing:.04em}
.plate figcaption a{color:inherit;text-decoration:none;
                    border-bottom:1px solid var(--rule-strong);padding-bottom:1px}
.plate figcaption a:hover{color:var(--ink);border-bottom-color:var(--ink)}
@media (max-width:640px){
  .plate figcaption{flex-direction:column;gap:6px}
}

/* ledger -------------------------------------------------------------- */
.ledger{border-top:1px solid var(--rule)}
.ledger .wrap{padding-top:clamp(56px,7vw,104px);padding-bottom:clamp(56px,7vw,104px)}
.lh{
  margin:clamp(34px,5vw,58px) 0 22px;font-weight:400;
  font-size:clamp(19px,2.1vw,26px);letter-spacing:-.02em;
}
.lh:first-of-type{margin-top:0}
.rows{margin:0;border-top:1px solid var(--rule)}
.row{
  display:grid;grid-template-columns:minmax(0,8.5rem) minmax(0,1fr);
  gap:clamp(14px,2.5vw,32px);
  padding:15px 0;border-bottom:1px solid var(--rule);
}
.row dt{font-size:11px;font-weight:500;letter-spacing:.15em;text-transform:uppercase;
        color:var(--faint);padding-top:.25em}
.row dd{margin:0;font-size:14px;color:var(--ink);overflow-wrap:anywhere}
.row dd code{font:400 13px/1.6 var(--mono)}
.row dd a{
  text-decoration:none;border-bottom:1px solid var(--rule-strong);padding-bottom:1px;
  transition:border-color .25s linear;
}
.row dd a:hover{border-bottom-color:var(--ink)}

/* Deploy feed, generated by tools/build-ledger.py. Reuses .rows/.row so the
   history reads as one more table in the ledger rather than a widget bolted on. */
.deploys .lrepo{color:var(--mute)}
.deploys .lsub{color:var(--faint)}
.lnote{margin:20px 0 0;max-width:72ch;font-size:12px;line-height:1.75;color:var(--faint)}
@media (max-width:640px){
  .deploys .lsub{display:block;margin-top:3px}
}

/* grounds ------------------------------------------------------------- */
/* Each major section carries a Piranesi etching from the Vedute di Roma as a
   ground. Held at a few per cent behind the content, never in front of type,
   and never carrying meaning: they are texture, so they are CSS backgrounds
   with no alt text rather than <img> elements.

   Etchings rather than the paintings the plate uses, for two reasons. They are
   line work with no hue, so nothing leaks into a page that is achromatic by
   rule; and their repeating arches read as rows, which is the same figure the
   mark and the index are built from.

   The assets are baked to greyscale rather than filtered here, so the bytes are
   smaller and the page needs no filter to hold the rule. Dark mode does need
   one: paper-and-ink line work vanishes against #0b0b0b, so it inverts to light
   lines on dark and comes up a shade in opacity to compensate. */
.ground{position:relative;isolation:isolate}
.ground::before{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background-size:cover;background-position:center;
  opacity:.07;
  /* Fade top and bottom so neighbouring sections do not meet at a hard tonal
     seam. Without this the boundary between two grounds reads as a band. */
  -webkit-mask-image:linear-gradient(to bottom,transparent 0,#000 14%,#000 86%,transparent 100%);
  mask-image:linear-gradient(to bottom,transparent 0,#000 14%,#000 86%,transparent 100%);
}
.hero.ground::before{background-image:url(/media/grounds/constantine.webp)}
.index.ground::before{background-image:url(/media/grounds/marcellus.webp)}
.studio.ground::before{background-image:url(/media/grounds/portico.webp)}
.ledger.ground::before{background-image:url(/media/grounds/basilica.webp)}

@media (prefers-color-scheme:dark){
  .ground::before{opacity:.09;filter:invert(1)}
}
/* Anyone who has asked the OS for more contrast has asked for exactly the thing
   a ground erodes, so they get none. */
@media (prefers-contrast:more){
  .ground::before{display:none}
}

/* footer -------------------------------------------------------------- */
footer{border-top:1px solid var(--ink)}
footer .wrap{padding-top:clamp(40px,5vw,64px);padding-bottom:clamp(40px,5vw,64px)}
.notice{margin:0;max-width:76ch;font-size:13px;line-height:1.7;color:var(--mute)}
.notice strong{color:var(--ink);font-weight:500}
.credits{
  margin:22px 0 0;max-width:78ch;
  font-size:11px;line-height:1.8;color:var(--faint);
}
.credits a{color:inherit;text-decoration:none;
           border-bottom:1px solid var(--rule);padding-bottom:1px}
.credits a:hover{color:var(--ink);border-bottom-color:var(--ink)}
.credits i{font-style:italic}

.colophon{
  display:flex;justify-content:space-between;gap:20px;flex-wrap:wrap;
  margin-top:clamp(30px,4vw,52px);
  font-size:11px;font-weight:500;letter-spacing:.16em;text-transform:uppercase;
  color:var(--faint);
}

/* responsive ---------------------------------------------------------- */
@media (max-width:1080px){
  .work>a{grid-template-columns:clamp(40px,6vw,64px) minmax(0,1fr) 28px}
  .meta{grid-column:2;padding-top:20px;flex-direction:row;gap:20px;flex-wrap:wrap;
        align-items:center}
}
@media (max-width:760px){
  .cols{grid-template-columns:1fr}
  .row{grid-template-columns:1fr;gap:5px}
  .row dt{padding-top:0}
  h1{max-width:none}
}

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important;
  }
  .ln>span{transform:none}
  .lede{opacity:1;transform:none}
}

/* Honour a viewer's dark preference by inverting the whole achromatic
   system: it is black-and-white, so a single flip is the entire dark mode. */
@media (prefers-color-scheme:dark){
  :root{
    --ink:#fff;--paper:#0b0b0b;--mute:#9b9b9b;--faint:#6e6e6e;
    --rule:#242424;--rule-strong:#3d3d3d;
  }
}
