/* Aerosphere v0.1 - a trade paper for the apron and the ops room.
   Precise, instrument-like, warm. Rules separate, never shadows.
   The departure board (mono, uppercase, tabular) marks dates, codes and counts.

   Two rules hold the system together:
   1. Only a feed card and the composer carry a border. Everything else is a
      ruled section on the page ground under a mono eyebrow.
   2. Seven type steps (12, 13, 15, 17, 21, 26, 32) and 10 spacing steps
      (4, 6, 8, 10, 12, 16, 20, 24, 32, 40), held in rem against a 16 px root,
      so a browser set to 20 px scales the page. The display face appears at
      21 px and above, nowhere else.
   3. D74: the measure is the number of characters, measured, not the number of
      ch. On /posts/31 the body renders 68 characters a line at 52ch and the
      lede 62 at 47ch. The ch unit measures a zero and prose runs narrower, so
      var(--measure) rendered 75 and 68ch rendered 85.

   Sections: tokens, base, type, top bar, menus, banner, layout, sections,
   feed cards, figures, chips, buttons, forms, lists, board rows, index rows,
   profiles, jobs, composer, messages, tables, footer, sphere, phone, motion. */

/* ------------------------------------------------------------------ tokens */

:root {
  --bg: #F2F3F0;
  --surface: #FFFFFF;
  --surface-2: #E9EBE6;
  --line: #D5D8D2;
  --field-line: #7E847B;
  --ink: #101A26;
  --ink-2: #58636F;
  /* D45. Each industry has its own accent, and the hue is the only thing that
     changes. The lightness is the number that had to clear 4.5:1 on --bg,
     --surface and --surface-2 for every hue the product ships: 27 per cent in
     light, 69 in dark. Business aviation lands on #114D78, a shade under the
     #145C8E it replaces. */
  --accent: hsl(var(--planet-hue) 75% 27%);
  --accent-ink: #FFFFFF;
  --bar: #101A26;
  --bar-ink: #F4F6F8;
  --bar-edge: transparent;
  --plate: #101A26;
  --silver: #7C8794;
  --gold: #A8780A;
  /* D100. The 3 state colours are fixed values and never read --planet-hue.
     A planet writes one number into its own JSON file, and on hue 148 the
     accent came out 1.06:1 from --good, so a link, a followed chip and an
     active Useful were one colour on planet 2. Good, warn and demo mean the
     same thing in every industry, so they are the same colour in every
     industry. The accent recipe below is unchanged. */
  --good: #277244;
  --warn: #AB4A25;
  --demo: #6D57B8;

  /* Text-safe metals. The plain tokens keep their value for rules and tints,
     which only need 3:1; these two carry the label text at AA. */
  --silver-ink: #5F6975;
  --gold-ink: #8C6209;

  /* The bar is dark in both themes, so it carries the light end of the hue. */
  --bar-accent: hsl(var(--planet-hue) 60% 69%);
  --bar-soft: rgba(255, 255, 255, .10);
  --bar-soft-2: rgba(255, 255, 255, .17);
  --bar-line: rgba(255, 255, 255, .45);
  --bar-faint: rgba(244, 246, 248, .62);

  /* E4 1. White initials at 13.6 px are small text, so the disc has to clear
     4.5:1 on every hue in the mock. At 40 per cent saturation that is 34 per
     cent lightness, and no hue moves. */
  --avatar-sat: 40%;
  --avatar-light: 34%;

  /* One planet, one hue. The template sets it on the root element, because
     --accent and --bar-accent are computed there. */
  --planet-hue: 205;

  --radius: 10px;
  --radius-sm: 8px;
  --radius-xs: 4px;
  --shadow: 0 10px 28px rgba(16, 26, 38, .18);

  --font: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
  --font-display: "Bricolage Grotesque", "Helvetica Neue", Arial, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* Seven steps, and no others. E4 5: in rem, so a member who sets the browser
     to 20 px gets a 20 px page. At the 16 px default every step is the pixel
     value it has always been: 12, 13, 15, 17, 21, 26, 32. */
  --t1: 0.75rem;
  --t2: 0.8125rem;
  --t3: 0.9375rem;
  --t4: 1.0625rem;
  --t5: 1.3125rem;
  --t6: 1.625rem;
  --t7: 2rem;

  /* Ten steps, and no others, and in rem for the same reason. The 2 half steps
     carry the small gaps the interface reaches for: 6 px between chips, 10 px
     under a panel title. At 16 px: 4, 6, 8, 10, 12, 16, 20, 24, 32, 40. */
  --s1: 0.25rem;
  --s1h: 0.375rem;
  --s2: 0.5rem;
  --s2h: 0.625rem;
  --s3: 0.75rem;
  --s4: 1rem;
  --s5: 1.25rem;
  --s6: 1.5rem;
  --s7: 2rem;
  --s8: 2.5rem;

  /* D74. The measure, in the unit the browser will honour, set to the number
     of characters it actually renders. Body 68, lede 62, measured on
     /posts/31 in IBM Plex Sans at 15 px. Every block of prose in the file
     reads one of these two, so the measure moves in one place. */
  --measure: 52ch;
  --measure-lede: 47ch;

  color-scheme: light;
}

/* Light is the default. Dark only when the member picks it with the toggle. */
:root[data-theme="dark"] {
  --bg: #0E1319;
  --surface: #161C24;
  --surface-2: #1F2731;
  --line: #2B3540;
  --field-line: #68727F;
  --ink: #E6E9EC;
  --ink-2: #9AA5B1;
  --accent: hsl(var(--planet-hue) 60% 69%);
  --accent-ink: hsl(var(--planet-hue) 55% 8%);
  --bar: #182029;
  --bar-ink: #E6E9EC;
  --bar-edge: #39434F;
  --plate: #1F2731;
  --silver: #9AA4AE;
  --gold: #D9AC3A;
  /* D100. Fixed here too, for the same reason. */
  --good: #63BD8A;
  --warn: #E38561;
  --demo: #AC95EA;
  --silver-ink: #B7C1CB;
  --gold-ink: #D9AC3A;
  --bar-accent: hsl(var(--planet-hue) 60% 69%);
  --bar-faint: rgba(230, 233, 236, .62);
  --avatar-sat: 35%;
  --avatar-light: 35%;
  --shadow: 0 10px 28px rgba(0, 0, 0, .5);
  color-scheme: dark;
}

/* -------------------------------------------------------------------- base */

* { box-sizing: border-box; }

/* A class that sets a display value beats the browser's own rule for the
   attribute, so the attribute says it louder. */
[hidden] { display: none !important; }

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

/* SC 2.4.11. The bar is sticky, so an in-page jump has to clear it. It is
   56 px on a wide screen and 2 rows on a phone, with the banner under it.
   E4 1 and D74: in rem, because the bar it clears is in rem. */
.has-sticky-bar { scroll-padding-top: 4rem; }

/* The bar folds to 2 rows at 949 px and the search takes a third at 699.
   E4 1 and D100. Measured on both planets at 700, 710, 760, 860 and 949 px:
   93 px at a 16 px root, 116 at a 20 px root, against 108 and 135 px of
   padding. The 164 px bar that beat this number at 700 px was the angles row
   wrapping the account disc onto a third row; the row is tighter now and holds
   one line down to 700. */
@media (max-width: 949px) {
  .has-sticky-bar { scroll-padding-top: 6.75rem; }
}

/* E4 5 and D93. The number follows the tallest bar either planet draws, not the
   one this planet happens to draw. At a 20 px root the drones bar is 4 rows and
   211 px from 480 to 699 px, against 161 px of business aviation, so 8.75rem
   left 36 px of the target under it. Eleven rem is 220 px at that root. */
@media (max-width: 699px) {
  .has-sticky-bar { scroll-padding-top: 11rem; }
}

/* E4 4 and D85. Under 480 px the open search field sits under the bar rather
   than in it, and at 320 px the brand holds its width and the bar takes a
   second row. Measured at a 20 px root that bar is 106 px, so 4rem left 26 px
   of the target underneath it. The padding follows the bar it clears. */
@media (max-width: 479px) {
  .has-sticky-bar { scroll-padding-top: 6.75rem; }
}

body {
  margin: 0; background: var(--bg); color: var(--ink);
  font-family: var(--font); font-size: var(--t3); line-height: 1.55;
}

p { margin: 0 0 var(--s2); }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

code {
  font-family: var(--font-mono); background: var(--surface-2);
  border-radius: var(--radius-xs); padding: 1px 5px; font-size: var(--t2);
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}
/* The page accent is 2.46:1 on the bar. The bar keeps its own. */
.topbar :focus-visible { outline-color: var(--bar-accent); }

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

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 50; padding: var(--s2h) var(--s4);
  background: var(--accent); color: var(--accent-ink);
}
.skip:focus { left: 0; }

.muted { color: var(--ink-2); }

/* -------------------------------------------------------------------- type */

/* Headings are the text face by default. The display face is added back by
   class, and only at 21 px and above, so a member hears 3 voices. */
h1, h2, h3, h4 {
  font-family: var(--font); font-weight: 600; line-height: 1.25;
  margin: 0 0 var(--s2); text-wrap: balance;
}

.display, .page-title, .brand,
.card-event .card-name, .card-job .card-name,
.event-row .list-name, .job-row .list-name,
.event-line .list-name, .job-line .list-name {
  font-family: var(--font-display); font-optical-sizing: auto;
  font-weight: 700; letter-spacing: -.01em;
}

.page-title { font-size: var(--t6); margin-bottom: var(--s1); overflow-wrap: break-word; }
/* A title with no lede under it still needs air before the first section. */
.page-title + .card, .page-title + .table-wrap, .page-title + .list,
.page-title + .notice, .page-head + .card { margin-top: var(--s5); }
.page-lede { color: var(--ink-2); margin-bottom: var(--s5); max-width: var(--measure-lede); }
.card-title { font-size: var(--t4); font-weight: 600; margin-bottom: var(--s1); }
/* A name is ink and takes the accent on hover. Only a real link in running
   text is blue, or a grid of 15 cards reads as 15 links and nothing else. */
.card-title a { color: var(--ink); }
.card-title a:hover { color: var(--accent); }

/* Data reads on the board: mono, tabular, one width per digit. */
.count, .counter, .card-time, .why, .badge, .tier, .demo-mark, .dateblock,
.sponsored-label, .result-count, .board-cell, .place, .profile-counts,
.stat-list, .stats, .footer-inner, .mono, .pay, .org-place,
.message-meta, .trend, .guide-number, .chip-code {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}
.count { font-weight: 500; }

/* D52. The eyebrow is reserved: a section label, a column head, a date, a code
   or a count. A face that marks everything marks nothing.
   Twelve pixels is the floor. Below that the apron loses it. */
.eyebrow, .panel-title, .section-title, .menu-head, .panel-subtitle, .step,
.stats-title, .month-head, .list-kicker, .result-count,
.demo-shortcut-label, .offer-label, .board-cell, .card-eyebrow,
.table th[scope="col"], .filter-rail .panel-title {
  font-family: var(--font-mono); font-weight: 500; font-size: var(--t1);
  text-transform: uppercase; letter-spacing: .06em; color: var(--ink-2);
  line-height: 1.4;
}

/* D52, the other half. A term in a definition list and a label on a form name
   a value, not a block, so they read in the text face at the size the value
   reads at. Row heads in a table are labels too; column heads are not. */
.details dt, .table th, .filter-field label:not(.check), .filter-field .label,
.filter-field > legend, .field > .label, .field > legend.label {
  font-family: var(--font); font-weight: 600; font-size: var(--t2);
  text-transform: none; letter-spacing: 0; color: var(--ink); line-height: 1.4;
}
/* The value is the answer, so the term stays the quieter of the two. */
.details dt { color: var(--ink-2); }

/* ----------------------------------------------------------------- top bar */

.topbar {
  position: sticky; top: 0; z-index: 30;
  background: var(--bar); color: var(--bar-ink);
  border-bottom: 1px solid var(--bar-edge);
}

/* E4 1 and D74. Every height in the bar is in rem. A member who sets the
   browser to 20 px gets a taller bar, not a clipped one, and the 4 control
   heights the bar is built from are 1.75, 2.125, 2.25 and 2.5rem. */
.topbar-inner {
  max-width: 1280px; margin: 0 auto; padding: 0 var(--s4);
  min-height: 3.5rem; display: flex; align-items: center; gap: var(--s3);
}

/* The brand may shrink. Nothing in the bar holds a floor wider than a phone. */
.topbar-brand { display: flex; align-items: center; gap: var(--s2h); flex: 0 1 auto; min-width: 0; }

.brand {
  display: inline-flex; align-items: center; gap: var(--s2); color: var(--bar-ink);
  font-size: var(--t5);
}
.brand:hover { text-decoration: none; color: var(--bar-ink); }
.brand-mark { display: block; flex: 0 0 auto; }
/* The horizon inside the mark is the one thing that changes per planet. */
.brand-mark .horizon { fill: hsl(var(--planet-hue) 52% 62%); }

.topbar .planet-picker > summary {
  height: 1.75rem; padding: 0 var(--s3); border-radius: 999px;
  background: var(--bar-soft); color: var(--bar-ink);
  font-size: var(--t2); font-weight: 600; white-space: nowrap;
}
.topbar .planet-picker > summary:hover { background: var(--bar-soft-2); }
/* The pill carries the industry's first word on a phone. The long name stays
   in the accessible name, so the control never renames itself. */
.pill-short { display: none; }

/* From 950 to 1099 px the bar is 1 row and the room runs out. The pill gives
   way before the search field does, and cuts its name rather than running
   under it. The wordmark and the caret hold their size.
   E4 1: the ceiling is in rem, so the pill is the same share of the bar at
   every root size, and it never grows into the search field. */
