/* SoCIETIE tiles — the course explained as hexagons.
 *
 * Used only by _includes/bands/tile_grid.html (which links this file), so
 * everything is namespaced under .societie-tiles. Content comes from
 * _data/societie_tiles.yml; the art behind each tile is a committed SVG in
 * _includes/tile-art/ (board + halo + icon, from
 * pipeline/scripts/generate_tile_art.py).
 *
 * RELATION TO principle-cards.css. Since 2026-09-14 these are the same
 * object: a paper hexagon, a hand-drawn Rough.js outline, an icon, a label,
 * and a lightbox back. cb asked for the convergence. The first cut of the
 * tiles was a solid Keynote-style colour FIELD with white text on it, which
 * is why the data still talks about colours sampled from a PDF — those
 * sampled values are now the accent (the line), not the fill.
 *
 * They remain separate files because the two decks are wired to different
 * data and different chrome (see the head of the include). What is genuinely
 * COPIED and must stay in step if it is ever re-derived:
 *
 *   - the hexagon clip path, and
 *   - the 0.28868 interlock constant (a pointy-top hexagon of width w is
 *     1.1547w tall, and rows nest by a quarter of that height — 0.28868w).
 *
 * principle-cards.css derives the same number and documents the gap-easing
 * term; the derivation is there, not repeated here. The two files reach the
 * interlock differently on purpose: the cards use percentage margins on grid
 * items (one formula for every comb shape), while these rows are flex rows
 * with a shared --st-w, so a calc() against that variable is exact and needs
 * no grid at all.
 *
 * SIZING. Everything is a proportion of --st-w, the tile width, so one
 * declaration resizes a comb: label, padding, hint and interlock all track
 * it. Pass `width=` to the include to set it per call — the intro deck's
 * slides do, because a 1920x1080 reveal canvas wants a much larger tile than
 * a page column does.
 *
 * COLOUR. --st-accent (the group's line colour, >= 3:1 on the paper) rides
 * each row; --st-accent-dark (>= 4.5:1 on the lightbox's #141414) rides each
 * dialog. Both are set inline by the include from the group's data, which is
 * where the measured figures are recorded. The BOARD tint is deliberately
 * NOT per group: it is the card deck's default warm paper for every tile,
 * because the accent already carries the grouping and a tinted board under a
 * tinted line makes both harder to see.
 *
 * BROWSER SUPPORT matches the card deck's floor (~2019: Safari/iOS 13,
 * Chrome 80, Firefox ESR 91): no container queries, no clamp(), no aspect-
 * ratio, no inset, and <dialog> is progressive (the include ships a fallback
 * modal for Safari <15.4 / Firefox <98). Autoprefixer adds -webkit- prefixes
 * at build time.
 */

.societie-tiles {
  --st-w: 210px; /* tile width; every other size is a proportion of it */
  --st-gap: 12px;
  --st-paper: #fbf9f5;
  --st-ink: #141414;
  /* The focus ring, on every control in the deck — the cards, and since
   * 2026-09-15 the hub hexagons. It was typed out at each of the seven
   * drop-shadows that build the ring; one name is what stops the sixth one
   * from being a slightly different gold. */
  --st-focus: #be830e;
  /* WHERE THE LABEL'S LAST LINE SITS, measured up from the hexagon's bottom
   * point. One number for the card and the hub alike, which is the whole
   * reason it is a variable: cb, 2026-09-15, "the 'choose when' layout has
   * the text too low. and the regular cards have their text a touch too
   * high. can we balance this right."
   *
   * They were out by exactly the height of the card's "Flip ↻" hint. Both
   * labels were flush to the bottom of the same box, but on a card the hint
   * sat under the label in the flex flow and pushed it up, and a hub has no
   * hint — so the two sat a hint apart and neither was where it wanted to
   * be. cb has since dropped the hint entirely (see tile_card.html), but the
   * variable is why they cannot drift apart again: it is the one number the
   * card and the hub both read, whatever either of them puts in the box.
   *
   * 0.235 is between where the two used to sit: the card's label comes down
   * about a fortieth of a tile and the hub's goes up about three times
   * that. */
  --st-label-foot: calc(var(--st-w) * 0.235);
  /* THE RIBBON'S WEIGHT, in the board sprite's own units — it is drawn on a
   * 100-unit cell and then scaled up to the hexagon, so the stroke scales
   * with it. The tiles draw ONE cell per hexagon (scale 4), where the
   * sprite's own 5 and 8 would land as a 20-unit band with a 32-unit casing:
   * a third of the way across the tile, behind a label. The principle cards
   * still draw a fine many-celled texture and set neither, so they keep the
   * sprite's values through the var() fallbacks.
   *
   * These land a little heavier than the tiles' old texture, which is the
   * point: three arcs across a hexagon are the pattern now, not the grain
   * behind it. */
  --pt-line: 2;
  --pt-casing: 3.2;
  --st-hex: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  margin: 2rem 0;
}

/* ── The comb ───────────────────────────────────────────────────────── */

.societie-tiles .st-row {
  list-style: none;
  margin: 0;
  padding: 0;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -ms-flex-wrap: wrap;
      flex-wrap: wrap;
  -webkit-box-pack: center;
      -ms-flex-pack: center;
          justify-content: center;
  -webkit-box-align: start;
      -ms-flex-align: start;
          align-items: flex-start;
}

/* The interlock: every row after the first rises into the notches of the row
 * above. The pull-up is the hex point height (0.28868 x width) eased by
 * 0.866 x the gap, so rows meet along the hexes' slanted edges at the same
 * distance the gap gives their vertical edges. */
.societie-tiles .st-row + .st-row {
  margin-top: calc(-0.28868 * var(--st-w) + 0.866 * var(--st-gap));
}

/* Rows are centred, so two rows whose lengths differ by an ODD number are
 * already half a pitch apart and nest for free. Every other pair — 6 over 6,
 * 3 over 3 — lands directly on top and has to be pushed half a pitch. The
 * include decides which rows get this (see COMB SHAPE there).
 *
 * A transform, not a margin: the old rule set margin-left to a FULL pitch and
 * relied on `justify-content: center` halving it, which is exact but reads as
 * a mistake and breaks the moment the row stops being centred. */
.societie-tiles .st-row.st-row-shift {
  -webkit-transform: translateX(calc((var(--st-w) + var(--st-gap)) / 2));
          transform: translateX(calc((var(--st-w) + var(--st-gap)) / 2));
}

.societie-tiles .st-cell {
  width: var(--st-w);
  height: calc(1.1547 * var(--st-w));
  margin: 0 calc(var(--st-gap) / 2);
  position: relative;
}

/* ── The map: every group in one field, with a camera ────────────────────
 * layout="map". The whole course as one connected field of hexagons — the
 * SoCIETIE Initiative at the centre, each group an arm off it — inside a
 * viewport that flies to a branch.
 *
 * The camera is a TRANSFORM on .st-map, not scrolling. Two reasons: the
 * field is several times wider than a slide, and scrollbars inside a reveal
 * slide are worse than useless; and a transform can scale, which is the
 * whole point of being able to zoom out to the shape of the course and back
 * in to one branch. transform-origin is the top-left corner so the script's
 * arithmetic is a plain translate-then-scale with no centre to correct for.
 *
 * Cell positions are the path layout's, unchanged — the map only rotates
 * each group's shape and drops it somewhere, both from the data.
 */

