/* ===========================================================================
   Regenerative Atlas — one stylesheet, no build step.

   The palette is the mockup's: ink on unbleached paper, with Miró's three
   primaries used only as accents, never as fill. Nothing here depends on a
   webfont being present — Enby Gertrude is used when it is installed in
   public/fonts, and the stack degrades to a decent serif when it is not.
   =========================================================================== */

/* Enby Gertrude — Valentin Chauveau & Léna Salabert-Triby, published by the
   Bye Bye Binary collective. Bundled complete in public/fonts/enby-gertrude/
   under its own terms (CUTE / OIFL), not under this project's MIT licence.
   See public/fonts/README.md. */
/* Tremplin — Benjamin Dott, published by Bye Bye Binary. Drawn for long text
   and titling, which is why it carries the interface. Same terms as below;
   bundled complete in public/fonts/tremplin/. */
/* `optional`, not `swap`. Swapping is what made the menu flicker between
   pages: the row was laid out in a stand-in face, painted, and then relaid
   the moment the real one arrived — the capsules changing width and the whole
   row sliding sideways, on every page you opened. Optional never swaps. The
   faces are preloaded from the head of each page, so they are nearly always
   there in time to be used from the first frame; on a connection too slow for
   that, the page is set in the fallback and stays set in it, which is a page
   that holds still rather than one that rewrites itself in front of you. */
@font-face {
  font-family: 'Tremplin';
  src: url('../fonts/tremplin/webfont/Tremplin-Light.woff2') format('woff2'),
       url('../fonts/tremplin/webfont/Tremplin-Light.woff') format('woff');
  font-weight: 300;
  font-style: normal;
  font-display: optional;
  /* The file declares two sets of vertical metrics that disagree — an ascent of
     1000 in one table and 700 in the other — and platforms choose between them
     differently: macOS takes the first, Linux and Windows the second. So the
     same line of Tremplin sat a tenth of an em higher or lower depending on
     whose screen it was on, and any nudge measured on one was wrong on the
     other. Stated here, the metrics are the same everywhere, and they are
     chosen so a line box is centred on the capitals: ascent less descent is
     exactly the cap height (700), so a label with equal padding above and
     below has its capitals in the middle of the pill, on every platform. The
     line gap keeps `line-height: normal` at the 1.2 it has always had here. */
  ascent-override: 90%;
  descent-override: 20%;
  line-gap-override: 10%;
}

@font-face {
  font-family: 'Tremplin';
  src: url('../fonts/tremplin/webfont/Tremplin-Regular.woff2') format('woff2'),
       url('../fonts/tremplin/webfont/Tremplin-Regular.woff') format('woff');
  font-weight: 400;
  font-style: normal;
  font-display: optional;
  ascent-override: 90%;
  descent-override: 20%;
  line-gap-override: 10%;
}

@font-face {
  font-family: 'Tremplin';
  src: url('../fonts/tremplin/webfont/Tremplin-Bold.woff2') format('woff2'),
       url('../fonts/tremplin/webfont/Tremplin-Bold.woff') format('woff');
  font-weight: 700;
  font-style: normal;
  font-display: optional;
  ascent-override: 90%;
  descent-override: 20%;
  line-gap-override: 10%;
}

@font-face {
  font-family: 'Enby Gertrude';
  src: url('../fonts/enby-gertrude/webfonts/woff2/Enby_Gertrude_roman.woff2') format('woff2'),
       url('../fonts/enby-gertrude/webfonts/woff/Enby_Gertrude_roman.woff') format('woff'),
       url('../fonts/enby-gertrude/otf/Enby_Gertrude_roman.otf') format('opentype');
  font-weight: 400;
  font-style: normal;
  font-display: optional;
}

:root {
  --paper: #f2ecdf;
  --paper-warm: #ece5d5;
  --ink: #100f0d;
  --ink-80: rgba(16, 15, 13, 0.8);
  --ink-55: rgba(16, 15, 13, 0.55);
  --ink-32: rgba(16, 15, 13, 0.32);
  --ink-14: rgba(16, 15, 13, 0.14);

  --red: #c2372a;
  --blue: #2c4a8c;
  --yellow: #c89e1e;

  --display: 'Enby Gertrude', 'EB Garamond', 'Hoefler Text', Garamond, Georgia, 'Times New Roman', serif;
  --sans: 'Tremplin', 'Century Gothic', Questrial, Futura, 'Avenir Next', 'Segoe UI', system-ui, sans-serif;

  /* How far the type comes down to sit in the middle of a button: nothing.

     Tremplin, the face every button is set in, has its vertical metrics stated
     in its @font-face so that a line box is centred on the capitals — on every
     platform, which is the part a number here could never manage. See there.

     The one pill set in Enby Gertrude, the landing page's way in, carries its
     own small drop: that face's metrics agree across platforms, so a measured
     constant is right everywhere for it. */
  --type-drop: 0em;

  --gutter: clamp(1.25rem, 4vw, 4.5rem);
  --rule: 1px solid var(--ink-14);
  --stroke: 1.5px;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: clamp(0.95rem, 0.9rem + 0.2vw, 1.05rem);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }

::selection { background: var(--ink); color: var(--paper); }

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

/* --- masthead ------------------------------------------------------------ */

.masthead {
  padding: clamp(1.75rem, 4vw, 3.25rem) var(--gutter) 0;
  text-align: center;
}

.masthead--left { text-align: left; }

.wordmark {
  font-family: var(--display);
  font-weight: 400;
  letter-spacing: -0.005em;
  line-height: 1.02;
  margin: 0;
  font-size: clamp(2.6rem, 7.5vw, 5.4rem);
}

.wordmark a { text-decoration: none; }

.masthead--compact .wordmark { font-size: clamp(1.7rem, 3.4vw, 2.5rem); }

.standfirst {
  font-family: var(--display);
  font-size: clamp(0.95rem, 1.4vw, 1.2rem);
  margin: 0.5rem 0 0;
  color: var(--ink-80);
}

.blurb {
  font-family: var(--display);
  max-width: 46ch;
  margin: 1.35rem auto 0;
  color: var(--ink-80);
  line-height: 1.55;
}

.masthead--left .blurb { margin-inline: 0; }

/* --- the black label box, straight from the mockup ----------------------- */

/* Every button reads the same way: an outline at rest, filled when it is the
   one you are on or pointing at. Nothing is filled by default. */
.label {
  display: inline-block;
  text-align: center;
  /* Opaque, always: a button with the drawing showing through it reads as a
     line crossing a button rather than as a button. */
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 400;
  font-size: clamp(1.05rem, 1.5vw, 1.45rem);
  line-height: 1.25;
  padding: calc(0.41em + var(--type-drop)) 1.05em calc(0.41em - var(--type-drop));
  text-decoration: none;
  border: var(--stroke) solid var(--ink);
  border-radius: 999px;
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}

/* As everywhere else, the fill follows a pointer that can actually hover; a
   touchscreen has none to leave, and a tapped button would stay lit. */
@media (hover: hover) {
  a.label:hover, button.label:hover { background: var(--ink); color: var(--paper); }
}
a.label:active, button.label:active,
a.label:focus-visible, button.label:focus-visible,
.label[aria-current='page'], .label.is-on {
  background: var(--ink);
  color: var(--paper);
}

/* One button in the record is filled at rest: taking the reading away with you
   is the thing you might not have known you could do, so it is the thing that
   is visible without being looked for. Pointing at it turns it out rather than
   in, since it is already as filled as it goes. */
.label--filled {
  background: var(--ink);
  color: var(--paper);
}
@media (hover: hover) {
  .label--filled:hover { background: var(--paper); color: var(--ink); }
}
.label--filled:active, .label--filled:focus-visible {
  background: var(--paper);
  color: var(--ink);
}

/* The row of things you can do with a record, at the top of it. */
.panel__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  margin: 0.9rem 0;
}