.topbar-brand > .brand { flex: 0 0 auto; }
.topbar .planet-picker { min-width: 0; max-width: 100%; }
.topbar .planet-picker > summary,
.pill-static { min-width: 0; max-width: 11rem; overflow: hidden; }
.topbar .planet-picker .pill-long {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.searchbar {
  flex: 1 1 15rem; max-width: 30rem; margin: 0 auto;
  display: flex; gap: var(--s2); min-width: 8.75rem; align-items: center;
}
.topbar .searchbar input {
  flex: 1 1 auto; min-width: 0; height: 2.125rem; padding: 0 var(--s3);
  font-size: var(--t3); background: var(--bar-soft);
  border: 1px solid var(--bar-line); border-radius: var(--radius-sm);
  color: var(--bar-ink);
}
.topbar .searchbar input::placeholder { color: var(--bar-faint); }
/* D93: the bar's own height, not the page button's floor. Every control in the
   bar is 2.125rem and the row is built on it. */
.topbar .searchbar .btn {
  height: 2.125rem; min-height: 0; padding: 0 var(--s3);
  background: var(--bar-soft); border: 1px solid var(--bar-line);
  color: var(--bar-ink); font-size: var(--t2); white-space: nowrap;
}
.topbar .searchbar .btn:hover { background: var(--bar-soft-2); }

/* The magnifier. It is a link to the search page until the script upgrades
   it to a control that opens the field in place. */
.search-icon {
  display: none; flex: 0 0 auto; width: 2.125rem; height: 2.125rem;
  align-items: center; justify-content: center;
  border-radius: var(--radius-sm); background: var(--bar-soft);
  border: 1px solid var(--bar-line); color: var(--bar-ink);
}
.search-icon:hover { background: var(--bar-soft-2); text-decoration: none; }

/* E4 1. The row wraps. At a 20 px root the 5 stops and the avatar need more
   room than a 480 px screen has, and a row that cannot wrap pushes the avatar
   menu past the edge and scrolls the page sideways. */
.angles { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; flex-wrap: wrap; }

.angles > .dropdown > summary, .topbar-link {
  height: 2.125rem; display: inline-flex; align-items: center; padding: 0 var(--s2h);
  border-radius: var(--radius-xs); color: var(--bar-ink);
  font-size: var(--t3); font-weight: 600;
}
.angles > .dropdown > summary:hover, .topbar-link:hover {
  background: var(--bar-soft);
  text-decoration: none;
}
.angles > .dropdown[open] > summary { box-shadow: inset 0 -2px 0 var(--bar-accent); }

.topbar-me {
  display: inline-flex; align-items: center; margin-left: var(--s1);
  min-height: 2.125rem;
}
/* D53. The avatar menu only exists on a phone, where the 3 angles move into
   it. Above 480 px the avatar is what it always was: a link to the profile. */
.topbar-me-menu { display: none; }
.topbar-me-menu > summary { list-style: none; cursor: pointer; padding: 0; position: relative; }
.topbar-me-menu > summary::-webkit-details-marker { display: none; }
.topbar-me-menu > summary .caret { margin-left: var(--s1); }
.menu-tall { max-height: 68vh; overflow-y: auto; }
.menu-tall .menu-head:not(:first-child) { margin-top: var(--s2); }

.badge {
  display: inline-block; min-width: 20px; margin-left: var(--s1); padding: 0 var(--s1h);
  border-radius: 999px; background: var(--accent); color: var(--accent-ink);
  font-size: var(--t1); font-weight: 500; line-height: 1.125rem; text-align: center;
}
.topbar .badge { background: var(--bar-accent); color: var(--bar); }

/* ------------------------------------------------------------------- menus */

.dropdown { position: relative; }

.dropdown > summary {
  list-style: none; cursor: pointer; height: 2.125rem; display: inline-flex;
  align-items: center; padding: 0 var(--s2h); border-radius: var(--radius-xs);
  font-size: var(--t3); color: var(--ink); user-select: none;
}
.dropdown > summary::-webkit-details-marker { display: none; }

/* The caret is drawn by a span the screen reader skips, so it never joins the
   accessible name of the control it decorates. */
.caret { flex: 0 0 auto; margin-left: var(--s1h); font-size: var(--t1); opacity: .7; }
.caret::after { content: "\25BE"; }
details[open] > summary .caret::after { content: "\25B4"; }

.dropdown .menu {
  position: absolute; top: 2.5rem; left: 0; min-width: 13.125rem; z-index: 40;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow); padding: var(--s1h);
}
.menu-wide { min-width: 15.625rem; }
.menu-head { padding: var(--s1h) var(--s2h) var(--s1); margin: 0; }

.menu-item {
  display: flex; align-items: center; min-height: 2rem; padding: var(--s1h) var(--s2h);
  border-radius: var(--radius-xs); color: var(--ink); font-size: var(--t3);
}
.menu-item:hover { background: var(--surface-2); text-decoration: none; }
.menu-item.is-current { color: var(--accent); font-weight: 600; }
.menu-item.is-off { color: var(--ink-2); cursor: default; }

/* ------------------------------------------------------------------ banner */

/* D54. One sentence in sentence case, in the text face. Thirty words of mono
   capitals on every page was a notice nobody could read at a glance. */
.banner {
  background: var(--surface-2); color: var(--ink-2); font-size: var(--t2);
  text-align: center; line-height: 1.4;
  padding: var(--s1h) var(--s4); border-bottom: 1px solid var(--line);
}

/* ------------------------------------------------------------------ layout */

.layout {
  max-width: 1280px; margin: 0 auto; padding: var(--s6) var(--s4) var(--s8);
  display: grid; gap: var(--s6);
  grid-template-columns: minmax(0, 1fr); align-items: start;
}

.col { min-width: 0; }
/* A thin page keeps a floor, so the footer never rides up the screen. */
.col-main { min-height: 52vh; }

/* D32. One column, and the DOM is the visual order: the strip and the feed
   inside main, then the industry rails, then the sphere sheet, which starts
   closed. No `order`, so the tab ring never jumps up the page and back. */

/* E3 r2-8. Between 700 and 1099 px there is room for the feed and 1 rail
   column, not 2. Both rails ride in that column, stacked in the order the
   DOM holds them, so no rail lands under the last card.

   E3 2 and D85. The feed spans both rows, so the grid split its 6,546 px
   across them and the second rail started 3,696 px down, under 2,200 px of
   empty column. The second row is flexible, so the tall spanning column
   gives nothing to the first row: the row is the height of the rail above,
   and the rails read as one column again. */
@media (min-width: 700px) {
  .layout { grid-template-columns: minmax(0, 1fr) 260px; }
  .col-main { grid-column: 1; grid-row: 1 / span 2; }
  .col-right { grid-column: 2; grid-row: 1; }
  .col-left { grid-column: 2; grid-row: 2; }
  .layout-wide .col-left { grid-row: 1; }
  .layout-plain { grid-template-columns: minmax(0, 1fr); }
  .layout-plain .col-main { grid-column: 1; grid-row: 1; }
  .layout-plain .col-left { grid-column: 1; grid-row: 2; }
}

/* E3 2 and D85. The 2 rails share one column here and the feed spans both of
   its rows, so the grid split the feed's 6,546 px across them and the second
   rail started 3,696 px down, under 2,200 px of empty column. A flexible
   second row takes the whole of that spanning column's overflow, so the first
   row is the height of the rail in it and the 2 rails read as one column. */
@media (min-width: 700px) and (max-width: 1099px) {
  .layout { grid-template-rows: min-content 1fr; }
  /* E3 5 and D93. One rail column at this width, so the order in it is a
     choice. The member's own card and the quarterly nudge were the last 2
     panels, 1,600 px down, and the nudge asked for an action nobody reached.
     They read first, which is where they sit past 1,100 px as well. */
  .col-left { grid-column: 2; grid-row: 1; }
  .col-right { grid-column: 2; grid-row: 2; }
}

@media (min-width: 1100px) {
  .layout { grid-template-columns: 280px minmax(0, 1fr) 320px; }
  .col-left, .layout-wide .col-left { grid-column: 1; grid-row: 1 / span 2; }
  .col-main { grid-column: 2; grid-row: 1; }
  .col-right { grid-column: 3; grid-row: 1; }
  .layout-wide { grid-template-columns: 280px minmax(0, 1fr); }
  /* A page with no planet has one column at every width, or the content
     lands in the 280 px rail slot and reads at half the measure. */
  .layout-plain { grid-template-columns: minmax(0, 1fr); }
  .layout-plain .col-main { grid-column: 1; grid-row: 1; }
}

/* E3 1 and D85. A reading page holds the measure on the box, not only on the
   sentence: the card, its actions and its chips stop where the text does. The
   number is the measure plus the card around it, the card's padding each side
   and its hairline, so the box follows the measure wherever the measure goes.
   It was 720 px, set when the measure rendered 75 characters; D74 cut the
   measure to 468 px and left a third of every card white. */
.page-read .col-main { max-width: calc(var(--measure) + 2 * var(--s5) + 2px); }

/* E3 6 and D61. The column was capped and the container was not, so a reading
   page sat left of centre with a void beside it. The container stops with the
   column: the rail, the gutter, the reading card and the page's own gutters. */
@media (min-width: 1100px) {
  .page-read .layout-wide {
    max-width: calc(280px + var(--s6) + var(--measure) + 2 * var(--s5) + 2px + 2 * var(--s4));
  }
  .page-read .layout-plain {
    max-width: calc(var(--measure) + 2 * var(--s5) + 2px + 2 * var(--s4));
  }
}

/* E3 4. Two pages of 4 lines, centred, at the width of the front door. */
.layout-narrow { max-width: 560px; }

/* E3 3 and D61. The admin is labels against values. Past 880 px the label and
   the number it names stop reading as a pair. */
.layout-mid { max-width: 880px; }

/* The front door. One column, centred, nothing else on the page: the mark,
   what this is, a sample of what people are writing, then the form. */
.page-signin .layout {
  max-width: 560px; padding-top: 8vh; padding-bottom: var(--s8);
}
.signin-mark {
  display: flex; align-items: center; justify-content: center; gap: var(--s2h);
  margin: 0 0 var(--s6); color: var(--ink);
  font-family: var(--font-display); font-optical-sizing: auto;
  font-weight: 700; font-size: var(--t7); letter-spacing: -.02em;
}
.signin-mark svg .horizon { fill: hsl(var(--planet-hue) 52% 46%); }
.page-signin .page-title, .page-signin .page-lede { text-align: center; }
.page-signin .page-lede { margin-left: auto; margin-right: auto; }
.page-signin .card.narrow { max-width: none; }

/* --------------------------------------------------------- ruled sections */
/* A section is an eyebrow, its content on the page ground, and a rule under.
   No box. The feed card and the composer are the exceptions, further down. */

.card {
  background: transparent; border: 0; border-radius: 0;
  padding: 0 0 var(--s5); margin: 0 0 var(--s5);
  border-bottom: 1px solid var(--line);
}
.card:last-child { border-bottom: 0; padding-bottom: 0; }
.card.narrow { max-width: 560px; }

.panel-title { margin: 0 0 var(--s2h); }
.section-title { color: var(--ink); margin: var(--s7) 0 var(--s3); }
.panel-subtitle { margin: var(--s5) 0 var(--s1h); }
.panel-foot { margin: var(--s3) 0 0; font-size: var(--t2); }
.card-meta { color: var(--ink-2); font-size: var(--t2); margin: 0; }

/* D74. Measure. The ch unit is the width of a zero and prose runs narrower, so
   the number of ch is never the number of characters. This is set to the value
   that renders the 68 the design asks for. The lede keeps its shorter one. */
.card-body, .comment-body, .message-body, .story-body > .card-body {
  white-space: normal; word-break: break-word; max-width: var(--measure);
}
.result-count { margin: 0 0 var(--s2h); }
.step { margin-bottom: var(--s1h); }
.crumbs { font-size: var(--t2); margin-bottom: var(--s3); }
.month-head { margin: var(--s6) 0 var(--s2); }
.card-eyebrow { margin: 0 0 2px; }
/* A commercial disclosure takes the gold the sponsored label uses. */
.eyebrow-paid { color: var(--gold-ink); }

/* The 2 blocks in the left column that keep a shape of their own. */
.col-left .card-me {
  background: var(--surface); border-radius: var(--radius);
  padding: var(--s4); border-bottom: 0; margin-bottom: var(--s5);
}
.col-left .card-nudge {
  border: 1px solid var(--accent); border-radius: var(--radius);
  padding: var(--s4); font-size: var(--t2); margin-bottom: var(--s5);
}
.col-left .panel-foot, .col-right .panel-foot { font-size: var(--t2); }

.filter-rail .panel-title { margin: var(--s5) 0 var(--s2); display: block; }
.filter-rail > .panel-title:first-child,
.filter-rail fieldset:first-of-type .panel-title { margin-top: 0; }
.filter-rail fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }

/* E3 7. A notice is a notice. The bar was --good, so founder pricing read as
   a success message. Green is kept for a state that actually went well. */
.notice {
  background: var(--surface-2); border-left: 3px solid var(--line);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: var(--s2h) var(--s3); margin-bottom: var(--s3);
}
/* E4 2. A refusal is not a success. The rule that was missing made every
   validation error take the green of the rule above it. */
.notice-warn { border-left-color: var(--warn); color: var(--warn); font-weight: 600; }
.notice-warn a { color: var(--warn); text-decoration: underline; }
/* E4 3. The notice takes focus on load, so it has to say where the focus is.
   :focus-visible does not match a programmatic focus on a paragraph. */
.notice[tabindex]:focus { outline: 2px solid var(--warn); outline-offset: 2px; }
/* The money offer, gold rather than green: it is an offer, not a result. */
.notice-founder { border-left-color: var(--gold); }
.notice-founder .count { color: var(--gold-ink); font-weight: 500; }
.error { color: var(--warn); font-weight: 600; }
.empty { color: var(--ink-2); }
li.empty { padding: var(--s2h) 0; }

/* -------------------------------------------------------------- feed cards */
/* The only bordered objects on the page, with the composer. */

.card-post, .card-event, .card-job, .card-news, .card-listing,
.card-story, .composer {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: var(--s5); margin: 0 0 var(--s3);
}
.card-post:last-child, .card-event:last-child, .card-job:last-child,
.card-news:last-child, .card-listing:last-child, .card-story:last-child {
  padding: var(--s5);
}

.card-head { display: flex; align-items: flex-start; gap: var(--s3); margin-bottom: var(--s2h); }
.card-face { flex: 0 0 auto; }
.card-who { flex: 1 1 auto; min-width: 0; }
.card-name {
  margin: 0 0 1px; font-weight: 600; font-size: var(--t3);
  line-height: 1.35; overflow-wrap: break-word;
}
.card-name a { color: var(--ink); }
.card-name a:hover { color: var(--accent); }
.card-follow { flex: 0 0 auto; }

.card-time {
  flex: 0 0 auto; font-size: var(--t1); color: var(--ink-2);
  white-space: nowrap;
}
a.card-time {
  display: inline-flex; align-items: center; min-height: 1.5rem;
}
a.card-time:hover { color: var(--accent); text-decoration: none; }

/* The why line: what put this card in front of you, and the topic it sits in.
   E3 5: the topic used to take a drawn row of its own under the actions, which
   cost a line on every card in the feed. */