.societie-tiles .st-viewport {
  position: relative;
  overflow: hidden;
  width: 100%;
  /* A LENGTH, not a vh. Inside reveal, vh resolves against the real window
   * while everything around it is measured on the deck's 1920x1080 canvas,
   * so a viewport set in vh comes out roughly a third the height it looks
   * like it asked for and the whole-map view clamps at minimum zoom. Pass
   * --st-map-h to change it. */
  height: var(--st-map-h, 620px);
  max-height: 88vh;
  /* A hairline of the paper, so the field reads as a thing you are looking
   * INTO rather than as tiles scattered on the slide. */
  background: rgba(20, 20, 20, 0.02);
  border-radius: 10px;
  cursor: -webkit-grab;
  cursor: grab;
  -ms-touch-action: none;
      touch-action: none; /* the pointer handlers own panning, not the browser */
}

.societie-tiles .st-viewport.st-dragging {
  cursor: -webkit-grabbing;
  cursor: grabbing;
}

.societie-tiles .st-map {
  /* NO GUTTER on the map, and that is structural rather than cosmetic: since
   * 2026-09-15 every tile's board art is a window onto ONE Tsuro board that
   * covers the whole map (pipeline/scripts/generate_tile_art.py), so the
   * ribbons run on from one hexagon into the next. A gap between hexagons is
   * a gap through every ribbon crossing it, which is exactly the look cb
   * asked to be rid of: "the tsuro pattern is decorative. Let's make it
   * connect up across the map." The other layouts keep the 12px — they are
   * rows and clusters of separate cards, not a field. */
  --st-gap: 0px;
  --st-pitch: calc(var(--st-w) + var(--st-gap));
  position: absolute;
  top: 0;
  left: 0;
  -webkit-transform-origin: 0 0;
          transform-origin: 0 0;
  width: calc(var(--st-spanxk) * var(--st-pitch) / 2 + var(--st-w));
  height: calc(
    var(--st-spanr) * 0.866 * var(--st-pitch) + 1.1547 * var(--st-w)
  );
}

.societie-tiles .st-map .st-cell {
  position: absolute;
  margin: 0;
  left: calc(var(--xk) * var(--st-pitch) / 2);
  top: calc(var(--r2) * 0.866 * var(--st-pitch));
}

/* ── Full screen, and expanding the map in place ────────────────
 * Drawn only when a PAGE asks for it (`toolbar=true`); a slide is already
 * full screen. Deliberately the same object as principle-cards.css's
 * .pc-fullscreen — the two bands sit on one page now, and a reader should not
 * have to learn the same control twice.
 *
 * ⚠️ IT SITS INSIDE THE VIEWPORT, over the top-right of the field, and there
 * is no toolbar around it any more. cb, 2026-09-17: "remove the title XX
 * cards / position the 'Full screen' button to be inside the active window
 * space." The bar above the map used to read "41 hexagons" beside the button
 * — a count of a thing the reader is looking at, which tells them nothing
 * they cannot see, and a strip of chrome between the heading and the map.
 *
 * ⚠️ .st-expanded, not :fullscreen, is the mechanism. iOS Safari has no
 * Fullscreen API outside <video>, so a button gated on it is absent from the
 * one device whose screen makes it worth pressing — and cb's report was a
 * phone report ("on a phone, there is not enough screen to scroll
 * properly"). A pinned overlay works everywhere; the real API is layered on
 * top where it exists, only to take the browser's own chrome.
 */

/* ⚠️ NO TOOLBAR AND NO COUNT. cb, 2026-09-17, of this band and the principle
 * comb together: "remove the title XX cards", "position the 'Full screen'
 * button to be inside the active window space". "38 hexagons" was a heading
 * above a map the reader is looking straight at, and the rule under it drew
 * a second line a few lines below the section heading.
 *
 * Top RIGHT, the same corner as .pc-fullscreen: the field is fitted with PAD
 * of air all round, so the corners are the emptiest part of the view. */
.societie-tiles .st-fullscreen {
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 3;
  /* Paper, not `none`: over hexagons an outlined button with the map showing
   * through it stops being readable as soon as a branch drifts under it. The
   * rgba() triple is this file's documented exception to "never write a hex"
   * — a custom property cannot go inside rgba(). */
  background: rgba(251, 249, 245, 0.92);
  font-family: "Roboto Mono", monospace;
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--st-ink);
  border: 1.5px solid var(--st-ink);
  border-radius: 0;
  padding: 10px 18px;
  cursor: pointer;
  -webkit-box-shadow: none;
          box-shadow: none;
}

.societie-tiles .st-fullscreen:hover,
.societie-tiles .st-fullscreen:focus {
  background: var(--st-ink);
  color: var(--st-paper);
}

.societie-tiles .st-fullscreen:focus-visible {
  outline: 3px solid var(--st-focus);
  outline-offset: 3px;
}

/* Longhand offsets, not `inset`: the floor here is Safari 13. */
.societie-tiles.st-expanded {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1200;
  margin: 0;
  padding: 1rem 1.25rem 1.25rem;
  -webkit-box-sizing: border-box;
          box-sizing: border-box;
  background: var(--st-paper);
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-orient: vertical;
  -webkit-box-direction: normal;
      -ms-flex-direction: column;
          flex-direction: column;
}

/* The viewport takes whatever height the toolbar and waypoints leave, rather
 * than its own fixed --st-map-h: the room IS the point of expanding. */
.societie-tiles.st-expanded .st-viewport {
  -webkit-box-flex: 1;
      -ms-flex: 1 1 auto;
          flex: 1 1 auto;
  height: auto;
  max-height: none;
  min-height: 0;
}

.societie-tiles:-webkit-full-screen {
  background: var(--st-paper);
}

.societie-tiles:-ms-fullscreen {
  background: var(--st-paper);
}

.societie-tiles:fullscreen {
  background: var(--st-paper);
}

/* ── Waypoints ──────────────────────────────────────────────────────────
 * The branch buttons under the map. They are the keyboard and screen-reader
 * route to a branch as well as the mouse one, which is why they are real
 * buttons with aria-pressed rather than a decorated list. */

.societie-tiles .st-waypoints {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -ms-flex-wrap: wrap;
      flex-wrap: wrap;
  -webkit-box-pack: center;
      -ms-flex-pack: center;
          justify-content: center;
  gap: 8px;
  margin: 14px 0 0;
}

/* The how-to line under the waypoints (the `hint=` include param). */
.societie-tiles .st-howto {
  margin: 10px 0 0;
  text-align: center;
  font-size: 0.85rem;
  color: var(--colour-text-muted);
}

.societie-tiles .st-waypoint {
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
  font-family: "Roboto Mono", monospace;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--st-ink);
  background: var(--st-paper);
  border: 1.5px solid var(--st-accent, #5a5a5a);
  border-radius: 999px;
  -webkit-box-shadow: none;
          box-shadow: none;
  padding: 7px 15px;
  cursor: pointer;
  line-height: 1;
}