button.label[disabled] { opacity: 0.4; cursor: progress; }

.label--wide { min-width: min(18rem, 70vw); }
.label--small { font-size: 0.95rem; padding: 0.32em 0.95em 0.38em; }

/* --- the connected navigation ---------------------------------------------
   A row of capsules whose outlines merge into one continuous curve. The curve
   is drawn into an SVG behind the links by js/connected-nav.js; until it is,
   each item carries its own border, so this is a row of buttons either way.
   -------------------------------------------------------------------------- */

[data-connected-nav] {
  position: relative;
  display: inline-flex;
  align-items: stretch;
  gap: 0.7rem;
}

/* The one place on the site that does not take the type drop. Everywhere else
   a pill is drawn around a line of type and the drop settles the ink into the
   middle of it; here the capsule is drawn from the anchor's own box, so the
   drop moves the type down inside a shape that did not move with it, and the
   words sat low on the curve. Symmetric padding puts them back on its
   centreline. */
[data-connected-nav] > a {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  background: var(--paper);
  padding: calc(0.37em) 1.05em;
  border: var(--stroke) solid var(--ink);
  border-radius: 999px;
  font-size: 0.95rem;
  line-height: 1.25;
  text-decoration: none;
  white-space: nowrap;
  transition: color 240ms ease-in-out;
}

/* Once the curve is drawn the individual borders and grounds would double it. */
[data-connected-nav].is-connected > a { border-color: transparent; background: none; }
[data-connected-nav] > a.is-on { color: var(--paper); }

[data-connected-nav]:not(.is-connected) > a[aria-current='page'] {
  background: var(--ink);
  color: var(--paper);
}

.navgroup__mark { aspect-ratio: 1 / 1; padding: 0; }

/* The Atlas' mark is solid: a drawn shape rather than an outlined one, so it
   holds at the size a button gives it and reads as a mark rather than as one
   more small o. It takes currentColor, so it goes to paper when its capsule
   fills. */
.navgroup__mark svg {
  width: 1.05em;
  height: 1.05em;
  fill: currentColor;
  stroke: none;
}

.connected__canvas {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: visible;
  pointer-events: none;
}

.connected__ground { fill: var(--paper); stroke: none; }

.connected__outline {
  fill: none;
  stroke: var(--ink);
  stroke-width: var(--stroke);
}

/* The capsule fills and empties on the same easing as the type inside it. It
   is written as an opacity rather than as a fill, because `none` is not a
   colour: a transition from `none` to ink has nothing to interpolate and the
   capsule snapped on instead of arriving. */
.connected__fill {
  fill: var(--ink);
  fill-opacity: 0;
  transition: fill-opacity 240ms ease-in-out;
}

.connected__fill[data-on] { fill-opacity: 1; }

/* While the row is being set for the first time on a page, nothing moves: the
   capsule under the page you opened is filled because that is where you are,
   not because something just happened. */
[data-connected-nav].is-settling > a,
[data-connected-nav].is-settling .connected__fill {
  transition: none;
}

/* --- site navigation ----------------------------------------------------- */

/* Three columns rather than three things spaced apart, because the count on
   the right is filled in by script a moment after the page appears: spaced
   apart, the nav in the middle was shoved sideways the instant the number
   landed, which is what made the menu jump on every page you opened. In a grid
   the middle column is the middle of the bar whatever the two beside it are
   doing, and the number can arrive without moving anything. */
.topbar {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 0.6rem 1.1rem;
  align-items: center;
  padding: 1.1rem var(--gutter);
  border-bottom: var(--rule);
}

.topbar__count { justify-self: end; }

/* The way back, at every width. It used to live at the head of the section row
   on a wide screen and as a button of its own on a phone, which made the same
   mark two different sizes and two different shapes depending on the window.
   One mark, one place: the left of the bar, where a reader looks for it. */
.atlas-mark {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  background: var(--paper);
  border: var(--stroke) solid var(--ink);
  border-radius: 999px;
  color: var(--ink);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}

@media (hover: hover) {
  .atlas-mark:hover { background: var(--ink); color: var(--paper); }
}
.atlas-mark:active, .atlas-mark:focus-visible { background: var(--ink); color: var(--paper); }

/* Solid: a drawn shape rather than an outlined one, so it holds at this size. */
.atlas-mark svg {
  display: block;
  width: 1.25rem;
  height: 1.25rem;
  fill: currentColor;
  stroke: none;
}

/* The mark and the name are one thing — who this is — so the row spreads the
   three things it actually has: that, the sections, and the count. */
.topbar__id {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  min-width: 0;
}

.topbar__brand {
  font-family: var(--display);
  font-size: clamp(1.15rem, 2.2vw, 1.6rem);
  text-decoration: none;
  white-space: nowrap;
}