.why {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--s1h) var(--s2);
  font-size: var(--t1); color: var(--ink-2); margin: 0 0 var(--s2h);
}
/* The dot belongs to the sentence, not to the row. As a flex item of its own
   it was stranded on a line above the words as soon as the why line wrapped. */
.why-text { flex: 0 1 auto; min-width: 0; }
.why-text::before {
  content: ""; display: inline-block; width: 6px; height: 6px; margin-right: var(--s2);
  border-radius: 50%; background: var(--accent); vertical-align: 1px;
}
/* E3 4 and D85. The why line sets mono and the chip inherited it, so one
   object had 2 faces: 13 px mono here, the text face in the rail and on the
   member row. The chip resets to the text face at the size of the line it
   sits on. */
.why > .chip {
  flex: 0 0 auto; font-family: var(--font); font-size: var(--t1);
  font-variant-numeric: normal;
}

/* The rule above the action row belongs to a bordered card. A ruled section
   already has its own rule under it, so it does not need a second one. */
.card-actions {
  display: flex; align-items: center; gap: var(--s2h) var(--s6); flex-wrap: wrap;
  margin-top: var(--s3);
}
.card-post > .card-actions, .card-event > .card-actions, .card-job > .card-actions,
.card-news > .card-actions, .card-listing > .card-actions,
.card-story .card-actions {
  padding-top: var(--s3); border-top: 1px solid var(--line);
}
/* E3 5 and E3 10. The topic no longer rides the action row at all. It sits on
   the why line, where the reason the card is here and the subject it is about
   read as one thought. */

/* E3 9. The one commercial click on a company post has to read as a link:
   accent text, and the domain in mono underneath so a member knows where the
   click lands before making it. */
.link-preview {
  display: block; margin-top: var(--s3); padding: var(--s2h) var(--s3);
  background: transparent; border: 1px solid var(--line);
  border-radius: var(--radius-sm); font-size: var(--t2); color: var(--accent);
}
.link-preview:hover { text-decoration: none; border-color: var(--accent); }
.link-title { display: block; font-weight: 600; }
.link-preview:hover .link-title { text-decoration: underline; }
.link-host {
  display: block; margin-top: 2px; font-family: var(--font-mono);
  font-size: var(--t1); letter-spacing: .02em; color: var(--ink-2);
}

/* Sponsored: the same card, a gold label and the faintest tint. */
.card-sponsored { background: var(--surface); }
@supports (background: color-mix(in srgb, red 6%, white)) {
  .card-sponsored { background: color-mix(in srgb, var(--gold) 6%, var(--surface)); }
}
.sponsored-label {
  flex: 0 0 auto; color: var(--gold-ink); font-size: var(--t1); font-weight: 500;
  text-transform: uppercase; letter-spacing: .06em; padding-top: 3px;
}

/* E3 10. One meaning per edge. The accent edge marks a question, and nothing
   else: a listing already says "Listing" in its eyebrow, so it kept a second
   edge that meant something different in the same column. */
.card-question { border-left: 3px solid var(--accent); }

/* D65 and E6 43. A closed account keeps its words and loses its face: a grey
   disc with no initials, no link and no employer. */
.avatar-gone {
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px var(--line);
}
.card-gone .card-name { color: var(--ink-2); }

/* Inspiration stories are cards like the rest: no bar, no tint. */
.card-story { display: block; }
.story-mark { display: none; }
.story-body { padding: 0; }

/* Event and job titles are the display face at 21 px. Nothing else in a
   card is. */
.card-event .card-name, .card-job .card-name { font-size: var(--t5); line-height: 1.2; }

/* ----------------------------------------------------------------- figures */

.avatar, .monogram {
  width: var(--size, 36px); height: var(--size, 36px); flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; overflow: hidden; color: #fff;
  background: hsl(var(--hue, 210) var(--avatar-sat) var(--avatar-light));
  font-family: var(--font-mono); font-size: calc(var(--size, 36px) * .34);
  font-weight: 600; letter-spacing: .02em; text-transform: uppercase;
}
.monogram { border-radius: var(--radius-sm); }

/* Inline, not inline-block: an atomic box would break away from the hard
   space that binds the mark to the last word of the name. */
.tier, .demo-mark {
  display: inline; font-size: var(--t1); font-weight: 500; line-height: 1.3;
  text-transform: uppercase; letter-spacing: .06em; padding: 1px 5px;
  border-radius: 3px; vertical-align: 1px; white-space: nowrap;
}
.tier-silver { color: var(--silver-ink); border: 1px solid var(--silver); }
.tier-gold { color: var(--gold-ink); border: 1px solid var(--gold); }
.demo-mark { color: var(--demo); border: 1px solid var(--demo); }

.tag {
  font-size: var(--t1); color: var(--ink-2); font-family: var(--font-mono);
  text-transform: uppercase; letter-spacing: .06em;
  border: 1px solid var(--field-line); border-radius: 999px; padding: 0 var(--s2);
}

/* The date block is the departure board in miniature. */
.dateblock {
  flex: 0 0 auto; width: 3rem; height: 3rem;
  border-radius: var(--radius-sm); background: var(--plate); color: var(--bar-ink);
  display: inline-flex; flex-direction: column;
  align-items: center; justify-content: center; line-height: 1.1;
}
.dateblock .d { font-size: var(--t5); font-weight: 500; }
.dateblock .m { font-size: var(--t1); text-transform: uppercase; letter-spacing: .04em; opacity: .82; }
.dateblock-big { width: 4rem; height: 4rem; }
.dateblock-big .d { font-size: var(--t6); }

/* ------------------------------------------------------------------- chips */
/* A filled pill is a topic you can click. An outline is a code or a type. */

.chips { display: flex; flex-wrap: wrap; gap: var(--s1h); margin: var(--s2h) 0 0; }

/* E3 1 and D93. The fill is --surface-2, which is the card. On the page ground
   it reads 1.06:1 and the chip disappears, so the role area on a member page and
   the topic on an event read as loose text between 2 outlined chip families. The
   border draws the shape wherever the chip lands. */
.chip {
  display: inline-block; background: var(--surface-2);
  border: 1px solid var(--line); border-radius: 999px; padding: 3px var(--s2h);
  font-size: var(--t2); line-height: 1.3; color: var(--ink); white-space: nowrap;
}
a.chip:hover { text-decoration: none; border-color: var(--field-line); }
/* SC 1.4.10. A chip holds its phrase on one line, but a chip wider than the
   screen has to break somewhere. At a 20 px root a topic name runs 290 px. */
.chip { max-width: 100%; }
@media (max-width: 479px) { .chip { white-space: normal; } }
.chip-on { border-color: var(--accent); color: var(--accent); background: var(--surface); }

/* U1 5. Two outlines, one rule between them. A code is 5 characters with no
   space in it and it is written in capitals wherever the industry writes it:
   LSGG, C6, A1. It reads on the departure board. Everything else is a name,
   an aircraft type and a language included, and it keeps the text face and
   the case it was typed in. A long list wraps rather than running off a
   phone. */
.chip-code {
  background: transparent; border-color: var(--field-line); color: var(--ink-2);
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--t1); text-transform: uppercase; letter-spacing: .06em;
  white-space: normal;
}
a.chip-code:hover { color: var(--accent); border-color: var(--accent); }

/* A capability, an aircraft type and a language are phrases, not codes, so
   they keep the text face, their own case and the page's own size. */
.chip-word {
  background: transparent; border-color: var(--field-line); color: var(--ink-2);
  font-family: var(--font); text-transform: none; letter-spacing: 0;
  white-space: normal;
}
a.chip-word:hover { color: var(--accent); border-color: var(--accent); }
/* D31. Open to offers is a state a member switched on, so it takes the token
   the other live states use rather than a colour of its own. */
.chip-offers { color: var(--good); border-color: var(--good); }

/* ----------------------------------------------------------------- buttons */

/* E4 2, SC 1.4.10 and D93. A button is a box that grows, not a box that cuts.
   "Recruitment module, 12 months, EUR 2,990" is 363 px of one line in a 290 px
   column at a 20 px root, and it scrolled /jobs/new and the admin 58 px
   sideways. The height is a floor now and the label wraps inside it. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: 2.25rem; padding: var(--s1) var(--s3); border-radius: var(--radius-sm);
  border: 1px solid transparent; font: inherit; font-size: var(--t2);
  font-weight: 600; cursor: pointer; background: transparent;
  color: var(--ink); white-space: normal; text-align: center;
  transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
}
.btn:hover { text-decoration: none; }
.btn-primary { background: var(--ink); color: var(--bg); }
.btn-primary:hover { background: var(--ink-2); color: var(--bg); }
.btn-secondary { border-color: var(--field-line); color: var(--ink); background: var(--surface); }
.btn-secondary:hover { background: var(--surface-2); }
.btn-quiet { color: var(--ink-2); }
.btn-quiet:hover { color: var(--ink); background: var(--surface-2); }
.btn.is-on { color: var(--accent); background: transparent; }
.btn.is-on:hover { background: var(--surface-2); }
/* E3 4 and D61. One colour for one state: Following is the accent, like every
   other toggle on the page. The green override is gone. The outlined ground
   stays, so the button holds its shape when it flips. */
.btn-secondary.is-on { background: var(--surface); }
.btn-small { min-height: 1.875rem; padding: 2px var(--s2h); font-size: var(--t1); }

/* E3 5. A destructive action wears the warn token: delete a post, delete an
   account, cancel a plan, and the button on the page that confirms it. */
.btn-warn { color: var(--warn); }
.btn.btn-warn { border-color: var(--warn); }
.btn.btn-warn:hover { background: var(--warn); color: var(--bg); }
.btn-text.btn-warn:hover { color: var(--warn); }

/* The check is drawn, not typed. */
.btn.is-on::before, .btn-text.is-on::before {
  content: ""; flex: 0 0 auto; width: 5px; height: 9px; margin: -3px 1px 0 0;
  border: solid currentColor; border-width: 0 2px 2px 0; transform: rotate(45deg);
}

.btn-text {
  display: inline-flex; align-items: center; gap: var(--s1h); min-height: 1.5rem;
  background: none; border: 0; padding: 2px 0; font-family: var(--font-mono);
  font-size: var(--t2); font-weight: 500; color: var(--ink-2);
  cursor: pointer; transition: color 120ms ease;
}
.btn-text:hover { color: var(--accent); text-decoration: none; }
.btn-text.is-on { color: var(--good); }
.btn-text.quiet { color: var(--ink-2); }
a.btn-text.quiet:hover { color: var(--accent); }

.inline-form { display: inline-flex; margin: 0; }
.row-actions { display: flex; gap: var(--s2h); flex-wrap: wrap; align-items: center; margin-top: var(--s3); }
/* Inside an index row the actions are a column of their own, on one line and
   level with the name, not a block that wraps under itself. */
.list-row > .row-actions { flex: 0 0 auto; flex-wrap: nowrap; margin-top: 0; }

.pager { display: flex; gap: var(--s2h); justify-content: center; margin: var(--s6) 0; }

/* E4 4 and D93. Two or 3 tabs fit a phone at a 16 px root and not at 20, so the
   row wraps rather than cutting "Requests" off the right edge. SC 1.4.10. */
.tabs {
  display: flex; flex-wrap: wrap; gap: var(--s1);
  border-bottom: 1px solid var(--line); margin-bottom: var(--s4);
}
.tab {
  display: inline-flex; align-items: center; min-height: 2.25rem; padding: var(--s1h) var(--s3);
  color: var(--ink-2); border-bottom: 2px solid transparent;
}
.tab:hover { text-decoration: none; color: var(--ink); }
.tab.is-current { color: var(--ink); border-bottom-color: var(--accent); font-weight: 600; }

/* ------------------------------------------------------------------- forms */

input[type="text"], input[type="search"], input[type="email"], input[type="url"],
input[type="month"], input[type="date"], input[type="number"],
textarea, select {
  font: inherit; font-size: var(--t3); color: var(--ink); background: var(--surface);
  border: 1px solid var(--field-line); border-radius: var(--radius-sm);
  padding: 0 var(--s3); width: 100%; max-width: 100%;
}
input[type="text"], input[type="search"], input[type="email"], input[type="url"],
input[type="month"], input[type="date"], input[type="number"], select { height: 2.5rem; }
select { padding: 0 var(--s2); }
textarea { padding: var(--s2h) var(--s3); resize: vertical; line-height: 1.55; }
input[readonly] { background: var(--surface-2); color: var(--ink-2); }

input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); }

/* No browser box around a group. A legend is an eyebrow, not a frame. */
fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
legend { padding: 0; }

.field { margin-bottom: var(--s4); }
.field > label, .field > .label, fieldset.field > legend { display: block; margin-bottom: var(--s1h); }
.field > label, fieldset.field > legend.plain {
  font-size: var(--t2); font-weight: 600; font-family: var(--font);
  text-transform: none; letter-spacing: 0; color: var(--ink);
}
fieldset.field { border: 0; margin: 0 0 var(--s4); padding: 0; min-width: 0; }
/* E3 2 and D100. Two fields on one row are one row, so they take one width.
   A select sizes to its longest option unless something stops it, so Country
   landed at 145 px beside a 190 px City in the same class, and the pay row read
   as 3 different kinds of field. The row holds the form width, the fields split
   it evenly, and the control inside each one fills its field and keeps the cap
   its scale class sets. Under the basis the row wraps, one field a line. */
.field-row { display: flex; gap: var(--s3); flex-wrap: wrap; max-width: 640px; }
.field-row > .field { flex: 1 1 11rem; min-width: 0; }
/* E3 1 and D74. The commonest paragraph in the app, and the only block of text
   with no measure at all: 159 characters a line under a lede set to 62. */
.form-help {
  color: var(--ink-2); font-size: var(--t2); margin: var(--s1h) 0 0;
  max-width: var(--measure);
}

/* Fields sized to their content, not to the column. A form field never runs
   wider than the line a member can read back. */
.field input, .field select, .field textarea { max-width: 640px; }
.field-xs input, .field-xs select { max-width: 96px; }
.field-sm input, .field-sm select { max-width: 180px; }
.field-md input, .field-md select { max-width: 320px; }
.field-lg input, .field-lg select, .field-lg textarea { max-width: 480px; }

/* E3 2 and D100. The field a recruiter writes most in was the narrowest on the
   page: 480 px of advert in a column of 640 px inputs. It takes the form width
   now, and the measure moves inside the box. The right padding is whatever is
   left over once the text column holds --measure, so the typed advert wraps at
   the 68 characters every other block of prose in the product wraps at, and on
   a narrow screen the padding falls back to the normal one. */