.societie-tiles .st-waypoint[aria-pressed="true"] {
  background: var(--st-accent, #5a5a5a);
  color: var(--st-paper);
}

.societie-tiles .st-waypoint:focus-visible {
  outline: 3px solid var(--st-focus);
  outline-offset: 2px;
}

/* ── Paths: connected hexagons whose shape is the argument ───────────────
 * The third layout (layout="path"), and the one that says something. A
 * cluster's ring says only "these belong together"; a path says the course
 * codes are a LADDER in unit order, the sessions are a CALENDAR with each
 * semester under the sessions it overlaps, the pathways are a SPECTRUM and
 * the three tasks are a SEQUENCE. Every tile names its own `at: "q,r"`.
 *
 * Same axial arithmetic as the cluster, with two differences. Positions are
 * measured from the shape's top-left corner rather than from a centre, and
 * the container is sized to the shape's BOUNDS — so a line, an L and a fork
 * each take exactly the room they need. The include hands over --st-spanxk
 * and --st-spanr (the bounds) and, per cell, --xk and --r2 (its offset
 * inside them).
 *
 * xk is the horizontal position DOUBLED (xk = 2q + r), so that the include
 * can do the whole calculation in integers — Liquid has no floats worth the
 * name. The halving happens here, once.
 */

.societie-tiles .st-path {
  --st-pitch: calc(var(--st-w) + var(--st-gap));
  position: relative;
  width: calc(var(--st-spanxk) * var(--st-pitch) / 2 + var(--st-w));
  height: calc(
    var(--st-spanr) * 0.866 * var(--st-pitch) + 1.1547 * var(--st-w)
  );
}

.societie-tiles .st-path .st-cell {
  position: absolute;
  margin: 0;
  left: calc(var(--xk) * var(--st-pitch) / 2);
  top: calc(var(--r2) * 0.866 * var(--st-pitch));
}

/* Paths are narrow and irregular, so two on one line want real air between
 * them — they are two separate claims, not two halves of one shape. */
.societie-tiles .st-path + .st-path {
  margin-left: calc(var(--st-w) * 0.5);
}

/* ── Clusters: a group gathered around its question ──────────────────────
 * The alternative to the comb (layout="cluster"). A hub hexagon carries the
 * choice the group represents and the tiles take the hexagon's own neighbour
 * positions around it — six of them, or three at 120 degrees.
 *
 * GEOMETRY. Each cell carries AXIAL hex coordinates as --q and --r, and the
 * two rules below are the whole conversion. For pointy-top hexes of pitch
 * P = width + gap:
 *
 *     x = (q + r/2) * P          y = r * 0.866 * P
 *
 * 0.866 is sqrt(3)/2 and is the SAME constant the comb's interlock uses (a
 * pointy-top hexagon of width w is 1.1547w tall and rows nest by a quarter of
 * that, so a row pitch is 0.866 of a column pitch). A cluster and a comb
 * therefore sit on one grid, which is why the two layouts can be mixed on a
 * slide without anything being re-measured.
 *
 * The container is sized in the same terms rather than by its contents,
 * because every cell is absolutely positioned and so contributes no height:
 * two column pitches plus a tile across, 1.732 row pitches plus a tile down.
 */

.societie-tiles .st-cluster {
  --st-pitch: calc(var(--st-w) + var(--st-gap));
  position: relative;
  width: calc(2 * var(--st-pitch) + var(--st-w));
  height: calc(1.732 * var(--st-pitch) + 1.1547 * var(--st-w));
}

/* Several clusters on one slide sit in a row and wrap on a narrow screen.
 *
 * NO `margin: 0 auto` on the cluster, however obvious it looks for centring a
 * lone one: an auto margin on a FLEX ITEM eats all the free space in the line,
 * so two clusters got shoved to opposite ends of the slide with a hexagon's
 * width of nothing between them. The flex container's justify-content centres
 * one cluster and several alike. */
.societie-tiles.st-clusters {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -ms-flex-wrap: wrap;
      flex-wrap: wrap;
  -webkit-box-align: center;
      -ms-flex-align: center;
          align-items: center;
  -webkit-box-pack: center;
      -ms-flex-pack: center;
          justify-content: center;
}

/* Clusters BUTT UP, they do not interlock. The comb's negative margin is
 * wrong here: each cluster's box is already tight to its hexagons, so the
 * east tile of one and the west tile of the next are on the same row and
 * would collide rather than nest. Two flowers only interlock if the second
 * is also dropped half a row, which reads as a mistake rather than a
 * pattern. A plain gap keeps them legible as two separate questions. */
.societie-tiles .st-cluster + .st-cluster {
  margin-left: calc(var(--st-w) * 0.28);
}

.societie-tiles .st-cluster .st-cell {
  position: absolute;
  margin: 0;
  left: calc(
    50% + (var(--q) + var(--r) / 2) * var(--st-pitch) - var(--st-w) / 2
  );
  top: calc(
    50% + var(--r) * 0.866 * var(--st-pitch) - 0.57735 * var(--st-w)
  );
}

/* The hub. It is the one place the Keynote's solid colour FIELD survives:
 * everything else became a line when the tiles turned into cards, but the
 * hub is not a card — it is the question, and a filled hexagon is what says
 * so at a glance. Paper text on the group's accent, which is why the accent
 * has a measured floor against the paper in both directions. */
.societie-tiles .st-hub {
  display: block;
  position: relative;
  width: 100%;
  height: 100%;
  /* The hub is a <span> in the cluster and path layouts and a <button> on the
   * map, where it flies the camera to its own branch. So it needs the SAME
   * button reset .st-card carries, for the same reasons — see the comments
   * there, and test_the_two_controls_share_one_button_reset, which exists
   * because the first cut of this copied four of these nine lines and the
   * hub hexagons shipped as black rectangles with clipped labels.
   *
   * Harmless on the span: not one of these does anything to a <span> that
   * the rules below do not already say. */
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  text-align: center;
  white-space: normal;
  -webkit-box-shadow: none;
          box-shadow: none;
  /* See .st-card: two activations is the gesture, and it is also how a
   * browser selects a word. */
  -moz-user-select: none;
   -ms-user-select: none;
       user-select: none;
  -webkit-user-select: none;
  /* currentColor for everything drawn ON the fill: the Rough.js edge, the
   * chosen tile's icon, the label and the hint. Paper by default; the two
   * accents too light to carry paper text at 4.5:1 take ink instead (see HUB
   * TEXT in the data). Both pass 3:1 as a line on every accent, so the edge
   * is right either way and the text decides. */
  color: var(--st-paper);
  -webkit-filter: drop-shadow(0 3px 8px rgba(0, 0, 0, 0.16));
          filter: drop-shadow(0 3px 8px rgba(0, 0, 0, 0.16));
}

/* Doubled up on .st-hexshape deliberately. The hub reuses that class for the
 * clip path and the box, but every property it overrides here — the fill, the
 * centring, the padding — would otherwise tie on specificity and lose to
 * .st-hexshape on source order alone, which is how the first cut rendered the
 * hub as a blank paper hexagon. */
/* SAME ANATOMY AS A TILE since 2026-09-15: icon above, label at the base.
 * cb: "update the layout of the hub cards so that the text lives at the base
 * (like the others) and has an icon."
 *
 * So the only thing this overrides now is the FILL — which is the one
 * difference that means something, because it is what says "this hexagon is
 * the question and those are the answers". It used to also centre the label
 * and widen the padding, from when a hub was words alone; a hexagon of
 * centred text sitting in a field of picture-and-caption hexagons read as a
 * heading that had fallen into the map.
 *
 * The side padding is the tile's 13% rather than the old 16%: the hub labels
 * are the longest strings in the deck ("Hand in your assessment") and they
 * need the width the icon does not. */
.societie-tiles .st-hexshape.st-hub-shape {
  background: var(--st-accent, #5a5a5a);
}

.societie-tiles .st-hub.st-hub-inked {
  color: var(--st-ink);
}

.societie-tiles .st-hub-label {
  display: block;
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: 0.01em;
  color: inherit;
  /* Smaller than a tile's 0.115: a hub asks a whole question where a tile
   * names one thing, so these are the longest labels in the deck and they now
   * share the hexagon with an icon. */
  font-size: calc(var(--st-w) * 0.092);
}

/* ── The hub as a waypoint ──────────────────────────────────────────────
 * On the map the hub carries `data-st-goto` and flies the camera to its own
 * branch — the same attribute and the same handler as the waypoint buttons
 * under the map. cb, 2026-09-15: "let's also make the hub hexes match the
 * function of the menus - zoom in, etc."
 *
 * It must therefore LOOK pressable, and it has no .st-front to lift (that
 * wrapper exists on a card to keep the drop-shadow outside the clip). The
 * hub's own drop-shadow is on .st-hub, so the lift and the focus ring go
 * there, which is also what keeps them outside .st-hexshape's clip-path. */
.societie-tiles .st-hub-goto {
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  -webkit-transition: -webkit-transform 0.2s ease, -webkit-filter 0.2s ease;
  transition: -webkit-transform 0.2s ease, -webkit-filter 0.2s ease;
  transition: transform 0.2s ease, filter 0.2s ease;
  transition: transform 0.2s ease, filter 0.2s ease, -webkit-transform 0.2s ease, -webkit-filter 0.2s ease;
}

.societie-tiles .st-hub-goto:hover {
  -webkit-transform: translateY(-4px);
          transform: translateY(-4px);
  -webkit-filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.2));
          filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.2));
}