.topbar nav {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.topbar__count {
  font-size: 0.8rem;
  color: var(--ink-55);
  font-variant-numeric: tabular-nums;
}

/* Every page is a column that fills the window, so anything asking for the
   leftover space — the map, above all — actually gets it. */
.page {
  display: flex;
  flex-direction: column;
  min-height: 100svh;
}

/* --- landing --------------------------------------------------------------
   The drawing and the type share one 1920x1080 frame. The hero is as wide as it
   can be while still fitting the viewport height, so the composition is never
   cropped; type is sized in cqw so it scales with the drawing rather than
   drifting off the lines that point at it.
   -------------------------------------------------------------------------- */

.page--landing { justify-content: center; overflow-x: hidden; }

.hero {
  position: relative;
  width: 100%;
  max-width: calc((100svh - 5rem) * 16 / 9);
  aspect-ratio: 16 / 9;
  margin: 0 auto;
  container-type: inline-size;
}

/* The field is the frame; the button is placed against it rather than against
   the hero, so the two travel together when the layout unstacks. */
.hero__field { position: absolute; inset: 0; }

/* The picture is only there to choose between the moving drawing and the still
   one; the image inside it is what the layout places. */
.hero__field picture { display: contents; }

.hero__drawing {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  user-select: none;
}

/* Everything on this page stands on the frame's vertical centreline: the type
   column, the button, the line about who tends it. The boxes here are the same
   boxes scripts/draw-hero.mjs keeps clear of marks, written as fractions of the
   1920x1080 frame, so the words land in the clearing the drawing left them. */
.hero__type {
  position: absolute;
  left: 50%;
  translate: -50% 0;
  width: 52%;
  top: 10%;
  text-align: center;
}

.hero .wordmark {
  font-size: 4cqw;
  line-height: 1;
  margin: 0;
}

.hero .standfirst {
  font-size: 1.55cqw;
  margin: 1.15cqw 0 0;
  color: var(--ink);
}

.hero .blurb {
  font-size: 1.18cqw;
  line-height: 1.5;
  max-width: 84%;
  margin: 2.5cqw auto 0;
  color: var(--ink);
}

/* Centring is done with `translate` rather than `transform`, so the hover below
   has `scale` to itself and does not have to know how the button is placed. */
/* The way in is set in the display face, like the name above it: it is the
   Atlas speaking, not the interface. Every other button on the site is the
   interface, and stays in the sans. */
.hero__atlas {
  font-family: var(--display);
  /* Enby Gertrude's capitals sit 0.08em above the middle of its line box, the
     same on every platform (its two sets of metrics agree), so the constant
     that brings them down is a true one. */
  --type-drop: 0.08em;
  position: absolute;
  left: 50%;
  top: 56.5%;
  translate: -50% 0;
  font-size: 2cqw;
  padding: calc(0.53em + var(--type-drop)) 1.5em calc(0.53em - var(--type-drop));
}

/* The one button that is filled at rest: it is the way in, and the page should
   not need a second look to find it. Pointing at it can't change its colour —
   there is no darker than black — so it comes forward instead, once, and then
   holds still. It used to breathe on a loop, which kept asking for attention
   the reader had already given it. */
.hero__atlas,
.hero__atlas:hover,
.hero__atlas:focus-visible {
  background: var(--ink);
  color: var(--paper);
}

.hero__atlas {
  transition: scale 220ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.hero__atlas:hover,
.hero__atlas:focus-visible {
  scale: 1.05;
}

/* Who tends it, in the clear ground below the button and inside the field
   rather than under it — the drawing goes on around the words. */
.hero__curator {
  position: absolute;
  left: 50%;
  translate: -50% 0;
  top: 68.5%;
  width: 40%;
  margin: 0;
  text-align: center;
  font-family: var(--display);
  font-size: 1.18cqw;
  line-height: 1.5;
  color: var(--ink);
}

/* Her name is the one link in the line, so it is underlined rather than left
   to be discovered by hovering. */
.hero__curator a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: from-font;
}

/* The type credit the licence asks for — set small and put at the very bottom
   of the page about the Atlas, because it is a colophon: there to be found
   rather than to be read on the way past. */
.colophon {
  max-width: 52ch;
  margin: 4rem 0 0;
  font-family: var(--display);
  font-size: 0.66rem;
  line-height: 1.5;
  color: var(--ink-55);
}

@media (max-width: 48rem) {
  /* Below this the frame is too small to read type off, so the composition
     unstacks: type first at ordinary sizes, the drawing kept as a coda.

     The whole landing is held to one screen. A phone gets the words, the way
     in and the line about who tends it without a scroll, because everything
     here is the introduction — a page you have to scroll to finish reading
     hides half of its own invitation. The parts that can give are the ones
     that give: the paddings and the field, which is a window on a drawing
     that carries on past it in every direction anyway. */
  .hero {
    max-width: none;
    aspect-ratio: auto;
    display: flex;
    flex-direction: column;
    /* Fill what the footer leaves, and no more — the field below takes up
       the slack, so the sum is the viewport rather than the content. */
    flex: 1 1 auto;
    min-height: 0;
    padding: clamp(1rem, 3.4svh, 2.5rem) var(--gutter) clamp(1.25rem, 4svh, 3rem);
    /* The drawing is wider than the phone and is meant to run off both edges. */
    overflow: hidden;
  }

  /* The field stops being the ground and becomes a band of its own, between
     the words and the line about who tends it. Behind the type it was clutter:
     a scatter of washed blots is not a texture you can read off. It is zoomed
     and cropped rather than shrunk, so a blot on a phone is still a blot. */
  /* Edge to edge, past the page's own margin: the field is wider than the
     phone and is meant to run off both sides, so a mark cut by the screen
     reads as the drawing carrying on rather than as a cropped picture. */
  /* Its height is whatever the type and the curator's line leave over. The
     band is a window, not a picture: the drawing is anchored to the button at
     its middle and continues past all four edges, so a shorter screen crops
     it further rather than shrinking anything inside it. */
  .hero__field {
    position: relative;
    order: 1;
    width: 100vw;
    margin-left: calc(50% - 50vw);
    flex: 1 1 auto;
    aspect-ratio: auto;
    height: auto;
    /* A floor, so the band never closes up around the button — but a floor
       measured against the screen too, because a phone held on its side has
       no 8.5rem to spare and a scroll would be the worse answer. */
    min-height: min(8.5rem, 20svh);
    max-height: 100vw;
    margin-top: clamp(0.5rem, 1.6svh, 1.25rem);
    /* The band decides how much room the drawing gets, not where it stops. A
       mark cut off in a straight line above a paragraph reads as a picture
       with an edge; the same mark running on under the words reads as a field
       the words are standing in, which is what it is. So the marks pass behind
       the type and are cut only by the page itself. */
    overflow: visible;
  }

  /* A window on the field rather than the whole of it, centred on the clearing
     the button stands in — so the marks surround the button here the way they
     do on a wide screen, instead of being squeezed into a band across it. It
     is zoomed rather than shrunk: a blot is very nearly the size on a phone
     that it is on a laptop, which is the point of drawing it at all. 60.6% is
     where the button's middle sits down the drawing, and the width is three
     screens whatever the band's height, so cropping never resizes a mark. */
  .hero__drawing {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 300vw;
    height: auto;
    margin: 0;
    transform: translate(-50%, -60.6%);
  }

  /* Relative, to sit over the drawing — so the offsets that place it in the
     frame on a wide screen have to be undone rather than left inert. Still
     centred: the composition is the same one, unstacked. */
  .hero__type {
    position: relative;
    z-index: 1;
    top: auto;
    left: auto;
    width: auto;
    translate: none;
    text-align: center;
    flex: 0 0 auto;
  }

  /* Sized against the short side as well as the long one: a phone held on a
     short screen shrinks the type rather than pushing the field off the end. */
  .hero .wordmark { font-size: clamp(2rem, min(10.5vw, 5.6svh), 3.4rem); }
  .hero .standfirst { font-size: clamp(0.92rem, 2svh, 1.05rem); margin-top: clamp(0.35rem, 0.9svh, 0.6rem); }
  .hero .blurb {
    font-size: clamp(0.88rem, 1.9svh, 1rem);
    line-height: 1.45;
    margin-top: clamp(0.7rem, 2svh, 1.4rem);
  }

  /* Still in the middle of the field, which is now a band rather than a frame. */
  .hero__atlas {
    top: 50%;
    left: 50%;
    translate: -50% -50%;
    z-index: 1;
    margin: 0;
    font-size: 1.25rem;
    padding: calc(0.43em + var(--type-drop)) 1.2em calc(0.43em - var(--type-drop));
  }

  /* The break is set for a line that has room for it. On a phone the first
     half does not fit on one line anyway, so letting it wrap where it will
     gives two lines rather than three with an orphan on the middle one. */
  .hero__break { display: none; }

  .hero__curator {
    position: relative;
    z-index: 1;
    top: auto;
    left: auto;
    translate: none;
    order: 2;
    flex: 0 0 auto;
    width: auto;
    max-width: 36ch;
    margin: clamp(0.8rem, 2.2svh, 1.75rem) auto 0;
    font-size: clamp(0.85rem, 1.9svh, 0.95rem);
  }

}

/* --- generic page body --------------------------------------------------- */

.sheet {
  padding: clamp(1.5rem, 4vw, 2.75rem) var(--gutter) 4rem;
  max-width: 82rem;
  margin: 0 auto;
  width: 100%;
}

/* The pages that are read rather than used — About and Add — share one column,
   the same width and in the same place on both, so that clicking between them
   moves nothing but the words. The column is the sheet rather than a measure
   set inside a wider one, which is what puts it in the middle of the page
   instead of against the left of an invisible box. */
.sheet--reading { max-width: 44rem; }

/* Inside it nothing sets a width of its own: the column is the measure. */
.sheet--reading .blurb,
.sheet--reading .prose { max-width: none; }

.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1rem;
  margin: 2.5rem 0 1rem;
}

.section-head h2 {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(1.3rem, 2.4vw, 1.9rem);
  margin: 0;
}

.section-head p { margin: 0; color: var(--ink-55); font-size: 0.9rem; }

/* --- tags ---------------------------------------------------------------- */

.tag-cloud { display: flex; flex-wrap: wrap; gap: 0.45rem; }

.tag {
  font: inherit;
  font-size: 0.85rem;
  background: none;
  border: var(--stroke) solid var(--ink);
  border-radius: 999px;
  padding: calc(0.28em + var(--type-drop)) 0.85em calc(0.28em - var(--type-drop));
  cursor: pointer;
  color: inherit;
  display: inline-flex;
  align-items: baseline;
  justify-content: center;
  text-align: center;
  gap: 0.45em;
  text-decoration: none;
  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}

/* Pointing at one fills it; pressing one fills it too. Only a device that can
   actually hover gets the hover rule — on a touchscreen it sticks after the tap
   and leaves a button looking switched on when it is nothing of the kind. */