.field-wide { max-width: 640px; }
.field-wide textarea {
  max-width: 640px;
  padding-right: max(var(--s3), calc(100% - var(--measure) - var(--s3)));
}

/* One counter, one phrasing, under the field it counts, and never wider than
   the field: it right-aligns to the box, not to the column. */
.counter {
  display: block; text-align: right; font-size: var(--t1);
  color: var(--ink-2); margin: var(--s1) 0 0;
}
.counter.is-over, .counter-note.is-over { color: var(--warn); font-weight: 600; }
.field .counter { max-width: 640px; }
.field-lg .counter { max-width: 480px; }
.field-md .counter { max-width: 320px; }
.field-sm .counter { max-width: 180px; }
.field-xs .counter { max-width: 96px; }

.checks { display: flex; flex-direction: column; }
.checks-wide {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  column-gap: var(--s6);
}
.check {
  display: flex; gap: var(--s2h); align-items: flex-start; font-size: var(--t2);
  font-weight: 400; padding: var(--s2) 0; min-height: 1.5rem;
}
.check input { margin-top: 3px; width: auto; }
/* The topic picker: ruled rows, a target a gloved hand can hit. */
.check-topic {
  border-bottom: 1px solid var(--line); padding: var(--s3) var(--s2) var(--s3) 0;
  cursor: pointer; font-size: var(--t3);
}
.check-topic:hover { background: var(--surface-2); }
.check-name { display: block; font-weight: 600; }
.check-note { display: block; color: var(--ink-2); font-size: var(--t2); }

/* E3 1. Filters read down from the top of the block. Bottom alignment put
   Country, Airport and Tier level with the foot of a 1,100 px category column
   and made the row look broken. */
.filters {
  display: flex; gap: var(--s3); flex-wrap: wrap; align-items: flex-start;
  background: transparent; border: 0; border-radius: 0;
  border-bottom: 1px solid var(--line); padding: 0 0 var(--s4); margin: 0 0 var(--s4);
}
.filter-field { flex: 1 1 160px; min-width: 140px; max-width: 260px; }
.filter-field label { display: block; margin-bottom: var(--s1); }
.filter-field .check { padding-top: var(--s6); }
.filter-actions { display: flex; gap: var(--s2); align-self: flex-end; }

/* E3 1, E2 1 and U2 5. The 18 categories are the filter, not a 260 px column
   beside it: the list takes the whole row and grids across it. Four columns
   at 1400 px, 3 at 900, 2 under 700 (in the phone block further down). */
.filter-categories { flex: 1 1 100%; min-width: 0; max-width: none; }
.filter-categories .checks-wide {
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  column-gap: var(--s3);
}

/* U1 2 and E2 7. Under 700 px the filter form folds behind one button, the
   shape the composer folds to. Above 700 the form is simply there and the
   summary is gone, so no desktop ever meets a control it does not need. */
.filter-fold { display: block; margin: 0 0 var(--s4); }
.filter-fold > .filter-summary {
  display: flex; align-items: center; width: 100%; height: 2.75rem;
  margin: 0; padding: 0 var(--s3); list-style: none; cursor: pointer;
  border: 1px solid var(--field-line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink-2);
  font-size: var(--t3); font-weight: 600;
}
.filter-fold > .filter-summary::-webkit-details-marker { display: none; }
.filter-fold > .filter-summary:hover { background: var(--surface-2); color: var(--ink); }
.filter-fold > .filter-summary .caret { margin-left: auto; font-size: var(--t2); opacity: 1; }
.filter-fold[open] > .filter-summary { margin-bottom: var(--s4); }
.filter-fold > .filters { margin-bottom: 0; }

@media (min-width: 700px) {
  .filter-fold > .filter-summary { display: none; }
  .filter-fold > .filters { margin-bottom: var(--s4); }
}

.searchrow {
  display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap;
  background: transparent; border: 0; border-radius: 0; padding: 0; margin: 0 0 var(--s4);
}
.searchrow input { flex: 1 1 220px; }

.result-bar {
  display: flex; flex-wrap: wrap; gap: var(--s3); align-items: center;
  justify-content: space-between; margin-bottom: var(--s2h);
}
.result-bar .form-help { margin: 0; }
.result-bar .inline-form { gap: var(--s2); align-items: center; }
.result-bar label { font-size: var(--t2); color: var(--ink-2); white-space: nowrap; }

/* ------------------------------------------------------------------- lists */

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

/* A list that is the page block itself sits on the page, ruled, not boxed. */
.list.card {
  background: transparent; border: 0; border-radius: 0;
  padding: 0; margin: 0 0 var(--s6); border-top: 1px solid var(--line);
}
.list.card .list-row:last-child { border-bottom: 1px solid var(--line); }

.list-row {
  display: flex; align-items: flex-start; gap: var(--s3);
  padding: var(--s3) 0; border-bottom: 1px solid var(--line);
}
.list-row:last-child { border-bottom: 0; }
.list-text { flex: 1 1 auto; min-width: 0; display: block; }
.list-name { font-weight: 600; color: var(--ink); display: inline; overflow-wrap: break-word; }
a.list-name, .list-name a { color: var(--ink); }
a.list-name:hover, .list-name a:hover { color: var(--accent); }

/* SC 2.5.8. A row title is the target a thumb reaches for, and 86 of them on
   one index measured 19 to 22 px. The box grows; the line does not move,
   because the extra height is padding under a 20 px line. */
a.list-name, .card-name a, .card-title a {
  display: inline-block; min-height: 1.5rem; padding-bottom: 2px;
}

/* An index row carries a real heading, so a screen reader can skim 46 rows.
   It reads as the line it always was: no size, weight or margin of its own. */
.list-h {
  display: block; margin: 0; font-family: inherit; font-size: inherit;
  font-weight: 600; line-height: 1.35; text-wrap: initial;
}
.list-meta { display: block; color: var(--ink-2); font-size: var(--t2); }
/* SC 2.5.8 and E4 2. A link on the meta line is a target too. On /associations/
   the website link was 17 px tall and under 12 px from the name above it, so a
   24 px circle on one met the other. The 4 failures in 3,606 targets. */
.list-meta a { display: inline-block; min-height: 1.5rem; padding-bottom: 2px; }
.list-note { display: block; color: var(--ink-2); font-size: var(--t2); margin-top: 3px; }
.list-kicker { display: block; margin-bottom: 2px; }

.mini-list { list-style: none; margin: 0; padding: 0; }
.mini-row { display: flex; gap: var(--s2h); align-items: flex-start; padding: var(--s2h) 0; border-bottom: 1px solid var(--line); }
.mini-row:last-child { border-bottom: 0; padding-bottom: 0; }
.mini-text { min-width: 0; }
.mini-name { font-weight: 600; color: var(--ink); font-size: var(--t2); }
.mini-name:hover { color: var(--accent); }
.mini-meta { display: block; color: var(--ink-2); font-size: var(--t2); }

.stat-list { list-style: none; margin: 0; padding: 0; font-size: var(--t2); }
.stat-list li { padding: 3px 0; color: var(--ink-2); }
.stat-list .count { color: var(--ink); }

.bullets { list-style: none; margin: 0; padding: 0; }
.bullets li { padding: var(--s2) 0; border-bottom: 1px solid var(--line); font-size: var(--t2); break-inside: avoid; }
/* Two columns only once there is enough to fill them. Splitting 3 items
   across 2 columns reads as a mistake, not as a layout. */
@media (min-width: 760px) {
  .bullets:has(li:nth-child(5)) { columns: 2; column-gap: var(--s7); }
}

.details { display: grid; grid-template-columns: 170px minmax(0, 1fr); gap: var(--s2h) var(--s4); margin: 0; }
.details dt { padding-top: 3px; }
.details dd { margin: 0; font-size: var(--t2); }
/* The Spend block: what a company paid, and what it bought, read as numbers. */
.details dd .count { color: var(--ink); font-weight: 500; }

.guide-number {
  flex: 0 0 auto; width: 1.75rem; height: 1.75rem; border-radius: 50%;
  background: var(--surface-2); color: var(--ink-2); display: inline-flex;
  align-items: center; justify-content: center;
  font-size: var(--t2); font-weight: 500;
}

/* ------------------------------------------------- board rows: event lists */

/* A place name wraps inside its column; a date never breaks. */
.board-cell { display: block; font-size: var(--t1); }
.board-when { color: var(--ink); white-space: nowrap; }

/* Fixed right-hand columns, so every row lines up down the page.
   E3 4, U1 2 and U1 6: the place column is the width of the longest place the
   board carries, measured. "Luton, United Kingdom" renders 166 px and the
   column was 152, so 2 rows in 18 broke their own value and the board lost
   its grid. The tracks are in rem, because the type in them is. */
/* U1 4 and D95: the board carries a third cell, how many members are going, so
   the place column keeps its measured width and the row grows by that cell. */
.event-row {
  display: grid; grid-template-columns: 3rem minmax(0, 1fr) 24rem 6rem;
  gap: var(--s3); align-items: start; padding: var(--s3) 0;
}
.event-row .inline-form { justify-content: flex-end; }
.event-row .inline-form .btn { min-width: 92px; }
.event-row .list-name, .job-row .list-name,
.event-line .list-name, .job-line .list-name { font-size: var(--t5); line-height: 1.2; }
.event-row .dateblock { margin-top: 2px; }
.board-cols { display: grid; grid-template-columns: minmax(0, 1fr) 6rem 5rem; gap: var(--s3); }

/* The job index reads down the same columns: city, employment, posted. */
.job-row {
  display: grid; grid-template-columns: 2.5rem minmax(0, 1fr) 28rem auto;
  gap: var(--s3); align-items: start;
}
/* U1 7. City, employment and posted ran together in one line of mono capitals
   with only spaces between them. A middle dot separates them, and the 3 fields
   keep their columns so the index still reads down the page.
   U1 2: the place column holds the longest place on the board on one line.
   "Farnborough, United Kingdom" renders 214 px and the column was 116. */
.board-cols-3 {
  display: grid; grid-template-columns: 14rem 6.5rem 6.5rem;
  gap: var(--s2); align-items: baseline;
}
.board-cols-3 > .board-cell { white-space: nowrap; }
/* U1 6. The separator is drawn by the value it introduces, so a wrap never
   leaves a dot hanging at the end of a line. It used to be an element of its
   own and wrapped away from the words it separated. */
.board-cols-3 > .board-cell + .board-cell::before {
  content: "\00B7"; margin-right: var(--s2); color: var(--ink-2);
}
.job-row .board-cols-3 { padding-top: 2px; }

/* E4 1, SC 1.4.10 and D93. Under 700 px no cell on either board holds its line.
   "LUTON, UNITED KINGDOM - CONTRACT - 2 DAYS AGO" is 566 px of nowrap on a
   480 px screen at a 20 px root, which scrolled the jobs index 86 px sideways
   and took the date off the edge. The separator is drawn by the value that
   follows it, so a wrap never strands a dot at the end of a line. */
@media (max-width: 699px) {
  .board-cell, .board-when, .board-cols-3 > .board-cell { white-space: normal; }
}

/* The board keeps its columns while the column it sits in is wide enough for
   them. From 700 to 1,099 px the layout gives 260 px of the row to the rail, so
   the main column is 584 px at 900 px and the 4 tracks left the name 20 px. The
   row stacks from there down: the name takes the width and the board reads as
   one line under it. */
@media (max-width: 1099px) {
  .event-row { grid-template-columns: 3rem minmax(0, 1fr) auto; row-gap: var(--s2); }
  .event-row .dateblock { grid-area: 1 / 1 / 3 / 2; align-self: start; }
  .event-row .list-text { grid-area: 1 / 2 / 2 / 3; }
  .event-row .board-cols { grid-area: 2 / 2 / 3 / 4; grid-template-columns: repeat(3, auto); justify-content: start; gap: var(--s2h); }
  /* U1 3, E3 6 and D85. Under 900 px the 2 cells leave their columns and run
     into one line: "LUTON, UNITED KINGDOM IN 27 DAYS". The 3-cell jobs board
     already separated the same facts, so the 2-cell board takes the same
     separator, drawn by the value that follows it. */
  .event-row .board-cols > .board-cell + .board-cell::before {
    content: "\00B7"; margin-right: var(--s2); color: var(--ink-2);
  }
  .event-row .inline-form { grid-area: 1 / 3 / 2 / 4; }

  .job-row { grid-template-columns: 2.5rem minmax(0, 1fr) auto; row-gap: var(--s2); }
  .job-row .monogram { grid-area: 1 / 1 / 3 / 2; }
  .job-row .list-text { grid-area: 1 / 2 / 2 / 3; }
  .job-row .board-cols-3 { grid-area: 2 / 2 / 3 / 4; grid-template-columns: repeat(3, auto); justify-content: start; gap: var(--s2h); }
  .job-row .inline-form { grid-area: 1 / 3 / 2 / 4; }
}

/* ------------------------------------------------ index rows: the supplier */

.supplier-row { padding: var(--s4) var(--s3); }
.tier-row-gold { background: transparent; }
@supports (background: color-mix(in srgb, red 5%, white)) {
  .tier-row-gold { background: color-mix(in srgb, var(--gold) 5%, var(--bg)); }
}
.supplier-row .list-name { font-size: var(--t4); }
.supplier-row .chips { margin-top: var(--s2); }
.job-row, .news-row { padding: var(--s4) var(--s3); }
.news-row .chip { margin-top: var(--s2); }
.place { font-size: var(--t1); color: var(--ink-2); }
.list-text .place { display: block; margin-top: 2px; }
.pay { font-size: var(--t2); }

/* ---------------------------------------------------------------- profiles */

.profile-top, .org-head { display: flex; gap: var(--s5); align-items: flex-start; flex-wrap: wrap; }
.profile-id, .org-id { flex: 1 1 260px; min-width: 0; }
/* Boxed buttons and quiet mono actions share this row, so the horizontal gap
   is wide enough that Save and Report never read as one phrase. */
.profile-actions { display: flex; gap: var(--s2) var(--s4); align-items: center; flex-wrap: wrap; }
.profile-head .page-title, .org-head .page-title { font-size: var(--t7); margin-bottom: var(--s1); }
.profile-statement { margin: var(--s4) 0 0; font-size: var(--t4); line-height: 1.45; }
.profile-counts { margin-top: var(--s3); font-size: var(--t1); }
.profile-counts .count { color: var(--ink); }
.org-place { font-size: var(--t1); }
.chip-group { margin-top: var(--s3); }