/* An outline would trace the button's rectangle, not the hexagon — the same
 * problem the cards have, and the same stacked-drop-shadow answer, in the
 * same gold, so focus reads identically wherever it lands in the map. */
.societie-tiles .st-hub-goto:focus {
  outline: none;
}

.societie-tiles .st-hub-goto:focus-visible {
  outline: none;
  -webkit-filter: drop-shadow(0 0 1.5px var(--st-focus)) drop-shadow(0 0 1.5px var(--st-focus))
    drop-shadow(0 0 2px var(--st-focus));
          filter: drop-shadow(0 0 1.5px var(--st-focus)) drop-shadow(0 0 1.5px var(--st-focus))
    drop-shadow(0 0 2px var(--st-focus));
}

/* The branch currently framed. The camera sets aria-pressed on every
 * [data-st-goto] it knows about, hexagons included, so this needs no state of
 * its own. A stage that frames two branches at once matches neither, which is
 * correct: no single branch is "the" one.
 *
 * ⚠️ NOT an inset box-shadow, which is what shipped first and what cb saw:
 * "some funny layout with the border of the hubs ... the one that is
 * selected". An inset shadow is painted inside the BORDER BOX — a rectangle —
 * and the clip-path then cuts it, so what survives is two vertical bars down
 * the hexagon's flat sides and nothing at all on its four slanted edges. It
 * is the same trap as `outline`, which this file warns about twice for the
 * cards, in the one place a hexagon was not being treated as a hexagon.
 *
 * So it is the stacked-drop-shadow ring instead — the technique the lit tile
 * and the focus ring both use, because a filter follows the clip-path's
 * alpha. In the ACCENT: currentColor on a hub is the paper or the ink it
 * draws its label in, and a paper glow on a white slide is invisible. The
 * accent is the hub's own fill, so the ring reads as that hexagon leaning
 * forward rather than as a new colour arriving. */
.societie-tiles .st-hub-goto[aria-pressed="true"] {
  -webkit-filter: drop-shadow(0 0 3px var(--st-accent))
    drop-shadow(0 0 3px var(--st-accent))
    drop-shadow(0 0 4px var(--st-accent));
          filter: drop-shadow(0 0 3px var(--st-accent))
    drop-shadow(0 0 3px var(--st-accent))
    drop-shadow(0 0 4px var(--st-accent));
}

/* The hub sits under the tiles: they overlap it along their shared edges,
 * and a filled hexagon reading over a paper one is the wrong way round. */
.societie-tiles .st-cell-hub {
  z-index: 0;
}

/* ── The card front ─────────────────────────────────────────────────── */

.societie-tiles .st-card {
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  width: 100%;
  height: 100%;
  font: inherit;
  color: inherit;
  text-align: center;
  /* The theme gives <button> white-space: nowrap, which inherits into every
   * span in the card and clips the label at the card's edge. */
  white-space: normal;
  /* The theme shadows <button> boxes; a rectangular glow around a hexagonal
   * card gives the square away, so the shadow moves to a drop-shadow filter
   * on the front, which follows the clip-path alpha instead of the box. */
  -webkit-box-shadow: none;
          box-shadow: none;
  /* A DOUBLE-CLICK IS THE GESTURE here — one activation lights the hexagon,
   * two turns it over — and a double-click is also how a browser selects a
   * word. So every flip left the label sitting in a blue selection box,
   * which on a filled hub reads as a second, wrong highlight. */
  -moz-user-select: none;
   -ms-user-select: none;
       user-select: none;
  -webkit-user-select: none;
  display: block;
  position: relative;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* Interlocked hexes overlap; the card being used comes to the top. */
.societie-tiles .st-card:hover,
.societie-tiles .st-card:focus-visible {
  z-index: 5;
}

/* The clip rides .st-hexshape, an inner wrapper, so the drop-shadow filter
 * and the Rough.js border overlay on .st-front can't be cut off by it —
 * filter applies after clip-path on the same element, which would clip the
 * shadow away. */
.societie-tiles .st-front {
  display: block;
  position: relative;
  width: 100%;
  height: 100%;
  color: var(--st-accent, #5a5a5a);
  -webkit-transition: -webkit-transform 0.2s ease, -webkit-filter 0.2s ease;
  transition: -webkit-transform 0.2s ease, -webkit-filter 0.2s ease;
  transition: transform 0.2s ease, filter 0.2s ease;
  transition: transform 0.2s ease, filter 0.2s ease, -webkit-transform 0.2s ease, -webkit-filter 0.2s ease;
  -webkit-filter: drop-shadow(0 3px 8px rgba(0, 0, 0, 0.14));
          filter: drop-shadow(0 3px 8px rgba(0, 0, 0, 0.14));
}

.societie-tiles .st-hexshape {
  position: relative;
  width: 100%;
  height: 100%;
  -webkit-clip-path: var(--st-hex);
          clip-path: var(--st-hex);
  background: var(--st-paper);
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-orient: vertical;
  -webkit-box-direction: normal;
      -ms-flex-direction: column;
          flex-direction: column;
  -webkit-box-align: center;
      -ms-flex-align: center;
          align-items: center;
  -webkit-box-pack: end;
      -ms-flex-pack: end;
          justify-content: flex-end;
  padding: 0 13% var(--st-label-foot);
  -webkit-box-sizing: border-box;
          box-sizing: border-box;
}

.societie-tiles .st-card:hover .st-front {
  -webkit-transform: translateY(-4px);
          transform: translateY(-4px);
  -webkit-filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.18));
          filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.18));
}

/* Focus can't be an outline: an outline traces the button's rectangle, not
 * the hexagon. Stacked drop-shadows on the clipped front build a ring that
 * follows the clip — the same technique, and the same gold, as the cards. */
.societie-tiles .st-card:focus {
  outline: none;
}

.societie-tiles .st-card:focus-visible {
  outline: none;
}

