/*
 * Member Portal — the designed interface (docs/SPECIFICATION.md section 15).
 *
 * Everything here comes from the Innovate Calgary Brand Guide 2024: Raleway,
 * the Coal / Slate / Grey neutral spine, the hexagon shape language, and the
 * hub colours used as an accent rather than as the page.
 *
 * Three rules the whole sheet follows:
 *
 * **The hub colour carries information, not decoration.** It fills a bar that
 * means a quantity, marks the sorted column, heads a section, and washes the
 * corner of a card. It is never applied because a page looked plain.
 *
 * **The accent and the ink are different colours.** --hub-accent is for
 * fills, rules and bars; --hub-ink is that colour darkened until it can be
 * read as text. AIH's yellow is why: #FFCD00 on white is about 1.6:1. Both
 * come from the request (apps/accounts/roles.py), so a page never has to
 * choose.
 *
 * **One card, one wash.** Every card is white through to a tenth-strength
 * tint of the hub in its bottom corner. Faint alone, unmistakable across a
 * page, and the reason a member's pages feel like their hub's.
 */

/* --- Raleway --------------------------------------------------------------
 * The brand face, self-hosted because it has to be: the Content-Security-
 * Policy restricts font-src to 'self', so a webfont pulled from Google would
 * be blocked and the page would silently fall back to the system sans.
 *
 * **One file per subset, not one per weight.** Raleway ships as a variable
 * font, so a single file carries the whole weight axis and the declaration
 * below names a range rather than a number. The three weights this design
 * uses -- Medium 500 for body, SemiBold 600 for headings and buttons, Bold
 * 700 for the odd emphasis -- all come out of it, in 48 KB rather than three
 * files of similar size each.
 *
 * The Latin subset covers everything the portal writes and most names members
 * have; latin-ext carries the rest and is fetched only when a page actually
 * contains one of its characters, which is what unicode-range is for.
 *
 * SIL Open Font License 1.1. The licence travels with the files, in
 * static/fonts/OFL.txt.
 */