/* E3 7. One section shape down the page. At work, Open jobs, the counts and
   Follows sat 8 px apart with nothing between them, while Your company and
   Your account carried a rule and 24 px. They are all sections, so they all
   read as one. */
.profile-head .chip-group, .profile-head .profile-counts {
  margin: var(--s5) 0 0; padding: 0 0 var(--s5);
  border-bottom: 1px solid var(--line);
}
.profile-head .chip-group:last-child,
.profile-head .profile-counts:last-child { padding-bottom: 0; border-bottom: 0; }

.offer + .card-body { margin-top: var(--s4); }
.card-body + .card-meta { margin-top: var(--s3); }

.offer {
  margin: var(--s4) 0 0; padding: var(--s3) var(--s4);
  border: 1px solid var(--gold); border-radius: var(--radius);
}
.offer-label { margin: 0 0 var(--s1); color: var(--gold-ink); }
.offer-body { margin: 0; }

.page-head { display: flex; gap: var(--s4); align-items: flex-start; justify-content: space-between; flex-wrap: wrap; }

/* E5 6 and E3. The tier belongs to the company, not to the sentence that says
   where somebody works, so it sits on a line of its own as a small mono tag. */
.card-tier { margin: var(--s1h) 0 0; }
.card-tier .tier { display: inline-block; vertical-align: baseline; }

.grid-2 { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); column-gap: var(--s7); }
/* The action sits on the floor of the cell, so a row of cells shows one line
   of buttons rather than 4 heights. */
.grid-2 .card { margin: 0 0 var(--s5); display: flex; flex-direction: column; }
.grid-2 .card > .card-actions { margin-top: auto; }
/* E3 10. A cell is a cell wherever it falls. The last-child rule that strips
   the rule and the padding dropped the last button 21 px below its row. */
.grid-2 > .card:last-child {
  padding-bottom: var(--s5); border-bottom: 1px solid var(--line);
}

/* -------------------------------------------------------------------- jobs */

.job-head, .event-head { display: flex; gap: var(--s5); align-items: flex-start; flex-wrap: wrap; margin-bottom: var(--s4); }
.job-head .card-meta, .event-head .card-meta { margin-bottom: var(--s1); }
.job-facts { display: flex; align-items: center; gap: var(--s2h); flex-wrap: wrap; margin: var(--s2) 0 0; }
.job-terms { margin: var(--s3) 0 0; grid-template-columns: 90px minmax(0, 1fr); }

.stats-strip {
  background: var(--surface-2); border-radius: var(--radius-sm);
  padding: var(--s3) var(--s4); margin: var(--s4) 0;
}
.stats-title { margin: 0 0 var(--s2); }
.stats { list-style: none; margin: 0; padding: 0; display: flex; gap: var(--s6); flex-wrap: wrap; font-size: var(--t2); color: var(--ink-2); }
.stats .count { color: var(--ink); }
/* E3 5 and D61. A movement is a reading, not a verdict: 2 opens fewer than
   last week is not an error, and --warn is the colour of Report and Delete.
   The word carries the direction; the token stays quiet. */
.trend { font-size: var(--t1); margin-left: var(--s1h); color: var(--ink-2); }

.owner-strip { border-left: 3px solid var(--gold); padding-left: var(--s3); }

/* The floor under a thin page: the claim prompt is a section, not a line. */
.claim-block .row-actions { margin-top: var(--s2h); }

/* The profile form waits behind Edit. Onboarding opens it.
   E3 8 and D52. This opens the form, so it is a control and it looks like one.
   In the eyebrow face it read as a section label, and the same words sat on a
   real button 2 blocks above it. */
.edit-panel > .edit-summary {
  cursor: pointer; list-style: none; display: inline-flex; align-items: center;
  justify-content: center; gap: var(--s2); height: 2.25rem; padding: 0 var(--s3);
  margin-bottom: var(--s3); border-radius: var(--radius-sm);
  border: 1px solid var(--field-line); background: var(--surface);
  font-family: var(--font); font-weight: 600; font-size: var(--t2);
  text-transform: none; letter-spacing: 0; color: var(--ink);
}
.edit-panel > .edit-summary::-webkit-details-marker { display: none; }
.edit-panel > .edit-summary .caret { margin-left: var(--s1); font-size: var(--t1); opacity: 1; }
.edit-panel > .edit-summary:hover { background: var(--surface-2); color: var(--ink); }

/* ---------------------------------------------------------------- composer */

/* U1 1. The folded composer: one full width button above the first card. The
   script shows it, so a member with the script blocked keeps the whole
   composer and never meets a button that does nothing. */
.composer-open {
  width: 100%; height: 2.75rem; margin: 0 0 var(--s3);
  justify-content: flex-start; font-size: var(--t3);
  border-color: var(--field-line); color: var(--ink-2); background: var(--surface);
}
.composer-open:hover { background: var(--surface-2); color: var(--ink); }

.composer { padding: var(--s4) var(--s5); }
.composer-head { display: flex; gap: var(--s3); align-items: flex-start; }
.composer-head textarea {
  flex: 1 1 auto; border: 0; border-bottom: 1px solid var(--field-line);
  border-radius: 0; background: transparent;
  padding: var(--s1h) 0; font-size: var(--t3); min-height: 3.75rem;
}
.composer-kind {
  display: flex; gap: var(--s5); border: 0; margin: var(--s2) 0 0; padding: 0;
}
.composer-kind .check { font-size: var(--t2); }
.composer-foot {
  display: flex; justify-content: space-between; align-items: center; gap: var(--s2h);
  margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--line); flex-wrap: wrap;
}
.composer-selects { display: flex; gap: var(--s2); flex-wrap: wrap; align-items: center; }
.composer-selects select { width: auto; min-width: 150px; }
.composer-as { display: flex; align-items: center; gap: var(--s2); }
.composer-right { display: flex; gap: var(--s3); align-items: center; }
.composer-right .counter { margin: 0; }

/* U2 2 and E3. Spending reach a company already paid for is a quiet row of
   its own inside the composer, not a third control crowding the topic. */
.composer-sponsored {
  flex: 1 0 100%; display: flex; align-items: center;
  margin-top: var(--s1h); padding-top: var(--s2h);
  border-top: 1px solid var(--line);
}
.composer-sponsored .check {
  padding: 0; font-size: var(--t2); color: var(--ink-2); min-height: 1.5rem;
  align-items: center;
}
.composer-sponsored .check input { margin-top: 0; }

/* ---------------------------------------------------------------- messages */

.messages { list-style: none; margin: 0 0 var(--s5); padding: 0; }
.message {
  background: var(--surface); border-radius: var(--radius-sm);
  padding: var(--s2h) var(--s3); margin-bottom: var(--s2); max-width: 80%;
}
.message.is-mine { margin-left: auto; background: var(--surface-2); }
.message-body { margin: 0; }
.message-meta { margin: var(--s1) 0 0; color: var(--ink-2); font-size: var(--t1); }
.thread-job { margin: var(--s2h) 0 0; color: var(--ink-2); font-size: var(--t2); }
.thread-row.is-current { background: var(--surface-2); }
/* The recruiter's pipeline control: a label, a select and one button. */
.thread-state {
  align-items: center; gap: var(--s2); flex-wrap: wrap; margin: var(--s4) 0 0;
}
.thread-state select { width: auto; min-width: 180px; }
.thread-state + .form-help { margin-top: var(--s2); }
.comment-body { display: block; margin-top: var(--s1h); }
.comments .card-actions { border-top: 0; padding-top: var(--s1h); margin-top: var(--s1h); }
.comment-edit { margin-top: var(--s1h); font-size: var(--t2); }
.comment-edit textarea { width: 100%; }

.demo-shortcut-label { color: var(--demo); margin-bottom: var(--s1h); }

.report-item {
  background: var(--surface-2); border-radius: var(--radius-sm);
  padding: var(--s3) var(--s4); margin-bottom: var(--s4);
}

/* ------------------------------------------------------------------ tables */

/* SC 2.1.1. The scroller takes focus, so it says where the focus is. */
.table-wrap { overflow-x: auto; }
.table-wrap:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.table { width: 100%; border-collapse: collapse; font-size: var(--t2); }
.table th, .table td { text-align: left; padding: var(--s2) var(--s2h); border-bottom: 1px solid var(--line); vertical-align: top; }
/* D52: a row head is a label and reads at the size of the value beside it.
   A column head keeps the eyebrow, which sets its own 12 px. */
.table th { font-size: var(--t2); }
.table td { font-variant-numeric: tabular-nums; }
/* A caption belongs to the table, so inside a scroller it is as wide as the
   table and runs off the screen. It sticks to the left edge instead, at the
   width of the box a member can actually see. */
.table caption {
  position: sticky; left: 0; box-sizing: border-box;
  width: min(var(--measure), calc(100vw - var(--s7)));
  text-align: left; color: var(--ink-2); font-size: var(--t2);
  margin: 0 0 var(--s2);
}
.plans td, .plans th { vertical-align: top; }

/* The money page. The metals name themselves in their own ink, a hairline
   separates one plan from the next, and the price is the loudest row.
   E3 8: the 3 tiers are one offer in 3 sizes, so they take one width. Basic
   was 95 px with a wrapped head against 190 px of silver and gold. */
.plans { table-layout: fixed; }
.plans thead th:first-child, .plans th[scope="row"] { width: 25%; }
.plans th + th, .plans th + td, .plans td + td { border-left: 1px solid var(--line); }
.plans .plan-name { display: block; font-weight: 600; }
.plans .plan-silver .plan-name { color: var(--silver-ink); }
.plans .plan-gold .plan-name { color: var(--gold-ink); }
/* E3 2. The loudest column earns the tint. The free column carries nothing,
   and Your plan stays a mono tag on whichever column the company is on. */
.plans .col-gold { background: transparent; }
@supports (background: color-mix(in srgb, red 5%, white)) {
  .plans .col-gold { background: color-mix(in srgb, var(--gold) 5%, var(--bg)); }
  /* E3 2. Five per cent of gold on a near-black ground is nothing. The dark
     theme needs 8 to show the column the money is in. */
  :root[data-theme="dark"] .plans .col-gold,
  :root[data-theme="dark"] .tier-row-gold {
    background: color-mix(in srgb, var(--gold) 8%, var(--bg));
  }
}
.plans .price-head th, .plans .price-head td { border-top: 2px solid var(--line); }
.plans .price-row th, .plans .price-row td { font-weight: 600; color: var(--ink); }
/* E3 8. The amount is what the row is for, so it is the loudest thing in its
   cell rather than the smallest. Twenty-one pixels, on the board. */
.plans .price-row .count { display: block; font-size: var(--t5); line-height: 1.2; }
/* E3 10 and D52. The mono face is reserved for a date, a code or a count.
   "Free" is none of those, so it takes the text face at the size of the
   amounts it sits beside. */
.price-word {
  display: block; font-family: var(--font); font-weight: 600;
  font-size: var(--t5); line-height: 1.2;
}

/* D48 and E3 9. One founder treatment, on the table and at the till: the gold
   mono tag. The counter is not part of it, because a counter printed once is
   a countdown and printed 5 times is noise. */
/* E3 3, E4 2 and D85: the tag wraps. At 206 px of nowrap in a 167 px column
   the silver tag crossed the column rule and the gold one was cut by the
   table edge, from 700 to 859 px and again from 1,100 to 1,260. */
.tag-founder, .price-founder {
  display: inline-block; max-width: 100%; font-family: var(--font-mono);
  font-variant-numeric: tabular-nums; font-size: var(--t1); font-weight: 500;
  text-transform: uppercase; letter-spacing: .06em; color: var(--gold-ink);
  border: 1px solid var(--gold); border-radius: 3px; padding: 0 var(--s1h);
  line-height: 1.5; white-space: normal;
}
.price-founder { margin: var(--s1) 0 var(--s2); }
.btn-choose { margin-top: var(--s1); }

/* U2 6 and D97. The founder price is the price this buyer pays, so it is the
   number on the row: the display size, on the board, in the gold ink the rest
   of the offer wears. The standard price reads after it as the note it is, in
   the text face, quiet. For 3 rounds it was the other way round and EUR 249 was
   the headline over a EUR 124 tag. */
.price-founder-lead {
  display: block; font-size: var(--t5); line-height: 1.2;
  color: var(--gold-ink); word-spacing: -.28em;
}
.price-standard {
  display: block; margin-top: 2px; font-family: var(--font);
  font-size: var(--t2); font-weight: 400; line-height: 1.35;
  color: var(--ink-2); text-transform: none; letter-spacing: 0;
}
/* From 700 to 899 px a table column is 150 px and the founder line takes 3 of
   them at the display size. It steps down one, so it still leads the standard
   price by 4 px and the price rows stay the height of a row. */
@media (min-width: 700px) and (max-width: 899px) {
  .plans .price-row .price-founder-lead { font-size: var(--t4); }
}

/* U2 4, E5 3 and D97. The add-on is not a tier, so it sits under the tiers as
   its own ruled row with its own Choose, in the shape every price row on the
   page has: what it is, what it adds, the button that buys it. */
.addon-row { margin: var(--s5) 0 0; border-top: 1px solid var(--line); }
.addon-row > .list-row { align-items: center; }
.addon-row .list-name { font-weight: 600; }
.addon-row .btn-choose { margin-top: 0; }

/* D49 and E1 5. The benchmark reads against the table, not inside it: its own
   line under the rule, where "median" cannot land under "Total". */
.benchmark-line {
  margin: var(--s3) 0 0; max-width: var(--measure);
  font-size: var(--t2); color: var(--ink-2);
}
.benchmark-line .count {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-weight: 500; color: var(--ink);
}

/* Yes and No are 39 cells of the same word. A mark reads faster than a word,
   and the word stays for anybody listening. */
.mark { font-family: var(--font-mono); font-size: var(--t3); line-height: 1; }
.mark-yes { color: var(--ink); }
.mark-yes::after { content: "\2713"; }
.mark-no { color: var(--ink-2); }
.mark-no::after { content: "\2212"; }

/* E2 8. Six columns of numbers do not fit a phone. Under 700 px the table
   stacks into one block a row, and every number keeps its column's label. */