@media (hover: hover) {
  .tag:hover, .chip:hover { background: var(--ink); color: var(--paper); }
}
.tag:active, .chip:active { background: var(--ink); color: var(--paper); }

.tag[aria-pressed='true'], .tag.is-on {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}

/* The count is the tag's weight, not part of its name, and a name pushed off
   centre by the number beside it reads as a button whose text is not centred —
   which is what it is. So the number is lifted out of the line and hung in
   space reserved for it at both ends, and the name sits in the middle of the
   pill where it belongs. */
.tag:has(.tag__count) { position: relative; padding-inline: 1.7em; }

.tag__count {
  font-size: 0.72em;
  font-variant-numeric: tabular-nums;
  opacity: 0.6;
}

.tag:has(.tag__count) .tag__count {
  position: absolute;
  right: 0.8em;
  top: 50%;
  /* Set on the same line as the word it counts, so the two agree. */
  translate: 0 calc(-50% + var(--type-drop));
}

.tag--ghost { border-style: dashed; }

/* The box at the end of a facet's chips for a tag of your own: a chip you
   write in. It is a chip to look at — same height, same text line — and a field
   to use, so it never fills on hover the way a chip you press does, and its
   outline goes solid while you are typing in it. */
.tag--own { cursor: text; }
.tag--own:hover, .tag--own:active { background: none; color: inherit; }
.tag--own:focus-within { border-style: solid; }

.tag--own input {
  font: inherit;
  color: inherit;
  line-height: inherit;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  outline: none;
  width: 8.5em;
  field-sizing: content;
  min-width: 8.5em;
  max-width: 18em;
}

.tag--own input::placeholder { color: var(--ink-55); opacity: 1; }

.country-field { max-width: 22rem; margin-top: 1rem; }

/* --- fields -------------------------------------------------------------- */

.field { display: block; margin-bottom: 1.25rem; }

.field > span {
  display: block;
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-55);
  margin-bottom: 0.4rem;
}

.field input, .field textarea, .field select {
  width: 100%;
  font: inherit;
  color: inherit;
  background: transparent;
  border: 1px solid var(--ink-32);
  border-radius: 0;
  padding: 0.6rem 0.75rem;
}

.field input:focus, .field textarea:focus { border-color: var(--ink); outline: none; }

.field textarea { min-height: 5.5rem; resize: vertical; }

.field__hint { font-size: 0.8rem; color: var(--ink-55); margin: 0.35rem 0 0; }

/* the "My reference…" pill from the mockup */
.pill-field {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  border: 1px solid var(--ink);
  border-radius: 999px;
  padding: 0.15rem 0.35rem 0.15rem 1.4rem;
  background: transparent;
  max-width: 40rem;
}

.pill-field input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  font-family: var(--display);
  font-size: clamp(1rem, 1.8vw, 1.35rem);
  padding: 0.7rem 0;
  color: inherit;
}

.pill-field input:focus { outline: none; }
.pill-field input::placeholder { color: var(--ink-32); }

.pill-field button {
  border: var(--stroke) solid var(--ink);
  border-radius: 999px;
  background: none;
  color: var(--ink);
  font: inherit;
  font-size: 0.9rem;
  padding: 0.55rem 1.2rem;
  cursor: pointer;
  white-space: nowrap;
  transition: background 140ms ease, color 140ms ease;
}

.pill-field button:hover, .pill-field button:focus-visible {
  background: var(--ink);
  color: var(--paper);
}

.pill-field button[disabled] { opacity: 0.4; cursor: progress; }

/* --- source records ------------------------------------------------------ */

.records { list-style: none; margin: 0; padding: 0; }

.record {
  padding: 1.15rem 0;
  border-top: var(--rule);
  display: grid;
  gap: 0.4rem;
}

.record:last-child { border-bottom: var(--rule); }

.record__title {
  font-family: var(--display);
  font-size: clamp(1.1rem, 1.9vw, 1.45rem);
  line-height: 1.2;
  margin: 0;
}

.record__title a { text-decoration: none; background-image: linear-gradient(var(--ink-32), var(--ink-32)); background-size: 100% 1px; background-repeat: no-repeat; background-position: 0 100%; }
.record__title a:hover { background-image: linear-gradient(var(--ink), var(--ink)); }

.record__meta { font-size: 0.85rem; color: var(--ink-55); margin: 0; }
.record__summary { margin: 0; max-width: 68ch; color: var(--ink-80); }
.record__note { margin: 0; max-width: 62ch; font-family: var(--display); border-left: 2px solid var(--ink-32); padding-left: 0.9rem; }

.empty {
  font-family: var(--display);
  color: var(--ink-55);
  padding: 3rem 0;
  text-align: center;
}

/* --- the map ------------------------------------------------------------- */

.map-shell {
  position: relative;
  flex: 1;
  min-height: 60vh;
  /* The record panel parks off the right edge; without this it widens the page. */
  overflow: hidden;
}

/* Absolutely positioned rather than 100%/100%: a canvas is a replaced element,
   so a percentage height falls back to its own width/height attributes — which
   this code sets from the measured box, and the loop never resolves. */
#constellation {
  display: block;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  touch-action: none;
  cursor: grab;
}
#constellation.is-dragging { cursor: grabbing; }
#constellation.is-over { cursor: pointer; }

/* The chrome over the map: the tag bar, and the drawer that drops out of it.
   One stack, so the drawer always hangs directly off the row it belongs to. */
.map-chrome {
  position: absolute;
  top: 1rem;
  /* The page's own margin, not a margin of its own: the search field, the
     filters and the mark in the bar above all stand on one line down the left
     of the window. A floating row inset by some other amount reads as a
     second, competing edge. */
  left: var(--gutter);
  right: var(--gutter);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.5rem;
  pointer-events: none;
  z-index: 3;
}

/* One row, always: the tags that fit are shown and the rest are behind More.
   A filter bar that grows to three lines as you pick tags moves the map under
   it every time, which is worse than not seeing every tag at once. */
.map-controls {
  display: flex;
  flex-wrap: nowrap;
  gap: 0.5rem;
  align-items: center;
  min-width: 0;
}

.map-controls > *, .tag-drawer { pointer-events: auto; }

/* The name of the thing, so the row is not a mystery row of words. It carries
   its own paper like everything else standing on the drawing. */
.tag-bar__name {
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-55);
  flex: 0 0 auto;
  background: var(--paper);
  border-radius: 999px;
  padding: 0.3rem 0.5rem;
}

#map-filters, #map-places {
  /* Sized by the script to exactly the tags that fit; until then it simply
     takes what is left, and the row is still a row. */
  flex: 0 1 auto;
  flex-wrap: nowrap;
  min-width: 0;
  overflow: hidden;
}

/* The tags keep their own width whatever the box around them is doing, so the
   script can collapse that box to nothing to take a measurement and still be
   measuring real tags. */
#map-filters > *, #map-places > * { flex: 0 0 auto; }

/* Anything the row could not hold is hidden rather than clipped in half. */
.tag[hidden], .chip[hidden] { display: none !important; }

/* Same for the caret: it says the button opens something, it is not its name.
   Written with both classes because .chip sets padding as a shorthand further
   down the sheet, and a shorthand beats a longhand that came before it. */
.chip--more { flex: 0 0 auto; position: relative; }
.chip.chip--more { padding-inline: 1.75em; }
.chip--more__narrow { display: none; }

.chip__caret {
  position: absolute;
  right: 0.7em;
  top: 50%;
  translate: 0 -50%;
  width: 0.6rem;
  height: 0.45rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: rotate 160ms ease;
}
[aria-expanded='true'] .chip__caret { rotate: 180deg; }

/* A button holding something open is on, whether or not you are pointing at it. */
.chip[aria-expanded='true'] { background: var(--ink); color: var(--paper); }