.societie-tiles .st-card:focus-visible .st-front {
  -webkit-filter: drop-shadow(0 0 1.5px var(--st-focus)) drop-shadow(0 0 1.5px var(--st-focus))
    drop-shadow(0 0 2px var(--st-focus));
          filter: drop-shadow(0 0 1.5px var(--st-focus)) drop-shadow(0 0 1.5px var(--st-focus))
    drop-shadow(0 0 2px var(--st-focus));
}

/* The art: the woven board, the halo and the tile's icon, full bleed behind
 * the label. The icon strokes currentColor, so it takes the row's accent
 * from .st-front. */
/* THE BOARD'S COLOUR comes from the tile's own accent — cb, 2026-09-15:
 * "let's also make the tsuro lines derived from the tile colour". The line
 * is drawn inside _tiles.svg as var(--pc-tile), the same hook the principle
 * cards use, so this is the one declaration it takes.
 *
 * A WASH, not the accent itself: the board sits UNDER the label, and a
 * ribbon at full strength competes with the text it is behind. 22% on the
 * card paper is about where the card deck's own family tints land.
 * color-mix keeps this derived rather than seven more values to maintain,
 * but it is well above this file's stated support floor, so it is behind
 * @supports — and that guard is doing real work, not being polite. A custom
 * property holds ANY token stream, so an unsupported color-mix would be
 * stored happily and only fail where it is used, making `stroke` invalid at
 * computed-value time — which falls back to inherit, not to the sprite's
 * stroke attribute. Left ungated, an old browser gets black ribbons rather
 * than the old look. Gated, --pc-tile is simply never set and var()'s own
 * fallback inside _tiles.svg takes over, which IS the old look.
 *
 * The casing stays white on purpose: it is what makes the ribbons weave
 * over and under each other rather than merge where they cross. */
@supports (color: color-mix(in srgb, red 50%, blue)) {
  .societie-tiles .st-cell {
    --pc-tile: color-mix(in srgb, var(--st-accent) 22%, var(--st-paper));
  }
}

/* ── A LIT TILE ────────────────────────────────────────────────────────
 * The first activation of a card lights it; the second turns it over. cb,
 * 2026-09-15: "a single click that gives you a highlight ... within the
 * group, if we were able to change the selection, it would change the
 * highlight. That way I could talk about different scenarios and use it as
 * a tool to explain how the course works."
 *
 * ⚠️ IT IS STILL A TILE. The first cut inverted it — accent field, paper
 * contents — borrowing the hub's look and the one a `choice: true` group
 * used to collapse to. cb, 2026-09-15: "the highlights are strange, with a
 * white outline. I think as the option is chosen, it needs to have black
 * text." Both halves of that are the same fault: on an inverted hexagon
 * everything drawn in currentColor goes paper, and one of those things is
 * the Rough.js edge, so the tile grew a white outline and lost the black
 * label the whole deck reads in. A highlight is not a different KIND of
 * hexagon; it is this hexagon, picked out.
 *
 * So the face stays paper, the label stays ink, and what changes is a WASH
 * of the group's accent across the face plus a ring of that accent around
 * the hexagon. The ring is the same stacked-drop-shadow technique as the
 * focus ring, for the same reason — an outline traces the button's
 * rectangle, not the hexagon — but in the accent and at twice the spread,
 * so focus (thin, gold) and lit (thick, the group's colour) stay two
 * different signals on the same card.
 *
 * NOT the gold --colour-highlight, though that is the site's highlight
 * token: it is already the focus ring on these cards, and since the palette
 * pass it is also the `codes` accent, so a gold-lit code tile would be lit
 * in its own colour. */
.societie-tiles .st-cell.st-lit .st-front {
  -webkit-filter: drop-shadow(0 0 3px var(--st-accent))
    drop-shadow(0 0 3px var(--st-accent))
    drop-shadow(0 0 4px var(--st-accent));
          filter: drop-shadow(0 0 3px var(--st-accent))
    drop-shadow(0 0 3px var(--st-accent))
    drop-shadow(0 0 4px var(--st-accent));
}

/* Interlocked hexagons overlap; the one being talked about comes up. Under
 * :hover and :focus-visible (z-index 5) on purpose — a pointer or a focus
 * ring is about what happens NEXT, and that still has to win. */
.societie-tiles .st-cell.st-lit {
  z-index: 4;
}

@supports (color: color-mix(in srgb, red 50%, blue)) {
  /* The wash, and the board's ribbons lifted to match it. Both derived from
   * the accent rather than being two more values to keep in step. Without
   * color-mix the ring alone carries the state, which is the whole signal
   * and not a degraded one. */
  .societie-tiles .st-cell.st-lit .st-hexshape {
    background: color-mix(in srgb, var(--st-accent) 16%, var(--st-paper));
  }

  .societie-tiles .st-cell.st-lit {
    --pc-tile: color-mix(in srgb, var(--st-accent) 40%, var(--st-paper));
  }
}

/* Nothing about lighting a tile moves, so prefers-reduced-motion is not
 * consulted here and should not be. */

.societie-tiles .st-art {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}

.societie-tiles .st-art svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* The hand-drawn hexagon border (injected by the include's script when
 * data-rough-hex="on"). It rides the front OUTSIDE .st-hexshape so the clip
 * can't cut the wobble off; currentColor keeps the stroke on the group's
 * accent; non-scaling-stroke holds the drawn line at 1px however the tile
 * scales. */
.societie-tiles .st-rough {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}

.societie-tiles .st-rough path {
  vector-effect: non-scaling-stroke;
}

/* Soft paper wash behind the label, so it stays legible where the board's
 * ribbons run under it. */
.societie-tiles .st-title {
  position: relative;
  display: block;
  z-index: 1;
}

.societie-tiles .st-title::before {
  content: "";
  position: absolute;
  top: calc(var(--st-w) * -0.07);
  right: calc(var(--st-w) * -0.12);
  bottom: calc(var(--st-w) * -0.05);
  left: calc(var(--st-w) * -0.12);
  background: radial-gradient(ellipse at center, rgba(251, 249, 245, 0.92) 55%, rgba(251, 249, 245, 0) 100%);
  z-index: -1;
}

/* ⚠️ NO HOUSE UPPERCASE, as of 2026-09-17. cb, comparing this band with the
 * principle comb on the same page: "the hexes here are title case, but the
 * [map ones] are upper case ... which would look better?"
 *
 * Title case, and the map is the one that moved. Two reasons, and neither is
 * a preference: the eighteen principle labels are PHRASES with punctuation
 * that matters ("'Our' students, not 'my' students"), which all-caps
 * flattens, so the principles could never have shouted; and two bands one
 * heading apart that capitalise differently read as two components rather
 * than as two views of one course, which is the drift cb has been pulling
 * these two back from all week.
 *
 * ⚠️ The `front:` values in _data/societie_tiles.yml are the labels as
 * rendered now — they were already Title Case because the transform was
 * doing the shouting. Two that had been typed IN CAPS to control their own
 * KNoTs capitals were recased when this rule went; there is no transform
 * left to hide a mis-cased entry.
 *
 * ⚠️ The size buckets below were RE-MEASURED the same day, once the labels
 * were lower-case: calibrated on capitals they had every bucket a size too
 * small, and a band of assorted type sizes is exactly the "two components"
 * read that recasing them was meant to close. The measurement is written up
 * in tile_card.html, above the buckets it sets. */
