/* Crews & Territory — panel and map chrome.
 *
 * Borrows the site's existing custom properties (--glass, --line, --mut, --shadow) so a theme
 * change reaches this without the file knowing themes exist. Squared rather than rounded: the
 * mode is about holding rectangles, and a pill-shaped card next to a tile grid looks borrowed.
 */

/* Lighter than what it sits on, not darker. rgba(10,15,30,.5) over the panel's own glass
   came out within one 8-bit value of the panel on every channel, so a card was visible only
   by its border, and a nested card, whose border is removed, by nothing at all. */
.crewcard {
  background: rgba(255, 255, 255, .035);
  /* One border, not three sides and an accent. A 2px coloured left edge is the shape of a
     pull quote, and the panel is six cards stacked: it read as six quotations rather than six
     blocks. */
  border: 1px solid var(--line, #243055);
  padding: 14px 15px;
  margin: 0 0 11px;
}
.crewcard h3 { margin: 0 0 6px; font-size: 14px; letter-spacing: .4px; text-transform: uppercase; }
.crewcard h4 { margin: 12px 0 6px; font-size: 12px; color: var(--mut, #9aa6c8); letter-spacing: .5px; text-transform: uppercase; }
/* `overflow-wrap: anywhere`, because this holds 280 characters of a crew's own free text and
   the most predictable thing in it is a URL. Without it a crew's Instagram link clipped 96px
   past the card at 390 and gave the whole panel 65px of sideways scroll; a long German
   compound did 219px and 188px. Three other rules in this file already handle it; the one
   place that needed it did not have it, which a stylesheet checker cannot see because an
   absent property is not a declaration. */
.crewcard p {
  overflow-wrap: anywhere; margin: 6px 0; font-size: 13px; line-height: 1.5; }
.crewcard code { background: rgba(255,255,255,.07); padding: 2px 6px; font-size: 12px; letter-spacing: 1px; }
.crewcard label { display: block; margin: 9px 0 0; font-size: 11px; color: var(--mut, #9aa6c8); letter-spacing: .5px; text-transform: uppercase; }
/* `textarea` is in this list because leaving it out made the description field the
   brightest object in the panel: white on black, grey border, no padding, 11px, and the
   browser's own focus ring, two points smaller than the one-word name field above it while
   holding 280 characters. */
/* 44px, like every button in this panel. A reviewer measured the crew-name field at 36 and
   the policy select at 35 -- the two controls a leader uses to change what their crew IS,
   below the minimum, in a feature where everything else clears it. Not the textarea: it sets
   its own 46 further down, and saying 44 here as well only gives the cascade checker a
   self-cancelling pair to complain about, correctly. */
.crewcard input[type=text], .crewcard input:not([type]), .crewcard select { min-height: 44px; }
.crewcard input[type=text], .crewcard input:not([type]), .crewcard select,
.crewcard textarea {
  display: block; width: 100%; margin-top: 4px; padding: 8px 10px;
  background: rgba(0,0,0,.35); border: 1px solid var(--line, #243055); color: inherit;
  font: inherit; font-size: 13px; border-radius: 0;
}
/* The OPEN list, which this card does not draw. A `<select>`'s popup is native chrome built
   from each option's own colours, so the rule above styled the closed control and nothing
   else: the options inherited the card's light ink and the popup kept its platform default,
   which is white. Light text on white, and no way to read your own choices.
   `color-scheme` is the part that makes the browser draw its widget dark -- the scrollbar, the
   hover row, the tick -- and the explicit colours cover engines that take the one and not the
   other. Opaque, not the field's translucent black: a popup has nothing behind it to blend
   with. */
.crewcard select { color-scheme: dark; }
.crewcard select option, .crewcard select optgroup {
  background: var(--pan, #0b1020); color: var(--ink, #eef1fb);
}
/* The one native control in the feature. Without this the browser draws its own grey
   "Choose File" button, in Arial, inside a dark themed card -- `input[type=file]` is excluded
   from the field rule above on purpose, so it had no box of its own at all.

   This sits BEFORE the focus rule below deliberately. `.crewcard input:focus` and
   `.crewcard input[type=file]` have the same specificity and neither selects a subset of the
   other, so whichever comes last wins: written afterwards, this `border` shorthand would have
   cancelled the focus ring's `border-color` on this field only. */
.crewcard input[type=file] {
  display: block; width: 100%; margin-top: 4px; padding: 7px 10px;
  background: rgba(0,0,0,.35); border: 1px solid var(--line, #243055);
  color: var(--mut, #9aa6c8); font: inherit; font-size: 12px; border-radius: 0;
}
/* the platform's button, wearing `.crewbtn.ghost`'s clothes */
.crewcard input[type=file]::file-selector-button {
  margin-right: 10px; padding: 5px 11px; background: transparent;
  color: var(--mut, #9aa6c8); border: 1px solid var(--line, #243055);
  font: inherit; font-weight: 700; font-size: 12px; letter-spacing: .3px;
  cursor: pointer; border-radius: 7px;
}
.crewcard input[type=file]::file-selector-button:hover {
  color: #fff; border-color: var(--sec, #ff8ad8);
}
.crewcard input:focus, .crewcard select:focus,
.crewcard textarea:focus { outline: none; border-color: var(--sec, #ff8ad8); }

.crewbtn {
  margin-top: 12px; padding: 9px 16px; background: var(--sec, #ff8ad8); color: #061020;
  border: 0; font: inherit; font-weight: 700; font-size: 13px; letter-spacing: .3px;
  cursor: pointer; border-radius: 7px;
  /* `.crewopen` is an <a>, not a <button>: without this, the primary action on the sign-in
     card is the only underlined button on screen. */
  text-decoration: none;
}
/* These rows are `<div>`s and `<tr>`s with `role="button"`, so they inherit no focus ring
   of their own. Keyboard only: a mouse press should not draw one. */
.crewtrow:focus-visible, .crewboard tr.sel:focus-visible, .crewrow:focus-visible,
.crewboard .pod:focus-visible {
  outline: 2px solid var(--sec, #ff8ad8); outline-offset: -2px;
}

.crewbtn:hover { filter: brightness(1.12); }
/* .35, not .5. A secondary button is already muted -- #9aa6c8 on this background -- so half
   of it lands around #576382, which is a colour an ENABLED control could plausibly be. Side
   by side with a live button you can tell; on its own, which is how a disabled Join in a list
   is actually met, you cannot, and the reader presses it. The shape and the box are untouched
   on purpose, so nothing moves when a button comes alive.
   The reason is already attached and stays so: every disabled control here carries a `title`
   and an `aria-describedby` pointing at the sentence that explains it. This is only about
   whether a dead button LOOKS dead. */
.crewbtn:disabled { opacity: .35; cursor: default; }
/* The moment after a copy. Full strength, not the dimmed look of a button you cannot
   use yet: it is disabled because it just worked, which is the opposite thing. */
/* The tick, sized to the line it replaces so the button keeps its height too. */
.crewtick { width: 15px; height: 15px; display: block; margin: 0 auto; }
.crewbtn.crewdone:disabled { opacity: 1; background: #3ddc84; color: #06220f;
  border-color: transparent; }
.crewbtn.ghost.crewdone:disabled { background: transparent; color: #3ddc84;
  border-color: #3ddc84; }
.crewbtn.ghost { background: transparent; color: var(--mut, #9aa6c8); border: 1px solid var(--line, #243055); }
.crewbtn.ghost:hover { color: #fff; border-color: var(--sec, #ff8ad8); }
.crewbtn.mini { margin: 0; padding: 5px 11px; font-size: 12px; }
.crewacts { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }

/* A content cap above the phone breakpoint. Each join row was emblem 46 + text ~400 + about
   380px of NOTHING + an 86px button, six times over, and the contributor chart had the same
   hole: a fixed 42% name column against ~120px names left ~150px of dead track before the
   bars. Nothing was broken; the layout simply had nothing to do with its width. Capped, the
   panel scales by whitespace at its edges rather than by holes in the middle of its rows.
   56ch is about where a line of this prose stops being comfortable to read anyway. */
@media (min-width: 561px) {
  /* The CARD's content column. Capping the list alone gave one card three right edges -- the
     filter at 1348, the rows at 1038, the toggle at 1348 -- and left `.crewcontrib` 156px
     narrower than the `.crewtargets` block directly above it in the same stack.
     Named by STACK, not by member, which is what "the card's content column" has to mean:
     `.crewcontrib` and `.crewtargets` are children of `.crewterr`, not of the card, so capping
     those two left them 147px short of every one of the card's own children and put three
     right edges in one card again -- 955.7, 808.7 and a border at 970.7, measured at 1280.
     Capping each stack's children instead gives the whole card one edge, and a nested stack
     inherits it because its own parent is already capped.
     In rem, not ch. `ch` is resolved in each element's OWN font size, so the same declaration
     gave the 13px blocks a 492px cap and the smaller `<p>` copy a 773.5px one -- two right
     edges again, from one rule, measured at 1280. A content column is a single length by
     definition. 31rem is the 56ch measure at the body size, which is where the number came
     from in the first place. */
  .crewjoin > *, .crewmine > *, .crewterr > * { max-width: 31rem; }
  /* And the CARD, or the cap is a hole rather than a column. Capping only the blocks left
     every rule, row and chip inside the crew card stopping 220px short of the card's own
     border -- 316.3 to 750.3 inside a card running 299.3 to 970.7 -- while the warning
     banner, the board and the rules beside it all reached 971.3. A reviewer read it as
     cropped, which is exactly what it was. 31rem of content plus this card's own 15px
     padding and 1px border on each side, so the chrome and the content share one edge and
     the slack falls outside the card, which is where the note above wanted it. */
  /* The board too, or two stacked cards share a left border and disagree about the right one.
     Capping `.crewjoin` and `.crewmine` and not this closed the dead band INSIDE the crew card
     and opened a 206px step between it and the board beneath -- crew card 299 to 765, board
     299 to 971 -- which a reviewer called the most visible flaw in the build. Capping rather
     than lifting both: the cap is here because these rows were measured with 380px of nothing
     in the middle of them, and the board's own grid needs about 293px, so 466 is room to
     spare. One measure for every card in the panel. */
  /* `.crewcard`, not the three cards that happened to be measured. The list above named
     `.crewjoin`, `.crewmine` and `.crewboard` and missed `.crewsign` and the eight bare
     `<div class="crewcard">`s -- the create-a-crew form and seven state cards. So at 1280 a
     signed-out visitor met a 673px sign-in card sitting directly on a 466px board: a 103px
     step on each side, on the first screen anybody sees. A crewless rider got JOIN A CREW at
     466, START A CREW at 673 and the board at 466 again.
     Every card in the panel is a `.crewcard`, so this is the rule the comment above always
     described. */
  /* `margin-inline: auto` with it, or they share a width and still disagree about where it
     starts: the board already centred itself and the sign-in card did not, so capping both
     left them the same size and 103px apart. One width and one position. */
  .crewcard, .crewhow { max-width: calc(31rem + 32px); }
  /* The centring is on the cards alone. `.crewhow` carries a `margin` SHORTHAND at the top
     level, which resets the inline sides whatever a longhand in here says -- the cascade
     checker caught it, which is what it is for. It does not need centring anyway: it is the
     manual, and the manual lives in the modal now. */
  .crewcard { margin-inline: auto; }
}
/* Full width, so it reads as more list rather than a stray action: it was the only control in
   the join card not in the button column the six rows above it end with. `.crewshowall` and
   not `.crewmore` -- that name was already taken by the lose card's "and 3 squares more", and
   borrowing it put a width and a margin on a hint paragraph in another card. */
.crewshowall { width: 100%; margin-top: 8px; }

/* a crew's identity at a glance: the colour, with its pattern drawn over it, exactly as the
   map draws it — so the swatch beside a name is the same thing you see on the ground */
.crewsw { display: inline-block; vertical-align: middle; border: 1px solid rgba(255,255,255,.55); position: relative; }

/* ---------- the twelve patterns, written once ----------
   A crew's identity is a colour with a pattern over it, and it is drawn in four places: the
   inline swatch beside a name, the key under the manual, the picker grid in the two forms,
   and the map itself. The map gets a canvas tile from crews.js; the other three each had
   their own copy of these gradients, which is how `stripes` came to exist at three different
   alphas in one stylesheet, `solid` was defined nowhere, and adding a pattern meant
   remembering three selectors in three parts of the file.
   Now a pattern sets `--pat` and whoever draws it paints `var(--pat)`. Going from four
   patterns to twelve touched this block and nothing else. */
.crewsw[data-p]::after, .crewlegend i[data-p]::after {
  content: ""; position: absolute; inset: 0;
  background-image: var(--pat, none);
  background-size: var(--patsize, auto);
  background-position: var(--patpos, 0 0);
}
/* One ink for all of them. The three copies used .42, .45 and .5 with nothing choosing
   between them, so two patterns on the same colour printed at different weights. */
/* .36, following the map down. The swatch is a preview of what the ground looks like, so
   when the stencil on the map was lightened to 0.65 this had to come with it or the picker
   would be promising a heavier pattern than the rider gets. */
[data-p] { --patink: rgba(0,0,0,.36); }

[data-p=solid] { --pat: none; }
[data-p=stripes] { --pat: repeating-linear-gradient(45deg, var(--patink) 0 2px, transparent 2px 5px); }
[data-p=backslash] { --pat: repeating-linear-gradient(-45deg, var(--patink) 0 2px, transparent 2px 5px); }
[data-p=vert] { --pat: repeating-linear-gradient(90deg, var(--patink) 0 2px, transparent 2px 5px); }
[data-p=horiz] { --pat: repeating-linear-gradient(0deg, var(--patink) 0 2px, transparent 2px 5px); }
[data-p=hatch] { --pat: repeating-linear-gradient(45deg, var(--patink) 0 2px, transparent 2px 6px),
                        repeating-linear-gradient(-45deg, var(--patink) 0 2px, transparent 2px 6px); }
[data-p=grid] { --pat: repeating-linear-gradient(90deg, var(--patink) 0 2px, transparent 2px 6px),
                       repeating-linear-gradient(0deg, var(--patink) 0 2px, transparent 2px 6px); }
[data-p=dots] { --pat: radial-gradient(var(--patink) 28%, transparent 30%); --patsize: 5px 5px; }
/* the one that has to stay apart from `dots` at fourteen pixels: one big spot per cell,
   not texture */
[data-p=bigdots] { --pat: radial-gradient(var(--patink) 40%, transparent 42%); --patsize: 9px 9px; }
[data-p=rings] { --pat: radial-gradient(transparent 24%, var(--patink) 26% 38%, transparent 40%);
                 --patsize: 9px 9px; }
[data-p=checker] { --pat: conic-gradient(var(--patink) 0 25%, transparent 0 50%,
                          var(--patink) 0 75%, transparent 0); --patsize: 8px 8px; }
/* Three layers, because bricks are courses with the joints offset row to row -- the whole
   thing that separates them from `horiz`. Layer one is the courses; two and three are the
   joints, one per row, half a brick apart. The canvas tile draws the same 16px cell. */
[data-p=bricks] { --pat: repeating-linear-gradient(0deg, var(--patink) 0 2px, transparent 2px 8px),
                         linear-gradient(90deg, var(--patink) 0 2px, transparent 2px),
                         linear-gradient(90deg, var(--patink) 0 2px, transparent 2px);
                  --patsize: auto, 16px 8px, 16px 8px;
                  --patpos: 0 0, 4px 0, 12px 8px; }

.crewhead { display: flex; gap: 12px; align-items: center; }
.crewlogo { width: 52px; height: 52px; flex: 0 0 52px; border: 1px solid var(--line, #243055); background: rgba(0,0,0,.3); }
.crewmeta { font-size: 12px; color: var(--mut, #9aa6c8); margin-top: 3px; }

.crewterr { margin: 12px 0 2px; padding: 10px 0; border-top: 1px solid var(--line, #243055); }
.crewbig { font-family: "Orbitron", ui-sans-serif, sans-serif; letter-spacing: .5px;
   font-size: 26px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1; }
.crewbig span { font-size: 13px; font-weight: 400; color: var(--mut, #9aa6c8); }
.crewsub { font-size: 12px; color: var(--mut, #9aa6c8); margin-top: 4px; }

.crewpend { border-top: 1px solid var(--line, #243055); margin-top: 10px; }
/* Three controls and a display name in a row that does not wrap. German's "Zum Offizier
   machen" beside "Entfernen" needs the name to be the thing that shortens, which it cannot
   do without this -- the neighbouring .crewrown has had it all along. */
.crewpendr > span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.crewpendr { display: flex; align-items: center; gap: 8px; padding: 6px 0; font-size: 13px; }
/* The flag and the name are one flex CHILD, not two, so the row's 8px gap never falls between
   them: measured 0.00px, the image's right edge exactly on the first letter, while the
   contributions list a few centimetres above gets 7px because there the avatar is its own
   child. Every row of THE CREW and every KNOCKING row, at every width. */
.crewpendr > span:first-child img { margin-right: 7px; vertical-align: middle; }
.crewpendr span { flex: 1; }
/* Its own line under the name, because the name's cell is `overflow: hidden` with an ellipsis
   and the facts were running straight into it: a reviewer read `SIM-Solo Rider 456c9.0 km` and
   `S13-Night Shift207 km`. I had added the class and never written a rule for it. A block
   rather than a margin, for the reason the margin above exists -- a literal space between two
   inline pieces is the thing that put these rows 3.17px out of line with the roster once
   already. The name may ellipsize; these must not, so they get their own measure. */
/* It wraps rather than clipping. The ellipsis here was copied from the name rule above
   without the reason coming with it: a name is one long token that has to be cut somewhere,
   and this is two short facts with a separator between them. German ("vor 4 Std. gefragt")
   and Turkish ("4 saat önce istedi") are half as long again as the English, and the row can
   simply be a line taller. */
.crewpendr .crewpendf { display: block; margin-top: 1px; font-size: 11px; line-height: 1.3;
  color: var(--mut, #9aa6c8); }

.crewlist { margin-top: 6px; }
/* Top, not centre. A 46px emblem centred against a three-line text block lined up with the
   distance rather than with the crew's name; the button stays centred, because a button level
   with a name's cap height reads as belonging to the name. */
.crewrow { display: flex; align-items: flex-start; gap: 10px; padding: 7px 0; border-top: 1px solid rgba(255,255,255,.05); }
.crewrow > .crewbtn { align-self: center; }
/* `display: flex` above is author CSS and `[hidden]`'s `display: none` comes from the user
   agent, so the author rule wins and a hidden row went on taking its 44px. Fifteen of the
   twenty-one rows in this list are hidden until you ask for them, so the toggle appeared to
   do nothing at all. */
.crewrow[hidden] { display: none; }
.crewrow:first-child { border-top: 0; }
.crewrown { flex: 1; min-width: 0; }
.crewrown b { display: block; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* This file says twice that truncating a crew's name is indefensible in a feature about
   naming your crew, and applies that to `.crewboard .pname` and the table's `.celln > span`.
   The join list -- where you pick a crew BY NAME -- kept the ellipsis: a 41-character name
   got 170px of a 390px row and read `Kreuzberg Nachtschwärm…`, and 141px at 360. It wraps.
   The description beside it is clamped instead, because 280 characters of blurb took one row
   to 202px against 91px for its neighbours and left the Ask button level with line three. */
@media (max-width: 560px) {

  .crewrown b { white-space: normal; overflow: visible; text-overflow: clip; }
}
.crewrow .crewmeta2 {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
  /* `anywhere`, for the same reason the crew card's own <p> has it: this is 280 characters of
     a crew's own free text, and an unbreakable 49-character compound measured 277px of
     content in a 132px box and was cut mid-word. `overflow: hidden` contains it, so the
     fault was a silent clip rather than a scrollbar -- which is why testing at two widths
     never showed it. */
  overflow-wrap: anywhere;
}
/* A flex row with a gap, which is what separates the facts now. A middot between two
   facts is a glyph at one line edge or the other once the row wraps -- trailing it dangled,
   leading it appeared on 6 of 6 rows at 390 -- and a gap cannot be stranded. */
.crewrown > span { font-size: 11px; color: var(--mut, #9aa6c8);
  display: flex; flex-wrap: wrap; column-gap: 9px; row-gap: 1px; }
.crewrown > span.crewmeta2 { display: -webkit-box; }

.crewrank { width: 100%; border-collapse: collapse; font-size: 13px; }
.crewrank td { padding: 6px 4px; border-top: 1px solid rgba(255,255,255,.05); }
.crewrank .rk { width: 26px; color: var(--mut, #9aa6c8); font-variant-numeric: tabular-nums; }
.crewrank .val { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.crewrank .val.sub { color: var(--mut, #9aa6c8); font-size: 11px; }
.crewrank tr.sel { cursor: pointer; }
.crewrank tr.sel:hover td { background: rgba(255,138,216,.1); }
.crewrank .celln { display: flex; align-items: center; gap: 8px; }

.crewident { display: flex; gap: 12px; align-items: center; margin: 12px 0 0; }
.crewident > div { flex: 1; }
/* The label line also carries the preview, pushed to its right edge: see `.crewidentrow`. */
.crewidentl { font-size: 11px; color: var(--mut, #9aa6c8); letter-spacing: .5px;
  text-transform: uppercase; display: flex; align-items: center; gap: 8px; }
.crewident select { margin-top: 4px; }

/* One axis. The card changed alignment five times -- centred heading, left-ragged subtitle
   directly under it, centred QR, centred code, then three left-ragged hints -- so the heading
   had no left edge in common with its own subtitle. Left throughout, with the code and the QR
   centring themselves inside the text column rather than against the card. `.crewsign .hint`
   used to force left against a centred parent and is now what everything inherits. */
/* Centred, which is the axis Erwin wants and still only ONE axis -- the rule that fixed this
   card was never "left", it was "stop mixing five of them in one card". */
.crewsign { text-align: center; }
/* Left, like its six siblings. Centred it put a 26px code with 7px of tracking, and the
   QR beside it, about 250px right of the heading's left edge -- and in the expired state the
   masked six dots floated alone in a 649x188 empty box. */
/* `padding-left` is the whole fix for a centred code that is not centred.

   `letter-spacing` puts its gap AFTER every character including the last, and
   `text-align: center` centres the box with that trailing gap inside it -- so the glyphs
   land half a letter-space to the left of the middle, every time. Measured on a 412px
   screen: the QR's ink sits 0.5px off the card's centre line and the code's sits 3.0px off,
   the 2.5px between them being exactly half of the 5px tracking. Erwin saw it as the code
   not lining up under its own QR, which is what it was.

   A left pad of one whole letter-space moves the line right by half of one, which cancels
   it. Taken from `--ls` rather than written again, because the tracking is 7px here and 5px
   on a phone and a second copy of either number is a second thing to forget.

   Note the Android app does NOT need this: Compose trims the trailing letter-space itself,
   which is why the 4dp nudge that was once in the app was wrong and came out. */
.crewsign .crewcode { text-align: center; padding-left: var(--ls); }
.crewsign .crewqr { justify-content: center; }
/* The version requirement is a precondition, not a footnote. It was the second of three
   identical hint lines with a pink one below it, so the newest and hardest requirement was
   the least visible thing in its own group. */
/* `.crewsign .crewneeds`, not `.crewneeds`: the element is `<p class="hint crewneeds">` and
   `.crewcard .hint` sets the muted ink at (0,2,0), so a single class could set the weight and
   never the colour. Two classes on one element is a relationship the stylesheet does not
   state and the cascade checker cannot see -- the same shape as `.crewbtn` un-hiding
   `.crewopen` two rounds ago. */
.crewsign .crewneeds { font-weight: 600; color: var(--ink, #eef1fb); }

/* A fact holds together; the gap on its flex parent separates it from the next one. The
   `.crewsep` span this used to cover is gone -- see `.crewrown > span` -- because a glyph
   between two facts sits at a line edge whichever side of the fact it is glued to. */
.crewfact { white-space: nowrap; }
.crewqr { min-height: 188px; display: flex; align-items: center; justify-content: center; margin: 12px 0 8px; }
.crewqr img { width: 188px; height: 188px; image-rendering: pixelated; background: #fff; padding: 7px; }
/* The same square, drawn as its own modules so they can land one at a time. White behind them
   and the quiet zone already in the matrix, so this needs no padding of its own -- a border a
   reader needs is data here, not decoration. */
.crewqrg { width: 188px; aspect-ratio: 1; display: grid; grid-auto-rows: 1fr;
  background: #fff; position: relative; }
.crewqrlogo {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 23%; height: 23%; object-fit: cover; border-radius: 3px;
  background: #fff; box-shadow: 0 0 0 3.5px #fff;
}
/* The crew's initials over the code's logo. Same geometry as `.crewqrlogo`, so it covers it
   exactly; the size comes from `sizeSheetMarks`, which measures the logo rather than
   guessing at it -- the code is 190px in the sheet and 124px in a list. */
.crewqrlogoi {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 23%; height: 23%; display: flex; align-items: center; justify-content: center;
  pointer-events: none; font-family: ui-monospace, Menlo, Consolas, monospace;
  font-weight: 700; font-size: var(--embi, 10px); line-height: 1; letter-spacing: .5px;
  color: #fff; text-shadow: 0 1px 2px #000, 0 0 6px #000, 0 0 2px #000;
}
.crewqrg i { background: #0b0f1a; }
.crewqrg i.o { background: transparent; }
/* Each module drops in, delayed by row + column, which `qrGrid` writes on the element: the
   sweep comes off the top-left corner as one movement. Short, because this is in front of
   somebody holding a phone up at it. */
@keyframes crewmod { from { opacity: 0; transform: scale(.25); } to { opacity: 1; transform: none; } }
.crewqrg i { animation: crewmod .26s ease both; }
/* A code that has expired is not arriving, so it does not animate in. */
.crewqr.dead .crewqrg i { animation: none; }
.crewcode { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 26px; --ls: 7px; letter-spacing: var(--ls); font-weight: 700; }

.crewmsg { margin: 9px 0; padding: 8px 11px; border-radius: 7px;
  background: rgba(255,138,216,.14); border-left: 2px solid var(--sec, #ff8ad8);
  font-size: 12px; }
/* A neutral-dark fill, not a second pink wash. The ask box it most often sits inside is
   `rgba(255,138,216,.1)` with a `#ff8ad8` rail; an error on `rgba(255,79,163,.13)` with a
   `#ff4fa3` rail was two pinks nested about 20 degrees apart, so the one state that must read
   differently from resting had almost no distance from it. The ink and the rail stay pink --
   that is the signal -- and the fill carries the difference. */
/* The neutral fill was right and left the text in the generic hint grey, so "GIVE IT A
   NAME." read as a label. Square corners too: it sits 4px under an input with no radius. */
.crewmsg.bad { background: rgba(10,6,14,.55); border-left-color: #ff4fa3;
  color: #ffb3d6; border-radius: 0; }
/* Amber, not the error pink: nothing has gone wrong, it is just not finished. */
.crewmsg.warn { background: rgba(255,210,74,.12); border-left-color: #ffd24a; color: #ffe9a8; }

/* Inside the dialog the sheet draws the box, so the manual brings none of its own. The two
   rules that used to hide the collapsed disclosure are gone with it -- and one of them had to
   go: `.crewhow:not([open])` matches a plain <div>, which this now is, so it would have hidden
   the manual inside the dialog too. */
.sheetbody .crewhow { border: 0; padding: 0; margin: 0; }
/* The same bottom margin every card has. Without it the board sat flush against this, 0px
   between two bordered boxes, which reads as one box with a rule across it. */
.crewhow { font-size: 12px; line-height: 1.55; color: var(--mut, #9aa6c8); border: 1px solid var(--line, #243055); padding: 0 13px; margin: 0 0 11px; }
.crewhow summary { cursor: pointer; padding: 11px 0; font-size: 12px; letter-spacing: .5px; text-transform: uppercase; color: #cfe0ff; }
.crewhow p { margin: 0 0 10px; }
/* The numbers are the point, so they get room and their own ink rather than sitting in the
   muted body colour where they read as punctuation. */
/* The answer, before the rules. Set apart from them so it reads as the thing it is rather
   than as rule zero. */
.crewintro { margin: 0 0 14px; color: var(--ink, #eef1fb); }
/* A section heading inside the manual. Quiet, because the numbers under it are the content. */
.crewhow h5 { margin: 0 0 7px; font-size: 11px; letter-spacing: .5px; text-transform: uppercase;
  color: #cfe0ff; font-weight: 700; }
.crewhow h5 + .crewrules { margin-top: 0; }
.crewrules { margin: 0 0 14px; padding: 0 0 0 1.6em; }
.crewrules li { margin: 0 0 9px; padding-left: 2px; }
.crewrules li::marker { color: var(--sec, #ff8ad8); font-weight: 700; }
/* Not a rule: the closing line, set apart from the list it follows. */
/* `.crewrulesend` stood here: the rule for the bare paragraph that used to close the manual.
   That paragraph was the BOARD's subtitle printed under the colours section with no heading
   and no antecedent, and it is a proper section now -- see "What the numbers mean". */
.crewhow b { color: #e6eeff; }
.crewedit summary, .crewfile { cursor: pointer; }
/* The one unstyled control in the feature. A bare `<input type=file>` renders the browser's
   grey "Choose File | No file chosen" inside an otherwise fully custom dark panel -- a
   reviewer called it out as the single thing that looks like it belongs to another product.
   The input keeps doing the work (it is what opens the picker, and it stays in the tab order
   and reachable by a screen reader); it is the label around it that is drawn. */
.crewfile > input[type=file] {
  /* Not `display: none` -- that takes it out of the accessibility tree and off the keyboard.
     Clipped to nothing instead, the same way `.crewsumsep` hides text it still wants read. */
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%);
}
/* A button, not a field. It was a bordered box with a left-aligned placeholder and a caret
   sitting in it, so three reviewers independently read it as somewhere to TYPE -- one said
   their first instinct was to type into it. The content is centred with its glyph beside the
   label now, and the fill is the one the ghost buttons use, so it reads as something you
   press. The input inside is untouched: still clipped rather than hidden, so it keeps its
   place in the tab order and in the accessibility tree. */
.crewfile {
  display: flex; align-items: center; justify-content: center; gap: 8px; position: relative;
  margin: 9px 0 0; padding: 11px 12px; min-height: 44px;
  background: rgba(255,255,255,.07); border: 1px solid var(--line, #243055);
  border-radius: 8px; font-size: 11px; letter-spacing: .5px; text-transform: uppercase;
  font-weight: 700; color: var(--ink, #e8edfb);
}
/* Beside the label rather than pushed to the far edge: `margin-left: auto` put it where a
   field's clear button lives, which was half the reason the whole thing read as an input. */
.crewfile::after {
  content: ""; order: -1; width: 13px; height: 13px; flex: 0 0 13px;
  border: 1.5px solid currentColor; border-radius: 3px;
  background:
    linear-gradient(currentColor, currentColor) center/7px 1.5px no-repeat,
    linear-gradient(currentColor, currentColor) center/1.5px 7px no-repeat;
}
.crewfile:hover, .crewfile:focus-within { color: #cfe0ff; border-color: var(--sec, #ff8ad8); }
.crewedit { margin-top: 12px; border-top: 1px solid var(--line, #243055); padding-top: 4px; }
.crewedit summary { padding: 8px 0; font-size: 11px; letter-spacing: .5px; text-transform: uppercase; color: var(--mut, #9aa6c8); }

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

/* The emblem block. Sized in JS from the zoom so it always fills the square of tiles the
   server picked for it — which is why territory is drawn on Mercator tiles: this image is
   square and it has to land in something square. */
/* `position: absolute; top: 0; left: 0` is MapLibre's own rule for a marker, and it is the
   whole of what makes one stick to the ground. This said `relative` instead, and crews.css
   loads after maplibre-gl.css, so at equal specificity it won -- every emblem was laid out in
   NORMAL FLOW inside the canvas container and then offset by the transform from wherever the
   flow had put it, which is not where the map thinks it is.
   Measured on Jonah's turf: the map projected his two badges to y=475 and y=185 and the
   browser drew them at 531 and 275, 56px and 90px low. The error is the stacked height of
   every marker before it in the DOM, so it grows with the number of crews and differs for
   each one -- the badges sat off their own ground, and `declutterEmblems` then hid them by
   comparing `map.project` positions that had nothing to do with where they were painted.
   Erwin: "see what happens now when I rotate, they go away". Nothing inside needs this to be
   a positioning context; `.crewemb-i` is absolute against `.crewemb-b`, which has its own. */
.crewemb {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  pointer-events: auto; cursor: pointer; transition: opacity .25s;
  position: absolute; top: 0; left: 0;
}
/* A plate behind the emblem. Without it the generated badge uses the crew's own colour over
   the crew's own fill, so it read as the map pattern showing THROUGH the logo rather than the
   logo sitting on top of it. The plate is dark and slightly inset, which separates the two at
   every zoom and works just as well for an uploaded logo with transparent corners. */
.crewemb img {
  width: 100%; height: 100%; object-fit: contain;
  padding: 7%;
  /* opaque enough that a 0.55 fill cannot read through the letterforms: at .66 the crew's
     own checkerboard showed straight through its own initials */
  background: rgba(4, 8, 16, .9);
  border: 1px solid rgba(255, 255, 255, .32);
  box-shadow: 0 3px 16px rgba(0, 0, 0, .78);
  box-sizing: border-box;
}
.crewemb-n {
  margin-top: 3px; font-size: 11px; font-weight: 700; letter-spacing: .3px;
  color: #fff; text-shadow: 0 1px 3px #000, 0 0 10px #000; white-space: nowrap;
}
.crewemb-n { display: none; }
.crewemb-b { position: relative; flex: 1 1 auto; min-height: 0; width: 100%;
  display: block; line-height: 0; }
.crewemb-i {
  position: absolute; left: 0; right: 0; top: 0; bottom: 0;
  display: flex; align-items: center; justify-content: center; pointer-events: none;
  font-family: ui-monospace, Menlo, Consolas, monospace; font-weight: 700;
  font-size: var(--embi, 10px); line-height: 1; letter-spacing: .5px;
  color: #fff; text-shadow: 0 1px 2px #000, 0 0 6px #000, 0 0 2px #000;
}

/* Opaque, not .94. Six per cent of the map came through the whole box, and the one place it
   read as a picture rather than a tint was the two buttons: they add rgba(255,255,255,.06)
   of their own, which lifts that six per cent just far enough for the crew's dots to resolve
   inside them. Erwin saw it there and asked whether the pattern was leaking -- it was, and
   through the popup rather than onto the buttons. The seam down the middle of the box was
   the same thing: a tile boundary on the ground underneath.

   A popup is the one surface whose whole job is to be read against a map that is deliberately
   busy, so it does not get to be glass. */
.crewpop .maplibregl-popup-content { background: #080d1a; border: 1px solid var(--line, #243055); border-radius: 0; padding: 9px 12px; box-shadow: 0 8px 26px rgba(0,0,0,.6); }
/* The tip is drawn by maplibre as a triangle of borders and takes its colour from its own
   rule, not from the content's -- so changing one above without this leaves a translucent
   arrow hanging off an opaque box. */
.crewpop .maplibregl-popup-tip { border-top-color: #080d1a; border-bottom-color: #080d1a; }
/* The tip was hidden for tidiness, which on a map where every square looks like every other
   square left the box ambiguous about which one it was describing. */
.crewpop .maplibregl-popup-tip { display: block; }
/* Above the map key. The key is `z-index: 3` and the popup had none, so at 390px the key's
   panel covered the bottom 10px of a 44px "Highlight" button -- `elementFromPoint` at the
   button's lower edge returned the key's own `<summary>`, so a tap along that strip collapsed
   the key instead of highlighting the patch, and the real target was 91x34. The key is
   furniture that is always there; the popup is a thing the reader has just asked for, so the
   popup wins. `.crewpop` is on the MARKER wrapper -- maplibre puts the class on the outer
   `.maplibregl-popup` -- which is the element that carries the stacking. */
/* Above the map's own children -- NOT above the panel, which this cannot reach.
   `#map` is `position: fixed; z-index: 0`, which is a stacking context, so everything inside
   it is painted as one layer at z-index 0 and no z-index on a descendant can lift it over a
   panel at 550. This number only orders the popup against markers and tips beside it.
   The overlap it was first written for is solved where it actually lives: `crews.js` anchors
   the popup upward when the point pressed is close enough to the panel that a downward popup
   would run under it. I claimed this line fixed that and it did not; a reviewer measured 44px
   of an 80px popup still behind the panel. */
.maplibregl-popup.crewpop { z-index: 560; }
.crewpop-in { display: flex; align-items: center; gap: 10px; }
.crewpop-in img { width: 34px; height: 34px; }
.crewpop-in b { display: block; font-size: 13px; color: #eaf2ff; }
.crewpop-in span { display: block; font-size: 11px; color: var(--mut, #9aa6c8); }
.crewpop-in span + span { color: #cfe0ff; font-variant-numeric: tabular-nums; }
/* The crew's name opens its details, which is where Details went when the second button
   became Next patch. Pressable like the name in a list row, and styled here rather than
   there because this one is a `<b>`: the block colours its lines by element and by sibling
   order, so the name could not be given a wrapper without repainting the two lines under it. */
.crewpopnm { cursor: pointer; border-radius: 4px; padding: 0 3px; margin: 0 -3px; }
.crewpopnm:hover { background: rgba(255,255,255,.08); text-decoration: underline; }
.crewpopnm:focus-visible {
  outline: none; box-shadow: 0 0 0 2px #0b0f1c, 0 0 0 4px var(--sec, #ff8ad8);
}
/* Which of their patches you are standing on. Muted against the two lines above it, which are
   facts about this square -- this one is about where the square sits in the crew's ground.
   Beats `span + span` on specificity, which would otherwise paint it as a second fact. */
.crewpop-in .crewpopct { color: var(--mut, #9aa6c8); font-variant-numeric: tabular-nums; }

/* --- Crew Pass --- */

/* The "open the app" button is only useful on the device the app is installed on, and that is
   also the one case where scanning is impossible — you cannot point a phone's camera at its
   own screen. So it shows on touch devices and hides on a desktop, where the QR is the whole
   point. `hover:none` + `pointer:coarse` is the honest test for that: it asks what the input
   device can do rather than guessing from the user agent string. */
/* `a.crewopen`, not `.crewopen`. The markup is `<a class="crewbtn crewopen">`, and
   `.crewbtn { display: inline-flex }` in the touch block below is later in the file at the
   same specificity, so it undid this for the button and not for the sentence -- a 390px
   desktop window got the pink call to action with nothing explaining it. One element
   selector settles it, and the show rule below carries the same weight so the pair stays
   gated together. */
a.crewopen, .crewsame { display: none; }

a.crewqr { display: flex; text-decoration: none; }
/* A link with no colour of its own computes to the browser default #0000EE, underlined, on a
   near-black card. The QR is an image so nobody saw it -- until the image failed, or the
   pairing expired and the retry link became the only control on the screen. */
a.crewqr, a.crewqr:visited { color: var(--acc, #2ea8ff); }

/* Five codes in a row went unanswered, so this pairing is over. The code was blanked to dots
   -- which reads as loading -- while a full-size scannable QR and a bright `eucplanet://`
   button both stayed live and went on offering a handshake that could not happen. */
/* A refusal, not a wait. The code slot showed six middots -- which read as loading -- with a
   spinner turning behind them forever, so an expired code and a spent rate limit both looked
   like a page still working.

   No `letter-spacing` here: the phone block sets 5px on `.crewcode` and a top-level
   `.dead` would beat it, which is the exact shape the checker reports -- and it told me so
   on the first run. One em dash does not need its tracking adjusted anyway. */
.crewcode.dead { color: var(--mut, #9aa6c8); }
/* The hint under the code wraps wherever the width runs out, which put "In the app:
   Settings," at the end of one line and "EUC Stats, Crews." alone on the next -- a path cut
   in half. `balance` makes the browser even the lines up instead, so the break lands near
   the sentence rather than inside the path. Ignored by browsers that do not have it, which
   leaves exactly today's wrapping. */
#crewcodehint, .crewsign .crewsame { text-wrap: balance; }
#crewqr.dead, #crewopen.dead {
  opacity: .25; filter: grayscale(1); pointer-events: none; cursor: default;
}

/* Anything anchored after a control that is itself a flex item -- a confirm prompt, an
   invite-code box, a failure message. Inserted as a bare div into a `.crewrow` it became a
   fourth column: `.crewrown` was squeezed to 0px, its meta span rendered 29px wide by 257px
   tall -- one word per line -- and the row grew from 60px to 378px and clipped at the panel
   edge. It takes the whole width and the row wraps to let it. */
.crewaskslot { flex: 1 0 100%; min-width: 0; }
/* Every flex row that can host one. `.crewinvbtns` was the one omission, and it is the row
   that holds "New code" -- so the one confirm in the feature that could not wrap landed
   BESIDE the three buttons instead of under them: 206px of horizontal overflow on a phone,
   with the body auto-scrolled to match and every card pushed off the left edge, and on a
   desktop a right edge 89px past the panel that clipped its own sentence mid-word while the
   three buttons stretched to 117px tall as flex siblings of the confirm. A reviewer measured
   both. A `:has()` list is a list, and a rule that grants wrapping to four of five hosts is
   a rule that breaks the fifth. */
.crewrow:has(> .crewaskslot), .crewpendr:has(> .crewaskslot),
.crewacts:has(> .crewaskslot), .crewtrow:has(> .crewaskslot),
.crewinvbtns:has(> .crewaskslot) { flex-wrap: wrap; }

/* A message anchored to the control that was pressed, rather than at the top of the panel
   five screens away from it. */
/* `text-transform: none`, because the slot is inserted as the input's next sibling, which
   puts it INSIDE the `<label>` -- and `.crewcard label` is uppercase. So the create form
   shouted "GIVE IT A NAME." and "THAT NAME'S TAKEN." while crew settings said the same
   things in sentence case, from the same validator. Settings only escaped it by anchoring
   its server error to the Save button, which was a bug of its own. */
.crewstatusnear { margin: 8px 0 2px; text-transform: none; letter-spacing: normal; }
.crewstatusnear .crewmsg { margin: 0; }
/* Inside the invite-code prompt, above the input it is about. */
.crewaskmsg { margin: 0 0 8px; }

/* The code and its copy button on one line, the button not stretched by the flex. */
.crewinvite { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* The label and the code take the first line together; the two buttons share the second. With
   all four in one wrapping row the second button fell alone onto a line of its own and read as
   an afterthought rather than as the other half of a pair. */
.crewinvite code { flex: 1 0 auto; }
/* The buttons are one item, not two. See the note where they are built: four separate children
   in a wrapping row put the break between the pair. */
.crewinvbtns { display: flex; gap: 8px; flex: 0 1 auto; }
.crewinvite button { flex: 0 0 auto; margin-top: 0; }

/* Narrowing sixteen crews, and the rest of them. */
.crewfilter {
  width: 100%; margin: 2px 0 8px; padding: 9px 10px; font: inherit;
  color: var(--ink, #eef1fb); background: rgba(0,0,0,.25);
  border: 1px solid var(--line, #243055); border-radius: 8px;
}
.crewfilter:focus { outline: none; border-color: var(--sec, #ff8ad8); }
/* The placeholder was the field's only visible label and it was the browser's own grey:
   measured 3.56:1 against the field, 2.31:1 sampled. Every other field in this panel is
   themed down to `::file-selector-button`; this one got colour and background and no
   placeholder. */
.crewfilter::placeholder { color: var(--mut, #9aa6c8); opacity: 1; }
/* The no-match state used to be one em dash, left-aligned, in a card keeping its full
   height -- which reads as a rendering fault rather than as an answer. The sentence is a real
   node now, written by `bindList`, so it can name the query and offer the way back. */
.crewlist[data-empty] { padding: 2px 0 6px; }
.crewempty { display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  color: var(--mut, #9aa6c8); font-size: 13px; }

/* Somebody is waiting on an answer. The count repeats on the shut accordion summary and
   beside the heading, because the list itself used to be 4.6 screenfuls down a panel. */
.crewknock h4 { display: flex; align-items: center; gap: 8px; }
/* Both decisions get a real target. A reviewer measured "No" at 39x44 -- under the 44px
   minimum on its short axis -- sitting 8px from a 56px "Let in", which is a mis-tap waiting
   to happen on the one row where the two outcomes are opposite. The decision is reversible
   (the declined list carries an Undo), so the answer is a bigger target rather than a confirm
   in front of an act that can be taken back. */
.crewknock .crewbtn { min-width: 44px; }
.crewknockn, .crewsumdot {
  min-width: 17px; height: 17px; padding: 0 4px; border-radius: 9px;
  background: var(--sec, #ff8ad8); color: #140a11; text-align: center;
  font: 700 11px/17px Orbitron, ui-sans-serif, sans-serif;
}
.crewsumdot { margin-left: 6px; flex: 0 0 auto; }

/* Handing the pass back, for the five paired states that have no crew card to put it in. */
.crewfoot {
  margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line, #243055);
  display: flex; justify-content: flex-end;
}

/* Where Disband lives now. It was in the same row as Leave crew, same shape, same ghost
   weight, so the control that ends your membership and the control that ends the crew for
   everybody in it were told apart by one word. A rule and a heading, which is the thing
   every reader already knows how to read, and the heading carries the same pink the button
   does so the whole block reads as one warning rather than a section with a red button in it.
   `h4` only takes a colour here: `.crewcard h4` above sets the size, the spacing and the
   caps, and this block has no reason to disagree with the other headings about those. */
.crewdanger {
  margin-top: 14px; padding-top: 10px; border-top: 1px solid rgba(255,122,156,.28);
}
.crewdanger h4 { color: #ff7a9c; }

/* --- standings, contributors, folded personal crew --- */

.crewboard { padding-bottom: 8px; }
/* the emblem sits in the site's own podium slot, so crews read like every other board */
.crewpodemb { width: 100%; height: 100%; object-fit: contain; }
/* The map gives every emblem a dark plate so a crew's own colour cannot read through it. The
   podium did not, so a cream emblem sat straight on the gold first-place gradient. */
.crewboard .podic {
  width: 46px; height: 46px; padding: 3px; box-sizing: border-box; border-radius: 0;
  background: rgba(4, 8, 16, .9); border: 1px solid rgba(255, 255, 255, .32);
  box-shadow: 0 2px 10px rgba(0, 0, 0, .6);
}
/* and the three cards were centred inside a full-width table, so nothing lined up with the
   ranked rows underneath them */
/* One row, one baseline. Nothing set this, so second and third sat five pixels apart from
   each other for no reason, and first stepped up by a different amount again. */
.crewboard .podium { gap: 10px; padding: 8px 0 12px; align-items: flex-end; }
/* flex-end aligns the margin box, and the host hands first place an 18px bottom margin, so
   the gold card floated while second and third agreed to two pixels. Zeroing it lined the
   three up and took away the only thing that said first place was first, so the step comes
   back as height rather than as a margin: all three sit on one baseline and the winner's
   card is taller. */
.crewboard .pod.p1 { margin-bottom: 0; padding-top: 20px; }

/* The figure, not its unit. "122" and "squares" were set at one size, so a third of the pink
   on the podium was a word the card heading already implies, and every hero wrapped.

   `inline-block`, not `block`. As a block the 26px numeral took the whole 113px line and the
   11px unit dropped underneath it, so the gold card read "122.0" / "points" on two rows --
   two of the seven rows that made it 230px tall against a 194px silver. Inline-block keeps
   the number's own line-height off the 11px text around it while letting the unit sit beside
   it; `vertical-align: baseline` is what puts the two on one baseline rather than hanging the
   small word off the numeral's middle. The pair still WRAPS where a locale needs it to --
   nothing here is nowrap, and these cards are `overflow: hidden`, so a nowrap would cut. */
.crewboard .pod .km { font-size: 11px; letter-spacing: .2px; }
.crewboard .pod .crewpodn { font-size: 26px; font-weight: 700; display: inline-block;
  line-height: 1.05; letter-spacing: 0; vertical-align: baseline; }
.crewboard .pod { max-width: none; flex: 1 1 0; }

/* Your own crew is folded away once you have one: you know what crew you are in, and the
   board is what you came to look at. It springs open while a join is still pending, which is
   the one state that needs attention. */
.crewmine-wrap { border: 1px solid var(--line, #243055); margin: 0 0 11px; }
.crewmine-wrap > summary {
  display: flex; align-items: center; gap: 10px; padding: 10px 12px; cursor: pointer;
  font-size: 13px;
  /* The containing block for `.crewsumsep` below. Without it that separator resolves against
     `#panel` and lands 348px past its bottom edge, which gives the panel a phantom scroll
     range -- and `reveal()`'s scrollIntoView then slices the top off the panel title and the
     close button, with no scrollbar to put it back. */
  /* `sticky`, which is also a containing block, so the note above still holds.
     The card deliberately has no heading of its own -- this summary names the crew eighteen
     pixels above it, and having both made the panel read "Peripherique" and then
     "PERIPHERIQUE" -- which left a card several screens long on a phone saying "4 riders .
     you run it" with no identity anywhere on it once you scrolled past this. Sticking it
     keeps the name without printing it twice. Needs its own background, or the rows scroll
     through it, and a z-index above the cards it slides over.
     Declared HERE rather than in a rule of its own: the cascade checker caught the second
     rule cancelling this one's `position` and refused the stylesheet, which is exactly what
     it is for. */
  /* It scrolls away. This was sticky so a long card would not leave you wondering whose crew
     you were reading -- a real problem, and not worth a bar pinned over your content on every
     scroll, which is what Erwin kept noticing. The card opens with the emblem, the standing
     and the member line directly under this, so the identity is still the first thing in it. */
  z-index: 3;
  background: var(--pan, #0b1020);
}
.crewmine-wrap > summary::-webkit-details-marker { display: none; }

/* ---------- the pinned card ----------
   The summary carries three things now: the crew, the figure the board ranks it on, and the
   two controls a member actually reaches for. It wraps, so the buttons take a line of their
   own at any width rather than squeezing the name -- the name's own `min-width: 8.5em` above
   is what the measuring in this file has repeatedly come back to. */
.crewmine-wrap > summary { flex-wrap: wrap; }
/* The standing, on your own crew, where it was missing: the card showed a rank and never the
   number the rank comes from. Tabular so it does not jitter between redraws.
   No `nowrap`. It would have been defensible -- `flex: 0 0 auto` in a wrapping row means a
   figure that does not fit moves to its own line rather than being cut -- but defensible is
   not measured, and this file's rule is that a `nowrap` ships with a measurement beside it or
   it does not ship. Allowed to wrap, the worst case is "122,0" over "Punkte", which is ugly
   and readable; `nowrap` on a wrong guess is a word with its end missing. */
.crewpinpts {
  margin-left: auto; flex: 0 0 auto; font-weight: 700; font-size: 13px;
  color: var(--sec, #ff8ad8); font-variant-numeric: tabular-nums;
}
/* What the crew says about itself, on its own line under the name. `1 0 100%` so it always
   takes a line rather than sharing one with the standing, and `order` keeps it above that
   line whatever the source order is. Wraps: it is up to 280 characters of a rider's own
   prose and there is no honest width to cut it at. */
.crewsumdesc {
  flex: 1 0 100%; order: 1; font-weight: 400; font-size: 12px;
  color: var(--mut, #9aa6c8); line-height: 1.4; overflow-wrap: anywhere;
}
.crewsumrole { order: 2; }

/* ---------- the other crews, on a member's panel ----------
   Facts, no Join: `join()` refuses while any membership exists, so a Join here could never be
   pressed, and a column of permanently dead buttons is what this feature was already caught
   doing once.

   The row is no longer one control. It was, and it scrolled to this crew's row on the board
   behind a bare `aria-hidden` chevron -- so the one glyph that looked like a disclosure opened
   nothing, and the destination was a second copy of facts this row can hold itself. The title
   lights their ground on the map, the fold under it opens into who rode for them, and the
   pointer and the focus ring belong to those two rather than to the whole strip. */
.crewrivals h4 { margin-top: 0; }
.crewrivrow {
  display: flex; align-items: flex-start; gap: 9px; padding: 7px 2px;
  border-radius: 4px;
}
.crewrivrow + .crewrivrow { border-top: 1px solid rgba(255,255,255,.06); }
/* The swatch sits on the name's line rather than at the top of a four-line block -- the same
   reading `.crewrow` records for its 46px emblem, one card up. */
.crewrivrow > .crewsw { margin-top: 2px; }
/* `min-width: 0` so a long crew name gives way instead of pushing the chevron off the card
   -- the same fix three other rows in this file carry. */
.crewrivtx { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
/* The name WRAPS. An ellipsis was the first instinct, and `.crewrowname` on the board does
   exactly that -- but that one ships with a measurement, and this one could not get an
   honest reading: the two-column split is a viewport media query, so shrinking the panel to
   a phone's width in a desktop window leaves the card at 218px and measures a layout no
   phone ever shows. A crew's name is the one thing on this row somebody is reading it for,
   these rows are already two lines tall, and a name that takes three is cheaper than a name
   with its end cut off. `anywhere`, because the longest single token in a crew name is
   whatever a rider typed. */
.crewrivtx b { font-size: 12.5px; overflow-wrap: anywhere; }
/* A flex row with a gap, which is how the join list next door has separated its facts
   since the round that measured the alternative: a middot between two facts sits at one line
   edge or the other once the row wraps, and it appeared on 6 of 6 rows at 390. This line was
   still a single string joined with " \00b7 " -- the older form -- and two of its facts are
   controls now, which is the other reason the dots had to go: a chevron beside a middot is
   two glyphs arguing about which of them is the one to press. */
.crewrivsub { font-size: 11px; color: var(--mut, #9aa6c8); line-height: 1.35;
  display: flex; flex-wrap: wrap; column-gap: 9px; row-gap: 1px; }

/* ---------- the title, and where the crew stands ----------
   The pressable part of a row in either list: the crew's name with its position and movement
   beside it. It carries the pointer and the focus ring the whole row used to carry, which is
   now the honest place for them -- the row holds a second control, and a focus ring around
   both of them says one thing is focused when two can be.

   `min-width: 0` on the name so a long one gives way instead of shoving the position off the
   card, the same fix four other rows in this file carry. The position does NOT shrink: it is
   four characters and an arrow, and a wrapped "2nd" is not a position. */
.crewrivhead {
  display: flex; align-items: baseline; gap: 7px; cursor: pointer;
  border-radius: 4px; padding: 1px 2px; margin: -1px -2px;
}
/* `min-width: 0` so the name gives way to the position rather than pushing it off the card,
   and `anywhere` because the name is now a FLEX ITEM and no longer a block filling the row.
   `.crewrown b` contains its overflow with `overflow: hidden`, but the phone media query
   turns that off so the name can wrap instead of being cut -- which leaves an unbreakable
   41-character token with nothing holding it in. This is the same answer `.crewrivtx b`
   already carries, for the reason recorded there: the longest single token in a crew name is
   whatever a rider typed. */
.crewrivhead > b { min-width: 0; overflow-wrap: anywhere; }
.crewrivhead:hover { background: rgba(255,255,255,.05); }
.crewrivhead:focus-visible {
  outline: none; box-shadow: 0 0 0 2px #0b0f1c, 0 0 0 4px var(--sec, #ff8ad8);
}
/* Tabular, so a column of positions lines up, and `nowrap` because "2nd" with the arrow
   wrapped under it reads as two facts. `.crewmoved` inside it keeps its own colours. */
.crewstand {
  flex: 0 0 auto; font-size: 11px; color: var(--mut, #9aa6c8); white-space: nowrap;
  font-variant-numeric: tabular-nums; display: inline-flex; align-items: baseline; gap: 4px;
}
/* The join list reaches this through `.crewrown > span`, which sets `font-size: 11px` and the
   muted colour on every direct span -- correct for the facts line and wrong for a line whose
   first child is the crew's name. The name takes its own size back from `.crewrown b`; this
   only has to stop the head inheriting the facts line's wrap and gaps. */
.crewrown > span.crewrivhead { column-gap: 7px; row-gap: 0; }

/* ---------- the two folds, opened from the counts on the facts line ----------
   The control the old chevron was mistaken for, doing what it was mistaken for doing -- and
   now sitting ON the number it is about rather than on a line of its own under it. It is a
   flex ITEM in the facts line, so it takes that line's gap like every other fact and needs no
   spacing of its own; the padding is here to give an 11px word a thumb to be pressed with,
   and the negative margin cancels it so the line still breaks where the gap says.

   The dotted underline is on the label and not on the button, which is why `whoBtn` wraps the
   words: an underline across the chevron reads as one 13px glyph sitting in a gap. */
.crewwho {
  display: inline-flex; align-items: center; gap: 4px;
  background: none; border: 0; padding: 2px 4px; margin: 0 -4px; border-radius: 4px;
  font: inherit; font-size: 11px; color: var(--acc, #2ea8ff); cursor: pointer;
}
.crewwho > span { text-decoration: underline dotted; text-underline-offset: 2px; }
.crewwho:hover { background: rgba(46,168,255,.1); }
/* ---------- Biggest patches, the fold beside it ----------
   Rows of facts about ground, so they are laid out like the facts line in the row above:
   flex items with a gap, which breaks BETWEEN facts and has no separator glyph to strand at
   the start or end of a wrapped line. The note on `.crewfact` records that reasoning at
   length; this is the same answer for the same reason. */
.crewprow {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 2px 8px; width: 100%;
  background: none; border: 0; border-radius: 4px; padding: 3px 4px; margin: 0 -4px;
  font: inherit; font-size: 11px; color: var(--mut, #9aa6c8); text-align: left;
  cursor: pointer;
}
.crewprow:hover { background: rgba(255,255,255,.06); color: var(--ink, #e8edfb); }
.crewprow:focus-visible {
  outline: none; box-shadow: 0 0 0 2px #0b0f1c, 0 0 0 4px var(--sec, #ff8ad8);
}
/* The ordinal, which is the patch's number in the walk the popup's Next patch follows. Fixed
   width so the facts beside it line up down the fold rather than stepping in and out with
   the digit. */
.crewpatn {
  flex: 0 0 auto; min-width: 1.1em; font-style: normal; font-variant-numeric: tabular-nums;
  color: var(--acc, #2ea8ff);
}
.crewpatf { flex: 0 0 auto; white-space: nowrap; font-variant-numeric: tabular-nums; }
.crewpatf:first-of-type { color: var(--ink, #e8edfb); }
/* A rider's name in Best riders, which flies to where they ride. The same treatment as the
   crew's name in the popup and for the same reason: it is inside a line of text, where a
   `<button>` would inherit none of the type and all of the chrome. */
.crewcfly { cursor: pointer; border-radius: 3px; padding: 0 2px; margin: 0 -2px; }
.crewcfly:hover { background: rgba(255,255,255,.08); text-decoration: underline; }
.crewcfly:focus-visible {
  outline: none; box-shadow: 0 0 0 2px #0b0f1c, 0 0 0 4px var(--sec, #ff8ad8);
}
.crewwho > i { font-style: normal; font-size: 13px; line-height: 1; transition: transform .16s; }
.crewwho[aria-expanded="true"] > i { transform: rotate(90deg); }
.crewwhopan { margin-top: 4px; }
.crewwhopan[hidden] { display: none; }
/* The fetch lasts about a tenth of a second, so this is three dots and no words -- a state
   with nothing to translate. */
.crewwhospin {
  display: block; width: 26px; height: 6px; margin: 6px 0;
  background: radial-gradient(circle 3px at 3px 3px, var(--mut, #9aa6c8) 95%, transparent 0)
              0 0 / 10px 6px repeat-x;
  animation: crewwhodots 1s linear infinite;
}
@keyframes crewwhodots { 0% { opacity: .3 } 50% { opacity: 1 } 100% { opacity: .3 } }
.crewwhonone { margin: 4px 0 2px; font-size: 11px; }
/* The list inside the fold is the same component the crew's own card uses, at the size this
   row can afford: no heading margin fighting the button above it. */
.crewwhopan .crewcontrib { margin-top: 0; }
/* The heading was hidden because the button above it said "Best riders". The button says "7
   riders" now, so the panel has to name the list it opened: these are the five who rode the
   most, not the seven the count was about. Smaller and tighter than `.crewcard h4`, because
   it heads a fold inside a row and not a card. */
.crewwhopan h4 { margin: 0 0 3px; font-size: 10px; }
/* The riders fold gets its hairline from `.crewcontrib`; this is the same line for the other
   one, so two open folds read as two sections rather than as a drift of rows. */
.crewpatpan { border-top: 1px solid var(--line, #243055); padding-top: 6px; }
.crewcmore { margin-top: 4px; }
/* `flex: 1` is `flex: 1 1 0%` -- a zero basis, so under any pressure this collapsed to its
   longest word while `.crewsumrole` beside it, at `flex: 0 1 auto`, kept the full width of
   its 48-character metadata string. Measured at 390: the crew's name got 61px and wrapped
   mid-name, the metadata got 129px. Two reviewers measured it separately.
   `auto` basis here and a floor, so the name takes its natural width first and the standings
   line gives way -- which is what the comment on `.crewsumrole` always claimed. */
/* The name and the standings line are one block beside the emblem now, so the row reads
   [image][name / standing][badge] instead of letting the standing fall out to the summary's
   own left edge when it wrapped. Wide enough and the two sit side by side; narrower and the
   standing drops to a second line that still starts where the name starts. */
/* `1 1 0`, not `1 1 auto`, now that the summary itself wraps.
   The comments above are about the row BEFORE it wrapped, where an `auto` basis was what
   stopped the name collapsing. With `flex-wrap: wrap` on the summary an `auto` basis means
   this block's hypothetical size is its content -- long, because the metadata line under the
   name is a sentence -- so it no longer fit beside the emblem and took a line of its own,
   leaving a 26px emblem alone on the line above it. Measured at a real 390px viewport: 177px
   of summary, the emblem orphaned, the figure on a third line.
   A zero basis puts the emblem, this block and the figure back on one row and the buttons on
   the next: 128px. The fear the older comment records does not come back -- the name is never
   cut (`scrollWidth === clientWidth` in every case tried), it keeps the full 149px column
   rather than collapsing to its longest word, and a 27-character name wraps to two lines
   inside that column, which is 147px of summary. The German metadata line changes none of
   those numbers. */
.crewsumtxt {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-wrap: wrap; align-items: baseline;
  column-gap: 8px; row-gap: 1px;
}
/* `auto` basis and a floor, not `flex: 1`. `flex: 1` is a zero basis, so under any pressure
   the name collapsed to its longest word while `.crewsumrole` beside it, at `flex: 0 1 auto`,
   kept the full width of its 48-character metadata string: measured at 390 the name got 61px
   and wrapped mid-name, the metadata got 129px. Two reviewers measured it separately. The
   name takes its natural width first and the standings line gives way, which is what the
   comment on `.crewsumrole` always claimed. */
.crewsumtxt > span:first-child {
  flex: 1 1 auto; min-width: 8.5em; font-weight: 700; }
.crewsumemb { width: 26px; height: 26px; flex: 0 0 26px;
  transition: transform .16s ease; transform-origin: left top; position: relative; }
/* Big enough to see what somebody drew. `transform` only, so the line it sits in does not
   move; origin at the left so it opens into the card rather than off the panel's edge; and
   a z-index because it has to come up over the card beneath it.
   Focus as well as hover: an emblem is the one thing on this card worth looking at, and a
   mouse is not the only way people read. */
.crewmine-wrap[open] > summary .crewsumemb { cursor: pointer; }

/* The sheet a click opens. Centred, because it is one object being looked at. */
.crewsheet { text-align: center; --sheet-sq: min(54vw, 190px); }
.crewsheet > p { color: var(--mut, #9aa6c8); margin: 0 0 14px; }
/* The wrapper is what centres now; the image keeps its size and gives up its margins, so
   the letters have something positioned to sit in. */
.crewsheetembb { position: relative; display: block; width: var(--sheet-sq);
  margin: 2px auto 14px; line-height: 0; }
.crewsheetembi {
  position: absolute; left: 0; right: 0; top: 0; bottom: 0;
  display: flex; align-items: center; justify-content: center; pointer-events: none;
  font-family: ui-monospace, Menlo, Consolas, monospace; font-weight: 700;
  font-size: var(--embi, 28px); line-height: 1; letter-spacing: 1px;
  color: #fff; text-shadow: 0 2px 6px #000, 0 0 16px #000, 0 0 4px #000;
}
.crewsheetemb {
  width: var(--sheet-sq); height: var(--sheet-sq); object-fit: contain;
  display: block; margin: 0; border-radius: 3px;
  background: rgba(4, 8, 16, .9); border: 1px solid rgba(255, 255, 255, .22);
  box-shadow: 0 10px 34px rgba(0, 0, 0, .6); padding: 5%; box-sizing: border-box;
}
.crewsheetfig { font-family: "Orbitron", ui-sans-serif, sans-serif; font-size: 26px;
  line-height: 1.1; letter-spacing: .5px; }
.crewsheetfig span { font-size: 13px; font-weight: 400; color: var(--mut, #9aa6c8); }
.crewsheetsub { font-size: 13px; color: var(--mut, #9aa6c8); margin-top: 3px; }
.crewsheetqr { margin-top: 16px; }
.crewsheetqr .crewqrg { margin: 0 auto; width: var(--sheet-sq); }
.crewsheetqr + .crewshareu { margin: 6px 0 0; }
.crewsheetacts { justify-content: center; flex-wrap: wrap; margin-top: 12px; }
.crewinvbox { margin: 10px 0 2px; padding: 9px 10px 8px; border-radius: 9px;
  background: rgba(255,138,216,.08); border: 1px solid rgba(255,138,216,.22); }
.crewinvhead { font-size: 11px; letter-spacing: .4px; text-transform: uppercase;
  color: var(--sec, #ff8ad8); margin-bottom: 6px; }
/* The sentence between the heading and the address. `.hint` already sets the colour and the
   size; this only takes the margin back off, because a `<p>` in this box was pushing the
   address row 1em down from a heading that is 6px above it. */
.crewinvp { margin: 0 0 7px; }
.crewinvrow { display: flex; align-items: center; gap: 6px; }
/* A real input, so the address can be selected, dragged and read by a password manager the
   way any other address can. Read-only rather than disabled: disabled text cannot be
   selected, which is the one thing this field is for. */
.crewinvurl { flex: 1 1 auto; min-width: 0; font-size: 12px; padding: 6px 8px;
  border-radius: 6px; border: 1px solid var(--line, #243055); background: rgba(0,0,0,.28);
  color: var(--ink, #e8edfb); font-family: ui-monospace, Menlo, Consolas, monospace; }
.crewinvurl:focus { outline: 2px solid var(--sec, #ff8ad8); outline-offset: 1px; }
/* 34px of target around an 18px mark. Labelled by title AND aria-label, because a drawn
   button with neither is one nobody can name. */
.crewicon { flex: 0 0 auto; width: 34px; height: 34px; display: flex; align-items: center;
  justify-content: center; padding: 0; border-radius: 7px; cursor: pointer;
  background: rgba(255,255,255,.06); border: 1px solid var(--line, #243055);
  color: var(--ink, #e8edfb); transition: background .15s, color .15s; }
.crewicon svg { width: 18px; height: 18px; }
.crewicon.sm { width: 28px; height: 28px; }
.crewicon.sm svg { width: 15px; height: 15px; }
.crewicon:hover, .crewicon:focus-visible { background: rgba(255,138,216,.2);
  color: var(--sec, #ff8ad8); }
.crewinvspoken { display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin: 8px 0 0; }
/* Everything you press once a year, or once ever, behind one fold. */
.crewmanage { margin-top: 12px; border-top: 1px solid var(--line, #243055); padding-top: 8px; }
.crewmanage > summary { cursor: pointer; padding: 8px 0; font-size: 11px;
  letter-spacing: .5px; text-transform: uppercase; color: var(--mut, #9aa6c8);
  list-style: none; display: flex; align-items: center; min-height: 44px; }
.crewmanage > summary::-webkit-details-marker { display: none; }
.crewmanage > summary::before {
  content: ""; width: 0; height: 0; margin-right: 7px; flex: 0 0 auto;
  border-left: 5px solid currentColor; border-top: 4px solid transparent;
  border-bottom: 4px solid transparent; transition: transform .18s;
}
.crewmanage[open] > summary::before { transform: rotate(90deg); }
.crewmanage > summary:hover { color: var(--ink, #e8edfb); }
/* No left border. `.hint` carries one app-wide, which reads as a callout rule beside a
   paragraph -- and this one is a centred caption under a QR code, so the rule landed as a
   stray blue bar floating in the margin with nothing to the left of it. */
.crewsheetqr .hint { margin-top: 8px; border-left: 0; padding-left: 0; text-align: center; }
/* Shut, this is what identifies the crew. Open, the card's own 52px emblem is 50px below it
   and they are the same picture twice -- the duplicated NAME was taken out of the card for
   this reason and the emblem was one element along. */
/* The emblem stays when the card opens. It was hidden because the card below showed a bigger
   one and two of the same picture in one column is one too many -- so the card's went instead,
   and this is the one that keeps it, because this is the line that names the crew. */
/* this one is wanted at every width: the name and the role share one line */
/* For the text layer only. The name span is `flex: 1`, so a visible separator is dragged
   to the far right and reads as an orphan bullet in front of the role -- measured 440px from
   the word it separates. The flex gap already does the job on screen; this keeps a screen
   reader and a copy-paste from getting "Peripheriqueleader". */
.crewsumsep {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
/* Not a badge any more: it carries the standing, the member count and your role, so it is a
   line of text and wants to read as one. No uppercase, no tracking, and it may shrink before
   the crew's name does. */
.crewsumrole { font-size: 11px; color: var(--mut, #9aa6c8); text-align: left;
  min-width: 0; flex: 0 1 auto; }
/* No border at all: the wrapper draws one on all four sides and the card sits inside it.
   The 2px accent that used to be here ran the full height of the tallest card in the
   panel, which is a pull quote however the rest of it is styled. */
.crewmine-wrap .crewcard { border: 0; margin: 0; }

.crewcontrib { margin-top: 12px; border-top: 1px solid var(--line, #243055); padding-top: 8px; }
.crewcrow { display: flex; align-items: center; gap: 7px; padding: 4px 0; font-size: 12px; }
/* `display: flex` above is author CSS and `[hidden]`'s `display: none` is the user agent's, so
   the author rule wins and a folded row goes on taking its height -- the same trap `.crewrow`
   fell into with fifteen of twenty-one rows, recorded two hundred lines above. The capped
   contributors list hides its tail this way, so without this "Show 3 more" would reveal rows
   that were never hidden. */
.crewcrow[hidden] { display: none; }
.crewcrow .av { width: 20px; height: 20px; flex: 0 0 20px; border-radius: 50%; }
/* A fixed column, so every bar starts at the same x. Sized to the name, each bar began
   where that rider's name happened to end and three bars had three origins, which is the one
   thing a bar chart may not do. */
.crewcname { flex: 0 0 42%; max-width: 42%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* a bar rather than a second number: the shape of who carries the crew reads at a glance */
.crewcbar { flex: 1; height: 5px; background: rgba(255,255,255,.07); min-width: 24px; }
/* `min-width` in PIXELS so a 1% rider still draws something. The width used to be floored
   at 2% in the script, which made the bar disagree with the figure printed beside it on
   exactly the rows where somebody is checking whether they count. Three pixels is visible
   and claims nothing. */
.crewcbar i { display: block; height: 100%; background: var(--sec, #ff8ad8); min-width: 3px; }
/* A fixed width, so the bar beside it is the same length on every row. Sized to its own
   text, "419 km 41%" and "31 km 3%" gave four contributor tracks of 238, 238, 234 and
   249px -- four different 100% reference lines and a 15px ragged right edge. */
.crewckm { flex: 0 0 9.5ch; text-align: right; font-variant-numeric: tabular-nums;
  color: var(--mut, #9aa6c8); }
.crewrole { margin-left: 5px; font-size: 11px; }
.crewrole.lead { color: #ffd24a; }
.crewrole.off { color: #8ef0c0; }
.crewrole.past { color: var(--mut, #9aa6c8); }

/* --- phone --- */


/* --- picking an identity --- */

/* Swatches, not a dropdown of hex codes. The thing being chosen is the thing that ends up on
   the map, so it is shown as it will look, pattern and all. */
/* A block, not a flex row. The preview used to be a 62px item beside a 330px grid, so about
   280px of empty gutter ran down the whole picker and the grid had 218px to lay 44px cells in
   -- four columns and six rows. The preview sits on the label line now and the grid gets the
   full width. */
.crewidentrow { display: block; margin: 12px 0 0; }
.crewpreview { flex: 0 0 auto; }
.crewpreview .crewsw { border-width: 2px; }

/* ---------- the two pinned ends of the create form ----------
   Sixty swatches and a select sit between the name field and the button, which on a phone
   puts the submit below the fold and the preview above the top of it: you pick a pattern
   with the thing it is drawn on off screen, then go looking for Create crew. Both ends are
   pinned to the card instead.
   `.pbody` is the scrolling ancestor and nothing between it and here clips, so `sticky`
   resolves against the panel's own scroll port. The card is one of six stacked in that port,
   so each strip sticks only while its own card is on screen, which is the behaviour wanted:
   neither follows you into the join list below. */
.crewnewtop {
  position: sticky; top: -14px; z-index: 2;
  display: flex; align-items: center; gap: 11px;
  /* -15px to the sides and the padding back on, so the strip's background reaches the card's
     border rather than stopping 15px short of it and letting swatches slide through the gap
     as they pass underneath. */
  margin: 0 -15px 12px; padding: 10px 15px;
  border-bottom: 1px solid var(--line, #243055);
  /* Opaque. The card's own background is 3.5% white over a blurred panel, so a translucent
     strip showed the grid moving through the preview it is there to hold still. */
  background: #121a33;
}
.crewnewtopw { display: flex; flex-direction: column; min-width: 0; gap: 2px; }
/* The name as typed, which can be 28 characters of anything. */
.crewnewtopw b { font-size: 14px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.crewnewtopw span { font-size: 11px; color: var(--mut, #9aa6c8); letter-spacing: .3px; }
/* And the other end. `bottom: -14px` cancels the card's own bottom padding the same way the
   strip above cancels its top, so the button sits on the card's edge while it is pinned and
   lands in its normal place when the card's end comes into view. */
.crewnewgo {
  position: sticky; bottom: -14px; z-index: 2;
  margin: 12px -15px 0; padding: 10px 15px;
  border-top: 1px solid var(--line, #243055); background: #121a33;
}
.crewnewgo .crewbtn { width: 100%; }
.crewpickc, .crewpickp {
  /* the grid counts the columns from the width there is, so the chip takes the cell it got
     rather than sitting in the corner of it */
  width: 100%; aspect-ratio: 1; padding: 0; border: 1px solid rgba(255,255,255,.22);
  cursor: pointer; background-clip: padding-box;   /* square: see the radius block below */
}
.crewpickp { background: transparent; display: flex; align-items: center; justify-content: center; }
.crewpickp .crewsw { width: 20px; height: 20px; border: 0; }
/* Two rings, not one. `outline: 2px solid #fff` is invisible on seven of the twenty-four
   swatches -- `#fffac8`, `#fabebe` (the default), `#aaffc3`, `#ffd8b1`, `#e6beff`, `#a9a9a9`
   measure about 1.1:1 against white -- so on a third of the palette, on the one grid whose
   only state channel IS the ring, a reviewer could not tell what was selected without
   reading the DOM. A dark ring inside a light one reads on both halves. */
.crewpickc.on, .crewpickp.on {
  outline: 2px solid #fff; outline-offset: 1px;
  box-shadow: 0 0 0 1px #0b0f1c inset, 0 0 0 4px rgba(11,15,28,.9);
}
/* Focus needs a channel of its own. Without a rule here the user agent drew a white outline
   in the same place on the same property as `.on`, so tabbing the palette left two chips
   ringed identically and nothing said which one was the crew's colour -- on the one grid
   whose only state channel IS the ring. Pink, and a shadow rather than an outline, so the
   two can be on the same chip at once and still be told apart. */
.crewpickc:focus-visible, .crewpickp:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px #0b0f1c, 0 0 0 4px var(--sec, #ff8ad8);
}
.crewpickc.on:focus-visible, .crewpickp.on:focus-visible {
  outline: 2px solid #fff; outline-offset: 1px;
  box-shadow: 0 0 0 1px #0b0f1c inset, 0 0 0 4px var(--sec, #ff8ad8);
}
/* `:not(.on)`, because these two are both 0-2-0 and neither is a subset of the other, so the
   later one simply won: resting the pointer on the colour you had just chosen dropped its 2px
   ring to the 1px ring every other cell shows, and on a 24-cell grid whose only state channel
   is that outline the feedback at the moment of choosing was nothing at all. */
.crewpickc:not(.on):hover, .crewpickp:not(.on):hover {
  outline: 1px solid rgba(255,255,255,.6); outline-offset: 1px;
}




/* --- consistency with the host panel --- */

/* The squares belong on the map, not in the panel. Cards sitting at radius 0 inside a 12px
   panel next to 7px tabs read as unfinished rather than as deliberate. */
.crewcard, .crewmine-wrap, .crewhow { border-radius: 10px; }
/* One box with a lid, not two rounded boxes stacked. Shut, the summary is the whole box and
   takes all four corners; open, its bottom two go flat and the card's top two go flat to meet
   them. Before this the title bar ended on a straight line and the card below it curved away,
   which is what a disclosure looks like when the radius was put on the parts rather than on
   the shape.
   9px inside the wrapper's 1px border: an inner corner drawn at the outer radius runs at a
   different rate from the border around it and leaves a lit sliver in the corner. */
.crewmine-wrap > summary { border-radius: 9px; }
.crewmine-wrap[open] > summary { border-radius: 9px 9px 0 0; }
.crewmine-wrap > .crewcard { border-radius: 0 0 9px 9px; }

/* kept square on purpose: these ARE the map's language */
.crewsw, .crewpickc, .crewpickp, .crewemb img, .crewembsm { border-radius: 0; }
/* the crew's own emblem at name size, in the standings rows and the join list */
.crewembsm { flex: 0 0 auto; vertical-align: -3px; object-fit: cover;
  box-shadow: 0 0 0 1px rgba(255,255,255,.14); }

/* a colour and pattern on every ranked row: this is a feature about identity and rows 4+
   carried none of it */
.crewrank .celln .crewsw, .crewboard table .celln .crewsw { flex: 0 0 auto; }

/* the colour grid was ragged; 12 to a row reads as a palette */
/* No `max-width`. It capped the grid at 353px, so in a 652px card at 1280 the picker was a
   strip of twelve 24.8px chips with 299px of empty gutter beside it -- the same dead gutter
   the preview used to make, moved one element along. The chips have a floor, so a wide card
   gets bigger swatches rather than more of them. */
.crewpick { display: grid; grid-template-columns: repeat(auto-fill, minmax(34px, 1fr));
  gap: 5px; margin: 5px 0 9px; }
/* The same track function as the colours, so the two grids share a column. `repeat(6, 24px)`
   against `auto-fill minmax` gave them pitches of 24 and 24.83px, which drifted 1.7px apart
   by the fourth chip and read as a rendering wobble. */
/* No cap. This survived the one removed from `.crewpick`, so in the create form four
   24x24 pattern swatches sat under a row of 36x36 colour swatches -- the same control at two
   sizes, and the same control again at full size in crew settings. */
.crewidentl .crewpreview { margin-left: auto; line-height: 0; }

/* A count, because a two-line box holding 280 characters still scrolls. */
.crewcount { display: block; margin-top: 2px; font-size: 11px; color: var(--mut, #9aa6c8);
  font-variant-numeric: tabular-nums; text-align: right; }
/* Past the limit, not merely at it. The name field used to clip on paste, so "28/28" was the
   only thing a reader got for a 36-character name and it reads as a full field rather than a
   refused one. The field accepts the overflow now and this is what says so, beside the rule
   spelled out under it. */
.crewcount.over { color: #ff8ad8; font-weight: 600; }

/* "Your crew" beside your own row in a list about personal contribution. The row tint is the
   same one the board uses for your crew, so one visual language covers both. */
/* A finger-sized target around a small mark.
   These emblems are 16px in a board row and 26px in a browse row, which is the picture's size
   and should stay it: the mark is meant to read as part of the name, not as a button. So the
   TARGET grows and the image does not -- `content-box` so the inline width/height keeps meaning
   the picture, padding to reach 44, and a matching negative margin so nothing in the row moves.
   Sized per context because one padding cannot serve a 16px mark and a 26px one, and a target
   wider than its row steals taps from the row itself. */
.crewembgo { box-sizing: content-box; }
.crewboard .crewembgo { padding: 14px; margin: -14px; }
.crewrow .crewembgo, .crewlist .crewembgo { padding: 9px; margin: -9px; }

.crewyou { font-size: 10px; letter-spacing: .5px; text-transform: uppercase;
  color: var(--sec, #ff8ad8); font-weight: 700; }

/* The crew's mark in settings, with the control that removes an upload sitting ON it rather
   than as a ghost button beside Save. 44px of target for a 20px glyph, because it throws away
   something a leader had to make. */
/* The emblem's own caption. Same weight as the field labels around it, because that is what
   it is -- the thing it labels is a picture rather than an input, which is the entire point:
   the word "Emblem" used to BE the file control, so pressing it opened a picker. */
.crewembhd { margin: 11px 0 0; font-size: 11px; letter-spacing: .5px;
  text-transform: uppercase; color: var(--mut, #9aa6c8); }
/* Tight under the caption, and the hint under it tighter still, so the three read as one
   block rather than three unrelated rows. */
.crewembhd + .crewlogo { margin-top: 5px; }
.crewlogo + .hint { margin: 2px 0 0; }

.crewlogo { position: relative; display: inline-block; margin: 8px 0 2px; line-height: 0; }
.crewlogo .crewembsm { border-radius: 3px; }
.crewlogox {
  position: absolute; top: -11px; right: -11px;
  /* 44px of target around a 22px disc: the glyph is small because the emblem is 52px and a
     big X would read as part of the picture, but the thing it does is not undoable. */
  width: 44px; height: 44px; display: flex; align-items: center; justify-content: center;
  background: none; border: 0; padding: 0; cursor: pointer;
  line-height: 1; font-size: 14px; color: var(--mut, #9aa6c8);
}
/* The disc is drawn on a span-less button, so it is a radial background rather than a
   pseudo-element: `::before` would paint over the glyph, which is a text node with no
   stacking context of its own. */
.crewlogox {
  background-image: radial-gradient(circle at 50% 50%,
    rgba(8,12,26,.94) 0 10.5px, rgba(36,48,85,1) 10.5px 11.5px, rgba(0,0,0,0) 11.5px);
}
.crewlogox:hover, .crewlogox:focus-visible { color: #ff8ad8; }

/* The line under the board for a crew with no rank yet. A crew holding nothing has no row, so
   the standings ran 1 to 15 and never mentioned the reader's own crew -- see the note at the
   call site. Quiet, because it is not a ranking: it is the board saying where you are not. */
.crewboardmine { display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--line, #243055);
  font-size: 12px; line-height: 1.35; color: var(--mut, #9aa6c8); }
.crewboardmine b { color: var(--ink, #e8edf8); font-weight: 600; }
.crewcard textarea { resize: vertical; min-height: 46px; }

/* patterns have to be separable at swatch size -- all twelve are defined in one block
   further up, beside `.crewsw` itself */

/* `#cf-patterns { grid-template-columns: repeat(6, 24px) }` used to sit here, directly under
   the comment above explaining that the cap had been taken off this very grid. It had not:
   the comment described the fix and the rule put it straight back, so the create form drew
   24px pattern chips under 36px colour chips while crew settings drew both at full size.
   Gone, so `.crewpick` governs both grids and the two share a pitch. */

/* ---------- a pair another crew already flies ----------
   Not hidden and not removed: forty-eight colours with gaps in the grid reads as a rendering
   fault, and a founder who liked one of them deserves to know it is spoken for rather than
   wonder where it went. So it stays in place, drained and crossed, with the reason in its
   title and its `aria-label`. `disabled` carries the rest: no press, no tab stop, and the
   arrow keys step over it. */
.crewpickc.gone, .crewpickp.gone { cursor: not-allowed; opacity: .34; position: relative; }
/* A hairline through the chip, drawn in both inks so it reads on a pale swatch and a dark one.
   Opacity alone was not enough: on the darker half of the palette a drained chip just looks
   like a slightly darker chip, which is a difference you can only see by comparing it with
   its neighbours -- and the neighbours are forty-seven other colours. Both grids get it; the
   pattern chips needed it most, since they are all drawn in the SAME colour and dimming was
   the only thing separating them. */
.crewpickc.gone::after, .crewpickp.gone::after {
  content: ""; position: absolute; inset: -1px; pointer-events: none;
  background: linear-gradient(to top right, transparent calc(50% - 1.4px),
    rgba(255,255,255,.85) calc(50% - 1.4px) 50%, rgba(0,0,0,.85) 50% calc(50% + 1.4px),
    transparent calc(50% + 1.4px));
}
.crewpickc.gone:hover, .crewpickp.gone:hover { outline: 0; }

/* the join list was nine identical filled buttons shouting over the crews themselves */
.crewrow .crewbtn.mini { min-width: 86px; text-align: center; }
/* the crew's own words, on their own line under the stats, not welded onto the end of them */
.crewmeta2 { display: block; margin-top: 1px; font-size: 11px; color: var(--mut, #9aa6c8);
  opacity: .85; }

/* the band legend under the board */
/* a grid, so the chips line up in columns instead of wrapping 4 / 3 / 1, and a
   wrapped label sits beside its swatch rather than under it */
.crewlegend { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 6px 14px; margin: 10px 0 0; font-size: 11px; color: var(--mut, #9aa6c8); }
.crewlegend span { display: flex; align-items: flex-start; gap: 5px; }
.crewlegnote { grid-column: 1 / -1; margin: 6px 0 0; font-size: 11px;
  color: var(--mut, #9aa6c8); opacity: .85; }
/* 26, not 22. Every band past the first is told apart by an edge -- a dashed ring, a thicker
   solid one, an inner loop -- and the comments below are right that the opacity step alone is
   invisible at this size. A 3px inset loop inside a 22px chip is the part of the chip doing
   the work, and a reviewer measuring them called "about to flip" plain grey. Four more pixels
   gives every one of those marks about a fifth more room without changing what they are. */
.crewlegend i { display: inline-block; width: 26px; height: 26px;
  flex: 0 0 26px; margin-top: 0; position: relative; background: var(--sec, #ff8ad8);
  border: 1px solid rgba(255,255,255,.25); }
/* The chips describe what the map paints and nothing else. A ten per cent opacity step is
   visible across a city block and invisible across sixteen pixels, so the two contested bands
   are told apart by the dashed edge, which is what the map really adds, and the steps are
   exaggerated enough to read at this size. Band 4 has no chip because the map draws no
   distinct mark for it; the tooltip says "Ringed off" in words instead. */
/* the opacity comes from crews.js, off the same array the map paints from. Only the edges
   live here, because an edge is the thing the map adds on top of the shade. */
/* The white wash, not the shade. `opacity` is written inline from the same array the map
   paints from, and a running animation outranks inline style, so animating opacity here threw
   that ladder away and made bands 1 and 2 identical. */
/* Each chip is the mark the map draws for that band, and nothing else. Four of these used to
   be one hatched square told apart by a ten per cent opacity step, which the comment two
   rules up correctly calls invisible at sixteen pixels; the answer was shapes the map has
   never drawn, so a rider learned a half-square and a corner wedge and went looking for them
   on ground that has neither. The map draws the two rings instead, and the key shows them.
     band 1  somebody is riding it   dashed white, as crew-contested paints
     band 2  about to flip           solid white and thicker, as crew-flipping paints
     band 3  going cold              no ring: nobody is taking it, it is fading on its own
     band 4  ringed off              an inner loop, which is how it was won */
.crewlegend .b1 i, .crewlegend .b2 i { border-color: #fff;
  animation: crewwash 5.2s ease-in-out infinite; }
.crewlegend .b1 i { border-style: dashed; }
.crewlegend .b2 i { border-style: solid; border-width: 3px; }
.crewlegend .b3 i { border-color: rgba(255,255,255,.1); }
.crewlegend .b4 i { box-shadow: inset 0 0 0 3px rgba(255,255,255,.55); }
@keyframes crewwash {
  0%, 100% { box-shadow: inset 0 0 0 16px rgba(255,255,255,0); }
  50% { box-shadow: inset 0 0 0 16px rgba(255,255,255,.26); }
}
/* solid, because the line on the map is solid */
.crewlegend .bt i { background: transparent; border: 2px solid #ffd24a;
  box-shadow: 0 0 0 1px rgba(0,0,0,.6); }
/* dashed, because the ring it stands for is dashed: the crews own the whole
   colour spectrum, so shape is the only channel a mark can keep to itself */
.crewlegend .bl i { background: transparent; border: 2px dotted #ff9f6b; }
/* 5.2s and half a cycle apart, which is what the map actually does -- but on its OWN range.
   `crewbreathe` runs .15 to .85, and a key chip is read at whatever instant you happen to
   look at it: a reviewer measured this one at .156, DIMMER than "going cold" at .3, for the
   state that is the most interesting thing on the map. It also passes straight through .72,
   which is exactly the resting opacity of "someone else is riding it", so one chip
   impersonated another twice a cycle.
   Bright, in a narrow band, with a ring of its own so the still frame reads too. */
.crewlegend .bf i { opacity: 1;
  animation: crewfresh 5.2s ease-in-out infinite; animation-delay: -2.6s; }
@keyframes crewfresh {
  0%, 100% { opacity: .82; box-shadow: 0 0 0 1px rgba(255,255,255,.5); }
  50% { opacity: 1; box-shadow: 0 0 0 3px rgba(255,255,255,.85); }
}
/* Band 0 is the baseline every other chip is read against, so it says plainly that it has
   no mark at all -- it was the same border as "taken this week" and told apart by nothing. */
.crewlegend .b0 i { border-color: rgba(255,255,255,.25); box-shadow: none; }




/* The mode's colour has to reach the numbers, not just the frame. These three come from the
   page's own stylesheet with a hardcoded --acc, which is the Riders blue, so the three
   largest figures on the board were rendering blue inside a pink panel. */
.panel[data-sec=crews] .rk,
.panel[data-sec=crews] .pod .km { color: var(--sec, #ff8ad8); }
/* The card's own left edge is the line for the area. Three stacked hints on the pass card
   drew three more inside it, each a separate stub with a gap above and below. */
.panel[data-sec=crews] .hint { border-left: 0; padding-left: 0; }
.panel[data-sec=crews] .sel:hover { background: rgba(255,138,216,.1); }

@keyframes crewbreathe { 0%, 100% { opacity: .15; } 50% { opacity: .85; } }
@media (prefers-reduced-motion: reduce) {
  /* A QR that fades in is a QR that is briefly not a QR. With this setting on it is
     simply there, which is the only correct answer for something being scanned. */
  .crewqrg i { animation: none; }
  /* The map stops for these readers, so everything in the panel stops with it. This used to
     name the three key chips and nothing else, which left the one animation the query exists
     for -- a pulsing ring that grabs the eye on purpose -- running. */
  .crewlegend .b1 i, .crewlegend .b2 i, .crewlegend .bf i { animation: none; }
  /* the ring is the channel that survives when the animation stops */
  .crewlegend .bf i { opacity: 1; box-shadow: 0 0 0 3px rgba(255,255,255,.85); }
  .panel[data-sec=crews] *, .panel[data-sec=crews] *::before, .panel[data-sec=crews] *::after,
  .crewtip, .crewtip *, .crewpop, .crewpop * {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}




/* The revealed card says so for a moment. Scrolling to it is not the same as noticing it,
   especially after an action that happened on another device. */
@keyframes crewflash {
  0% { box-shadow: 0 0 0 0 rgba(255,138,216,.0); }
  22% { box-shadow: 0 0 0 3px rgba(255,138,216,.55); }
  100% { box-shadow: 0 0 0 0 rgba(255,138,216,0); }
}
.crewflash { animation: crewflash 1.4s ease-out; }

/* touch, in one block and in cascade order */
@media (hover: none) and (pointer: coarse) {
  a.crewopen { display: block; text-align: center; text-decoration: none;
    order: 2; margin-top: 4px; }
  .crewsame { display: block; order: 1; }
  /* The QR is not dimmed. A faded code reads as a loading failure, and the honest signal is
     simply that the button is the easier path on the device the app is installed on. */
  .crewqr img { opacity: 1; }
  /* On the device the app is installed on, the button is the thing that works and the QR is
     the thing that physically cannot. Put them in that order. */
  .crewsign { display: flex; flex-direction: column; }
  /* And smaller, for the same reason it is ordered last here. At 188px it was 188 of the
     sign-in card's 427, which pushed the board's podium to y=723 of a panel ending at 759 --
     so a stranger on a phone met a pairing wall and 36px of the game. It stays big enough for
     another phone's camera, which is the only case it serves on this device. */
  /* The fold, which only exists on this pointer. Its summary is the control; the QR behind
     it is for the case where a SECOND phone scans this screen. */
  .crewqr { order: 3; margin-top: 10px; min-height: 124px; }
  .crewqr img, .crewqr .crewqrg { width: 124px; height: 124px; }
  .crewcode { order: 4; }
  #crewcodehint { order: 5; }
  .crewsign h3 { order: -2; }
  .crewsign > .hint:first-of-type { order: -1; }
}



/* Asking inside the panel rather than through the browser. */
.crewask {
  margin: 9px 0; padding: 11px 13px; border-radius: 7px;
  background: rgba(255,138,216,.1); border-left: 2px solid var(--sec, #ff8ad8);
}
.crewask p { margin: 0 0 9px; font-size: 13px; }
.crewask input {
  display: block; width: 100%; margin: 0 0 9px; padding: 8px 10px;
  background: rgba(0,0,0,.35); border: 1px solid var(--line, #243055); color: inherit;
  font: inherit; font-size: 13px; letter-spacing: 2px; text-transform: uppercase;
}
/* Except when it is a name. The capitals and the tracking are for a six-character code being
   copied off another screen; the disband gate asks you to type your crew's name back, and
   showing it to you as HOLMENKOLLEN CLIMB reads as a box that did not take what you typed. */
.crewask input.crewaskplain { letter-spacing: normal; text-transform: none; }
.crewask .crewbtn { margin-right: 7px; }


.crewboardsub { margin: -2px 0 10px; }
/* Every link in a crew card. There was no rule for one, so an anchor fell through to the
   browser's own `rgb(0, 0, 238)` on a navy card -- the default blue, unreadable here, and
   invisible to anyone who had never put a link in one of these. `--acc` is what this site
   means by a link; the underline waits for hover so a link inside a sentence does not fence
   the sentence. */
.crewcard a:not(.crewbtn):not(.crewqr) { color: var(--acc, #2ea8ff); text-decoration: none; }
.crewcard a:not(.crewbtn):not(.crewqr):hover,
.crewcard a:not(.crewbtn):not(.crewqr):focus-visible { text-decoration: underline; }


/* Where to ride next. The distance leads, because that is the thing a rider weighs. One
   variable sets the number column so the phone rule cannot drift out of step with it. */
.crewtargets {
  /* --kmw is set inline per list from that list's own longest phrase; --kmc is what the
     column uses, so the phone rule can cap it without being outranked by the inline. */
  --kmw: 96px;
  --kmc: min(var(--kmw, 96px), 46%);
  margin-top: 12px; border-top: 1px solid var(--line, #243055); padding-top: 8px;
}
.crewtargets.nums { --kmc: 58px; }
/* Above the phone breakpoint the phrase stops taking the slack. `--kmw` is set inline from the
   list's LONGEST phrase, so on a desktop the column was 217.7px for a row reading "one lap
   0.2 km" while the neighbourhood got 119.9px and wrapped to four lines -- a 68.3px row about
   70% empty, in a list whose other rows are 31-41px. Sizing to the longest phrase is right at
   390, where the alternative is that phrase wrapping inside 46%; it is wrong once there is
   width to spare, because the slack lands on the column that needs it least. The phone rule is
   deliberately untouched -- every number in it was measured against a real bug. */
@media (min-width: 561px) {
  /* 33% was 148.5px of a 450px row, and `--kmw` computed to 26ch = 143.2px, so the column
     was the smaller of two numbers that were both too small: the longest of the six rung
     phrases, "the long way round 1.4 km", measures 156.6px. Six of eight rows wrapped to
     double height while `.crewtwho` beside them held 246.8px for a 25-character string -- the
     slack was all on the column that needed it least, which is the same complaint this cap
     was introduced to fix, now pointing the other way. 38% is 171px: enough that `--kmw` is
     the binding constraint again, which is what the comment above means by calling the cap a
     safety net, and far from the 58% that was measured making the WHO column wrap instead. */
  .crewtargets { --kmc: min(var(--kmw, 96px), 38%); }
  /* The losses card is not the targets card. Its phrase is a whole sentence -- "one lap 0.1 km
     and it's theirs" against the targets card's "one lap 0.2 km" -- so a third of the column
     is about 162px for something that needs 210, and a reviewer measured the effort column
     wrapping on nearly every one of 37 rows at BOTH widths, which is this cap of mine making
     the card double-height and ragged.
     50%, swept and measured rather than reasoned about. Zero wrapping in the effort column
     sounds like the goal and is not: past half the row the WHO column starts wrapping instead
     and the rows get taller anyway. Measured on 37 rows at 1280 -- 46%: 26 double-height rows,
     1921px of card; 50%: 7 and 1752px; 58%: no effort wrapping at all but 28 double-height
     rows and 2074px; 62%: 34 and 2157px, worse than where this started. The shortest, most
     regular card is the one to want, and it is at 50%. */
  .crewtargets.crewlose { --kmc: min(var(--kmw, 96px), 50%); }

  /* One column for the whole panel, not just for each card. The cap above gives every card the
     same 466px measure, but flush left in a 719px body that is 19px of air on one side and 234
     on the other, and the warning banner -- the one block never capped -- ran the full 673
     above cards stopping 207px short of it. Capping the banner to the same measure gives the
     stack a single right edge from the top of the panel down; centring the set splits the
     slack instead of piling it beside the cards, which is the "scales by whitespace at its
     edges" the cap block set out to do.
     Here rather than beside the cap: `.crewmsg { margin: 9px 0 }` is declared further down the
     file and a shorthand there beats `margin-inline` declared earlier. */
  .crewmsg.warn, .crewmine-wrap { max-width: calc(31rem + 32px); }
  .crewjoin, .crewmine, .crewboard, .crewhow, .crewmsg.warn, .crewmine-wrap
    { margin-inline: auto; }
}
.crewtargets .hint { margin: 0 0 8px; }
/* Top-aligned, because the phrase wraps and the other two columns do not: centred, the
   bearing on a two-line row sat level with neither line and the column staggered down. */
.crewtrow {
  display: flex; align-items: flex-start; gap: 9px; padding: 6px 8px; margin: 0 -8px;
  border-top: 1px solid rgba(255,255,255,.05); cursor: pointer; font-size: 13px;
  /* So a consequence clause with nowhere left on the line drops to its own line instead of
     being squeezed into a 53px column -- see `.crewtquiet`. Declared here, in the base rule,
     rather than appended at the end of the file: the phone block sets its own `row-gap: 3px`
     and a later top-level declaration would outrank it. The cascade checker caught exactly
     that, which is the second time this file's media block has been the thing I broke. */
  flex-wrap: wrap; row-gap: 2px;
}
.crewtrow:first-of-type { border-top: 0; }
.crewtrow:hover { background: rgba(255,138,216,.1); }
/* A phrase, not a figure: "a short ride" in a 58px tabular-nums column wrapped onto two
   lines. The monospace column comes back only when the admin asks for exact kilometres. */
/* gold, because the ring this column points at is gold. The lose card is orange for the same
   reason. That pairing is the only thing telling the two cards apart at a glance. */
/* `min-width: 0`, or the explicit width above is only a suggestion: a flex item's
   `min-width` is `auto`, which floors its used width at min-content, so a row whose longest
   word did not fit the shared column quietly got a wider one. Measured consequence: the
   bearing beside it took five different left edges across eight rows, spanning 36px, and read
   as a ragged column. The badge next door was given this treatment for the same reason; the
   phrase column never was. */
.crewtkm {
  flex: 0 0 auto; width: var(--kmc); min-width: 0;
  font-weight: 600; color: #ffd24a;
}
.crewtargets.nums .crewtkm {
  font-family: ui-monospace, Menlo, Consolas, monospace; font-variant-numeric: tabular-nums;
  font-weight: 700; text-align: right;
}
.crewtdir {
  flex: 0 0 auto; min-width: 26px; font-size: 11px; font-weight: 700; letter-spacing: .4px;
  color: var(--mut, #9aa6c8);
}
/* min-width:0 or a long crew name in a long locale pushes the badge off a 390px screen */
/* 1 rather than 0 on the grow, so a long neighbourhood takes the slack the phrase column
   leaves instead of pushing itself onto a second line. */
/* Wraps at every width. `nowrap` here was inherited by the `i` inside it, so removing the
   ellipsis from the child was not enough: at 1440 the holder line still ran 35px past its own
   box and over the chip beside it, and the row carrying the card's loudest news was the one
   that got cut. The column holds a rival's name and what taking the square is worth; both are
   the point of the row, so it takes the height it needs. */
.crewtwho {
  /* `1 1 0`, not `1 1 auto`. With an auto basis the column starts at its content width and
     the two richest rows -- "Kolbotn · Nordlys Collective has it 12th · 15 squares in one
     piece" -- could not fit beside the effort and bearing columns, so they wrapped to a line
     of their own and took the consequence chip with them. A reviewer measured the place name
     at left=681 on six of eight rows and left=472 on the other two: a 209px jump inside one
     list at 1920, and 361 against 152 at 1280. It reads as a glitch rather than a variant.
     A zero basis makes the column size from the track it is given instead of from its
     longest string, so every row's place name lands in the same column and the long ones
     wrap inside it. */
  /* A floor, because `min-width: 0` plus a zero basis means "may be starved to nothing", and
     that is what happened: the sibling tag below takes its natural 165-180px and this column
     was left 36-51px, turning "Kolbotn · Nordlys Collective has it 9th · 15 squares in one
     piece" into eight one-word lines and a 123px row, with one place name overflowing 28px
     and painting on top of the tag beside it. Rows WITHOUT a tag got the full 225px and
     looked fine, which is the tell: it is the pair that fails, not the column.
     Two rounds ago this was `flex: 1 1 auto`, which let the richest rows take a line of their
     own and left the place name at two different left edges. Both of those are mine, each
     fixing the other's symptom. A floor on both, and the row -- which already wraps -- puts
     the tag on its own line when they cannot share one. */
  flex: 1 1 0; min-width: 120px; color: var(--mut, #9aa6c8); font-size: 12px;
}
/* nothing to ride here today: present, legible, and plainly not the thing to do next */
.crewtrow.done { opacity: .72; }
.crewtrow.done .crewtkm { color: var(--mut, #9aa6c8); font-weight: 400; }
.crewtag {
  flex: 0 1 auto; margin-left: auto; max-width: 38%; font-size: 11px; letter-spacing: .3px;
  text-transform: uppercase; padding: 2px 6px; border-radius: 7px;
  white-space: normal; text-align: right; line-height: 1.25;
  /* Balanced, so the two lines are near-equal rather than one full line and a tail. At 165px
     "BREAKS THEIR BLOCK, 15 SQUARES GO" was breaking after the number, which put "15" at the
     end of one line and "SQUARES GO" at the start of the next -- a quantity separated from
     its unit, in a right-aligned box, in capitals. Advisory, and the fallback is the ordinary
     wrapping this already had. */
  text-wrap: balance;
  opacity: .95;
}
/* Two different claims had one identical gold chip; and the hottest label in the panel was
   #ff6b6b, which is the Countries button in the dock below it and this panel's own error
   red. The hot one moves into the crews family instead of borrowing another section's. */
.crewtag.first { color: #ffd24a; background: rgba(255,210,74,.12); }
.crewtag.joins { color: #9fe8ff; background: rgba(159,232,255,.12); }
.crewtag.kills { color: #ff4fa3; background: rgba(255,79,163,.16); }
/* The same move, one or two squares' worth: true of every border square, so it is stated
   rather than shouted. The hot pink stays for a hole big enough to matter whoever you are. */
/* Two meanings had one identical grey chip. `drops` is a rival's number falling, which is
   news about the board; `done` is "there is nothing to ride here", which is the absence of
   news. The first keeps a box and a little warmth, the second is plain dim text. */
.crewtag.drops { color: #c9b89a; background: rgba(201,184,154,.1); }
.crewtag.done { color: var(--mut, #9aa6c8); background: none; padding-left: 0; padding-right: 0; }
/* Moving up the board yourself. Not the hot pink that means breaking somebody's block --
   that is news about them, and this is news about you. */
.crewtag.youpass { color: #6ee7a0; background: rgba(110,231,160,.12); }

/* the other half of the game, same shape so the two read as a pair */
.crewlose .crewtkm { color: #ff9f6b; }




/* Resting on a square. Small, out of the way, and it never appears while the map is moving. */
.crewtip {
  /* Orders this against the map's other children, like `.crewpop` above -- and like it, this
     cannot climb over the panel, because `#map` is its own stacking context. The phone case
     (a tap producing a readout under the sheet the same tap opens) is a placement problem,
     not a paint-order one. */
  position: absolute; z-index: 560; pointer-events: none;
  transform: translate(14px, -50%);
  /* `max-content`, so the width does not depend on where the tip is. Absolutely positioned
     with only a `max-width`, this box shrink-to-fits against the room between its anchor and
     the container's right edge -- so a tap at x=378 in a 390px map gave it 12px to work with
     and it collapsed to 36px wide by 626px tall, one character per line, 96px below the fold.
     `placeTip` reads `offsetWidth` to decide which side to use, so it was measuring the
     collapsed box and every decision after it was arithmetic over the wrong number. An
     intrinsic width is min(content, 230px) wherever it sits, which makes the measurement true
     and one measure-then-correct pass stable. */
  /* FIXED, not just position-independent. `max-content` fixed this morning's bug -- the
     width no longer depends on where the tip is -- and left it depending on what is in it, so
     a reviewer measured 141px on one square and 165px on its neighbour and the tip jittered
     as the pointer crossed the map. A tooltip is normally one size; this is that size. */
  width: 230px;
  padding: 6px 9px; border-radius: 7px; max-width: 230px;
  background: rgba(6, 10, 20, .94); border: 1px solid rgba(255, 255, 255, .16);
  box-shadow: 0 4px 18px rgba(0, 0, 0, .55);
  font-size: 11px; line-height: 1.45; white-space: normal; overflow-wrap: anywhere;
  animation: crewtipin .18s ease both;
}
.crewtip.left { transform: translate(calc(-100% - 14px), -50%); }
/* near the top of the map there is nothing above the pointer to hang it from */
.crewtip.below { transform: translate(14px, 14px); }
.crewtip.left.below { transform: translate(calc(-100% - 14px), 14px); }
.crewtip b { display: block; font-size: 12px; overflow-wrap: anywhere; }
.crewtip span { display: block; color: var(--mut, #9aa6c8); }
/* the band is the left edge, so the shades are learned where they are used */
.crewtip.b1 { border-left: 3px solid rgba(255, 255, 255, .75); }
.crewtip.b2 { border-left: 3px solid #ff8ad8; }
.crewtip.b3 { border-left: 3px solid var(--mut, #9aa6c8); }
.crewtip.b4 { border-left: 3px solid #ffd24a; }
/* a ring's own tooltip, in the ring's own colour */
.crewtip.bt { border-left: 3px solid #ffd24a; }
.crewtip.bl { border-left: 3px solid #ff9f6b; }
.crewtip.b0 { border-left: 3px solid rgba(255,255,255,.28); }
.crewtip em { font-style: normal; color: #ffd24a; }
.crewtip.bt em { color: #ff4fa3; }
/* opacity only: a `transform` here animates a flipped tip across the cursor,
   because the implicit `to` is whichever transform the flip class set. */
@keyframes crewtipin { from { opacity: 0; } }


/* An in-panel question opens beside the control that raised it. Those controls live in flex
   rows, so the row has to let it take a line of its own or the crew name and the input are
   squeezed into slivers at 390px. */
.crewrow > .crewask, .crewacts > .crewask { flex: 1 0 100%; margin-top: 8px; }

/* the badge is longer than the room left for it once the number column is subtracted */



/* Eight rows naming the same crew eight times reads as a rendering fault. The repeat stays,
   because the column has to line up, but it stops shouting. */
/* de-emphasised, not erased: .34 measured under 2:1 against the panel, and on a
   phone there is no hover to bring it back. */
/* An explicit colour rather than a second opacity: on a `done` row the two multiplied down
   to 2.5:1 against the card, which is below any threshold worth naming. */
/* 78% of the muted colour was a step nobody could see: a reviewer could not tell row 1's
   holder line from row 2's in their own screenshot, so eight identical sentences still
   read as eight. Far enough back now that the eye skips it and goes to the row that
   says something new. */
.crewtrow .rpt { color: color-mix(in srgb, var(--mut, #9aa6c8) 62%, transparent); }
.crewtrow:hover .rpt { color: var(--mut, #9aa6c8); }


/* what a crew took this week, on its own row of the board */
/* a mint of our own: #39d98a is the Records button sitting in the same dock */
/* the space between this and the patch count measured 2.6px, which reads as no gap */
/* the space between this and the patch count measured 2.6px, which reads as no gap */
.crewgain { color: #8ef0c0; font-variant-numeric: tabular-nums; margin-left: 5px; }

/* The podium's sub used to be three stacked blocks -- squares, patches, gain -- each with a
   rule of its own and one of them reserving an empty line so the three cards stayed level.
   It is one sentence now ("92 squares in 2 patches · +2"), so the gain runs inline inside it
   and there is nothing left to line up. Still no `white-space: nowrap` anywhere near it: the
   cards carry `overflow: hidden` for their shine sweep, so a nowrap here does not overflow,
   it CUTS -- that shipped once and removed the last word in eleven of eighteen locales. The
   card has no fixed height, so a wrap grows it instead. */
.crewboard .pod .psub .crewgain { margin-left: 0; }



/* The scoreline was inside the cell the phone hides, so the one piece of news in the mode
   vanished on the device most people use. The tile count goes instead. */
/* Everything the phone changes, in one place and in cascade order. Seven separate
   blocks is how a rule ends up silently cancelling an earlier one instead of
   deliberately overriding it, which had happened three times in here. */
/* The neighbourhood leads the last column and the holder sits under it, so a long name
   cannot widen the two-letter bearing beside it. */
/* Hidden, not absent. On a wide screen the place name is a block and the line break
   already separates it from what is happening there, so the dot would be noise -- but it
   stays in the text layer at every width, because that is what a screen reader, a
   copy-paste and a find-in-page read. It was `.crewtat + i::before`, which none of them
   can see: the page read back `OsloHolmenkollen Climb hat es`. */
.crewtsep { display: none; }

.crewtat { display: block; font-weight: 600; letter-spacing: 0; color: var(--ink, #eef1fb);
 text-transform: none; line-height: 1.25; 
  }
/* No ellipsis. This held one short phrase when it was written and now carries the holder's
   name AND their standing, which overran it: 62px outside the card at 390 with no scrollbar
   to reveal it, and "…13 squares in o…" on row 1 at 1440. The file already says a few lines
   down that truncating a crew's name is indefensible in a feature about naming your crew;
   the same goes for the number telling you what taking it is worth. It wraps. */
.crewtwho i { display: block; font-style: normal; font-size: 11px; line-height: 1.25; }
.crewtdir { line-height: 1.25; }

/* Hit areas, on their own trigger. These used to sit inside the width block while the pass
   reorder sat in the coarse-pointer block a hundred and sixty lines away, so a phone turned
   to landscape -- 844x390, which matches one and not the other -- quietly gave every one of
   them back: mini buttons to 28px, target rows to 31px, summaries to 32px. A thumb is a
   thumb in both orientations. Measured before these rules: mini button 28px, target row
   31px, ranked row 31px, settings summary 32px, pattern chip a hard 30px. */
@media (max-width: 560px), (hover: none) and (pointer: coarse) {
  .crewbtn, .crewbtn.mini { min-height: 44px; padding-top: 0; padding-bottom: 0;
    display: inline-flex; align-items: center; justify-content: center; }
  .crewtrow { min-height: 44px; padding-top: 8px; padding-bottom: 8px; }
  .crewrank td, .crewboard table td { padding-top: 13px; padding-bottom: 13px; }
  .crewedit summary, .crewhow summary, .crewmine-wrap > summary {
    min-height: 44px; display: flex; align-items: center; }
  /* flex on a summary drops the browser's own marker, so it is drawn here */
  .crewedit summary::-webkit-details-marker, .crewhow summary::-webkit-details-marker {
    display: none; }
  .crewedit summary::before, .crewhow summary::before {
    content: ""; flex: 0 0 auto; width: 0; height: 0; margin-right: 7px;
    border: 4px solid transparent; border-left-color: currentColor;
    transition: transform .15s ease; }

  /* Wider tracks rather than a min-height: a min-height beats aspect-ratio, and a 33px-wide
     chip stretched to 44 is a portrait rectangle in a feature whose language is squares. */
  .crewpickp .crewsw { width: 32px; height: 32px; }

  /* The contribution bar gets its own line. Inline, it is the last thing in a row that already
     holds an avatar, a 42% name column, two gaps and "830 km 45%" -- measured at 390 the track
     was squeezed to 30px of its 24px minimum, so 65% drew 19px and 35% drew 10px and a 2:1
     split read as nine pixels. The number carried all the meaning and the bar was decoration.
     Full width on its own line it is about 292px, so the same split draws 190px against 102px.
     The target rows already wrap a part onto its own line at this width for the same reason. */
  /* The row has to be allowed a second line, or the one above is a lie: `order` and a 100%
     basis only mean anything where the row wraps, and `.crewcrow` is `nowrap`. Without this
     the bar stayed inline at its full 292px, could not shrink back (`flex` shrink is 0), and
     pushed the panel 232px wider than the phone it was measured on. */
  .crewcrow { flex-wrap: wrap; }
  .crewcbar { flex: 1 0 100%; order: 3; min-width: 0; margin-top: 3px; }
}

@media (max-width: 560px) {
  /* A contributor row is read, not tapped: six riders at 44px is 264px of panel spent on a
     bar chart nobody can press. */
  /* The `#cf-patterns` copy of the line below was here to beat a base rule that no longer
     exists, so `.crewpick` now sets the track for both grids once. */
  .crewcard { padding: 12px 12px; }
  .crewhead { gap: 9px; }
  .crewlogo { width: 42px; height: 42px; flex: 0 0 42px; }
  /* 342px of a 455px panel went on the podium before the first ranked row, to show three
     facts that barely move from week to week. This hands back about ninety pixels and two
     more crews. */
  .crewboard .podic { width: 30px; height: 30px; }
  /* 122 and "squares" were set at the same size, in a 78px column, so every card wrapped and
     the unit ended up shouting louder than the number it belongs to. */
  /* "squares" is seven characters of Orbitron and the column is 78px, so the unit wraps
     under the figure rather than running past the card's edge. */
  .crewboard .pod .km { font-size: 10px; line-height: 1.2; white-space: normal;
    overflow-wrap: anywhere; }
  .crewboard .pod .crewpodn { font-size: 22px; }
  .crewboard .pod.p1 { padding-top: 14px; }
  .crewboard .pod .psub { font-size: 10px; line-height: 1.2; }
  .crewboard .pod { padding: 8px 6px; }
  .crewboard .podium { padding: 4px 0 8px; }
  .crewbig { font-size: 22px; }
  .crewqr img { width: 150px; height: 150px; }
  .crewcode { font-size: 22px; --ls: 5px; }
  .crewident { flex-wrap: wrap; }
  /* Four columns in 317px. The site's own podium builds this table, so its cells arrive
     nowrap and sized for a desktop: rank + name + "44 squares" + "8 new this week" came to
     459px and the last two were cut off the right edge. Nothing is dropped, because the week
     count is the only thing here that says the map moved. The numbers wrap, the padding
     comes in, and the name column gives up its width first. */
  .crewboard table { width: 100%; }
  .crewboard table td { padding-left: 5px; padding-right: 5px; }
  /* The three podium cards are a third of a 412px card each, so they are narrower than any
     row beneath them and used to wear the longest strings. Clamped to two lines with a
     common minimum height, so the podium reads as a podium rather than as three cards that
     happen to be adjacent. */
  .crewboard .podium { align-items: stretch; }
  .crewboard .pod { min-height: 150px; }
  .crewboard .pod .pname { display: -webkit-box; -webkit-line-clamp: 2;
    -webkit-box-orient: vertical; overflow: hidden; }
  .crewboard table .val { white-space: nowrap; width: 1%; }
  .crewrank .val.sub { display: none; }
  .crewacts .crewbtn { flex: 1 1 auto; }
  .crewpickp .crewsw { width: 26px; height: 26px; }   /* thumbs, not cursors */
  /* truncating crew names is indefensible in a feature about naming your crew */
  /* wrapping was not enough on its own: the ellipsis is inherited, so the name wrapped AND
     truncated. Truncating a crew name in a feature about naming your crew is indefensible. */
  .crewboard .pname {
    white-space: normal; overflow: visible; text-overflow: clip;
    font-size: 11px; line-height: 1.15;
  }
  .crewboard table .celln { align-items: flex-start; }
  .crewboard table .celln > span { white-space: normal; overflow: visible;
    text-overflow: clip; line-height: 1.2; }
  .crewpick { grid-template-columns: repeat(auto-fill, minmax(44px, 1fr)); }
  /* the action buttons were clipping against the panel's bottom edge */
  .crewmine .crewacts { padding-bottom: 14px; }
  /* a 150px image was sitting in a 188px box */
  .crewqr { min-height: 0; }
  /* The badge was hidden here, which took the explanation off exactly the rows that need one:
     a phone showed "ridden, Cykelslangen has it" and no reason why riding it again would do
     nothing. It wraps onto its own line instead, lined up under the text. */
  /* Two lines: what it costs and which way, then where it is and who has it. The phrase
     column takes what it needs rather than a percentage of the row, but it must not grow
     into the rest, or the bearing ends up at the far margin 150px from the figure it
     modifies while the place it describes sits on the next line at the far left. */
  .crewtargets { --kmc: auto; }
  .crewtkm { flex: 0 1 auto; }
  /* `auto`, not 6px. The chip flowed inline right after a label of variable length, so
     across eight rows its left edge landed anywhere from 144px to 213px -- 69px of scatter
     in what reads as a column, inside a 294px card. Pushed to the right edge it IS one.
     The same rows at 1280 already line up, because the grid applies there. */
  .crewtdir { margin-left: auto; text-align: right; }
  .crewtwho { flex: 1 0 100%; order: 3; font-size: 11px; margin-top: 1px; }
  .crewtat, .crewtwho i { display: inline; }
  /* the two facts on that line are different kinds of fact, so they are separated rather
     than run together */
  .crewtsep { display: inline; opacity: .85; }
  .crewtargets.nums { --kmc: 52px; }
  .crewtrow { flex-wrap: wrap; row-gap: 3px; }
  /* Its own line and no box. `flex-basis: 100%` sizes the chip, not just its position, so
     a two-word badge became a 294px tinted bar with the text in its left fifteenth, two of
     them stacked above a row separator at 5% white: four rows read as seven bands. The
     three families are already told apart by hue, so on a phone they are simply coloured
     text under the row they belong to. */
  /* `background: none; padding: 0` here is outranked by the six-family rule below and in
     that sense does nothing -- a reviewer checked the live DOM and found no bare `.crewtag`
     at all. It stays because it is the INTENT ("no box on a phone") that the rule below
     carries out, and because sixteen pinned cases in tests/test_crews_css.py use it as the
     broad rule whose defeat is the round-six bug. Deleting it makes the checker unable to
     demonstrate the thing it exists for. */
  .crewtag { flex: 1 0 100%; margin-left: 0; max-width: none; text-align: left;
    background: none; padding: 0; font-size: 11px; }
  /* One class cannot strip what two classes painted: every coloured family kept its tinted
     box here, which is the 294px bar this rule exists to abolish. */
  .crewtag.first, .crewtag.joins, .crewtag.kills, .crewtag.drops,
  .crewtag.done, .crewtag.youpass { background: none; padding: 0; }
  /* The place, the holder and the holder's standing do not fit on one phone line, and the
     one that was being cut mid-word is the rival's name -- the thing the row is about.
     It wraps here instead; the row is already more than one line. */
  .crewboard table .val.sub { font-size: 11px; text-align: right; width: 1%; }
}



/* How long a crew has held a square, filled in after the popup opens -- `/territory/at`
   answers separately. `display: none` while empty meant the popup opened at 189x141 and
   became 189x161 when the answer landed, growing 20px and shifting 20px UP, under the thumb
   that had just tapped it. A reviewer measured the jump at 27ms on localhost; on a real
   network that gap is the whole point of the measurement. The line is reserved instead, so
   the box is its final size from the first frame and the words simply appear. */
.crewpop-since { display: block; min-height: 1.25em; }
.crewmore { margin: 6px 0 0; opacity: .8; }


/* The phrase says how hard, the figure says how far. "the long way round" covers 1.6 km to
   5.0 km, and the client has the number already. */
/* The phrase says how hard and the figure says how far, and the figure is the one that
   tells two rows apart: four in a row can read "a few streets" and mean 0.8, 0.8, 0.9 and
   1.9 km. It used to be the smallest, faintest text on the screen while the word that did
   not distinguish them was the loudest. */
.crewtkm i { font-style: normal; font-size: 12px; font-weight: 700; opacity: .9;
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.crewtargets.nums .crewtkm i { display: none; }

/* the key carries the crew's pattern, the same way every other swatch in the panel does --
   and from the same `--pat` definitions, so the key cannot print a pattern differently from
   the swatch two cards above it */

/* the figure behind the phrase, on the square itself: same weight as in the two cards */
.crewtip span i, .crewpop-in span i { font-style: normal; font-size: 11px; opacity: .62;
  font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Tapped rather than hovered: it has to survive the finger coming off, so it gets a shadow
   deep enough to sit above the fill and room enough to read at arm's length. */
.crewtip.tap { font-size: 13px; padding: 9px 11px; box-shadow: 0 6px 22px rgba(0,0,0,.6); }
/* The line naming the move was the smallest of the three, because .crewtip b is pinned at
   12px and the tapped tip is 13. */
.crewtip.tap b { font-size: 14px; }
/* and a tail, because on a grid of identical squares a box with nothing joining it to one of
   them is ambiguous about which one it means */
/* The box's own fill, not a near-miss of it, and on whichever edge faces the square: the tip
   flips side near the right margin and flips to hang downward near the top, and a tail pinned
   half way down the left pointed at empty map in the second case. */
.crewtip.tap::after { content: ""; position: absolute; left: -6px; top: 50%;
  margin-top: -6px; border: 6px solid transparent;
  border-right-color: rgba(6, 10, 20, .94); border-left-width: 0; }
.crewtip.tap.left::after { left: auto; right: -6px; border-right-width: 0;
  border-right-color: transparent; border-left-width: 6px;
  border-left-color: rgba(6, 10, 20, .94); }
.crewtip.tap.below::after { top: -6px; margin-top: 0; left: 14px;
  border: 6px solid transparent; border-bottom-color: rgba(6, 10, 20, .94);
  border-top-width: 0; }
.crewtip.tap.below.left::after { left: auto; right: 14px; border-left-width: 6px;
  border-left-color: transparent; border-bottom-color: rgba(6, 10, 20, .94); }

/* The neighbourhood, under the bearing. Small and quiet: it answers "where" for somebody who
   knows the city, and costs nothing to skip for somebody who does not. */
/* the answer to "I rode it, where is it", under the rows that prompted the question */
/* The redraw clock. Not dimmed to .75 any more: it is the one concrete time in the whole
   feature and the only reason to come back later, and it used to sit at the bottom of two
   cards as a question about the list above it. */
.crewdrawn { margin: 8px 0 0; font-size: 12px; color: var(--mut, #9aa6c8); }
.crewterr + .crewdrawn { margin-top: 2px; }

/* the neighbourhood, at the top of the tip the ring opens */
.crewtipat { display: block; font-weight: 600; opacity: .8; margin-bottom: 1px; }

/* Disband cannot be undone and sat in the same ghost styling as Leave, which can. The only
   irreversible control in the panel says so before it is pressed, not only in its confirm. */
.crewbtn.danger { color: #ff7a9c; border-color: rgba(255,122,156,.42); }
/* Measured at 390: Disband 294px against Share crew 143px and Leave crew 143px -- the one
   action that cannot be undone was 2.06x the width of the reversible ones and full-bleed
   underneath them, because it wrapped onto a line of its own and `.crewacts` stretched it.
   It takes its own width like everything else now. A dangerous button should be reachable,
   not loud. */
.crewacts .crewbtn.danger { flex: 0 0 auto; }
.crewbtn.danger:hover { background: rgba(255,122,156,.12);
  border-color: rgba(255,122,156,.7); }

/* An empty ring beside a name reads as an image that failed to load, three times over, on
   the one chart in the feature. */
.crewcrow .av:empty { background: rgba(255,255,255,.07);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.12); }

/* The bearing is 11px against a 13px phrase and sat on the row's top edge, which reads as a
   superscript. `align-self: baseline` did nothing: it was the only baseline item in the line,
   and a baseline group of one aligns to the line's start, which is where it already was. A
   padding equal to the difference in cap height is what actually drops it onto the line. */
.crewtdir { padding-top: 2px; }

/* `display: flex` on this summary drops the browser's marker and the replacement only
   covered the other two, so the one disclosure people open most had no triangle anywhere. */
.crewmine-wrap > summary::before {
  content: ""; flex: 0 0 auto; width: 0; height: 0; margin-right: 7px;
  border: 4px solid transparent; border-left-color: currentColor;
  transition: transform .15s ease;
}
/* At every width, not only behind the phone query: `display: flex` on these summaries drops
   the native marker on a desktop too, so the replacement has to turn there as well. */
.crewedit[open] > summary::before,
.crewhow[open] > summary::before,
.crewmine-wrap[open] > summary::before { transform: rotate(90deg); }

/* why the button is not there */
.crewgone { font-size: 11px; color: var(--mut, #9aa6c8); opacity: .8; }

/* Where you stand and how far off the crew above, on the card that used to say neither. */
.crewgap { color: #ffd24a; font-weight: 600; margin-right: 7px; }


/* the share of the crew's riding, beside the distance it is a share of */
.crewckm i { font-style: normal; font-size: 11px; color: var(--mut, #9aa6c8); margin-left: 5px;
  font-variant-numeric: tabular-nums; }

/* Where the crew holding this square stands. Quiet, because it is context for the name
   beside it rather than a third fact competing with the two above. */
/* was `.crewtwho u`, with the underline turned straight back off: the only thing the
   tag was still doing was telling a screen reader that a crew's standing is
   underlined for emphasis, which is a layout choice. */
.crewtwho .crewtst { opacity: .82; }   /* .72 measured 4.08:1 on a hovered row, under
   AA: the row's hover tint lightens the backdrop under already-dimmed text */

/* Worth one square: the same sentence the chip carries, without the box or the colour, so a
   row that has a small reason says it and the chips go on meaning something. */
/* 50 and not 38. 38% is a cap chosen against English: "drops them to 90" fits it and
   `drückt sie auf 90` needs 85px against 81, `les fait tomber à 90` 98 against 94 and
   `збиває їх до 90` 76 against 74 -- two to four pixels short, two lines each, and the first
   row of the list opened ragged in three locales and square in the rest. */
/* `flex-shrink: 1` let this be squeezed to whatever the two columns before it left over,
   and on a desktop that was 53px -- so "drops them to 90", the consequence clause that is the
   whole point of the row, set as "drops / them to / 90" across three lines, 41px tall, inside
   a 450px row with slack in the middle column. The same string sits on one line at 390px,
   where the phone rule below gives it a line of its own, so the wide layout was reading worse
   than the narrow one.
   It keeps its content width now (`flex-shrink: 0`) up to half the row, and the row wraps so
   that a clause with nowhere to go drops to its own line instead of being milled into a
   column. `flex-wrap` on the row rather than `flex-basis: 100%` on this: most rows have room
   for it on the first line and should keep it there, beside the number it qualifies. */
.crewtquiet { flex: 0 1 auto; margin-left: auto; min-width: 150px; max-width: 50%;
  font-size: 11px;
  /* Two even lines rather than a full one and an orphan. These read "BREAKS THEIR BLOCK, 15
     SQUARES GO" and were breaking after the number, so the quantity and its noun landed on
     different lines of a right-aligned box -- "15" ending one line and "SQUARES GO" starting
     the next. `balance` is advisory and falls back to ordinary wrapping, which is what this
     was doing anyway. */
  text-wrap: balance;
  text-align: right; line-height: 1.25; color: var(--mut, #9aa6c8); opacity: .8; }
@media (max-width: 560px) {
  .crewtquiet { flex: 1 0 100%; margin-left: 0; max-width: none; text-align: left; }
  /* The name takes its own line, like the chip above does. Whose row this is matters more
     than fitting three things across 292px, and one of the three deletes them. */
  .crewpendr { flex-wrap: wrap; }
  .crewpendr > span:first-child {
    flex: 1 0 100%; overflow: visible; text-overflow: clip; white-space: normal;
  }
}
/* A name and its role mark are one thing; the phone was wrapping the diamond onto its own
   line under the name. The mark keeps the nowrap, not the whole name: with it on the span,
   the name was the thing that shortened, and the two buttons beside it are `flex: 0 0 auto`
   -- in German "Zum Offizier machen" and "Entfernen" leave 45px of a 292px row, so three
   roster rows read `S14·Fj…`, `SIM·V…`, `S14·K…` beside a button that removes somebody. */
.crewpendr > span .crewrole { white-space: nowrap; }

/* A SHORT screen, which this file had no rule for: `@media` appeared only at 560px wide, for
   hover and for reduced motion. At 360x640 the panel body is 288px tall, so the podium -- the
   tallest thing in the panel and the first -- was cut through the middle of its own figures,
   with pink half-glyphs bleeding into the dock below. The three places still read as three
   places; they just stop being a monument. */
@media (max-height: 700px) {
  .crewboard .podium { align-items: stretch; }
  /* `.pod.p1` carries the gold plinth's own `padding-top: 20px` at top level, which is more
     specific than this, so without naming it the winner stayed tall while the other two
     shrank. */
  .crewboard .pod, .crewboard .pod.p1 { padding-top: 8px; padding-bottom: 8px; }
  .crewboard .pod .crewpodn { font-size: 18px; }
  .crewboard .pod .av, .crewboard .pod .podic { width: 30px; height: 30px; margin-bottom: 4px; }
  /* The patch count and the gain line are the two things a 288px window cannot afford, and
     both are in the table two rows below. */
  .crewboard .pod .psub { display: none; }
}

/* `[hidden]` means hidden, everywhere in the panel.
   `[hidden] { display: none }` is a user-agent rule, so ANY author `display` outranks it. That
   has now shipped three times in this one file: `.crewrow` got its own guard (see the note
   beside it), a comment further up flags `.crewbtn { display: inline-flex }` as the same
   hazard, and the two elements that actually needed it -- `.crewempty` at `display: flex` and
   the show-all button -- did not have it. The visible result was a "Nothing matches" notice
   that could not be dismissed for the rest of the session, sitting under a full list, beside a
   second "Show all" doing something different.
   So: not another exception. `#pbody [hidden]` is specificity (1,1,0), which nothing else in
   this stylesheet reaches, and it covers every element the panel hides by attribute including
   the ones nobody has written yet. An absent declaration is invisible to the cascade checker,
   which is why this has to be structural. */
#pbody [hidden] { display: none; }

/* The three crews actually taking ground, above the squares they are taking. Hot pink is the
   ink this panel already uses for a rival breaking a block, so the summary reads as the same
   kind of news as the rows under it -- louder than the rows, because it is fewer lines and
   somebody has to read it first. */
/* One group per rival, headed by the sentence that used to be printed above the flat list.
   The summary became the structure: closed, the card is four lines; opened, every square it
   was ever going to show is still in it. */
.crewlosegrp { margin-top: 4px; }
.crewlosegrp > summary {
  cursor: pointer; list-style: none;
  font-size: 12px; font-weight: 600; color: #ff8ad8;
  padding: 3px 0; overflow-wrap: anywhere;
}
.crewlosegrp > summary::-webkit-details-marker { display: none; }
.crewlosegrp > summary::after {
  content: ""; display: inline-block; margin-left: 7px;
  border: 4px solid transparent; border-left-color: currentColor;
  transition: transform .15s ease;
}
.crewlosegrp[open] > summary::after { transform: rotate(90deg); }
/* Ground nobody is taking is not a threat, so it is not in the threat ink. Orange, which this
   card already uses for the effort of getting back to a square -- the problem here is that
   nobody has. */
.crewlosegrp.crewcold > summary { color: #ff9f6b; font-weight: 400; }

/* The exact moment a cooldown lifts, beside the sentence that says how long is left. Quieter
   than the sentence, because it is the precise version of something already said. */
/* Its own line. Appended inline, the date landed after a full stop as a bare token --
   "Keeps people from hopping around farming ground. Oct 13" -- which reads as a sentence that
   lost its verb. A date on a line of its own reads as a date.
   It now reads "Back in from 11 Oct." rather than "11 Oct": a line of its own was half the
   fix, and a reviewer still found two timings on one card with nothing joining them. The
   label is a string and the date is not, so the browser goes on formatting it in the
   reader's own locale.
   No `white-space: nowrap` any more. It was right for a bare token and would cut a sentence
   in the locales whose "Back in from" is three words -- the trap this file has hit three
   times. The date itself is held together by a non-breaking space in `whenAgain`. */
.crewwhen {
  display: block; margin-top: 5px; font-variant-numeric: tabular-nums;
  font-weight: 600; opacity: .9;
}

/* Which way a crew is going. The green and the orange are the two already in this file for
   gaining and losing ground, so the board's arrows mean what the map's colours mean. Quiet:
   it is a second reading of a rank that is already printed beside it, and the one round that
   made a chip loud on every row had to undo it twice. */
.crewmoved {
  margin-left: 6px; font-size: 10px; font-weight: 700; letter-spacing: .2px;
  font-variant-numeric: tabular-nums; vertical-align: 1px; opacity: .85;
}
.crewmoved.up { color: #39d98a; }
.crewmoved.down { color: #ff9f6b; }


/* The losses card is a disclosure when it is long enough to bury the controls under it. Its
   summary carries the heading it used to print directly, so it has to look like one -- and
   the count beside it is the honest version of the sentence that used to characterise the
   squares it was hiding. */
.crewlose > summary { cursor: pointer; list-style: none; }
.crewlose > summary::-webkit-details-marker { display: none; }
.crewlose > summary h4 { display: inline-flex; align-items: center; gap: 7px; }
.crewlosen { font-size: 11px; font-weight: 400; color: var(--mut, #9aa6c8); }
.crewlose > summary::after {
  content: ""; display: inline-block; margin-left: 7px;
  border: 4px solid transparent; border-left-color: var(--mut, #9aa6c8);
  transition: transform .15s ease;
}
.crewlose[open] > summary::after { transform: rotate(90deg); }
/* The name rule, shown beside the counter while there is still something to do about it. Its
   own colour because it is a correction rather than a count, and `:empty` so the label does
   not reserve a line for a message that is not there. */
.crewwhy { display: block; margin-top: 3px; font-size: 11px; color: #ff8ad8; }
.crewwhy:empty { display: none; }
#cf-name[aria-invalid="true"],
#ce-name[aria-invalid="true"] { border-color: #ff8ad8; }


/* ---------- Share crew ----------
   The modal behind "Share crew": the code big enough to scan off a phone held up to somebody,
   the address under it in full, and the two things you can do with it. It reuses `.crewqrg`
   from the sign-in card rather than a second QR style -- same modules, same animation, one
   definition. */
.crewshare { text-align: center; }
.crewshare .crewqrg { max-width: 230px; margin: 0 auto; }
/* The address is the half of this that survives a photocopier, so it is readable text and not
   a hint: somebody will type it in. `anywhere` because a slug can be long and a URL that
   overflows its card is one you cannot read the end of. */
.crewshareu { margin: 12px 0 2px; font-size: 13px; overflow-wrap: anywhere; }
.crewshareu a { color: var(--acc, #ff8ad8); }
.crewshare .crewacts { justify-content: center; margin-top: 14px; }
/* `<a>` in a row of `<button>`: without this it sits on the text baseline and runs a few
   pixels taller than the button beside it. */
.crewshare .crewacts .crewbtn { display: inline-flex; align-items: center;
  justify-content: center; text-decoration: none; }

/* ---------- the two actions on a tapped square ----------
   In the popup rather than behind a hover delay or a long press: this mode is used with a
   thumb, phones have no hover, and a long press is the OS's gesture before it is ours. The
   popup is already on screen and already names the crew, so it is where the actions belong. */
.crewpopacts { display: flex; gap: 6px; margin-top: 8px; }
/* Wraps rather than `nowrap`. Two buttons share about 200px of popup, so each gets ~95px,
   and "Подсветить" next to "Подробнее" does not fit on one line at 12px. A button on
   two lines is fine; a button with its word cut in half is not. */
.crewpopb { flex: 1 1 0; min-width: 0; padding: 7px 8px; font: inherit; font-size: 12px;
  line-height: 1.15; cursor: pointer; border-radius: 6px;
  border: 1px solid var(--line, #243055); background: rgba(255,255,255,.06);
  color: var(--ink, #e8edfb); }
.crewpopb:hover { background: rgba(255,255,255,.12); }
.crewpopb:focus-visible { outline: none; box-shadow: 0 0 0 2px #0b0f1c, 0 0 0 4px var(--sec, #ff8ad8); }
/* Which of the two is the one to press. `Details` opens the crew, which is what somebody who
   has just tapped a rectangle is asking for; `Highlight squares` is the specialist action and
   now says so twice over, in its weight and in its words. `flex: 0 1 auto` on the filled one
   so the longer label gets the room it needs -- it still wraps rather than being cut, which is
   the trap the comment above this block is about. */
.crewpopb.prime { flex: 0 1 auto; padding-inline: 12px; font-weight: 600;
  border-color: transparent; background: var(--sec, #ff8ad8); color: #10131f; }
.crewpopb.prime:hover { background: var(--sec, #ff8ad8); filter: brightness(1.08); }
.crewpopb.ghost { background: transparent; color: var(--mut, #9fb0d9); }
.crewpopb.ghost:hover { background: rgba(255,255,255,.08); color: var(--ink, #e8edfb); }

/* The board row "Details" lands on. The panel can be a long scroll, and arriving silently in
   the middle of a table leaves you hunting for what just happened. */
/* `outline`, not `background`. The rows this lights are two different elements: a `<tr>` in
   the table and a `<div class="pod gold1">` on the podium -- and the podium cards carry
   `background: linear-gradient(...) !important` from the host stylesheet, which an animation
   cannot beat. Measured: the animation ran for twelve sampled frames and the computed
   background stayed `rgba(0,0,0,0)` for every one of them.
   Nothing sets an outline on either, it draws over the gradient, and it costs no layout.
   The background goes with it for the table rows, where it does land. */
.crewlit { outline: 3px solid transparent; outline-offset: -3px;
  animation: crewlit 2.2s ease-out 1; }
@keyframes crewlit {
  0% { outline-color: rgba(255,138,216,.95); background-color: rgba(255,138,216,.42); }
  100% { outline-color: rgba(255,138,216,0); background-color: rgba(255,138,216,0); }
}
@media (prefers-reduced-motion: reduce) {
  /* No wave and no flash: the Highlight button still works, it simply lights the whole patch
     at once rather than sweeping, which `shine` does by its rings landing together. */
  .crewlit { animation: none; outline-color: rgba(255,138,216,.9);
    background-color: rgba(255,138,216,.22); }
}

/* The role, in words, beside the glyph. A phone has no hover, so `title="officer"` on a
   diamond was unreadable on the device this is built for -- and the roster offers a button
   called "Make officer" whose result was named nowhere on screen. */
.crewrolew { font-weight: 400; font-size: 10px; letter-spacing: .3px; text-transform: uppercase;
  color: var(--mut, #9aa6c8); margin-left: 4px; }

/* ---------- the crew header, on a phone ----------
   At the END of the file on purpose: these override top-level declarations of the same
   specificity, and at equal specificity the later rule wins. Written higher up, the cascade
   checker caught all three losing to the rules they were meant to replace.

   The crew's name gets the line. Beside the standings string it had 111px of a 318px header
   and still broke mid-name -- "Nordlys / Collective" -- because both want about 135px and
   there is only room for one. They are a heading and its subtitle, so they stack. */
@media (max-width: 560px) {
  /* The wrapping happens INSIDE the text block now, so the standing lands under the name
     rather than under the disclosure triangle. The summary itself no longer needs to wrap,
     which is what used to push the standing out to the far left. */
  .crewsumtxt > span:first-child { min-width: 0; flex: 1 0 100%; }
  .crewsumrole { flex: 1 0 100%; }
}

/* ---------- touch targets and two contrast failures ----------
   Measured by a reviewer, all on the phone this feature is built for. */

/* 78.6 x 29.1px, against a 44px minimum. The popup is the only route to Highlight and
   Details, so these are not optional chrome. */
.crewpopb { min-height: 44px; }

/* The crew label on a map marker ran 23px past the right edge of a 390px viewport with no
   clip and no ellipsis, rendering "Nordlys Collec". A marker label is a hint, not a heading:
   it gives up its tail rather than the viewport. */
.crewemb-n { max-width: 42vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* A badge standing down because a bigger one holds that spot -- see `declutterEmblems`.
   `visibility`, not `opacity`: `sizeEmblems` writes `style.opacity` on every marker every
   frame, and an inline declaration beats a class, so an opacity rule here would simply lose.
   `pointer-events` with it, because an invisible marker that still takes a tap hands the
   reader a crew they cannot see. */
.crewemb.crewembhid { visibility: hidden; pointer-events: none; }

/* The crew whose poster sent this reader here, on the sign-in card. Brighter than the hint it
   follows -- it is the specific reason, and the lines above it are the general pitch. */
.crewinvnote {
  margin: 0 0 10px; padding: 7px 9px; font-size: 12px; line-height: 1.35;
  color: var(--ink, #eef1fb); background: rgba(255,138,216,.10);
  border-left: 2px solid var(--sec, #ff8ad8); border-radius: 0 6px 6px 0;
}

/* What the marks beside the names mean, for the readers who cannot hover a `title`. */
.crewrolekey { margin: 6px 0 0; font-size: 11px; }
@media (hover: hover) and (pointer: fine) { .crewrolekey { display: none; } }

/* The two column wrappers, which exist only so a wide screen has something to put side by
   side. `contents` takes them out of the box tree entirely, so at every width below the
   desktop breakpoint the cards are laid out exactly as they were before the wrappers existed. */
.crewcol { display: contents; }

/* Your own crew on the standings. It was indistinguishable from every other line -- same
   background, same ink, same weight -- on the one screen whose job is telling you where you
   stand against everyone. A tint and a rule rather than a bolder weight: the row already
   carries a rank, a name, a swatch and two numbers, and making all of it heavier would read
   as emphasis on the numbers rather than on whose row it is. */
.crewboard tr.crewrowmine > td:first-child { box-shadow: inset 2px 0 0 var(--sec, #ff8ad8); }
/* Under the crew's name, inside the row's own cell. `.celln` is a flex LINE, so `display:
   block` here does not start a new one -- it just makes this another item on the same line,
   which is what the first attempt did: the board read "Spree Shift37 in one · 37 he" with
   the name clipped to make room. A full-basis flex item on a wrapping line is what actually
   puts it underneath. */
/* `podList` wraps whatever `label()` returns in a nowrap, ellipsising <span> -- shared with
   every other panel's table, so it is not ours to change. `:has()` opens that one span up
   on the rows that carry a detail line, and leaves every other table in the product alone. */
.crewboard table .celln > span:has(.crewrowdet) {
  display: block; white-space: normal; overflow: visible; min-width: 0; }
/* The name keeps the one-line ellipsis the shared span used to give it. */
.crewrowname { display: block; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
/* Wraps, and must. On `nowrap` this line refuses to shrink and the auto table layout grows
   the name column to fit it: measured in Ukrainian at 412px, `37 одним шматком · 37
   усього · 82 km²` held the cell at 199px and pushed the 329px table past the 340px
   card, so the board scrolled sideways. Nothing clipped -- the overflow moved outward
   instead, which is worse, because the row still looks intact while the card no longer
   fits. German is 163px and fine either way; Ukrainian is the one that decides it. */
.crewrowdet { display: block; margin-top: 2px; font-size: 11px; line-height: 1.25;
  color: var(--mut, #9aa6c8); font-variant-numeric: tabular-nums; white-space: normal; }
/* The standing on a podium card: small, above the patches-and-gained line, because the card
   leads with squares and this is the number that explains its position. */
.crewpodpts { display: block; font-size: 11px; letter-spacing: .3px;
  color: var(--mut, #9aa6c8); font-variant-numeric: tabular-nums; }
/* The ranked rows carry their gain inside the detail line under the name, so `podList`'s
   fourth column comes out empty on every row and must not reserve a gutter beside the score.
   Not in the phone block: this board is capped at a 31rem measure, so the rows look the same
   at 390 and at 1920 and a rule that only fires below 560 would leave the gap everywhere
   else. */
.crewboard table td.val.sub:empty { padding: 0; width: 0; }
.crewboard tr.crewrowmine > td { background: rgba(255,138,216,.10); }
/* The podium card needs more than the ranked row does, because it already has a medal rule
   of its own in gold: a 1.33px half-alpha outline at -0.67px offset sat directly against that
   and read as a focus ring at 1x. Solid, two pixels, and the same 10% wash the rows carry, so
   the two treatments are recognisably one idea. */
.crewboard .pod.crewrowmine {
  outline: 2px solid var(--sec, #ff8ad8); outline-offset: -2px;
  background-image: linear-gradient(rgba(255,138,216,.12), rgba(255,138,216,.12));
}

/* Good news, with the weight the bad news has always had. The three one-shot notices in this
   mode -- turned down, removed, folded -- are full cards with a heading; taking ground was a
   grey line set in 11px. It sits at the top of the crew card, where the diff already was, and
   it is gold rather than pink because every other pink thing on this panel is either a control
   or a warning and this is neither. */
.crewgood {
  margin: 0 0 12px; padding: 11px 13px;
  background: linear-gradient(135deg,
    rgba(255,210,74,.14), rgba(255,210,74,.05));
  border: 1px solid rgba(255,210,74,.34);
  border-radius: 8px;
}
.crewgood h4 {
  margin: 0 0 3px; font-size: 14px; letter-spacing: .3px;
  color: var(--gold, #ffd24a);
}
.crewgood p { margin: 0; font-size: 12.5px; line-height: 1.4; color: var(--ink, #eef1fb); }



/* The podium name box is 113px at 1280 and 78px at 390 -- but the narrow one WRAPS and the
   wide one clips, so the bigger screen showed less of the name. Two lines there too. */
.pod .pname { white-space: normal; overflow-wrap: anywhere; text-overflow: clip; }

/* Two lines reserved AND two lines at most. Reserving without capping is a guess about the
   longest name: "Nordlys Collective 456c" takes three lines in a 70px pod, so the staircase
   came back at 11.5px where it had been 12.6. A reserve that is also a cap makes the two
   numbers the same number.

   This lived under `@media (max-height: 700px)` -- a short-screen rule -- so it applied at
   neither 390x844 nor 1280x860, which is every phone held upright and every laptop. A
   reviewer measured the consequence at both: card tops at 2ND 369.9 / 1ST 363.9 / 3RD 354.3,
   so THIRD place stood 9.6px taller than first and 15.6px taller than second, purely because
   "Nordlys Collective 53db" wrapped and the other two did not. `.pod.p1`'s `padding-top` is
   doing elevation work, and a long name was silently outbidding it. Nothing about the reason
   for this rule was ever about the height of the screen. */
/* 2.6em, and the line-height pinned in the same rule so the two numbers cannot drift: two
   lines of 1.3 is 2.6em exactly. At 2.4em the reserve was 28.8px while two 12px lines need
   31.2px, so it fell 2.4px short of its own second line and third place still stood 2.4px
   taller than second -- most of the staircase gone and a measurable remainder left. */
.crewboard .pod .pname {
  line-height: 1.3; min-height: 2.6em; display: -webkit-box; -webkit-line-clamp: 2;
  -webkit-box-orient: vertical; overflow: hidden;
}

/* The leader's star was the first thing truncation ate: `.crewcname` clips at 123px of a
   294px row and the overflowing glyph was exactly the mark identifying the leader. The mark
   is pulled out of the clipped box so the NAME gives way instead. */
.crewcname { min-width: 0; }
.crewcrow .crewrole { flex: 0 0 auto; }

/* The hook on the signed-out card: the first thing anybody reads about this feature, and
   until now there was nothing there at all. Bigger than body copy and lighter than the
   heading, because it is a promise rather than a label. */
.crewhook { margin: 2px 0 6px; font-size: 15px; line-height: 1.3; font-weight: 600;
  color: var(--ink, #e8edfb); }
.crewwhat { margin: 0 0 14px; }

/* ---------- reviewer D's measurements ----------

   The swatch strip sat flush against the heading that follows it: 0px, where every other
   section boundary in that modal is 14px. The legend's own `margin: 10px 0 0` gave it no
   bottom at all. */
.crewhow .crewlegend { margin-bottom: 14px; }

/* Column counts that DIVIDE the grids. `auto-fill minmax(34px, 1fr)` resolved to 11 at
   1280, so 48 colours broke 11/11/11/11/4 and 12 patterns broke 11/1 -- one swatch alone
   under a row of eleven. 48 and 12 share 12, 8, 6, 4; those are the only counts either grid
   should ever use. */
@media (min-width: 561px) {
  .crewpick { grid-template-columns: repeat(8, 1fr); }
}
@media (min-width: 900px) {
  .crewpick { grid-template-columns: repeat(12, 1fr); }
}

/* The bar belonged to neither row: its centre sat 22px below its own name and 21px above
   the next one. Pulled up against the name it describes, with the gap below the pair. */
.crewcontrib .crewcrow { padding: 2px 0 6px; }

/* "Since you last looked" — the first line on the crew card, and the only thing in the panel
   that is news rather than state. Accent-tinted so it reads as a change and not a heading. */
.crewsince { margin: 0 0 6px; font-size: 12px; color: var(--sec, #ff8ad8);
  font-weight: 600; letter-spacing: .2px; }

/* ---------- the desktop actually being a desktop ----------

   The panel is `width: min(94vw, 720px)` for every section, which at 1280 leaves 560px of
   window unused and stacks a 2234px column through a 508px viewport. A reviewer measured it
   and called it the phone layout centred, which it was: content column 434px inside a 466px
   card inside a 719px panel inside a 1280px window.

   The answer is not a wider single column -- a 1000px line length reads worse than a 460px
   one, and the measures in this file were tuned against that 450px row. It is two columns:
   what this rider can DO on the left, the standings beside it rather than a screen below.
   Both columns keep a comfortable measure and the panel stops being a tall straw.

   `.crewcol` is `display: contents` everywhere below this breakpoint, so the phone layout is
   untouched -- the wrappers are not in the box tree at all there. */
@media (min-width: 1100px) {
  .panel[data-sec=crews] { width: min(94vw, 1100px); }
  #crewpanel { display: flex; align-items: flex-start; gap: 0 22px; }
  /* `display` FIRST. Without it the wrappers stay `contents`, their cards become flex items
     of `#crewpanel` directly, and the whole left column lays itself out as a ROW -- which
     looks correct for exactly as long as there are two cards on screen and falls apart at
     three. `flex` on an element that is not in the box tree does nothing at all.
     `min-width: 0` on both, or a long crew name pushes the panel wider than it is and the
     body scrolls sideways. */
  .crewcol { display: block; flex: 1 1 0; min-width: 0; }
  /* The standings stay put while you read your own card. The two columns are independent and
     the left one is roughly twice the height of the right, so scrolling past the board left a
     466x708 rectangle of nothing -- 46% of the open panel at 1920, measured. Sticky alone is
     not enough: the board is taller than the panel body, and a sticky element taller than its
     scrollport pins its top and puts its own bottom permanently out of reach. So it gets a
     height and its own scroll, which is the one place in this panel where a nested scroller
     earns itself -- the alternative is half a window of blank.
     The panel is `min(76dvh, ...)` capped at 760px and the header takes 52; this follows it. */
  .crewcolside {
    position: sticky; top: 0;
    /* The panel's OWN formula, minus its header. This was `min(76dvh - 60px, 700px)`, which
       ignores the second term of `.panel`'s height -- `calc(100dvh - var(--panres))` -- and
       that term wins whenever the dock and the champions card leave less than 76dvh. At
       1280x860 a reviewer measured the column running 97.2px past the panel's clipped edge,
       with its own scroll track below the fold and two scrollbars side by side 25px apart.
       Two formulas for one height is one formula too many.
       The 72 is the panel header plus `.pbody`'s own 12px top padding, which the column
       starts below: subtracting only the header left it ending 3.6px past the clipped edge. */
    max-height: min(calc(76dvh - 72px), calc(100dvh - var(--panres, 0px) - 72px), 688px);
    overflow-y: auto;
    /* Chaining, not `contain`. Contained, a wheel over the standings ran them to their end and
       then stopped dead, so the panel only moved if you moved the pointer off the board --
       which is what a reviewer described as two scrollers 18px apart doing different things.
       Letting it chain means one gesture carries through: the standings scroll, and when they
       are spent the panel carries on, which is what a wheel is expected to do. */
    overscroll-behavior: auto;
    scrollbar-width: thin;
  }
  /* Signed out, the two columns swap sides instead of collapsing.
     This was one narrow column, because the left half was a single sign-in card against a
     board four times its height and a reviewer measured 515x471 of nothing -- 27% of the
     split area -- on the first screen a stranger meets. True, and stacking them cost more
     than it saved: the pairing QR is 190x190 and fills the top of the panel, so the board and
     the podium went below the fold on a 1440px screen. The thing that would sell this became
     the thing you scroll for.
     The board takes the main column and the sign-in card sits beside it at the width it was
     drawn for. Nothing is blank and nothing is buried. Not sticky here: in this arrangement
     the board IS the column, so pinning it to the viewport pins the whole panel. */
  #crewpanel:has(> .crewcolsolo) > .crewcolsolo {
    order: 2; flex: 0 0 360px; max-width: 360px;
  }
  #crewpanel:has(> .crewcolsolo) > .crewcolside {
    order: 1; flex: 1 1 0; position: static; max-height: none; overflow-y: visible;
  }
}