@media (max-width: 699px) {
  .table-stack, .table-stack thead, .table-stack tbody,
  .table-stack tr, .table-stack th, .table-stack td { display: block; }
  .table-stack thead {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  .table-stack tr {
    padding: var(--s3) 0; border-bottom: 1px solid var(--line);
  }
  .table-stack tr:last-child { border-bottom: 0; }
  .table-stack td { border: 0; padding: 2px 0; }
  .table-stack td[data-label] {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--s3);
  }
  .table-stack td[data-label]::before {
    content: attr(data-label);
    font-family: var(--font-mono); font-weight: 500; font-size: var(--t1);
    text-transform: uppercase; letter-spacing: .06em; color: var(--ink-2);
  }
  .table-stack .stack-head { padding-bottom: var(--s2); }
  .table-stack .cell-value { display: inline-flex; gap: var(--s1h); align-items: baseline; }
}

/* E3 4. Under 700 px 4 columns of prices squeeze to 4 lines a cell and the
   table runs 1,000 px tall. It stacks instead: a block a line of the table,
   the 3 tiers under it, gold first.
   E4 1 and D85: nothing is reordered. `order` used to pile the cells into one
   block a tier while the DOM kept them row by row, so the tab ring ran basic,
   silver, gold under a gold, silver, basic reading order and focus jumped
   717 px backwards on every price. SC 2.4.3 and 1.3.2, both level A.
   E4 5: the same 699 px the other table stacks at. Two breakpoints for one
   problem left a 530 px table in a 369 px scroller at 700 px. */
@media (max-width: 699px) {
  .plans { display: grid; grid-template-columns: minmax(0, 1fr); }
  .plans tbody, .plans tbody tr { display: contents; }
  .plans caption { width: auto; }
  /* E3 6 and D93. The legend was a table head with no table under it: every
     block below names its 3 tiers again, and the 3 blocks above the fold name
     them a third time. It leaves the page and stays in the accessibility tree,
     where it is still what names the columns. */
  .plans thead {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }

  /* The line of the table is the block heading now, and it takes the width of
     the block rather than the 25 per cent column it holds on a wide screen. */
  .plans th[scope="row"] {
    display: block; width: auto; margin: var(--s5) 0 0; padding: var(--s2h) 0;
    border: 0; border-bottom: 2px solid var(--line);
    font-size: var(--t4); font-weight: 600;
  }
  .plans td {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--s3); padding: var(--s2) 0; border: 0;
    border-bottom: 1px solid var(--line);
  }
  /* D52: stacked, this label is the column head it replaces, so it keeps the
     text face. The eyebrow is for the block heading above it. */
  .plans td::before {
    content: attr(data-tier); flex: 0 1 auto;
    font-family: var(--font); font-weight: 600; font-size: var(--t2);
    color: var(--ink);
  }
  /* E4 1 and D85. Gold is the argument, so it opens, and it opens in the DOM:
     the template writes gold, silver, basic. The 3 `order` declarations that
     used to move the cells left the tab ring running basic, silver, gold
     against a gold, silver, basic reading order, a jump of 717 px backwards.
     SC 2.4.3 and 1.3.2. */
  .plans th + th, .plans th + td, .plans td + td { border-left: 0; }
  .plans .price-head th, .plans .price-head td { border-top: 0; }
  .plans .price-row td { flex-wrap: wrap; }
  .plans .price-founder { flex: 1 0 100%; text-align: right; }
  /* U2 6 and D97: the charged price sits against its label, the note about the
     price after the founder months takes the line under it. */
  .plans .price-standard { flex: 1 0 100%; text-align: right; }
  .plans .btn-choose { flex: 0 0 auto; margin-top: 0; }
}

/* ------------------------------------------------------------------ footer */

.footer { border-top: 1px solid var(--line); background: transparent; }
.footer-inner {
  max-width: 1280px; margin: 0 auto; padding: var(--s5) var(--s4) var(--s7); display: flex;
  gap: var(--s6); align-items: center; flex-wrap: wrap; font-size: var(--t1); color: var(--ink-2);
}
.footer-copy { margin: 0; }
.footer-links { display: flex; gap: var(--s6); flex-wrap: wrap; }
.footer-links a { display: inline-flex; align-items: center; min-height: 1.5rem; }
.theme-toggle {
  margin-left: auto; font-family: var(--font-mono); font-size: var(--t1);
  font-weight: 500; min-height: 1.5rem; height: auto; padding: var(--s1) var(--s2);
}

/* ------------------------------------------------------- the sphere sheets */

.sphere > .sphere-summary {
  cursor: pointer; list-style: none; font-weight: 600; font-size: var(--t2);
  padding: var(--s2h) var(--s3); background: var(--surface);
  border: 1px solid var(--field-line); border-radius: var(--radius-sm); margin-bottom: var(--s4);
  display: flex; align-items: center;
}
.sphere > .sphere-summary::-webkit-details-marker { display: none; }
.sphere > .sphere-summary .caret { color: var(--ink-2); font-size: var(--t2); opacity: 1; margin-left: auto; }

@media (min-width: 700px) {
  .sphere > .sphere-summary { display: none; }
}

.me-head { display: flex; gap: var(--s3); align-items: center; color: var(--ink); }
.me-head:hover { text-decoration: none; }
.me-text { min-width: 0; }
.me-name { display: block; font-weight: 600; }
.me-meta { display: block; color: var(--ink-2); font-size: var(--t2); }
.me-statement { margin: var(--s3) 0 0; color: var(--ink-2); font-size: var(--t2); line-height: 1.5; }

/* ------------------------------------------------------------------- phone */

/* The bar folds to 2 rows as soon as one row stops fitting: brand and search,
   then the menus. The angles pack left; Messages and the avatar sit right. */
@media (max-width: 949px) {
  .topbar-inner { height: auto; padding: var(--s2) var(--s4); flex-wrap: wrap; row-gap: var(--s2); }
  .topbar-brand { order: 1; }
  .searchbar { order: 2; flex: 1 1 130px; max-width: none; margin: 0 0 0 auto; }
  .angles { order: 3; flex: 1 0 100%; justify-content: flex-start; gap: var(--s1); }
  /* E4 1, SC 2.4.11 and D100. The second row holds 6 stops and the account
     disc, and at 700 px on a 20 px root it needed 664 px of a 660 px row: the
     disc wrapped to a third bar row, the bar stood 164 px against 135 px of
     scroll padding, and a skip-link jump left 29 px of the target underneath
     it. Both planets did it, at the same 5 px. Five pixels a stop buys the row
     24 px and the disc stays on the row with the menus it belongs to. */
  .angles > .dropdown > summary, .topbar-link { padding: 0 var(--s1h); }
  /* E3 9. The gap goes before Replies, once. On both links it split the free
     space in two and left Replies alone in the middle of the row with 200 px
     of nothing each side. Replies and Messages are a pair. */
  .angles > .angle-menu + .topbar-link { margin-left: auto; }
}

@media (max-width: 699px) {
  /* E4 1. The row wraps when it must, so it is worth 4 px a stop and 2 px a gap
     to make sure it does not have to at 480. The 6 stops measured 453 px in a
     448 px row. */
  .angles { gap: 2px; }
  .angles > .dropdown > summary, .topbar-link { padding: 0 var(--s1); font-size: var(--t2); }

  /* Search folds to a magnifier and keeps the 16 px gutter. Open, it takes
     a row of its own. */
  .searchbar { flex: 0 0 auto; gap: 0; min-width: 0; }
  .searchbar input, .topbar .searchbar .btn { display: none; }
  .search-icon { display: inline-flex; }
  .searchbar.is-open { order: 3; flex: 1 0 100%; margin: 0; gap: var(--s2); }
  .searchbar.is-open input { display: block; }
  .searchbar.is-open .search-icon { display: none; }
  .angles { order: 4; }

  .layout { padding: var(--s4) var(--s4) var(--s8); gap: var(--s4); }
  .card-post, .card-event, .card-job, .card-news, .card-listing,
  .card-story { padding: var(--s4); }
  .composer { padding: var(--s3) var(--s4); }
  .col-left .card-me, .col-left .card-nudge { padding: var(--s3); }
  .page-title { font-size: var(--t5); }
  .profile-head .page-title, .org-head .page-title { font-size: var(--t6); }
  .job-head .page-title, .event-head .page-title { font-size: var(--t5); }
  .card-event .card-name, .card-job .card-name,
  .event-row .list-name, .job-row .list-name { font-size: var(--t4); }
  .supplier-row, .job-row, .news-row { padding: var(--s3) var(--s2); }
  .details { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .details dd { margin-bottom: var(--s2h); }
  .message { max-width: 92%; }
  .stats { gap: var(--s3); }
  .card-actions { gap: var(--s4) var(--s5); }

  /* E2 1. The filters sit above the results now, so their height is the
     distance to the first company. Sixteen categories read as 2 columns. */
  .filter-categories .checks {
    display: grid; grid-template-columns: 1fr 1fr; column-gap: var(--s3);
  }

  /* SC 1.4.10 and U4 8. A 4th control joined the index row when the pipeline
     mark arrived, and a row of 4 that cannot wrap is 406 px on a 320 px
     screen. On a phone the controls wrap and the box gives way. */
  .list-row > .row-actions { flex: 0 1 auto; flex-wrap: wrap; justify-content: flex-end; }

  /* The rails follow the feed and read compact. */
  .col-right .card { padding-bottom: var(--s4); margin-bottom: var(--s4); }
  .col-right .mini-row { padding: var(--s2) 0; }
  .col-right .mini-list > .mini-row:nth-child(n+3) { display: none; }
}

/* ------------------------------------------------------ real phones, 320 up */
/* SC 1.4.10. Nothing below holds a floor wider than the narrowest screen we
   support, so the page reflows at 320 px with no sideways scroll. */

@media (max-width: 479px) {
  /* D53. One row: mark, search, Messages, avatar. The 3 angles live behind
     the avatar. The bar was 3 rows and 132 px on a 568 px screen. */
  .topbar-inner {
    padding: var(--s2) var(--s3); gap: var(--s2); position: relative;
    min-height: 3.5rem; flex-wrap: wrap; row-gap: 0;
  }
  .angles > .angle-menu, .topbar-replies, .topbar-me-link { display: none; }
  .topbar-me-menu { display: inline-flex; }
  .angles {
    order: 3; flex: 0 0 auto; flex-wrap: nowrap; gap: var(--s1);
    margin-left: var(--s1);
  }
  .angles .topbar-link { margin-left: 0; }
  .searchbar { order: 2; margin-left: auto; }
  /* Open, the field takes the row under the bar rather than the bar itself. */
  .searchbar.is-open { position: absolute; top: 100%; left: 0; right: 0;
    margin: 0; padding: var(--s2) var(--s3); background: var(--bar); z-index: 2; }

  /* SC 2.5.8. The mark is a target, not a decoration, and it keeps a thumb's
     width from the industry pill beside it. */
  .brand-word { display: none; }
  .brand { gap: 0; }
  .brand-mark { width: 1.5rem; height: 1.5rem; }
  .topbar-brand { gap: var(--s3); }

  /* U1 10 and D53. The pill shortens to the industry's own short form, which
     the planet package holds, so it never reads "Business". The full name
     stays in the accessible name and heads the avatar menu, so the control
     never renames itself. */
  .planet-picker .pill-long, .pill-static .pill-long {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  /* The pill gives way before anything else in the bar, and it says so with
     an ellipsis rather than a hard cut. Its caret is decoration at this width. */
  .planet-picker .pill-short, .pill-static .pill-short {
    display: inline-block; max-width: 100%; overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom;
  }
  /* E4 1. A pill cut to 2 letters names no industry. The short form is the
     floor: the brand holds its width and the bar takes a second row instead,
     which is what a 20 px root asks for at 320 px. */
  .topbar-brand { min-width: auto; }
  .topbar .planet-picker > summary, .pill-static {
    padding: 0 var(--s2); max-width: 7.5rem;
  }
  .topbar .planet-picker > summary .caret { display: none; }

  /* A menu is a sheet the width of the bar, never a 210 px box hanging off
     the right edge. */
  .topbar .dropdown { position: static; }
  .topbar .dropdown .menu {
    top: calc(100% + 2px); left: var(--s3); right: var(--s3);
    min-width: 0; width: auto;
  }

  .layout { padding: var(--s4) var(--s3) var(--s7); }
  .footer-inner { padding: var(--s4) var(--s3) var(--s6); gap: var(--s4); }
  .banner { padding: var(--s1h) var(--s3); }

  /* A card head gives the name the width and drops the time to its own line
     when the two no longer fit. The marks stay whole either way. */
  .card-head { flex-wrap: wrap; }
  .card-who { flex-basis: 50%; }
  .card-time, .sponsored-label { text-align: right; }

  /* U1 6: a cell keeps the separator it carries, so the values wrap as whole
     phrases. The wrap itself is set for everything under 700 px, above. */
  .event-row .board-cols, .job-row .board-cols-3 {
    display: flex; flex-wrap: wrap; gap: var(--s1) var(--s2);
  }
  .event-row .inline-form .btn { min-width: 0; }

  /* A filter is a full row, not a 140 px column. */
  .filter-field { flex: 1 1 100%; min-width: 0; max-width: none; }
  .searchrow input { flex: 1 1 100%; }
  .composer-selects select { flex: 1 1 140px; min-width: 0; }
  .field-md input, .field-md select, .field-lg input, .field-lg select { max-width: none; }

  .supplier-row, .job-row, .news-row { padding: var(--s3) 0; }
  .message { max-width: 100%; }
  .details { gap: 2px; }
  /* An index row gives the whole width to the text and drops its controls to
     the line below, rather than squeezing the name into a third of a phone. */
  .list-row { flex-wrap: wrap; }
  /* The text shrinks beside the figure instead of wrapping under it. */
  .list-row > .list-text { flex-basis: 0; }
  .list-row > .row-actions, .list-row > .inline-form {
    flex: 1 0 100%; margin-top: var(--s2);
  }
  /* The state does the same, rather than taking a third of the name's width. */
  .list-row > .state { flex: 1 0 100%; margin-top: var(--s1); padding-left: 48px; }
  .page-signin .layout { padding-top: var(--s6); }
}

/* ------------------------------------------------------------------ motion */

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

@media print {
  .topbar, .footer, .col-left, .col-right, .banner { display: none; }
}


/* ------------------------------------------------ the pages wave 2 added */

/* D32. The phone strip: the next event, the open jobs and the newest listing
   on one line each, above the composer, gone the moment the rails fit. */
.strip { display: none; }

@media (max-width: 699px) {
  .strip {
    display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5);
    margin: 0 0 var(--s4); padding: 0 0 var(--s3);
    border-bottom: 1px solid var(--line);
  }
  .strip-item { display: flex; align-items: baseline; gap: var(--s2); min-height: 1.5rem; }
  .strip-item:hover { text-decoration: none; }
  .strip-item:hover .strip-text { color: var(--accent); }
  .strip-label {
    font-family: var(--font-mono); font-weight: 500; font-size: var(--t1);
    text-transform: uppercase; letter-spacing: .06em; color: var(--ink-2);
    white-space: nowrap;
  }
  .strip-text { font-size: var(--t2); font-weight: 600; color: var(--ink); }
}

/* A blocked member is a state word, not a control: the mono outline. */
.tag-blocked { white-space: nowrap; color: var(--warn); border-color: var(--warn); }

/* E3 3 and U4 7. A pipeline state is not a token, so it is not a chip. It is
   a filled mark and the word: green where the conversation moved forward,
   warn where it stopped, quiet where nothing has happened yet. The word
   carries the state; the colour only makes 40 rows scannable. */
.state {
  flex: 0 0 auto; display: inline-flex; align-items: baseline; gap: var(--s1h);
  font-family: var(--font-mono); font-weight: 500; font-size: var(--t1);
  text-transform: uppercase; letter-spacing: .06em; color: var(--ink-2);
  white-space: nowrap;
}
.state-dot {
  flex: 0 0 auto; width: 8px; height: 8px; border-radius: 50%;
  background: currentColor; transform: translateY(-1px);
}
.state-shortlist { color: var(--good); }
/* E3 5 and D61. Declined is an answer, not a warning. --warn is reserved for
   Report and for an action that destroys something. */
.state-declined, .state-open, .state-archived { color: var(--ink-2); }
.state-archived .state-dot { background: transparent; box-shadow: inset 0 0 0 2px currentColor; }

/* U4 5 and E3 10. Working and done are the 2 states a recruiter separates at
   a glance, and they shared one green. Done is a drawn check in ink: a
   different shape, not a different shade. A closed report reads the same way. */
.state-hired, .state-closed { color: var(--ink); }
.state-hired .state-dot, .state-closed .state-dot {
  width: 6px; height: 10px; border-radius: 0; background: transparent;
  border: solid currentColor; border-width: 0 2px 2px 0;
  transform: rotate(45deg) translate(-1px, -2px);
}

/* D36. Where a silver field would sit on a basic page: the eyebrow's own
   face, quiet, so the gap reads as a rule rather than as a hole. */
.locked-line {
  font-family: var(--font-mono); font-weight: 500; font-size: var(--t1);
  text-transform: uppercase; letter-spacing: .06em; color: var(--ink-2);
  line-height: 1.4; margin: var(--s4) 0 0; padding-top: var(--s3);
  border-top: 1px dashed var(--line); max-width: var(--measure);
}

/* U4 2. The Spend block: 2 lines, one a product, and every number on the
   board so the eye runs down them rather than through the sentence. */
.spend-lines {
  list-style: none; margin: var(--s4) 0 0; padding: 0;
  border-top: 1px solid var(--line); font-size: var(--t2);
}
.spend-lines li {
  padding: var(--s2h) 0; border-bottom: 1px solid var(--line);
  color: var(--ink-2); max-width: var(--measure);
}
.spend-lines .count {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-weight: 500; color: var(--ink);
}

/* U2 6. The benchmark is the row that reads against the others, not with
   them: a rule above it, and the label says what it compares. */
.benchmark-row th, .benchmark-row td { border-top: 1px solid var(--line); color: var(--ink-2); }
.benchmark-row .count { color: var(--ink); }

/* E3 6. A count on the topic list reads on the board, like every other. */
.count-line {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--t2);
}