.societie-tiles .st-label {
  display: block;
  font-weight: 800;
  letter-spacing: 0.01em;
  font-size: calc(var(--st-w) * 0.115);
  line-height: 1.08;
  color: var(--st-ink);
}

/* Long labels, bucketed by the tile's longest word in tile_card.html: a word
 * cannot wrap, so past a certain length the only thing that keeps it inside
 * the hexagon's narrowing lower half is type size.
 *
 * Measured, not guessed: a sweep of every label in both bands compares each
 * rendered line against the hexagon POLYGON at that line's height (the
 * side padding is breathing room, not the edge). 0.095 rather than the old
 * 0.1 because "Transformation" is what sits in this bucket now, and 0.1 puts
 * it a hair outside the padding box. */
.societie-tiles .st-label.st-long {
  font-size: calc(var(--st-w) * 0.095);
}

.societie-tiles .st-label.st-xlong {
  font-size: calc(var(--st-w) * 0.08);
}

/* ── The flip side: a lightbox dialog per tile ──────────────────────────
 * The <dialog> itself is a transparent, padding-less box so a click that
 * lands on the ::backdrop (reported as the dialog element) closes it;
 * .st-dialog-card is the visible card — black with the group's accent. */
.societie-tiles .st-dialog {
  border: 0;
  padding: 0;
  background: transparent;
  width: 92vw;
  max-width: 34rem;
  max-height: 88vh;
}

/* A browser without <dialog> (Safari <15.4, Firefox <98) has no UA style for
 * it, so every flip side would render inline under the comb. Hide closed
 * dialogs explicitly (the UA sheet does the same where it exists). */
.societie-tiles .st-dialog:not([open]) {
  display: none;
}

/* Fallback modal for that same browser: the include's script adds
 * .st-no-dialog when showModal() is missing and then opens cards by setting
 * the open attribute itself, with a .st-backdrop div in place of ::backdrop.
 * Scoped on the class so a native modal keeps the UA's top-layer centring. */
.societie-tiles.st-no-dialog .st-dialog[open] {
  display: block;
  position: fixed;
  z-index: 10001;
  top: 50%;
  left: 50%;
  margin: 0;
  -webkit-transform: translate(-50%, -50%);
          transform: translate(-50%, -50%);
}

.societie-tiles .st-backdrop {
  position: fixed;
  z-index: 10000;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: rgba(12, 12, 12, 0.6);
}

.societie-tiles .st-dialog::-webkit-backdrop {
  /* ::backdrop doesn't inherit custom properties — literal colour only. */
  background: rgba(12, 12, 12, 0.6);
}

.societie-tiles .st-dialog::backdrop {
  /* ::backdrop doesn't inherit custom properties — literal colour only. */
  background: rgba(12, 12, 12, 0.6);
}