/* The drawer: the whole vocabulary, dropped out of the bar. It is a panel on a
   wide screen and the tag menu itself on a narrow one. */
.tag-drawer {
  background: var(--paper);
  border: var(--stroke) solid var(--ink);
  border-radius: 1rem;
  padding: 0.85rem 0.95rem;
  max-height: min(24rem, 46vh);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.tag-drawer .tag-cloud { gap: 0.4rem; }


.chip {
  font: inherit;
  font-size: 0.8rem;
  background: var(--paper);
  border: var(--stroke) solid var(--ink);
  border-radius: 999px;
  padding: calc(0.35em + var(--type-drop)) 0.85em calc(0.35em - var(--type-drop));
  cursor: pointer;
  color: inherit;
}

.chip.is-on { background: var(--ink); color: var(--paper); border-color: var(--ink); }

/* A chip carrying a drawn mark rather than a word: square, so the circle in it
   sits in the middle of a circle rather than at the end of a lozenge. */
/* The same circle as the Atlas' mark in the bar above, at the same size and on
   the same left edge: they are the two round things at the top-left corner of
   the page, and two circles nearly the same size read as a mistake where two
   the same read as a column. */
.chip--icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  width: 2.5rem;
  height: 2.5rem;
  flex: 0 0 auto;
}

.chip--icon svg {
  width: 1.15rem;
  height: 1.15rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
}

@media (hover: hover) {
  .chip--icon:hover { background: var(--ink); color: var(--paper); }
}
.chip--icon:focus-visible, .chip--icon[aria-expanded='true'] { background: var(--ink); color: var(--paper); }

/* Filled at rest. Everything else in this row is an outline until you point at
   it; the way back to the whole library is the one control worth seeing before
   you go looking for it. */
.chip--filled {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

@media (hover: hover) {
  .chip--filled:hover { background: var(--paper); color: var(--ink); }
}
.chip--filled:focus-visible { background: var(--paper); color: var(--ink); }

/* A drawing rather than a letter, so it is centred on itself: the type drop
   above is a correction for the empty descender well under a line of type, and
   a stroke has no descender to allow for. The padding goes back to even. */
.chip__mark {
  display: block;
  /* A line box tall, so the button keeps the height of the words beside it,
     and square, so it keeps the circle it had when it was a character. */
  width: 1.5em;
  height: 1.5em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
}

.chip:has(.chip__mark) {
  padding: 0.35em;
  display: inline-grid;
  place-items: center;
  aspect-ratio: 1;
}

/* The zoom cluster sits in the controls row like any other chip on a wide
   screen, so it is nothing at all until the phone layout gives it a box. */
.map-zoom { display: contents; }
.chip__icon { display: none; }

/* Filters at one end, the view's own controls at the other: the space left over
   in the row falls between the two, which is where the difference is. */
.map-zoom > :first-child { margin-left: auto; }

/* Nothing found.
   In the middle of the ground it would otherwise be drawn on, because that is
   where you are looking when the map goes blank — not at the corner where the
   legend lives. It takes no pointer events but its own button does, so the map
   underneath can still be dragged out from under the message. */
.map-empty {
  position: absolute;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  padding: 0 var(--gutter);
  text-align: center;
  pointer-events: none;
}

/* `hidden` is a property, not a wish: a class that sets `display` outranks the
   browser's own rule for a hidden element, so without this the message was
   painted over every map whatever the script said about it. Any rule that
   gives an element a display has to say what hiding it means. */
.map-empty[hidden] { display: none; }

.map-empty > * { pointer-events: auto; }

.map-empty__text {
  margin: 0;
  max-width: 28ch;
  font-family: var(--display);
  font-size: clamp(1.05rem, 1.6vw, 1.3rem);
  line-height: 1.45;
  color: var(--ink-80);
}

.map-legend {
  position: absolute;
  left: var(--gutter);
  bottom: 1rem;
  max-width: min(20rem, 46vw);
  font-size: 0.78rem;
  color: var(--ink-55);
  line-height: 1.45;
  pointer-events: none;
}

.map-legend b { color: var(--ink); font-weight: 500; }

/* The one line under the map that is an offer rather than a description, so it
   is underlined: everything else down here is text, and this is a door. */
.map-legend__export {
  /* A line of its own, above the count, but only as wide as its own words —
     the underline is the door, and a door the width of the column is not. */
  display: block;
  width: fit-content;
  margin-bottom: 0.35rem;
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 0.18em;
  pointer-events: auto;
}

@media (hover: hover) {
  .map-legend__export:hover { text-decoration-thickness: 2px; }
}

/* The record is a card laid on the map rather than a wall built beside it: it
   floats clear of every edge, outlined in ink, and the map keeps running
   underneath it. --head is the colour of its title bar, set per record. */
.panel {
  --head: var(--ink);
  position: absolute;
  /* Measured in map.js: the filter row wraps to two lines on a narrow window,
     and a fixed top would put the card over the tags. */
  top: var(--panel-top, 4.25rem);
  z-index: 2;
  right: 0.85rem;
  bottom: 0.85rem;
  width: min(25rem, calc(100% - 1.7rem));
  /* The card is backed in its title bar's colour, and the record paints paper
     over the rest. Backed in paper, any seam the rounded corners leave under a
     transform shows as a pale line between the stroke and the colour. */
  background: var(--head);
  border: var(--stroke) solid var(--ink);
  border-radius: 0.9rem;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transform: translateX(calc(100% + 1rem));
  transition: transform 220ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* A card parked off the right of the screen and a sheet parked under the bottom
   of it are the same card in two layouts, and crossing between them changes
   which way it is parked. Without this you watch a closed drawer slide across
   the window as you resize it: an animation of a thing that is not there. */
.panel.is-resizing { transition: none; }

.panel.is-open { transform: none; }
.panel[hidden] { display: flex; }

/* Only a bottom sheet has a grip. */
.sheet-grip { display: none; }

/* --- the title bar -------------------------------------------------------- */

.panel__head {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  padding: 0.85rem 0.9rem 0.9rem 1rem;
  /* The title's own measure, so the marks on either side of it can be set on
     the line rather than near it. */
  --title-size: 1.35rem;
  --title-leading: 1.12;
  --title-line: calc(var(--title-size) * var(--title-leading));
  background: var(--head);
  /* A piece of research is ink, and so are most tags: what is written on the
     title bar follows the colour of the bar rather than the kind of record. */
  color: var(--head-ink, var(--ink));
}

.panel__title {
  font-family: var(--display);
  font-weight: 400;
  font-size: var(--title-size);
  line-height: var(--title-leading);
  margin: 0;
  flex: 1 1 auto;
  color: inherit;
}

/* The mark sits on the centreline of the first line of the title, whether the
   title runs to one line or three: a box exactly one line tall, aligned to the
   top of the type, with the drawing centred inside it. Nudged by hand it drifts
   the moment the type changes size. */
/* The share, in the head beside the way out. An empty circle with the mark
   drawn in it, like every other round button on the site: the head is already
   the record's own colour, and a filled disc on top of it reads as a second
   thing sitting on the bar rather than as a control on it. */
.panel__share {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.7rem;
  height: 1.7rem;
  margin-top: calc((var(--title-line) - 1.7rem) / 2);
  padding: 0;
  background: none;
  border: var(--stroke) solid currentColor;
  border-radius: 999px;
  color: inherit;
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}

.panel__share svg {
  width: 0.92rem;
  height: 0.92rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linecap: round;
}

/* Filled only while pointed at, the way the rest of them are. */
@media (hover: hover) {
  .panel__share:hover { background: var(--head-ink); color: var(--head); }
}
.panel__share:focus-visible { background: var(--head-ink); color: var(--head); }

/* What happened, said under the bar and then taken back. It is always in the
   page and always transparent, because a message that is created when it is
   needed has nothing to fade from — and the fade is the whole of the manners
   here: long enough to read, gone without being dismissed. */
.panel__said {
  position: absolute;
  top: 100%;
  right: 0.9rem;
  z-index: 2;
  margin-top: 0.45rem;
  padding: calc(0.3em + var(--type-drop)) 0.75em calc(0.3em - var(--type-drop));
  background: var(--ink);
  color: var(--paper);
  font-family: var(--sans);
  font-size: 0.72rem;
  border-radius: 999px;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  translate: 0 -0.25rem;
  transition: opacity 260ms ease, translate 260ms ease;
}

.panel__said.is-said {
  opacity: 1;
  translate: 0 0;
}

@media (prefers-reduced-motion: reduce) {
  .panel__said { transition-duration: 1ms; }
}

.panel__glyph, .panel__close {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  height: var(--title-line);
}

.panel__glyph { width: 1.7rem; }

/* When the drawer is a search, what it is about is what you are typing, so the
   field takes the place the title would have had — a pill on the head's own
   ground, in the head's own ink, so it reads as part of the bar rather than as
   a form dropped onto it. */
.panel__search {
  display: none;
  flex: 1 1 auto;
  min-width: 0;
  font: inherit;
  font-family: var(--sans);
  font-size: 0.95rem;
  color: var(--head-ink);
  background: none;
  border: var(--stroke) solid var(--head-ink);
  border-radius: 999px;
  padding: calc(0.36em + var(--type-drop)) 1em calc(0.36em - var(--type-drop));
}

/* The browser puts its own clear button in a search field — a blue cross in
   nobody's typeface, drawn to nobody's line weight. The drawer already has a
   way out, in its own hand, at the end of the same bar. */
.panel__search::-webkit-search-cancel-button,
.panel__search::-webkit-search-decoration { -webkit-appearance: none; display: none; }

.panel__search::placeholder { color: var(--head-ink); opacity: 0.6; }
/* The same line as the link button beside it and nothing added on focus: a
   ring drawn outside the border to say "you are typing here" made the field
   read a weight heavier than every other outline in the head, while the caret
   was already saying it. */
.panel__search { -webkit-appearance: none; appearance: none; }
.panel__search:focus { outline: none; box-shadow: none; }

/* The search head is the field and the way out, and nothing else. */
.panel[data-kind='search'] .panel__glyph,
.panel[data-kind='search'] .panel__title { display: none; }
.panel[data-kind='search'] .panel__search { display: block; }

.panel__glyph svg { display: block; width: 100%; height: auto; }
.panel__glyph .crosshair circle,
.panel__glyph .crosshair path { fill: none; stroke: currentColor; stroke-width: 1.6; }
.panel__glyph .blot { fill: currentColor; }

.panel__close {
  border: 0;
  background: none;
  padding: 0;
  margin: 0;
  width: 1.6rem;
  cursor: pointer;
  color: inherit;
  opacity: 0.75;
}

.panel__close svg { display: block; width: 100%; height: auto; fill: none; stroke: currentColor; stroke-width: 2; }
.panel__close:hover { opacity: 1; }

/* --- the record itself ---------------------------------------------------- */

.panel__scroll {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  background: var(--paper);
}

#panel-body {
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 1.1rem 1.15rem 1.5rem;
  /* The card draws its own scrollbar; see .scroll-rail. */
  scrollbar-width: none;
}

#panel-body::-webkit-scrollbar { display: none; }

/* The card's scrollbar, drawn rather than borrowed: one hairline the height of
   the record and a single ink dot at your place in it. Every browser draws its
   native bar differently and a phone draws a slab over the content; this is the
   same delicate mark everywhere, and it is never in the way. */
.scroll-rail {
  position: absolute;
  top: 0.9rem;
  bottom: 0.9rem;
  right: 0.42rem;
  width: 9px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms ease;
}

.scroll-rail::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 1px;
  margin-left: -0.5px;
  background: var(--ink-14);
}