/* E3 3, E2 3 and D97. A capability is a phrase, so on the company page it is a
   ruled list. On an index row 3 of them ran together as one hyphenated sentence
   under a place line that uses the same hyphen, so a location and 3 separate
   claims read alike. They are word chips on the row now, the shape a member row
   already gives a short fact, and the page keeps its ruled list. */
.list-caps { margin-top: var(--s2); }
.bullets-tight li { padding: var(--s1) 0; font-size: var(--t2); }
.bullets-tight li:last-child { border-bottom: 0; }
.details dd .bullets { margin: 0; }

/* E3 3. One shape for every multi-value field on a company page: 2 ruled
   columns of equal width, whatever the field is called and however many
   values it holds. The multicolumn it replaces balanced its 2 columns to
   different widths, so the ratings and the scope read as 2 components. */
ul.bullets.bullets-cols {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--s7); columns: auto;
}
@media (max-width: 599px) {
  ul.bullets.bullets-cols { grid-template-columns: minmax(0, 1fr); }
}
/* A code is not a phrase. Airport codes read on 1 line, on the board. */
.chips-inline { margin: 0; }

/* U4 7 and E3 5. A receipt reads as a ruled list: what was bought against
   what it cost, one rule a line, the way a printed one does. */
.details-ruled { gap: 0 var(--s4); border-top: 1px solid var(--line); }
.details-ruled dt, .details-ruled dd {
  margin: 0; padding: var(--s2h) 0; border-bottom: 1px solid var(--line);
}
@media (max-width: 699px) {
  .details-ruled dt { padding-bottom: 0; border-bottom: 0; }
  .details-ruled dd { padding-top: 0; }
}
/* E3 5. The amount is what the page is about, so it is the size of a title,
   not the size of its own label. E3 9: in the mono face a single space at 21
   and 26 px is a whole character cell, and "EUR  249" read as 2 spaces. The
   currency and the figure are one amount, so they take a word space. */
.buy-amount .count { font-size: var(--t6); line-height: 1.2; }
.buy-amount .count, .plans .price-row .count,
.details dd .count, .spend-lines .count { word-spacing: -.28em; }

/* What you are about to delete, quoted back before you confirm it. */
.confirm-quote {
  margin: var(--s4) 0; padding: 0 0 0 var(--s4);
  border-left: 3px solid var(--line); color: var(--ink-2); max-width: var(--measure);
}

/* D31. The 3 privacy settings are their own block inside the profile form. */
.privacy-block { border-top: 1px solid var(--line); padding-top: var(--s4); margin-top: var(--s6); }
.privacy-block > .label { margin-bottom: var(--s3); }
.privacy-block .form-help { margin: 0 0 var(--s3) 34px; max-width: var(--measure); }
.privacy-block .label { margin-top: var(--s3); }

/* D52. This labels a control, so it is a form label: the text face at the
   size of the value beside it, not the departure board. */
.label-inline {
  font-family: var(--font); font-weight: 600; font-size: var(--t2);
  color: var(--ink); align-self: center; margin-right: var(--s1);
}

/* E3 3 and D93. The terms of a course used to be 2 inline spans on the row, a
   price and an audience 12 px apart, and at 375 px the gap fell on a line break.
   The row carries the same labelled `.event-terms` the event page carries, so
   the rules for both live in one place, further down. */

/* The audience block on the money page reads as a number, not a sentence. */
.audience-line { max-width: var(--measure); }
.audience-line .count {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-weight: 500; color: var(--ink);
}

/* The front door. Counts in mono, the invitation as a notice, the sample
   posts as index rows with no controls on them. */
.landing-counts { margin: 0 0 var(--s5); color: var(--ink-2); font-size: var(--t2); }
.page-signin .landing-counts { text-align: center; }
.landing-invite { text-align: left; }
.landing-posts .list-note { margin-top: var(--s1); max-width: var(--measure); }

/* Your reports: the reference is the first thing on the row. */
.report-ref { color: var(--ink-2); }


/* ------------------------------------------- the pages wave 2, round 5 (C5) */

/* D57. Without a session the bar is 3 things: the mark, the planet and Sign
   in. The planet is a fact here, not a control, so it is a pill that does not
   press, and it gives way the same as the picker does when the room runs out.
   Sign in is the one button on the bar, so it is outlined. */
.pill-static {
  display: inline-flex; align-items: center; min-width: 0; max-width: 100%;
  height: 1.75rem; padding: 0 var(--s3); border-radius: 999px;
  background: var(--bar-soft); color: var(--bar-ink);
  font-size: var(--t2); font-weight: 600; white-space: nowrap; overflow: hidden;
}
.pill-static .pill-long {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.angles-public { margin-left: auto; }
.topbar-signin {
  border: 1px solid var(--bar-line); border-radius: var(--radius-sm);
  padding: 0 var(--s3);
}
.topbar-signin:hover { background: var(--bar-soft-2); }

/* E2 6 and D53. Under 480 px the Replies link is not on the bar, so its count
   rides the avatar that opens the menu it moved into. The ring in the bar's
   own ground keeps the number legible over the disc. */
.badge-avatar {
  position: absolute; top: -2px; left: 17px; z-index: 1;
  min-width: 0; margin: 0; padding: 0 var(--s1h);
  line-height: 1rem; font-size: var(--t1);
  box-shadow: 0 0 0 2px var(--bar);
}

/* U3 5. A rating is a class and the types it covers. On one unlabelled list
   "above 5,700 kg" and "5,700 kg and below" sat on consecutive rows and read
   as an error. Each group names itself, inside the ruled list it lives in. */
.details-groups { grid-template-columns: minmax(0, 1fr); gap: 0; margin: 0; }
.details-groups .group-label { padding-top: var(--s2); }
.details-groups > .group-label:first-child { padding-top: 0; }
.details-groups dd { margin: 0; }

/* E1 5. The whole mail, wrapped: the invitation link lives in the body, so a
   body cut at 60 characters is a body nobody can use. */
.outbox-body {
  margin: 0; max-width: var(--measure); white-space: pre-wrap; overflow-wrap: anywhere;
  font-family: var(--font-mono); font-size: var(--t2); line-height: 1.5;
  color: var(--ink-2);
}

/* D62. One count, and the reason the rest of the industry is not on the list. */
.directory-count { max-width: var(--measure); margin: 0 0 var(--s4); }

/* U1 3. A member with no topics reads one message, in the column the feed is
   in. It takes the shape of the rail's nudge, because it is the same nudge. */
.card-no-topics {
  border: 1px solid var(--accent); border-radius: var(--radius);
  padding: var(--s4); margin: 0 0 var(--s3); border-bottom: 1px solid var(--accent);
}
.card-no-topics .empty { color: var(--ink); margin: 0 0 var(--s3); }
.card-no-topics .panel-foot { margin: 0; }

/* D58. What the founder year turns into, under the amount it follows. */
.founder-after {
  margin: var(--s2) 0 0; font-variant-numeric: tabular-nums;
}

/* E3 7. On /me/replies the answer is the thing and the post it answers is the
   context. They read at one weight and one size, so the eye took the wrong
   one first. The quote goes behind a rule at 13 px; the reply reads at the
   size the page reads at. */
.reply-quote {
  border-left: 2px solid var(--line); padding-left: var(--s2h);
  color: var(--ink-2); font-size: var(--t2); max-width: var(--measure);
}
.reply-body {
  margin-top: var(--s2); color: var(--ink); font-size: var(--t3);
  line-height: 1.5; max-width: var(--measure);
}


/* ------------------------------------------- the pages wave 2, round 6 (C6) */

/* E4 6. From 700 px to 1,099 px the grid puts a 260 px rail beside the column,
   which left the plans table 369 px of the 685 and the gold column reading
   "GOL". The table is what this page is, so it takes the width and the rail
   reads under it. Nothing is dropped. Past 1,100 px the rail is the 280 px one
   on the left and the column is wide enough for the table. */
@media (min-width: 700px) and (max-width: 1099px) {
  .page-table .layout { grid-template-columns: minmax(0, 1fr); }
  .page-table .col-main { grid-column: 1; grid-row: 1; }
  .page-table .col-right { grid-column: 1; grid-row: 2; }
  .page-table .col-left, .page-table .layout-wide .col-left { grid-column: 1; grid-row: 3; }
}

/* D75. The line that says what is inside, above the member count. It is the
   second sentence a visitor reads, so it reads at the size of a statement. */
.landing-inside {
  margin: 0 0 var(--s2); font-size: var(--t4); line-height: 1.45;
  max-width: var(--measure-lede); font-variant-numeric: tabular-nums;
}
.page-signin .landing-inside { margin-left: auto; margin-right: auto; text-align: center; }

/* D68 and E1 1. The plan a buyer chose, carried through the claim. It is a
   notice on the index, so it holds the measure and its link reads as a link. */
.plan-carry { max-width: var(--measure); }
.plan-carry a { font-weight: 600; }

/* U4 3 and D68. The 12-month module stands beside the single posting, and the
   crossover is the arithmetic under them, not a third offer. Two rows, one
   rule, the quiet line last. */
.module-offer {
  margin-top: var(--s4); padding-top: var(--s4);
  border-top: 1px solid var(--line); gap: var(--s2h) var(--s3);
}
.module-line { margin-top: var(--s2h); color: var(--ink-2); }

/* E3 2 and D100. A buy row sells one thing 4 ways, so the 4 controls take one
   width. Sized to their labels they ran 108, 168, 372 and 388 px in one row,
   and a price wore its length as importance. Equal tracks, equal heights, and
   the row drops to one button a line where 2 no longer fit. */
.buy-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: var(--s2h) var(--s3); margin-top: var(--s4);
}
.buy-row > .btn, .buy-row > .inline-form > .btn { width: 100%; }
@media (max-width: 699px) {
  .buy-row { grid-template-columns: minmax(0, 1fr); }
}

/* U4 8 and D69. A member already in the inbox, marked on the index row before
   a recruiter writes to them twice. A state, so it reads on the board. */
.in-pipeline {
  font-family: var(--font-mono); font-weight: 500; font-size: var(--t1);
  text-transform: uppercase; letter-spacing: .06em; color: var(--ink-2);
  white-space: nowrap;
}
a.in-pipeline:hover { color: var(--accent); }

/* U2 4 and U3 5. A multi-value field with named groups, inside the ruled list
   the admin reads down: one group a line, the label in front of its values. */
.field-groups { display: block; }
.field-group { display: block; margin-top: 2px; }
.field-group:first-child { margin-top: 0; }
.field-group .group-label {
  font-family: var(--font-mono); font-weight: 500; font-size: var(--t1);
  text-transform: uppercase; letter-spacing: .06em; color: var(--ink-2);
  margin-right: var(--s1h);
}

/* E5 9 and D63. The words the member typed, handed back on the refusal page.
   A quiet box, because it is not a field they are meant to edit. */
.stale-kept {
  background: var(--surface-2); border-color: var(--line); color: var(--ink);
  font-size: var(--t2); line-height: 1.5;
}


/* ------------------------------------------- the pages wave 2, round 7 (C7) */