.societie-tiles .st-dialog-card {
  position: relative;
  background: #141414;
  color: #d9d4ca;
  border-radius: 8px;
  border-top: 6px solid var(--st-accent-dark, #a8a8a8);
  padding: 28px 32px 26px;
  -webkit-box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
          box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
  max-height: 82vh;
  overflow-y: auto;
  scrollbar-width: thin;
  -webkit-box-sizing: border-box;
          box-sizing: border-box;
  text-align: left;
}

.societie-tiles .st-close {
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
  position: absolute;
  top: 8px;
  right: 10px;
  background: none;
  border: 0;
  -webkit-box-shadow: none;
          box-shadow: none;
  padding: 6px 10px;
  font-size: 1.5rem;
  line-height: 1;
  color: #d9d4ca;
  cursor: pointer;
}

.societie-tiles .st-close:focus-visible {
  outline: 3px solid var(--st-accent-dark, #a8a8a8);
  outline-offset: 2px;
}

/* The icon chip: the front's art SVG re-included, board and halo hidden,
 * cropped to the icon's fixed region of the 346.4x400 viewBox. The icon
 * group sits at translate(108.4,87.2) scale(5.4) — a 129.6-unit box — so for
 * a 56px chip the SVG renders at 56/129.6 of natural size and shifts up-left
 * by the group's offset at that scale. These are the CARD's numbers, and
 * they only hold because generate_tile_art.py imports the card generator's
 * ICON_GROUP_OPEN rather than declaring its own transform. */
.societie-tiles .st-dialog-icon {
  position: absolute;
  top: 30px;
  right: 26px;
  width: 56px;
  height: 56px;
  overflow: hidden;
  color: var(--st-accent-dark, #a8a8a8);
}

.societie-tiles .st-dialog-icon svg {
  display: block;
  width: 149.7px;
  height: 172.8px;
  margin: -37.7px 0 0 -46.8px;
}

.societie-tiles .st-dialog-icon use,
.societie-tiles .st-dialog-icon circle {
  display: none;
}

.societie-tiles .st-dialog-icon g {
  stroke-width: 0.85;
}

.societie-tiles .st-eyebrow {
  display: block;
  font-family: "Roboto Mono", monospace;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--st-accent-dark, #a8a8a8);
  margin: 0 88px 12px 0; /* right margin clears the icon chip */
}

.societie-tiles .st-claim {
  display: block;
  font-weight: 600;
  font-size: 1.45rem;
  line-height: 1.25;
  color: var(--st-accent-dark, #a8a8a8);
  margin: 0 88px 16px 0; /* right margin clears the icon chip */
}

.societie-tiles .st-detail {
  margin: 0 0 14px;
  font-size: 1.02rem;
  line-height: 1.6;
  letter-spacing: normal;
  color: #d9d4ca;
}

/* The tile's own short facts — the numbers that were printed on the Keynote
 * hexagon under its title. They are the only thing from the old front that
 * had nowhere else to go once the front became label-only. */
.societie-tiles .st-facts {
  list-style: none;
  margin: 0 0 18px;
  padding: 0;
  font-family: "Roboto Mono", monospace;
  font-size: 0.82rem;
  line-height: 1.7;
  letter-spacing: 0.02em;
  color: #b8b2a6;
}

.societie-tiles .st-facts li {
  margin: 0;
  padding: 0 0 0 14px;
  position: relative;
}

.societie-tiles .st-facts li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 6px;
  height: 6px;
  background: var(--st-accent-dark, #a8a8a8);
}

.societie-tiles .st-more {
  display: inline-block;
  font-family: "Roboto Mono", monospace;
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #141414;
  background: var(--st-accent-dark, #a8a8a8);
  padding: 10px 16px;
  text-decoration: none;
  border-radius: 3px;
}

.societie-tiles .st-more:hover,
.societie-tiles .st-more:focus {
  color: #141414;
  text-decoration: underline;
}

.societie-tiles .st-more:focus-visible {
  outline: 3px solid #fbf9f5;
  outline-offset: 2px;
}

/* The hub's art, in the slot a tile's board occupies.
 *
 * Normally it is the group's icon and nothing else — hub-<group key>.svg,
 * written by the same generator with no board and no halo, because a hub is
 * a FILLED hexagon: the board's warm paper tints would read as dirt on it
 * and a halo has no paper to lift anything off.
 *
 * The exception is a hub that WEARS one of its group's tiles (`hub_tile:` in
 * the data — the values hub is the SoCIETIE Initiative). That art is a whole
 * tile's, so its halo `circle` is hidden here, exactly as the lightbox's icon
 * chip hides it: a halo is a radial paper glow that lifts a dark icon off a
 * busy board, and on a filled hexagon it would land on the accent as a pale
 * blob. One asset, two jobs. */
.societie-tiles .st-hub-art {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
  --pc-tile: var(--st-paper);
  --pt-casing: 0;
}

.societie-tiles .st-hub-art svg {
  display: block;
  width: 100%;
  height: 100%;
}

.societie-tiles .st-hub-art circle {
  display: none;
}

/* THE PATHS RUN THROUGH THE HUB. cb, 2026-09-15: "with the hub tiles, can we
 * have a lighter tsuro line to keep the paths follow?"
 *
 * The hubs used to hide their board on the reasoning that a woven texture
 * would read as dirt on a filled hexagon. That was true of a texture and is
 * false of this: at one board cell per hexagon the board IS three arcs
 * joining the hexagon's own six edge midpoints, and a hub sits in the middle
 * of its branch — so a blank one cut every path that ran through the head of
 * a branch. Seven holes in a field whose whole point is that it has none.
 *
 * Lighter, then, not absent. Three things do it, and the one thing that must
 * NOT change is the width: a ribbon that changes gauge at a seam reads as two
 * ribbons that happen to meet.
 *
 *   --pc-tile   paper, thinned by the opacity below — LIGHTER than the fill
 *               on all seven accents, which is what was asked for. (The
 *               hub's own currentColor was the first cut and is wrong on the
 *               two light fills: it draws the ribbons in ink, which is
 *               darker than the hexagon and reads as a crack in it.)
 *   --pt-casing off. The casing is a white halo that opens a gap where two
 *               ribbons cross — invisible on paper, a bright scar on an
 *               accent fill.
 *   opacity     on the cells alone, not the group, so the icon above them
 *               keeps its full contrast.
 *
 * The first two ride the .st-hub-art rule above; only the opacity needs a
 * selector of its own, because it has to miss the icon.
 *
 * HOW light is the whole question, and 0.38 was too light — cb, 2026-09-15:
 * "hub tsuro paths are too light - they are now distracting. can we make
 * them a little closer to the hub colour (more subtle)." Lower opacity IS
 * closer to the hub colour: paper at 38% over an accent is a pale line that
 * competes with the icon, and the same paper at 16% is the fill itself,
 * lifted just enough to say a path continues. The number is the only knob
 * here — the colour is already the paper and the width is not ours to
 * touch. */
.societie-tiles .st-hub-art use {
  opacity: 0.16;
}

.societie-tiles .st-hub-label {
  position: relative;
  z-index: 1;
}

/* Narrow screens: the include's row chunking is a comb for the width it was
 * asked for, and below ~560px a row wraps inside itself — at which point the
 * negative margin would overlap two rows that are no longer nested. Drop the
 * interlock and the shift, and let it be an honest wrapped grid. */
@media (max-width: 559px) {
  .societie-tiles {
    --st-w: 148px;
  }

  .societie-tiles .st-row + .st-row {
    margin-top: calc(var(--st-gap) / 2);
  }

  .societie-tiles .st-row.st-row-shift {
    -webkit-transform: none;
            transform: none;
  }

  /* Clusters need no unpicking here: every position inside one is a
   * proportion of --st-w, so the whole flower shrinks with the tile, and two
   * clusters that no longer fit side by side wrap onto their own lines. The
   * comb is the layout that has to be taken apart on a narrow screen,
   * because its interlock and half-pitch shift are what stop being legible
   * once the hexagons are small. */
  .societie-tiles .st-cluster + .st-cluster {
    margin-left: 0;
  }
}

/* ── Inside a reveal deck ───────────────────────────────────────────────
 * The canvas is 1920x1080 at a 16px root, so tile sizes are set in rem by
 * the slide itself (see the intro deck). All this has to do is stop the
 * site's list, button and link styling from reaching in — and keep the
 * dialog readable, since reveal scales its own content but a top-layer
 * <dialog> escapes that transform and is laid out against the real
 * viewport. */
.reveal .societie-tiles {
  margin: 0;
}

.reveal .societie-tiles .st-row {
  list-style: none;
}

.reveal .societie-tiles .st-dialog-card {
  font-size: 1rem;
}

/* ── The map on a PAGE: sticky, with the acts scrolling past it ─────────
 * /societie/introduction/ (_societie_hub/050_resources/010_introduction.md).
 * Until 2026-09-16 that URL was the reveal deck and the four acts were
 * fragments; cb moved it to a page — "maybe this could be a full-screen
 * explorer, and we can set a section for ... #/how-we-teach as different
 * sections on the page ... Being hidden in a deck might make them a little
 * obscure."
 *
 * A page has no fragments, so the ACTS drive the camera by scroll: the map
 * stays put and whichever act is crossing the middle of the window owns the
 * view (the driver is in tile_grid.html, keyed on [data-st-acts]). That is
 * the same flight the deck's arrow keys start — only the trigger differs.
 *
 * THE MAP MUST NOT BE TALLER THAN THE WINDOW or sticky has nothing to stick
 * to: a sticky box taller than the viewport scrolls with the page until its
 * bottom edge arrives, which looks exactly like sticky being broken. Hence
 * heights in vh, well under 100, and no padding on the stage that would add
 * to them. (vh is safe HERE in a way it is not inside reveal, where the
 * canvas is 1920x1080 and vh resolves against the real window instead —
 * that is why --st-map-h defaults to a pixel length further up.)
 *
 * NOT A FLEX ROW BELOW 992px. One column, map first and sticky at the top,
 * acts under it — which is the same reading order the markup is in, so
 * nothing reorders and the tab order stays honest at every width. */

/* ── The flip side, docked: the page's side third ────────────────────────
 * cb, 2026-09-17, of the two bands on /societie/introduction/: "the two
 * panels on this page are different ... let's rework the lightbox to match
 * the Principles." They were: the map opened a black modal over the whole
 * window while the principle comb, a heading below it, opened the paper
 * side-third panel. Same page, same idea, two flip sides.
 *
 * So this is the same panel, and deliberately the same rules as
 * .principle-cards .pc-dialog.pc-docked in principle-cards.css: a full-height
 * wash on the DIALOG, the card stripped of everything that made it an object,
 * paper type, a ringed close disc at the opaque edge, no icon chip.
 *
 * ⚠️ THREE COPIES, AND WHY. The slide's copy lives inside
 * `.reveal .slides > section.tiles` in mccusker-reveal.scss, outranks
 * anything here, and restates every SIZE a third larger for a projector —
 * and its panel is the left third OF THE SLIDE, which the map is laid out
 * beside, where this one OVERLAYS the field. What all three share is the
 * WASH, and that is the part that would read as a mistake if it drifted:
 * test_the_three_washes_are_one_gradient pins these stops against
 * $st-column-stops character for character.
 *
 * On a page the type is already set for a desk, so nothing here names a
 * font-size. The colours still change: this card's palette is built for
 * black (--st-accent-dark is measured on #141414) and this is paper.
 *
 * Below the band's ST_DOCK_MIN of FIELD width there is no dock at all and the
 * modal opens instead. There is no media query for it because the decision is
 * about the field's width, which a media query cannot see. */
.societie-tiles .st-dialog.st-docked {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: auto;
  width: 34%;
  min-width: 18rem;
  max-width: none;
  max-height: none;
  /* ⚠️ The UA sheet gives <dialog> `height: fit-content`, which is NOT `auto`
   * and beats a top/bottom pair: the box would size to the words and take the
   * wash with it, painting a short panel down a tall field. */
  height: auto;
  margin: 0;
  padding: 0;
  border: 0;
  z-index: 4;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-align: center;
      -ms-flex-align: center;
          align-items: center;
  /* `cursor` inherits, and the viewport's is `grab`. */
  cursor: auto;
  /* These four stops are $st-column-stops in mccusker-reveal.scss. An rgba()
   * triple is this file's documented exception to "never write a hex" — a
   * custom property cannot go inside rgba(). */
  background: -webkit-gradient(
    linear,
    left top, right top,
    from(rgba(251, 249, 245, 0.96)),
    color-stop(88%, rgba(251, 249, 245, 0.96)),
    to(rgba(251, 249, 245, 0))
  );
  background: linear-gradient(
    to right,
    rgba(251, 249, 245, 0.96) 0%,
    rgba(251, 249, 245, 0.96) 88%,
    rgba(251, 249, 245, 0) 100%
  );
}

.societie-tiles .st-dialog.st-docked.st-dock-right {
  left: auto;
  right: 0;
  background: -webkit-gradient(
    linear,
    right top, left top,
    from(rgba(251, 249, 245, 0.96)),
    color-stop(88%, rgba(251, 249, 245, 0.96)),
    to(rgba(251, 249, 245, 0))
  );
  background: linear-gradient(
    to left,
    rgba(251, 249, 245, 0.96) 0%,
    rgba(251, 249, 245, 0.96) 88%,
    rgba(251, 249, 245, 0) 100%
  );
}

/* ⚠️ .st-viewport carries `touch-action: none` so the camera owns every
 * finger on the map, and a CHILD CANNOT TAKE THAT BACK — the browser
 * intersects touch-action down the ancestor chain, so a docked flip side
 * would not scroll on a tablet, which is exactly the device wide enough to
 * dock one. The band adds .st-panelled while a panel is open. */
.societie-tiles .st-viewport.st-panelled {
  -ms-touch-action: auto;
      touch-action: auto;
}

.societie-tiles .st-dialog.st-docked .st-dialog-card {
  width: 100%;
  max-height: 100%;
  overflow-y: auto;
  /* Everything that made this an OBJECT comes off. The wash above is the
   * panel; this is only the words in it. A card with a shadow inside a wash
   * that deliberately has no edge is two panels, and the seam shows. */
  background: none;
  border: 0;
  border-radius: 0;
  -webkit-box-shadow: none;
          box-shadow: none;
  /* The close button stays anchored to the WORDS: the panel is full-height
   * and the words are centred in it, so a button in the panel's corner
   * floats free of the block it belongs to. */
  position: relative;
  /* Asymmetric on purpose: wider on the side the fade is, so no line ends in
   * the gradient with a hexagon showing through the last two words. The wash
   * holds full strength to 88%. */
  padding: 1.25rem 3rem 1.25rem 1.5rem;
}

.societie-tiles .st-dialog.st-docked.st-dock-right .st-dialog-card {
  padding: 1.25rem 1.5rem 1.25rem 3rem;
}

/* ── The type, on paper ─────────────────────────────────────────────────
 * --st-accent-dark is the DARK accent, measured on #141414, and must not
 * appear here. Nor can everything take --st-accent: that is measured at
 * >= 3:1, the GRAPHICAL threshold, because on a tile face it is a line and
 * not words — three of the seven groups sit between the two thresholds on
 * paper (codes 3.10, sessions 3.76, knots 3.77). So the accent keeps the
 * claim, which is large text, and the fact bullets, which are graphical;
 * every piece of small text goes to ink. Same split as the slide's. */
.societie-tiles .st-dialog.st-docked .st-eyebrow {
  color: var(--st-ink);
  /* The right margin cleared the icon chip, which is hidden below; the close
   * disc shares this line instead, on the opaque side. */
  margin-right: 0;
  padding-left: 2.5rem;
}

.societie-tiles .st-dialog.st-docked.st-dock-right .st-eyebrow {
  padding-left: 0;
  padding-right: 2.5rem;
}

.societie-tiles .st-dialog.st-docked .st-claim {
  color: var(--st-accent);
  margin-right: 0;
}

.societie-tiles .st-dialog.st-docked .st-detail,
.societie-tiles .st-dialog.st-docked .st-facts {
  color: var(--st-ink);
}

.societie-tiles .st-dialog.st-docked .st-facts li::before {
  background: var(--st-accent);
}

/* On black this was a filled chip — ink on the accent. On paper a filled chip
 * is another hard-edged object in a panel that has just stopped being one,
 * and its fill would have to answer the same contrast question the eyebrow
 * did. A rule under the words instead. */
.societie-tiles .st-dialog.st-docked .st-more {
  color: var(--st-ink);
  background: none;
  border-radius: 0;
  border-bottom: 3px solid var(--st-accent);
  padding: 0.3rem 0 0.15rem;
}

.societie-tiles .st-dialog.st-docked .st-more:hover,
.societie-tiles .st-dialog.st-docked .st-more:focus {
  color: var(--st-ink);
}

.societie-tiles .st-dialog.st-docked .st-close {
  top: 0.9rem;
  left: 1.5rem;
  right: auto;
  padding: 0;
  color: var(--st-ink);
  border: 1px solid currentColor;
  border-radius: 50%;
  width: 2rem;
  height: 2rem;
  line-height: 1;
  font-size: 1.25rem;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-align: center;
      -ms-flex-align: center;
          align-items: center;
  -webkit-box-pack: center;
      -ms-flex-pack: center;
          justify-content: center;
  opacity: 0.55;
}

.societie-tiles .st-dialog.st-docked.st-dock-right .st-close {
  left: auto;
  right: 1.5rem;
}

.societie-tiles .st-dialog.st-docked .st-close:hover,
.societie-tiles .st-dialog.st-docked .st-close:focus-visible {
  opacity: 1;
}

.societie-tiles .st-dialog.st-docked .st-close:focus-visible {
  outline-color: var(--st-ink);
}

/* The icon chip goes. It is the TILE'S OWN MARK and the hexagon carrying it
 * is on the same field a few hundred pixels away — the same drawing twice. In
 * the modal it earns its place because there is no map behind it. It also
 * could not stay where it was: pinned to the card's right edge it lands on
 * the 88% stop, the first pixel of the fade. */
.societie-tiles .st-dialog.st-docked .st-dialog-icon {
  display: none;
}

/* ── The four acts, under the map ───────────────────────────────────────
 * ⚠️ WHAT WAS HERE, AND WHY IT IS NOT. Until 2026-09-17 this was .st-scrolly:
 * a two-column scrollytelling block, the map sticky in 58% from 992px up and
 * the acts scrolling past in the rest, each act taking the camera as it
 * crossed the middle of the window. cb: "let's make it full width."
 *
 * That took out the sticky stage, the act min-heights, the 45vh tail of
 * scroll room the last act needed to reach the middle of the window, and the
 * @media print rule that unpinned the whole arrangement on paper — a page
 * that is ordinary flow at every width prints as ordinary flow. It also took
 * out the rule beside the active act: nothing tracks which act is active any
 * more, because nothing needs to (see the deleted driver in
 * bands/tile_grid.html). The four acts are four paragraphs.
 *
 * The map's height is now the band's own --st-map-h default rather than a
 * value this block overrode at two breakpoints. */
.st-acts {
  margin: 1.5rem 0 0;
}

.st-act {
  padding: 1.25rem 0;
}

.st-act__title {
  font-family: "Roboto Mono", monospace;
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin: 0 0 0.5rem;
}

.st-act p + p {
  margin-top: 0.75rem;
}