.scroll-rail.is-live { opacity: 1; }

.scroll-rail__dot {
  position: absolute;
  top: 0;
  left: 50%;
  width: 7px;
  height: 7px;
  margin-left: -3.5px;
  border-radius: 50%;
  background: var(--ink);
}

.panel__meta { font-size: 0.85rem; color: var(--ink-55); margin: 0 0 1rem; }
.panel__summary { margin: 0 0 1.25rem; color: var(--ink-80); }

.panel h4 {
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-55);
  margin: 1.5rem 0 0.6rem;
  font-weight: 500;
}

#panel-body > :first-child { margin-top: 0; }

/* The card is the one thing that scrolls. A list long enough to need scrolling
   inside it put a second scrollbar against the first, and nested scrollers are
   a trap for a wheel and worse for a thumb. */
.panel__links {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.55rem;
}

.panel__links a {
  text-decoration: none;
  border-bottom: 1px solid var(--ink-14);
  padding-bottom: 0.4rem;
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
}

.panel__links a:hover, .panel__links a:focus-visible { border-color: var(--ink); }
.panel__links small { display: block; color: var(--ink-55); font-size: 0.78rem; }
.thread__name { flex: 1 1 auto; }

/* Where the work is grounded, on its own line under the citation and set in
   the interface's face rather than the book's: it is a fact about the record
   rather than part of how the record cites itself, and a list is scanned down
   this column. */
.thread__place {
  display: block;
  margin-top: 0.15rem;
  font-family: var(--sans);
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  color: var(--ink-32);
}

/* The mark a piece of research wears on the map, worn again in the list — and
   pointed at, it fills in here exactly as it fills in out there. */
.crosshair {
  flex: 0 0 auto;
  width: 1.05rem;
  height: 1.05rem;
  margin-top: 0.12rem;
  overflow: visible;
}

/* The mark a piece of research wears on the map, worn again in the list — and
   pointed at, its circle fills in here exactly as it fills in out there. */
.crosshair circle { fill: none; stroke: var(--ink); stroke-width: 1.6; transition: fill 120ms ease; }
.crosshair path { fill: none; stroke: var(--ink); stroke-width: 1.6; transition: stroke 120ms ease; }

.panel__links a:hover .crosshair circle,
.panel__links a:focus-visible .crosshair circle { fill: var(--ink); }
.panel__links a:hover .crosshair path,
.panel__links a:focus-visible .crosshair path { stroke: var(--paper); }

/* A tag chip carries its blot's colour, so a record's tags are the same five
   colours you just saw around it on the map. */
.tag--wash { background: var(--chip, transparent); color: var(--chip-ink, inherit); }
.tag--wash:hover { background: var(--ink); color: var(--paper); }

/* The map is a single screen: the page under it never scrolls, so nothing can
   reserve a strip at its edge for a scrollbar. */
.page--map { overflow: hidden; }

/* --- scrollbars ----------------------------------------------------------- */

/* A scrollbar here is a measurement, not a slab: one hairline down the track
   and a single ink dot riding it. The dot is painted at the centre of the
   thumb, so where it sits is where you are in the list.
   
   Only browsers that expose the scrollbar's parts can be given this. Setting
   the standard `scrollbar-color` anywhere would switch those parts off, so it
   is kept to the two cases that cannot draw the dot at all — see below. */
::-webkit-scrollbar { width: 11px; height: 11px; }

::-webkit-scrollbar-track {
  background: linear-gradient(
    to right,
    transparent calc(50% - 0.5px),
    var(--ink-14) calc(50% - 0.5px),
    var(--ink-14) calc(50% + 0.5px),
    transparent calc(50% + 0.5px)
  );
}

::-webkit-scrollbar-track:horizontal {
  background: linear-gradient(
    to bottom,
    transparent calc(50% - 0.5px),
    var(--ink-14) calc(50% - 0.5px),
    var(--ink-14) calc(50% + 0.5px),
    transparent calc(50% + 0.5px)
  );
}