@font-face {
  font-family: Raleway;
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("../fonts/raleway-latin.e401cae7a129.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
    U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
    U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: Raleway;
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("../fonts/raleway-latin-ext.77d813a04981.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F,
    U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
    U+A720-A7FF;
}

:root {
  --coal: #15181F;
  --slate: #2F353B;
  --white: #FFFFFF;
  --grey: #EEEEED;
  --ground: #F7F7F6;
  --line: #EEEEED;
  --line-strong: #E4E4E2;
  --muted: #6b7280;
  --faint: #9aa0a6;

  /* Set per request in base.html from the signed-in person's hub. */
  --hub-accent: #2F353B;
  --hub-accent-ink: #FFFFFF;
  --hub-ink: #2F353B;
  --hub-tint: #F1F2F3;

  /* Semantic, and deliberately not the accent: "this went wrong" must not
     change meaning when a member's hub does. */
  --good-bg: #E6F4EC;   --good-ink: #14603C;
  --warn-bg: #FDF3D3;   --warn-ink: #6A4B00;
  --bad-bg:  #FBE3E3;   --bad-ink:  #8C1024;
  --info-bg: #E5EFFF;   --info-ink: #1C3F7A;

  --radius: 4px;
  --rail: 16.5rem;
  --pad: 2.75rem;
}

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

body {
  margin: 0;
  font-family: Raleway, "Segoe UI", system-ui, -apple-system, sans-serif;
  font-weight: 500;
  color: var(--coal);
  background: var(--ground);
  line-height: 1.55;
  min-height: 100vh;
  display: flex;
}

h1, h2, h3, h4, button, .btn { font-weight: 600; letter-spacing: -0.01em; }
h1 { font-size: 2rem; margin: 0 0 1.25rem; }
h2 { font-size: 1.2rem; margin: 0 0 .85rem; }
h3 { font-size: 1rem; margin: 0 0 .4rem; }
a { color: var(--hub-ink); }

.skip-link { position: absolute; left: -999px; background: var(--white); padding: .5rem 1rem; }
.skip-link:focus { left: 1rem; top: 1rem; z-index: 10; }

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

/* --- the rail -------------------------------------------------------------
   One dark column carrying the wordmark, the interface, the hub, the tools
   and the account controls, in that order: where am I, what does that cover,
   what can I do, who am I. */

.rail {
  width: var(--rail); flex: none;
  background: var(--coal); color: #C9CDD2;
  border-top: 4px solid var(--hub-accent);
  padding: 1.6rem 0;
  display: flex; flex-direction: column;
}

.rail__head { padding: 0 1.5rem 1.4rem; }
.rail__logo { width: 9.9rem; height: auto; display: block; }
.rail__surface {
  margin: .85rem 0 0; font-size: .7rem; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; color: #8b9199;
}

.rail__hubs {
  list-style: none; margin: .75rem 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: .25rem .9rem;
}
.rail__hubs li {
  display: flex; align-items: center; gap: .45rem;
  font-size: .7rem; font-weight: 600; letter-spacing: .05em;
  text-transform: uppercase; color: #C9CDD2;
}

/* The brand's hexagon, as a 14px mark. Used wherever a hub or a section needs
   a token rather than a word. */
.hex {
  display: inline-block; width: .875rem; height: .875rem; flex: none;
  background: var(--hub-accent);
  clip-path: polygon(50% 3%, 93% 27%, 93% 73%, 50% 97%, 7% 73%, 7% 27%);
}

.rail__nav { display: flex; flex-direction: column; gap: .12rem; padding: 0 .75rem; }
.rail__group {
  margin: 1.4rem .75rem .5rem; font-size: .65rem; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
}
.rail__link {
  display: block; padding: .65rem .75rem; border-radius: 3px;
  color: #C9CDD2; text-decoration: none; font-size: .875rem; line-height: 1.35;
}
.rail__link:hover { background: var(--slate); color: var(--white); }
.rail__link--current {
  background: var(--hub-accent); color: var(--hub-accent-ink); font-weight: 600;
}
.rail__link:focus-visible { outline: 2px solid var(--white); outline-offset: -2px; }

.rail__account {
  margin: auto .75rem 0; padding: 1.35rem .75rem 0;
  border-top: 1px solid var(--slate);
}
.rail__who { margin: 0 0 .15rem; font-size: .8rem; font-weight: 600; color: var(--white); }
.rail__role { margin: 0 0 .6rem; font-size: .75rem; color: #8b9199; }
.rail__account-links { display: flex; gap: .9rem; }
.rail__account-links a { color: #C9CDD2; font-size: .75rem; text-decoration: none; }
.rail__account-links a:hover { color: var(--white); text-decoration: underline; }

/* --- the content column --------------------------------------------------- */

.column { flex: 1; min-width: 0; display: flex; flex-direction: column; }

main {
  flex: 1;
  padding: 2.1rem var(--pad) 2.5rem;
  max-width: 110rem;
}

/* Kept because templates set it. It used to narrow member pages to a reading
   measure; the designed pages are full-bleed within the column and set their
   own measures per block, so it no longer needs to do anything. */
.wrap--staff { }

.page-header {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  margin: 0 0 1.4rem;
}
.page-header h1 { margin: 0; }
.page-header .btn { margin-left: auto; }

/* The hub's name above the page's, in the hub's own ink. */
.eyebrow {
  margin: 0 0 .45rem; font-size: .7rem; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; color: var(--hub-ink);
}

.breadcrumb { margin: 0 0 1rem; font-size: .75rem; font-weight: 600; letter-spacing: .03em; }
.breadcrumb a { color: var(--hub-ink); text-decoration: none; }
.breadcrumb a:hover { text-decoration: underline; }

.lede { font-size: 1rem; color: var(--slate); max-width: 78ch; }

/* --- the footer -----------------------------------------------------------
   Full width, from the rail to the right edge. It read as stopping partway
   across when the acknowledgement carried a reading measure. */

.site-footer {
  margin: 2.25rem var(--pad) 0;
  padding: 1.4rem 0 2rem;
  border-top: 1px solid var(--line-strong);
  font-size: .75rem; color: var(--muted);
}
.site-footer a { color: var(--hub-ink); text-decoration: none; }
.site-footer a:hover { text-decoration: underline; }
.land-acknowledgement { margin: 0 0 .9rem; line-height: 1.65; }
.land-acknowledgement a { color: var(--muted); }
.site-footer__row {
  display: flex; align-items: baseline; gap: 1.25rem; flex-wrap: wrap;
}
.footer-links { display: flex; gap: 1.25rem; flex-wrap: wrap; font-weight: 600; }
.copyright { margin: 0 0 0 auto; color: var(--faint); }

/* --- signed out ----------------------------------------------------------
   No rail, because there is nothing to navigate to yet. A Coal panel carries
   the wordmark instead, so the brand still opens the page. */

.entry__banner {
  width: 24rem; flex: none;
  background: var(--coal); color: #C9CDD2;
  border-top: 4px solid var(--hub-accent);
  padding: 2.4rem 2.5rem;
  display: flex; flex-direction: column; justify-content: space-between; gap: 3rem;
  position: relative; overflow: hidden;
}
/* The brand's hexagons, at the strength of a watermark. The only decoration
   in the portal, on the one page that has nothing else to carry. */
.entry__banner::after {
  content: ""; position: absolute; right: -3rem; top: 32%;
  width: 16rem; height: 16rem; opacity: .13;
  background: var(--hub-accent);
  clip-path: polygon(50% 3%, 93% 27%, 93% 73%, 50% 97%, 7% 73%, 7% 27%);
}
.entry__logo { width: 11.5rem; height: auto; display: block; }
.entry__surface {
  margin: .9rem 0 0; font-size: .7rem; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; color: #8b9199;
}
.entry__blurb {
  margin: 0; position: relative; font-size: .95rem; line-height: 1.6; color: #C9CDD2;
}
.entry main { display: flex; justify-content: center; padding-top: 4rem; }
.entry main > * { width: 100%; max-width: 27rem; }
.entry .card--wide { max-width: 44rem; }
.entry .card { margin-bottom: 0; }

/* --- cards ---------------------------------------------------------------- */

.card {
  background:
    linear-gradient(152deg, var(--white) 0%, var(--white) 58%, var(--hub-tint) 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.4rem 1.5rem;
  margin: 0 0 1.25rem;
}
.card--wide, .card--flush { max-width: none; }
.card--flush { padding: 1.4rem 1.5rem; }
.card > :last-child { margin-bottom: 0; }

/* A section heading with the hub's hexagon in front of it. The mark is the
   structure, not an ornament: it says a new part of the page starts here. */
.card > h2, .card--flush > h2, .landing-section__head h2,
.disclosure > summary > h2 {
  display: flex; align-items: center; gap: .6rem;
  font-size: 1.1rem; text-transform: none; letter-spacing: -0.01em;
  color: var(--coal);
}
.card > h2::before, .card--flush > h2::before, .landing-section__head h2::before,
.disclosure > summary > h2::before {
  content: ""; width: .8rem; height: .8rem; flex: none;
  background: var(--hub-accent);
  clip-path: polygon(50% 3%, 93% 27%, 93% 73%, 50% 97%, 7% 73%, 7% 27%);
}

/* The one action a page wants taken sits on the wash at full strength, with
   the brand's hexagon field over it. Reserved: two of these on a page and
   neither is the promoted one. */
.card--feature {
  border: 0;
  background:
    radial-gradient(circle at 88% 22%, rgba(255,255,255,.55) 0 22%, transparent 23%),
    radial-gradient(circle at 96% 62%, rgba(255,255,255,.45) 0 18%, transparent 19%),
    linear-gradient(146deg, var(--hub-tint) 0%, #FCEFE6 52%, #FFFAE8 100%);
}

.detail-grid {
  display: grid; gap: 1.25rem;
  grid-template-columns: repeat(auto-fit, minmax(21rem, 1fr));
  align-items: start;
}

/* --- key/value lists ------------------------------------------------------ */

.detail { display: grid; grid-template-columns: max-content 1fr; gap: .1rem 1.5rem; margin: 0; }
.detail dt {
  color: var(--muted); font-size: .8rem; padding: .5rem 0;
  border-bottom: 1px solid var(--line);
}
.detail dd {
  margin: 0; padding: .5rem 0; font-weight: 600; overflow-wrap: anywhere;
  border-bottom: 1px solid var(--line);
}
.detail dt:last-of-type, .detail dd:last-of-type { border-bottom: 0; }

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

label {
  display: block; margin: 0 0 .35rem;
  font-size: .7rem; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--hub-ink);
}
input[type="email"], input[type="text"], input[type="password"],
input[type="search"], input[type="number"], input[type="url"],
input[type="date"], input[type="time"], input[type="datetime-local"],
select, textarea {
  width: 100%; padding: .55rem .7rem; margin: 0 0 1rem;
  border: 1px solid #D6D6D4; border-radius: 3px;
  background: var(--white); font: inherit; color: var(--coal);
}
textarea { min-height: 7rem; }
input[type="checkbox"], input[type="radio"] { accent-color: var(--hub-accent); margin: 0; }
input:focus-visible, select:focus-visible, textarea:focus-visible,
a:focus-visible, button:focus-visible {
  outline: 2px solid var(--hub-accent); outline-offset: 2px;
}
p.field { margin: 0 0 1rem; }
.field label { text-transform: uppercase; }
.field ul { list-style: none; padding: 0; margin: 0; }
.field ul li label { text-transform: none; font-size: .9rem; color: var(--coal); }

.helptext {
  /* The negative top margin tucks a hint up under the field it explains,
     closing the gap the field's own bottom margin leaves. */
  display: block; margin: -.6rem 0 1rem;
  color: var(--muted); font-size: .78rem; line-height: 1.6;
  text-transform: none; letter-spacing: 0; font-weight: 500;
}
/* ...but after a form or a button there is no field to tuck under, and the
   same margin pulls the text up into the button instead. That is what put
   "Forgotten your password?" underneath the Sign in button on all three login
   pages: a button is inline-block, so the paragraph began on a new line and
   was then dragged back over it. */
form + .helptext,
button + .helptext,
.btn + .helptext { margin-top: .5rem; }
.errorlist {
  color: var(--bad-ink); margin: 0 0 .75rem; padding-left: 1.1rem; font-size: .82rem;
}

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

.btn {
  display: inline-block; border: 0; border-radius: 3px;
  background: var(--hub-accent); color: var(--hub-accent-ink);
  padding: .6rem 1.15rem;
  font: inherit; font-size: .75rem; font-weight: 600;
  letter-spacing: .7px; text-transform: uppercase;
  cursor: pointer; text-decoration: none;
}
.btn:hover { filter: brightness(.92); }
.btn--secondary {
  background: transparent; color: var(--coal);
  box-shadow: inset 0 0 0 1.5px var(--coal);
}
.btn--danger { background: var(--bad-ink); color: var(--white); }

/* Row-sized. Actions that live inside a table cell need button chrome -- an
   outline and a real hit area -- but not the weight of a page's primary
   button, which would shout from every row. */
.btn--small { padding: .38rem .65rem; font-size: .68rem; letter-spacing: .5px; }
/* Destructive, but quietly: an outline rather than a filled red, because a
   solid Remove in every row reads as an alarm rather than an option. */
.btn--danger-quiet {
  background: transparent; color: var(--bad-ink);
  box-shadow: inset 0 0 0 1.5px var(--bad-ink);
}

/* The actions cell of a table row. Remove and Change Tier used to sit as two
   underlined links separated by whitespace, which read as a single option;
   they are now separate buttons with a gap that keeps them apart at any
   width. Badges sit in the same flow, so a scheduled change and the way to
   call it off stay together. */
.row-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; }
.row-actions form { margin: 0; }

/* A form control that is really a link: "Make an Admin", "Cancel this
   request". Styled as one so a row of actions reads as a row of actions. */
.linkish {
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; font-size: .75rem; font-weight: 600; letter-spacing: .03em;
  color: var(--hub-ink); text-decoration: underline;
}

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

.messages { padding: 1.25rem var(--pad) 0; }
.message {
  margin: 0 0 .5rem; padding: .7rem 1rem; border-radius: var(--radius);
  background: var(--white); border: 1px solid var(--line); border-left: 3px solid var(--slate);
}
.message--error { border-left-color: var(--bad-ink); background: #FFF7F7; }
.message--success { border-left-color: var(--good-ink); background: #F4FBF7; }
.message--warning { border-left-color: var(--warn-ink); background: #FFFCF2; }

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

.queue-wrap {
  background:
    linear-gradient(152deg, var(--white) 0%, var(--white) 66%, var(--hub-tint) 100%);
  border: 1px solid var(--line); border-radius: var(--radius);
  margin: 0 0 1rem; overflow-x: auto;
}
.queue { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.queue th, .queue td { text-align: left; padding: .8rem 1rem; vertical-align: middle; }
.queue thead tr { background: color-mix(in srgb, var(--hub-tint) 55%, #FFFFFF); }
.queue thead th {
  padding: 0; font-size: .68rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--slate); border-bottom: 1px solid var(--line-strong); white-space: nowrap;
}
.queue thead th:not(:has(.sort)) { padding: .7rem 1rem; }
.queue tbody td { border-bottom: 1px solid #F2F2F1; font-size: .85rem; }
.queue tbody tr:last-child td { border-bottom: 0; }
.queue tbody tr:hover { background: rgba(255, 255, 255, .55); }
.queue .col-name { font-weight: 600; }

/* --- the hub a row belongs to -----------------------------------------------
 * A colour down the left edge of every row in the two staff queues, in that
 * Membership's own hub colour. It answers "whose is this" before the Hub
 * column has been read, which matters on a page listing both hubs at once.
 *
 * Per-row, so it cannot use --hub-accent: that is one colour for the whole
 * page, set from the person signed in, and these rows each carry their own.
 * The two brand colours therefore appear here as literals -- the same values
 * as HUB_ACCENT_COLOURS in apps/accounts/roles.py, which is the only other
 * place they are written.
 *
 * The transparent default keeps every row the same width, so a Membership
 * with no hub recorded does not sit three pixels out of line with the rest.
 * Borders on a row need border-collapse, which .queue already sets. */
.queue tbody tr[data-hub] { border-left: 3px solid transparent; }
.queue tbody tr[data-hub="lsih"] { border-left-color: #AE1966; }
.queue tbody tr[data-hub="aih"] { border-left-color: #FFCD00; }
.queue tbody tr[data-hub="programs"] { border-left-color: var(--slate); }

/* The same hub, named. Square like the state badges rather than a pill, and
   short: "Life Sciences Innovation Hub" wraps to three lines in a table cell
   and says the same thing all the way down a page. The full name stays in the
   title attribute for anyone who does not know the abbreviation.

   The tints and inks are the same pairs as HUB_TINTS and HUB_TEXT_INK in
   apps/accounts/roles.py. AIH's yellow is why the text colour is a separate
   value: #FFCD00 on white is about 1.6:1 and cannot be read. */
.hub-pill {
  display: inline-block; padding: .15rem .45rem; border-radius: 3px;
  font-size: .7rem; font-weight: 700; letter-spacing: .04em; white-space: nowrap;
}
.hub-pill--lsih { background: #FAEBF2; color: #8D1453; }
.hub-pill--aih { background: #FFF8E1; color: #7A5C00; }
.hub-pill--programs { background: #F1F2F3; color: var(--slate); }

/* The second line on a stat tile: the age of the oldest thing in a queue, or
   what else is waiting behind the headline count. Quieter than the label
   above the figure, because it qualifies the figure rather than naming it. */
.stat__aside {
  margin: -.2rem 0 0; font-size: .78rem; color: var(--muted);
}

/* The attention list on a member's landing page. One line per stalled thing,
   each ending in where it is dealt with. Not a bulleted list: these are
   sentences to act on rather than items to count. */
.attention { list-style: none; margin: .4rem 0 0; padding: 0; }
.attention li {
  padding: .45rem 0; border-bottom: 1px solid var(--line);
  display: flex; flex-wrap: wrap; gap: .5rem; align-items: baseline;
}
.attention li:last-child { border-bottom: 0; }
.attention a { font-weight: 600; white-space: nowrap; }
.queue .muted { color: var(--muted); }
/* Plain links in a table take the hub ink. A button that happens to be an
   anchor must not: this selector outranks .btn--secondary and would repaint
   Remove in the hub colour, which on AIH is a yellow-brown. */
.queue a:not(.btn) { color: var(--hub-ink); }
.queue .row--problem { background: #FFF7F7; }
.queue .row--problem .col-name { color: var(--bad-ink); }

/* --- sortable headings ----------------------------------------------------
   Every heading is a control, and says so before it is hovered: an unsorted
   column carries an outlined mark, the sorted one is filled in. Two signals
   for the same fact, because one of them survives a screenshot. */

.queue thead .sort {
  display: flex; align-items: center; justify-content: space-between; gap: .6rem;
  padding: .55rem .75rem; margin: .15rem .25rem;
  border-radius: 3px; color: var(--slate); text-decoration: none; white-space: nowrap;
}
.queue thead .sort:hover { background: var(--white); color: var(--coal); }
.queue thead .sort:focus-visible { outline: 2px solid var(--hub-accent); outline-offset: -2px; }
.sort__mark {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 1.15rem; height: 1.15rem; border-radius: 3px;
  border: 1px solid #C9CBC9; background: var(--white); color: var(--slate);
  font-size: .65rem; line-height: 1;
}
.queue thead .sort:hover .sort__mark { border-color: var(--slate); color: var(--coal); }
/* The sorted column, filled with the hub accent.
 *
 * These selectors are scoped through `.queue thead` for one reason: without
 * it, `.sort--active` is a single class and loses to `.queue thead .sort`,
 * which sets the slate text colour. The background applied and the colour
 * did not, so the sorted heading was slate on slate -- unreadable, and worst
 * on staff pages, where no hub is known and the accent falls back to slate
 * itself. Keep the scoping on any rule here that overrides a colour. */
.queue thead .sort--active {
  background: var(--hub-accent); color: var(--hub-accent-ink); font-weight: 700;
}
.queue thead .sort--active:hover {
  background: var(--hub-accent); color: var(--hub-accent-ink);
}
.queue thead .sort--active .sort__mark,
.queue thead .sort--active:hover .sort__mark {
  background: transparent; border-color: currentColor; color: inherit;
}

/* --- badges ---------------------------------------------------------------
   Square rather than pill: a pill reads as a count, and these are states. */

.badge {
  display: inline-block; padding: .18rem .5rem; border-radius: 2px;
  font-size: .68rem; font-weight: 600; letter-spacing: .04em; white-space: nowrap;
  background: var(--grey); color: var(--slate);
}
.badge--waiting { background: var(--info-bg); color: var(--info-ink); }
.badge--problem { background: var(--bad-bg);  color: var(--bad-ink); }
.badge--done    { background: var(--good-bg); color: var(--good-ink); }
.badge--warn    { background: var(--warn-bg); color: var(--warn-ink); }
.badge--muted   { background: var(--grey);    color: var(--muted); }
/* The hub's own tint, for a value that belongs to the hub rather than to a
   state: a Tier, a Home Hub, a billing cadence. */
.badge--hub { background: var(--hub-tint); color: var(--hub-ink); }

.row--testing { background: #FFFCF2; }

/* --- pagination and result counts ---------------------------------------- */

.pagination {
  display: flex; gap: 1.25rem; align-items: center; flex-wrap: wrap;
  font-size: .78rem; color: var(--muted);
}
.pagination a { color: var(--hub-ink); font-weight: 600; text-decoration: none; }
.pagination a:hover { text-decoration: underline; }
.scope-note { margin-top: .9rem; font-size: .78rem; }

/* Rows per page, as a segmented control beside the pager. */
.page-size { display: inline-flex; align-items: center; gap: .55rem; }
.page-size__label {
  font-size: .68rem; font-weight: 600; letter-spacing: .05em;
  text-transform: uppercase; color: var(--muted);
}
.page-size__options {
  display: inline-flex; border: 1px solid #D8D8D6; border-radius: 3px; overflow: hidden;
}
.page-size__options a, .page-size__options span {
  padding: .35rem .9rem; font-size: .75rem; font-weight: 600;
  color: var(--slate); background: var(--white); text-decoration: none;
}
.page-size__options a + a, .page-size__options a + span, .page-size__options span + a {
  border-left: 1px solid #D8D8D6;
}
.page-size__options .is-current {
  background: var(--hub-accent); color: var(--hub-accent-ink);
}

/* --- filters -------------------------------------------------------------- */

.filters {
  display: flex; flex-wrap: wrap; gap: .75rem 1rem; align-items: flex-end;
  padding: 1rem 1.15rem;
}
.filters .filter { display: flex; flex-direction: column; gap: .25rem; }
.filters label { margin: 0; }
.filters select, .filters input[type="search"], .filters input[type="text"] {
  margin: 0; padding: .45rem .6rem; min-width: 8.5rem;
}
.filter--search input[type="search"] { min-width: 14rem; }
.filters .btn { margin: 0; }

/* --- panels --------------------------------------------------------------- */

.panel--warning { border-left: 3px solid var(--warn-ink); background: #FFFCF2; }
.panel--danger  { border-left: 3px solid var(--bad-ink);  background: #FFF7F7; }
.notice {
  padding: .85rem 1rem; border-radius: 3px;
  background: var(--hub-tint); border-left: 3px solid var(--hub-accent);
}
.inset { background: #FBFAF6; border-radius: var(--radius); padding: .9rem 1.1rem; margin: 0 0 1.25rem; }
.inset h3:first-child { margin-top: 0; }
.phase-note {
  border-left: 3px solid var(--hub-accent); padding-left: 1rem;
  color: var(--muted); font-size: .82rem;
}
.danger-zone { margin: 2rem 0 0; font-size: .8rem; font-weight: 600; letter-spacing: .03em; }
.danger-zone a { color: var(--bad-ink); }

/* --- match resolution: one block per record -------------------------------
   **Size follows the decision, not the controls.** What a Hub Manager is
   deciding about is the applicant's own words -- the company name, the
   person's name and email -- so that line is the largest thing in each
   block, and the legend above it is the quiet label saying which record it
   belongs to. The search box, its label, its results and the correction
   field are instruments: set below body size and in the neutral ink, they
   sit under the submitted value rather than over it.

   It used to read the other way round. The submitted company name was the
   smallest, greyest line on the page, beneath a bold hub-coloured SEARCH
   SALESFORCE BY COMPANY NAME that pulled the eye to a field nobody needs
   unless the match is wrong. */

.record-choice {
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--white); padding: 1rem 1.1rem; margin: 0 0 .9rem;
}
.record-choice legend {
  font-weight: 600; padding: 0 .35rem;
  font-size: .68rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted);
  display: flex; gap: .5rem; align-items: baseline; flex-wrap: wrap;
}
.record-choice--refused { border-color: #F2DADA; background: #FFFCFC; }
/* The submitted value: the subject of the whole block, so it leads it. */
.record-choice__subject {
  margin: .1rem 0 .7rem; color: var(--coal);
  font-size: 1.05rem; font-weight: 600; line-height: 1.35;
  overflow-wrap: anywhere;
}
.matches { list-style: none; padding: 0; margin: 0 0 .4rem; }
.matches li { padding: .1rem 0; border-top: 1px solid #F2F2F1; }
.matches label, .record-choice__new {
  display: flex; gap: .6rem; align-items: flex-start; margin: 0; padding: .45rem 0;
  text-transform: none; letter-spacing: 0; font-size: .8rem; color: var(--coal);
}
.matches label .helptext, .record-choice__new .helptext { margin: .15rem 0 0; }
.record-choice .helptext { font-size: .72rem; }
.record-choice__override {
  display: flex; gap: .6rem; align-items: flex-start;
  margin: .8rem 0 0; padding-top: .7rem;
  border-top: 1px solid var(--line); font-size: .8rem;
}
.record-choice__search { margin: .9rem 0 0; }
.record-choice__search > label,
.record-choice .field label {
  margin: 0 0 .3rem; font-size: .62rem; color: var(--muted);
}
.record-choice__search-row { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }
.record-choice__search input[type="search"],
.record-choice .field input[type="text"] { font-size: .8rem; padding: .42rem .6rem; }
.record-choice__search input[type="search"] { margin: 0; min-width: 12rem; flex: 1 1 12rem; }
.record-choice__search .btn { padding: .45rem .8rem; font-size: .68rem; }
.record-choice__search [id^="results-"]:not(:empty) { margin-top: .6rem; }
.record-choice .field { margin: .9rem 0 0; }

/* --- a section that opens -------------------------------------------------
   A card whose body is folded away until it is wanted. The marker is drawn
   rather than inherited: the browser default sits to the left of the
   heading, where it collides with the hexagon that marks every section
   heading in this interface. */

.disclosure > summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: center; gap: .6rem;
}
.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure > summary > h2 { margin: 0; }
.disclosure > summary::after {
  content: ""; width: .45rem; height: .45rem; flex: none; margin-left: auto;
  border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted);
  transform: translateY(-.15rem) rotate(45deg);
}
.disclosure[open] > summary::after { transform: translateY(.1rem) rotate(-135deg); }
.disclosure[open] > summary { margin-bottom: .85rem; }

/* --- progress ------------------------------------------------------------- */

.htmx-indicator { opacity: 0; transition: opacity .2s; }
.htmx-indicator.htmx-request, .htmx-request .htmx-indicator { opacity: 1; }
.progress { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin: 1rem 0 0; }
/* Any display value beats [hidden] { display: none }, so the progress line
   showed -- claiming to be writing to Salesforce -- before the button had
   been pressed. */
[hidden] { display: none !important; }
.progress__spinner {
  width: 1rem; height: 1rem; flex: none;
  border: 2px solid var(--grey); border-top-color: var(--hub-accent);
  border-radius: 50%; animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .progress__spinner { animation: none; border-top-color: var(--grey); }
}

code { font-size: .85em; color: var(--slate); font-family: ui-monospace, Consolas, monospace; }

/* --- what to book --------------------------------------------------------- */

.booking-choice { margin: 0 0 1.25rem; }
.booking-choice ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .6rem; }
.booking-choice__link {
  display: block; padding: .6rem 1.1rem;
  border: 1px solid var(--line-strong); border-radius: 3px;
  background: var(--white); color: var(--coal); text-decoration: none;
  font-size: .875rem; font-weight: 600;
}
.booking-choice__link:hover { border-color: var(--slate); }
.booking-choice__link--current {
  background: var(--coal); border-color: var(--coal); color: var(--white);
}
.booking-choice__role {
  display: block; font-weight: 600; font-size: .68rem;
  letter-spacing: .05em; text-transform: uppercase; color: var(--hub-ink);
}
.booking-choice__link--current .booking-choice__role { color: #C9CDD2; }

.card--flush iframe { border: 0; display: block; border-radius: 3px; }

/* --- events ---------------------------------------------------------------
   A grid of cards rather than a table: an event is a poster, not a row of
   data, and the picture is what a member scans for. */

.event-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr)); gap: 1.25rem;
}
.event-card { padding: 0; overflow: hidden; display: flex; flex-direction: column; margin: 0; }
.event-card__photo { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; display: block; }
.event-card__body { padding: 1.1rem 1.25rem 1.25rem; display: flex; flex-direction: column; gap: .45rem; flex: 1; }
.event-card__when {
  margin: 0; font-size: .72rem; font-weight: 600; color: var(--hub-ink);
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
}
.event-card h2, .event-card h3 { margin: 0; font-size: 1.05rem; line-height: 1.3; }
.event-card h2::before, .event-card h3::before { content: none; }
.event-card p { margin: 0; font-size: .85rem; color: var(--slate); }
.event-card__body > p:last-child { margin-top: auto; padding-top: .5rem; }

/* --- landing page --------------------------------------------------------- */

.landing-section { margin: 0 0 2rem; }
.landing-section__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; margin-bottom: .85rem;
}
.landing-section__head h2 { margin: 0; }
.landing-section__head a {
  font-size: .72rem; font-weight: 600; letter-spacing: .05em;
  text-transform: uppercase; text-decoration: none;
}
.landing-section__head a:hover { text-decoration: underline; }

.announcement__when { margin: 0 0 .3rem; font-size: .72rem; color: var(--muted); }
.announcement h2, .announcement h3 { margin: 0 0 .45rem; }
.announcement h2::before, .announcement h3::before { content: none; }
.announcement__body { overflow-wrap: anywhere; font-size: .9rem; color: var(--slate); }
.announcement__body p:last-child { margin-bottom: 0; }
/* The newest one carries the hub's rule. Order is the only hierarchy these
   have, so it is worth marking. */
.landing-section .announcement:first-of-type { border-left: 3px solid var(--hub-accent); }

/* --- the member's own figures --------------------------------------------
   Three cards across the top of the landing page. The hub colour fills the
   bars, which is the point: it is carrying the quantity, not decorating it. */

.stat-strip {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1rem; margin: 0 0 1.75rem;
}
.stat { margin: 0; }
.stat__label {
  margin: 0 0 .4rem; font-size: .68rem; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; color: var(--hub-ink);
}
.stat__figure { margin: 0 0 .6rem; font-size: 1.7rem; font-weight: 600; line-height: 1; }
.stat__figure small { font-size: .95rem; font-weight: 500; color: var(--muted); }
.stat__note { margin: .5rem 0 0; font-size: .78rem; color: var(--slate); }
.bar { height: 5px; border-radius: 3px; background: color-mix(in srgb, var(--hub-accent) 22%, #FFFFFF); overflow: hidden; }
.bar > span { display: block; height: 100%; background: var(--hub-accent); }
.bar-segments { display: flex; gap: 3px; }
.bar-segments > span { height: 5px; flex: 1; border-radius: 2px; background: color-mix(in srgb, var(--hub-accent) 22%, #FFFFFF); }
.bar-segments > span.is-on { background: var(--hub-accent); }

/* --- company logos --------------------------------------------------------
   Members upload these themselves, at whatever size and shape they had, so
   every logo is drawn inside a fixed slot and scaled to fit inside it --
   never stretched to fill it, and never cropped. */

.logo-slot {
  display: inline-flex; align-items: center; justify-content: flex-start;
  height: 2.5rem; flex: none;
}
.logo-slot--large { height: 3.5rem; }
.logo-slot--small { height: 1.5rem; }
.logo-slot img, .company-logo {
  max-height: 100%; max-width: 12rem; width: auto; height: auto;
  object-fit: contain; display: block;
}
.company-logo { max-height: 2.5rem; vertical-align: middle; }
.company-logo--large { max-height: 3.5rem; max-width: 20rem; }

/* A company with no logo is not an empty space. The hexagon monogram is what
   they look like, rather than a placeholder waiting to be replaced. */
.logo-monogram {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.5rem; height: 2.5rem; flex: none;
  background: var(--hub-tint); color: var(--hub-ink);
  clip-path: polygon(50% 3%, 93% 27%, 93% 73%, 50% 97%, 7% 73%, 7% 27%);
  font-size: .8rem; font-weight: 600; letter-spacing: .02em;
}
.logo-monogram--large { width: 3.5rem; height: 3.5rem; font-size: 1.05rem; }
.logo-monogram--small { width: 1.5rem; height: 1.5rem; font-size: .55rem; }

.company-identity { display: flex; align-items: center; gap: .9rem; }

/* --- narrow screens -------------------------------------------------------
   The rail becomes a strip across the top. A 264px column and a table cannot
   both have the width on a phone, and the rail is the one that can fold. */

@media (max-width: 62rem) {
  :root { --pad: 1.25rem; }
  body { flex-direction: column; }
  .rail {
    width: 100%; border-top-width: 3px;
    padding: 1rem 0 .75rem; flex-direction: column;
  }
  .rail__head { padding: 0 1.25rem .9rem; }
  .rail__nav { flex-direction: row; flex-wrap: wrap; gap: .25rem; padding: 0 1rem; }
  .rail__group { width: 100%; margin: .75rem .25rem .25rem; }
  .rail__link { padding: .45rem .7rem; }
  .rail__account {
    margin: .9rem 1rem 0; padding-top: .9rem;
    display: flex; align-items: baseline; gap: .9rem; flex-wrap: wrap;
  }
  .rail__who, .rail__role { margin: 0; }
  .rail__account-links { margin-left: auto; }
  .entry__banner {
    width: 100%; flex-direction: row; align-items: center;
    gap: 1.25rem; padding: 1.1rem 1.25rem;
  }
  .entry__banner::after { display: none; }
  .entry__logo { width: 8.25rem; }
  .entry__surface { margin: .3rem 0 0; }
  .entry__blurb { display: none; }
  .entry main { padding-top: 2rem; }
  main { padding: 1.5rem var(--pad) 2rem; }
}

/* --- the landing page's two columns ---------------------------------------
   Announcements and events take the width; the membership and account cards
   are a narrow column beside them. Below 68rem they stack, because a 20rem
   sidebar and an event grid cannot both have the page. */

.landing {
  display: grid; grid-template-columns: minmax(0, 1fr) 20rem;
  gap: 1.75rem; align-items: start;
}
.landing__side { display: flex; flex-direction: column; gap: 0; }
.landing__main > .landing-section:last-child { margin-bottom: 0; }

@media (max-width: 68rem) {
  .landing { grid-template-columns: minmax(0, 1fr); }
}

/* --- a row of figures ------------------------------------------------------
   Three or four numbers read across rather than down a definition list: a
   balance is scanned, not read, and the one that matters carries the hub's
   ink so the eye lands on it first. */

.figures { display: flex; flex-wrap: wrap; gap: 1.6rem; margin: 0 0 .9rem; }
.figure { min-width: 4rem; }
.figure__value {
  margin: 0; font-size: 1.5rem; font-weight: 600; line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.figure__value--accent { color: var(--hub-ink); }
.figure__label { margin: .15rem 0 0; font-size: .75rem; color: var(--muted); }

/* --- what it costs, and your details --------------------------------------
   Side by side on the first User's page: the form is the answer to the price
   table, and reading one to fill in the other should not mean scrolling
   between them. */

.pricing {
  display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
  gap: 1.5rem; align-items: start;
}
@media (max-width: 68rem) {
  .pricing { grid-template-columns: minmax(0, 1fr); }
}

/* --- the approval decision -------------------------------------------------
   What was submitted on the left, what happens next on the right. The
   decision stays on screen while the answers are read, which is the whole
   point: approving is a judgement about the answers, and scrolling away from
   them to make it is how the wrong Tier gets chosen. */

.decision {
  display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  gap: 1.5rem; align-items: start;
}
.decision__main { min-width: 0; }
.decision__side { position: sticky; top: 1.5rem; }
.decision__side .card { margin-bottom: 1.25rem; }
.decision__side .card:last-child { margin-bottom: 0; }

@media (max-width: 72rem) {
  .decision { grid-template-columns: minmax(0, 1fr); }
  .decision__side { position: static; }
}