/* U4 6 and D78. The faults inside a refusal notice: the lede says what went
   wrong once, and each field that failed reads on a line of its own. No
   marker, because the notice already marks the block. */
.list-plain { list-style: none; margin: var(--s2) 0 0; padding: 0; }
.list-plain > li { margin: 0; padding: 1px 0; }

/* U1 5 and D78. Price and For on an event, as a pair of terms rather than 2
   sentences inside the body. The term hugs its value, because there are 2 of
   them and a 170 px column would strand both. */
.event-terms {
  grid-template-columns: auto minmax(0, 1fr); gap: 2px var(--s3);
  margin: var(--s2h) 0 0; max-width: var(--measure);
}
.event-terms dt { padding-top: 0; }
@media (max-width: 699px) {
  .event-terms { grid-template-columns: minmax(0, 1fr); }
  .event-terms dd { margin-bottom: var(--s1); }
}

/* E3 5 and D85. The status slot on a member row said 2 things in 2 faces: a
   mono uppercase label that was the link, and a text-face sentence that was
   not. One face for both, and the link takes the link colour, so the
   clickable one is the one that looks clickable. */
.member-row .row-actions > .btn-text,
.member-row .row-actions > .in-pipeline {
  font-family: var(--font); font-weight: 500; font-size: var(--t2);
  text-transform: none; letter-spacing: 0; white-space: normal;
}
.member-row .row-actions > a.in-pipeline { color: var(--accent); }
.member-row .row-actions > a.in-pipeline:hover { text-decoration: underline; }

/* E4 5 and D85. The number in the Waiting on you cell was a 7.8 px target.
   The link is the cell, so a thumb has the whole column width and 24 px of
   height, stacked on a phone as well as ruled on a wide screen. SC 2.5.8. */
.cell-link {
  display: block; min-height: 1.5rem; padding: 2px 0;
}
@media (max-width: 699px) {
  /* Stacked, the cell is a label and its value, so the link takes the rest
     of the row rather than the width of the digit in it. */
  .table-stack td[data-label] > .cell-link { flex: 1 1 auto; text-align: right; }
}

/* E3 9 and D85. The front door reads on one alignment. The mark, the title,
   the lede and the counts were centred and everything under them ranged
   left, so the page changed its mind halfway down. Left, like every other
   page in the product, and the 4 counts read on the board with the member
   count under them. */
.page-signin .page-title, .page-signin .page-lede,
.page-signin .landing-inside, .page-signin .landing-counts { text-align: left; }
.page-signin .page-lede, .page-signin .landing-inside {
  margin-left: 0; margin-right: 0;
}
.signin-mark { justify-content: flex-start; }
.landing-inside .count { color: var(--ink); }


/* ------------------------------------------- the pages wave 2, round 8 (C8) */

/* U2 2, E3 and D92. The price page is the one page a phone reader arrives at
   with a wallet, and by feature it was 13 headings and 39 tick rows: gold ran
   2,600 px and a buyer held 15 answers in their head to assemble one offer.
   Under 700 px the page is 3 tier blocks, gold first, each a ruled block with
   what it holds, its 2 prices and its Choose buttons. The by-feature table
   folds behind one disclosure. Above 700 px the blocks are gone and the table
   is the page. Two rules hold it together: the DOM reads gold, silver, basic
   with no `order`, and only one set of price controls exists at any width,
   because the other set is display: none or inside a closed disclosure. */
.tier-blocks { margin: var(--s4) 0 0; border-top: 1px solid var(--line); }
.tier-block { padding: var(--s5) 0; border-bottom: 1px solid var(--line); }
.tier-block-name {
  font-family: var(--font-display); font-optical-sizing: auto;
  font-weight: 700; letter-spacing: -.01em; font-size: var(--t5);
  margin: 0 0 var(--s2);
}
/* The metal names itself in its own ink, as it does in the table head. */
.tier-block.plan-gold .tier-block-name { color: var(--gold-ink); }
.tier-block.plan-silver .tier-block-name { color: var(--silver-ink); }
.tier-block-name .tag { margin-left: var(--s2); vertical-align: 2px; }

/* A plain list. A tick against every line is a second alphabet on a page that
   already has one, and the block only carries the lines the tier holds. */
.tier-block-list { list-style: none; margin: 0; padding: 0; font-size: var(--t2); }
.tier-block-list li { padding: var(--s1) 0; max-width: var(--measure); }

.tier-block-prices { list-style: none; margin: var(--s4) 0 0; padding: 0; }
.tier-block-price {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: var(--s2) var(--s3); padding: var(--s2h) 0;
  border-top: 1px solid var(--line);
}
/* D52. This labels the amount beside it, so it is a form label, not a column
   head: the text face at the size of the value it names. */
.tier-block-term {
  flex: 0 0 auto; font-family: var(--font); font-weight: 600; font-size: var(--t2);
  color: var(--ink);
}
.tier-block-price .count { font-size: var(--t5); line-height: 1.2; word-spacing: -.28em; }
.tier-block-price .price-word { font-size: var(--t5); }
.tier-block-price .price-founder { flex: 1 0 100%; margin: 0; }
/* U2 6 and D97. The row reads term, then the price that is charged; the note
   about what it becomes later takes the line under it, beside Choose. */
.tier-block-price .price-founder-lead { flex: 0 1 auto; }
.tier-block-price .price-standard { flex: 1 0 100%; margin: 0; }
.tier-block-price .btn-choose { flex: 0 0 auto; margin: 0 0 0 auto; }

.compare-fold { margin: var(--s5) 0 0; }
.compare-fold > summary {
  display: inline-flex; align-items: center; min-height: 2.25rem;
  list-style: none; cursor: pointer;
  font-size: var(--t2); font-weight: 600; color: var(--ink);
}
.compare-fold > summary::-webkit-details-marker { display: none; }
.compare-fold > summary:hover { color: var(--accent); }
.compare-fold > summary .caret { margin-left: var(--s2); opacity: 1; }

@media (max-width: 699px) {
  /* The blocks above are where a phone buys. Opened, the table answers one
     question, what silver adds over basic, and it carries no second Choose for
     the same price: one set of price controls at every width, fold open or
     shut. */
  .compare-fold .btn-choose { display: none; }
  .compare-fold .price-founder { flex: 1 1 auto; text-align: left; }
}

@media (min-width: 700px) {
  /* One offer, one set of controls. The blocks leave the page and the
     accessibility tree with it. */
  .tier-blocks { display: none; }
  /* The page script opens the fold and hides the summary at this width, so a
     browser with the script blocked keeps a control that reaches the table. */
  .has-js .compare-fold > summary { display: none; }
  .compare-fold { margin-top: var(--s4); }
}

/* E3 4 and D93. One action position on the 4 indexes. Save sits top right on a
   member row, level with the name, where the jobs and events boards put it; the
   actions only a member row carries read as their own line under the text. The
   group used to wrap and strand Save alone on every row marked "Already in
   your inbox". */
.member-row {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  gap: var(--s2) var(--s3); align-items: start;
}
.member-row > .avatar, .member-row > .monogram { grid-area: 1 / 1 / 3 / 2; }
.member-row > .list-text { grid-area: 1 / 2 / 2 / 3; }
.member-row > .inline-form { grid-area: 1 / 3 / 2 / 4; justify-self: end; margin-top: 0; }
.member-row > .row-actions {
  grid-area: 2 / 2 / 3 / 4; flex-wrap: wrap; margin-top: 0;
}

/* E2 3, E3 3 and D97. The fourth index joins the other 3. Save sits top right,
   level with the company name, and it holds that corner on a phone the way the
   member row does, rather than dropping to a line of its own under the
   capability chips. */
.supplier-row {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  gap: var(--s2) var(--s3); align-items: start;
}
.supplier-row > .inline-form { justify-self: end; margin-top: 0; }

/* U1 4 and D95. How many members are going, on the board with the place and the
   countdown. It is the quieter of the 3, because it is the newest fact on the
   row and not the one a member scans for. */
.board-going { color: var(--ink-2); }

/* U1 5 and D95. The toggle on the event page is the shape Save is: a quiet mono
   action, not a bordered button, so the 2 read as one row of marks. On, it takes
   the accent every other toggle in the product takes, and the count sits beside
   it because the control renames itself. */
.going-form { align-items: baseline; gap: var(--s2); }
.going-button.is-on { color: var(--accent); }
.going-count { font-size: var(--t1); color: var(--ink-2); }

/* E2 3 and D87. Sign out and Sign in read as the footer's own links, not as
   page buttons that wandered into it. */
.footer-signout .btn, .btn.footer-signin {
  min-height: 1.5rem; padding: 0; background: transparent;
  font-family: var(--font-mono); font-weight: 500; font-size: var(--t1);
  color: var(--ink-2);
}
.footer-signout .btn:hover, .btn.footer-signin:hover {
  background: transparent; color: var(--accent); text-decoration: underline;
}

/* U4 7 and D89. What the till just sold, counted where the next advert is
   written. A count, so it reads on the board. */
.bought-line {
  display: block; margin-top: var(--s1);
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--t1); color: var(--ink-2);
}

/* E3 2 and D93. The company page is a data sheet, so the page is not capped at
   the measure. The prose inside it still is: a statement, an offer and the
   sentence under a claim block. */
.profile-statement, .offer-body, .claim-block p { max-width: var(--measure); }

/* E4 7 and SC 2.5.8. A quiet link that closes a rail panel is a target: on the
   drones jobs index "Manage who you follow" sat 14 px centre to centre from the
   Save button beside it, the only spacing clash in 14,402 targets. */
.col-left .panel-foot a, .col-right .panel-foot a {
  display: inline-flex; align-items: center; min-height: 1.5rem;
}

/* ------------------------------------------ staging: the door and feedback */
/* feedback/ and BUILD.md step 5. The closed door, the Feedback button on every
   staging page, and the feedback list. Only staging renders these classes. */

/* The door: one short card in the middle of an empty page, no bar. */
.door {
  min-height: 100vh; display: grid; place-items: center;
  padding: var(--s6) var(--s4);
}
.door-card { width: 100%; max-width: 30rem; }
.door-card .page-lede { margin-bottom: var(--s3); }

/* The dock. Closed, it is one small button in the corner. Open, the panel sits
   above the button and scrolls inside itself, so it never runs off a phone. */
.feedback-dock {
  position: fixed; right: var(--s4); bottom: var(--s4); z-index: 35;
  font-size: var(--t3);
}
.feedback-dock > summary {
  list-style: none; display: inline-flex; align-items: center; gap: var(--s1h);
  min-height: 2.25rem; padding: var(--s1) var(--s3); border-radius: 999px;
  background: var(--ink); color: var(--bg); box-shadow: var(--shadow);
  font-size: var(--t2); font-weight: 600; cursor: pointer; user-select: none;
}
.feedback-dock > summary::-webkit-details-marker { display: none; }
.feedback-dock > summary:hover { background: var(--ink-2); }
/* Open, the button draws a cross, so it reads as the way to close the panel. */
.feedback-dock[open] > summary::after {
  content: "\00d7"; font-size: var(--t4); line-height: 1; margin-right: -2px;
}
.feedback-dock-panel {
  position: absolute; right: 0; bottom: calc(100% + var(--s2));
  width: 22rem; max-width: calc(100vw - 2 * var(--s4));
  max-height: calc(100vh - 8rem); overflow-y: auto; overscroll-behavior: contain;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: var(--s4);
}
@supports (height: 100dvh) {
  .feedback-dock-panel { max-height: calc(100dvh - 8rem); }
}
.feedback-dock-panel .panel-title { margin-bottom: var(--s2h); }
.feedback-dock-label {
  display: block; margin-bottom: var(--s1);
  font-size: var(--t2); font-weight: 600; color: var(--ink);
}
.feedback-dock-kinds { margin: 0 0 var(--s3); }
.feedback-dock-kinds .check { padding: var(--s1) 0; }
.feedback-dock .field { margin-bottom: var(--s3); }
.feedback-dock textarea { min-height: 6rem; }
.feedback-dock .row-actions { margin-top: 0; }
.feedback-dock .notice { font-size: var(--t2); }
.feedback-dock .form-help { margin-top: var(--s2); }

/* A result that went well. The product's notices have no green of their own. */
.feedback-notice-good { border-left-color: var(--good); }

/* The last line of the footer stays clear of the closed button. */
body:has(> .feedback-dock) .footer-inner { padding-bottom: calc(var(--s7) + 2.5rem); }

@media (max-width: 479px) {
  .feedback-dock { right: var(--s3); bottom: var(--s3); }
  .feedback-dock-panel { max-width: calc(100vw - 2 * var(--s3)); padding: var(--s3); }
}

@media print {
  .feedback-dock { display: none; }
}

/* The feedback list. */
.feedback-filters {
  display: flex; flex-wrap: wrap; gap: var(--s3); align-items: flex-end;
  margin: 0 0 var(--s5);
}
.feedback-filters .field { margin-bottom: 0; }
.feedback-filters select { width: auto; min-width: 11rem; }
.feedback-meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1h) var(--s2h);
  margin: 0 0 var(--s1);
}
.feedback-meta .mono { font-size: var(--t1); }
.feedback-status {
  font-family: var(--font-mono); font-size: var(--t1); font-weight: 500;
  text-transform: uppercase; letter-spacing: .06em;
}
.feedback-status-new { color: var(--accent); }
.feedback-status-planned { color: var(--gold-ink); }
.feedback-status-done { color: var(--good); }
.feedback-status-declined { color: var(--ink-2); }
.feedback-path { font-size: var(--t1); overflow-wrap: anywhere; }
.feedback-body {
  white-space: pre-line; overflow-wrap: anywhere; max-width: var(--measure);
  margin: var(--s2) 0;
}
.feedback-reply {
  border-left: 3px solid var(--accent); padding: var(--s1) 0 var(--s1) var(--s3);
  margin: var(--s2) 0;
}
.feedback-reply .feedback-body { margin: 0; }
.feedback-device { overflow-wrap: anywhere; }
.feedback-edit {
  display: grid; grid-template-columns: minmax(0, 11rem) minmax(0, 1fr);
  gap: var(--s2) var(--s3); align-items: start;
  max-width: 640px; margin-top: var(--s3); padding-top: var(--s3);
  border-top: 1px dashed var(--line);
}
.feedback-edit .field { margin-bottom: 0; }
.feedback-edit .row-actions { grid-column: 1 / -1; margin-top: 0; }
@media (max-width: 479px) {
  .feedback-edit { grid-template-columns: minmax(0, 1fr); }
}