::-webkit-scrollbar-thumb {
  min-height: 26px;
  min-width: 26px;
  background: radial-gradient(circle at center, var(--ink) 0 3.2px, transparent 3.4px) center / 11px 11px
    no-repeat;
}

::-webkit-scrollbar-corner { background: transparent; }

/* Firefox has no parts to style; it gets the thin native bar, in ink, instead.
   A touch browser is deliberately left alone: asking it for a colour makes it
   swap its overlay bar for one that takes a column of the page, and that column
   came out of the map — a strip of bare paper down the side of everything. */
@supports (-moz-appearance: none) {
  * { scrollbar-width: thin; scrollbar-color: var(--ink-32) transparent; }
}

/* --- about ----------------------------------------------------------------
   A page of prose, which the rest of the site is not. It is set at reading
   width in the display face, because it is the one place where somebody is
   being told something rather than shown it.
   -------------------------------------------------------------------------- */

/* The same measure as the .blurb above it: two different good measures on one
   page still read as two columns that do not quite line up. */
.prose {
  max-width: 46ch;
  margin-top: 2.25rem;
  font-family: var(--display);
  font-size: clamp(1rem, 1.2vw, 1.12rem);
  line-height: 1.6;
  color: var(--ink-80);
}

.prose h2 {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(1.25rem, 2vw, 1.6rem);
  line-height: 1.2;
  color: var(--ink);
  margin: 2.4rem 0 0.6rem;
}

.prose h2:first-child { margin-top: 0; }
.prose p { margin: 0 0 1rem; }
.prose strong { font-weight: 400; color: var(--ink); }
.prose em { font-style: italic; }

/* A button standing in the middle of the reading, rather than at the end of
   it: it is the thing the paragraph under it is about. */
.prose__action { margin: 1.6rem 0 1.1rem; }

.about__ways {
  display: flex;
  flex-wrap: wrap;
  gap: 0.7rem;
  margin-top: 2.75rem;
}

.about__ways .label { font-size: clamp(0.95rem, 1.2vw, 1.1rem); }

/* --- notices ------------------------------------------------------------- */

.notice {
  border: 1px solid var(--ink);
  padding: 0.9rem 1.1rem;
  margin: 0 0 1.5rem;
  font-size: 0.92rem;
}

.notice--bad { border-color: var(--red); color: var(--red); }
.notice--good { border-color: var(--blue); color: var(--blue); }
.notice a { color: inherit; }

.spinner-note { font-size: 0.85rem; color: var(--ink-55); }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}

/* --- responsive ---------------------------------------------------------- */

@media (max-width: 46rem) {
  /* --- the top bar on a phone -------------------------------------------
     The section row is two wrapped lines here, taken out of the one screen the
     map has, so it stands down and the bar keeps one control: the Atlas' mark,
     which is the way back to the page the sections are laid out on. */
  /* No nav here, so there is no middle to hold: two things, one at each end. */
  .topbar {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 0.5rem;
    padding: 0.7rem var(--gutter);
  }

  .atlas-mark { width: 2.3rem; height: 2.3rem; }
  .atlas-mark svg { width: 1.15rem; height: 1.15rem; }

  /* The name gives up a little rather than the sections: at 360px the mark and
     the two capsules leave it about 127 pixels, and "Regenerative Atla…" is a
     worse thing to read than the whole name set slightly smaller. It takes its
     full size back as soon as the screen can hold it. */
  .topbar__brand {
    font-size: clamp(0.88rem, 3.8vw, 1rem);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* On a phone the bar is two things, not three: who this is, and where else
     to go. The count goes — a number nobody is going to act on is the first
     thing to give up its room — and the sections take the right-hand end,
     drawn exactly as they are on a wide screen.

     Pushed there with a margin rather than placed there by a grid: this bar is
     a flex row at this width, not the three-column grid it is on a wide
     screen, and `justify-self` on a flex item is a line that does nothing. */
  .topbar__count { display: none; }

  /* Written against the bar as well as the attribute, because `.topbar nav`
     further up the sheet already claims a display and would keep it. */
  .topbar nav[data-connected-nav] {
    display: inline-flex;
    /* Centred, not on a baseline: a word next to a capsule wants to sit across
       the middle of it, and a shared baseline puts it up by the capsule's own
       padding. */
    align-items: center;
    margin-left: auto;
    flex-wrap: nowrap;
    gap: 0.75rem;
  }

  /* Explore is the map, and on a phone the map is where the Atlas' own mark
     leads and where the way in on the landing page goes. There is no room for
     a third way to the same place. */
  .topbar nav[data-connected-nav] > a[href='/map/'] { display: none; }

  /* No curve here: one of the two is a button and the other is a word, and
     there is nothing to join. `--connect: 0` is how the sheet says so — the
     script reads it and leaves the row alone, so each link keeps the outline
     it is given below rather than one drawn round the pair. */
  .topbar nav[data-connected-nav] > a { --connect: 0; font-size: 0.9rem; }

  /* Add is the thing to do, so it is the button. */
  .topbar nav[data-connected-nav] > a[href='/add/'] {
    padding: calc(0.37em + var(--type-drop)) 1em calc(0.37em - var(--type-drop));
  }

  /* About is a page to read, so it is a word you can follow. */
  .topbar nav[data-connected-nav] > a[href='/about/'] {
    padding: 0;
    border: none;
    background: none;
    color: var(--ink);
    text-decoration: underline;
    text-underline-offset: 0.2em;
    text-decoration-thickness: from-font;
  }

  /* Being on it is said by the underline it already has, not by a fill: a
     filled word with no capsule round it is a highlighter mark. */
  .topbar nav[data-connected-nav]:not(.is-connected) > a[href='/about/'][aria-current='page'],
  .topbar nav[data-connected-nav] > a[href='/about/'].is-on {
    background: none;
    color: var(--ink);
  }

  /* On a phone the record is a sheet at the bottom of the map rather than a
     card beside it: it comes up showing its title bar and the line under it,
     and you drag or tap it open when you want the rest. The map keeps the
     screen, which is the thing you are actually navigating. */
  .panel {
    --sheet-peek: 9rem;
    width: 100%;
    z-index: 4;
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    height: min(78svh, 32rem);
    /* One continuous stroke round the whole sheet: a top border alone stopped
       dead where the corners turn. */
    border: var(--stroke) solid var(--ink);
    border-radius: 1.1rem 1.1rem 0 0;
    transform: translateY(100%);
    /* Height too: pulled above its open position the sheet stretches rather
       than lifting off the bottom of the screen, and settles back the same way. */
    transition: transform 280ms cubic-bezier(0.22, 0.61, 0.36, 1),
      height 280ms cubic-bezier(0.22, 0.61, 0.36, 1);
    touch-action: none;
  }

  .panel.is-open { transform: translateY(calc(100% - var(--sheet-peek))); }
  .panel.is-open[data-sheet='open'] { transform: translateY(0); }
  .panel.is-dragging { transition: none; }

  /* The grip rides on the title bar rather than on a strip of paper above it,
     so the colour runs all the way to the sheet's rounded top edge. */
  .sheet-grip {
    display: block;
    flex: 0 0 auto;
    width: 100%;
    padding: 0.55rem 0 0.15rem;
    margin: 0;
    background: none;
    border: 0;
    cursor: grab;
  }

  .sheet-grip__bar {
    display: block;
    width: 2.6rem;
    height: 4px;
    margin: 0 auto;
    border-radius: 999px;
    background: var(--ink-32);
  }

  /* The grip is on the title bar, so it takes the bar's own contrast too. */
  .panel.is-open .sheet-grip__bar { background: var(--head-ink, var(--ink-32)); opacity: 0.5; }

  .panel.is-dragging .sheet-grip { cursor: grabbing; }

  .panel__head { padding: 0.5rem 0.85rem 0.8rem 0.95rem; }

  /* Peeked, the sheet shows the title bar and the line under it and no more: a
     preview that ends on a whole line rather than on a clipped one. */
  .panel[data-sheet='peek'] #panel-body > * { display: none; }
  .panel[data-sheet='peek'] #panel-body > .panel__meta { display: block; margin: 0; }

  .panel[data-sheet='peek'] .panel__title {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  #panel-body { touch-action: pan-y; padding: 0.9rem 1.05rem 1.4rem; }
  .panel[data-sheet='peek'] #panel-body { overflow: hidden; touch-action: none; }
  .panel[data-sheet='peek'] .scroll-rail { display: none; }

  /* No room for a row of tags on a phone, so there isn't one: the bar is a
     search field and a Tags menu, on its own band of paper so it never sits on
     top of the drawing, and the whole vocabulary drops out of it. */
  .map-chrome {
    top: 0;
    left: 0;
    right: 0;
    gap: 0;
  }
  .map-controls {
    /* Full-width band of paper, but its contents keep the page's margin, so
       the search field starts where the mark above it starts. */
    padding: 0.6rem var(--gutter);
    background: var(--paper);
    border-bottom: var(--rule);
  }
  .map-controls > * { flex: 0 0 auto; }
  .map-search { flex: 1 1 auto; min-width: 5rem; }
  .tag-bar__name, #map-filters, #map-places { display: none; }
  .chip--more__wide { display: none; }
  .chip--more__narrow { display: inline; }

  /* The menu itself: the width of the band it drops from, and the map visible
     under the rest of the screen rather than a page of tags. */
  .tag-drawer {
    border: 0;
    border-bottom: var(--stroke) solid var(--ink);
    border-radius: 0;
    max-height: min(22rem, 45svh);
    padding: 0.75rem var(--gutter);
  }

  /* The sentence about islands has nowhere to go on a phone, but taking the
     library with you should not depend on the size of the glass. So the
     legend stays, holding only its one door, and rides above the sheet the
     way the zoom cluster opposite it does. */
  .map-legend__text { display: none; }

  .map-legend {
    position: fixed;
    left: var(--gutter);
    right: auto;
    max-width: none;
    bottom: calc(0.9rem + env(safe-area-inset-bottom, 0px));
    z-index: 3;
    transition: bottom 280ms cubic-bezier(0.22, 0.61, 0.36, 1), opacity 160ms ease;
  }

  .map-shell:has(.panel.is-open) .map-legend {
    bottom: calc(var(--sheet-peek) + 0.9rem + env(safe-area-inset-bottom, 0px));
  }

  .map-shell:has(.panel.is-open[data-sheet='open']) .map-legend {
    opacity: 0;
    pointer-events: none;
  }

  /* Zoom and recentre leave that scrolling row — at the end of it they were
     only ever found by accident — and stand on the map itself, bottom right,
     where a thumb already is. Fixed rather than absolute so the row they are
     written inside cannot scroll them away or clip them. */
  .map-shell { --sheet-peek: 9rem; }

  .map-zoom {
    display: flex;
    flex-direction: column;
    margin: 0;
    gap: 0.45rem;
    position: fixed;
    right: var(--gutter);
    bottom: calc(0.9rem + env(safe-area-inset-bottom, 0px));
    z-index: 3;
    transition: bottom 280ms cubic-bezier(0.22, 0.61, 0.36, 1), opacity 160ms ease;
  }

  /* They ride above the drawer rather than under it, and step aside entirely
     when it is all the way up and there is no map left to steer. */
  .map-shell:has(.panel.is-open) .map-zoom {
    bottom: calc(var(--sheet-peek) + 0.9rem + env(safe-area-inset-bottom, 0px));
  }
  .map-shell:has(.panel.is-open[data-sheet='open']) .map-zoom {
    opacity: 0;
    pointer-events: none;
  }

  .map-zoom > :first-child { margin-left: 0; }

  .chip--round {
    width: 2.85rem;
    height: 2.85rem;
    /* Twice the drop, because a centred grid item splits its padding. */
    padding: calc(var(--type-drop) * 2) 0 0;
    display: grid;
    place-items: center;
    font-size: 1.25rem;
    line-height: 1;
    /* Paper is not enough on its own over a drawing: the shadow is what makes
       them read as instruments sitting on the map rather than marks in it. */
    box-shadow: 0 1px 6px rgba(28, 26, 23, 0.14);
  }

  .chip--round .chip__label {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0);
    white-space: nowrap; border: 0;
  }

  .chip__icon {
    display: block;
    width: 1.4rem;
    height: 1.4rem;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.7;
    stroke-linecap: round;
    stroke-linejoin: round;
  }
  .chip--round .chip__icon circle { fill: currentColor; stroke: none; }

  /* These carry drawings rather than letters, and a drawing is already
     centred on itself. */
  .chip--round:has(.chip__icon),
  .chip--round:has(.chip__mark) { padding: 0; }

  .chip--round .chip__mark { width: 1.4rem; height: 1.4rem; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* A field for machines only. Taken out of the layout rather than hidden with
   `display:none`, which the more careful sort of bot knows to skip. */
.honeypot {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- the review page -------------------------------------------------------
   One submission, read the way a stranger would read it, and a decision. It is
   set narrow and quiet on purpose: this page is not part of the Atlas anybody
   browses, it is the moment before something joins it. */

.sheet--narrow { max-width: 46rem; }

.review { margin-top: 2.5rem; }

.review__status {
  font-family: var(--display);
  font-size: 1.05rem;
  color: var(--ink-55);
}

.review__status.is-bad { color: var(--red); }
.review__status.is-good { color: var(--ink); }

.review__entry {
  border-top: var(--stroke) solid var(--ink);
  padding-top: 1.5rem;
}

.review__entry h2 {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(1.5rem, 3.2vw, 2.1rem);
  line-height: 1.2;
  margin: 0 0 0.4rem;
}

.review__cite { margin: 0 0 0.35rem; color: var(--ink-55); }

.review__link { margin: 0 0 1.5rem; font-family: var(--sans); font-size: 0.9rem; }

.review__prose { margin: 0 0 1.4rem; max-width: 40ch; line-height: 1.55; }

.review__note {
  border-left: var(--stroke) solid var(--ink-32);
  padding-left: 1rem;
  margin: 0 0 1.6rem;
  max-width: 40ch;
}

.review__note h3, .review__tags h3 {
  font-family: var(--sans);
  font-weight: 400;
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-55);
  margin: 0 0 0.4rem;
}

.review__note p { margin: 0; line-height: 1.55; }

.review__tags { margin: 0 0 1.2rem; }

.review__tags ul {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
}

/* A chip here is a label, not a control: nothing happens if you press it. */
.review__tags li.chip { cursor: default; }

.review__by {
  margin: 1.6rem 0 0;
  font-family: var(--sans);
  font-size: 0.85rem;
  color: var(--ink-55);
}

.review__decide {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin: 2.25rem 0 0.9rem;
}

.review__caveat {
  margin: 0;
  max-width: 44ch;
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--ink-55);
}


/* --- the duplicate warning ------------------------------------------------ */

/* The Add page's one popup: a link already in the library stops the form, so
   it is said over the page rather than in a line above it. Paper and ink like
   the drawers, and the page dimmed behind it rather than hidden. */
.duplicate {
  width: min(30rem, calc(100vw - 2rem));
  background: var(--paper);
  color: var(--ink);
  border: var(--stroke) solid var(--ink);
  border-radius: 1rem;
  padding: 1.5rem 1.6rem 1.35rem;
}

.duplicate::backdrop { background: rgb(20 18 14 / 0.35); }

.duplicate h2 {
  font-family: var(--display);
  font-weight: 400;
  font-size: 1.45rem;
  line-height: 1.2;
  margin: 0 0 0.6rem;
  text-wrap: balance;
}

.duplicate p { margin: 0 0 1.1rem; }

.duplicate__title { font-style: italic; }

.duplicate__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0 !important;
}
