/* The public job board. White, Old Glory Blue and Old Glory Red -- the
   same tokens the internal screens use, so a candidate who sees a role
   here and then receives a Brief should recognise the same firm.

   REPAINTED 2026-08-14 with the rest of HCS. The VHS Brief and the
   Thursday client email are rendered OUTSIDE this repository, from the
   design system in docs/design/tokens/, which is still navy and gold.
   So the board and the collateral a candidate receives after it do NOT
   match today. Flagged for Justin rather than papered over: this is the
   two-brand-truths problem the 2026-08-09 token adoption existed to
   end, arriving from the other direction.

   NOTHING HERE RANKS A ROLE. No "featured", no "urgent", no badge that
   makes one search look more worth having than another. Ordering is by
   when a role was published, and the page says so. */
* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--surface-page);
  color: var(--text-primary);
  font: var(--t-body-lg)/var(--lh-body) var(--font-ui);
  -webkit-font-smoothing: antialiased;
}

.masthead {
  background: var(--surface-dark);
  color: var(--text-on-dark);
  padding: 22px 24px;
}
.masthead-inner { max-width: 1080px; margin: 0 auto; display: flex;
  align-items: baseline; gap: 14px; flex-wrap: wrap; }
/* THE SHIELD SITS ON THE TYPE, not on the baseline with it. The row is
   baseline-aligned so the wordmark and the tagline share one line, and
   an image has no baseline of its own -- left to itself it would hang
   its bottom edge on the text baseline and push the masthead 12px
   taller. `align-self: center` takes it out of that calculation.

   `flex: none` so it never shrinks: a squashed shield is worse than a
   wrapped tagline, and the tagline is the thing allowed to wrap. */
.masthead .shield {
  align-self: center; flex: none;
  height: 56px; width: 56px;
  border-radius: 0;   /* the global img rule rounds corners; a shield
                         with clipped points is not the mark */
}
.wordmark {
  font: var(--w-bold) var(--t-display-sm)/1 var(--font-ui);
  letter-spacing: var(--track-tight);
}
.wordmark em { color: var(--accent-on-dark); font-style: normal; }
.tagline {
  font: var(--t-ui-sm)/1 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-on-dark-2);
}

main { max-width: 1080px; margin: 0 auto; padding: 40px 24px 80px; }

h1 {
  font: var(--w-semi) var(--t-display-lg)/var(--lh-tight) var(--font-display);
  margin: 0 0 8px;
  text-wrap: balance;
}
.count {
  font: var(--t-ui-sm)/1 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-tertiary);
  margin: 0 0 30px;
}

.eyebrow {
  font: var(--t-ui-sm)/1 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--accent-on-paper);
  margin: 0 0 10px;
}
.lede { color: var(--text-secondary); max-width: 62ch; margin: 0 0 26px; }

/* --- what this board is ---------------------------------------------
   A bordered card with a red left accent, between the subhead and the
   filters -- the design handoff's placement, and it is the right one:
   the reader meets the explanation before the controls, and never
   again. Set in the reading face rather than the UI sans, because these
   are three sentences of prose and not an interface label.

   The last line is the promise and is deliberately quieter: it is a
   statement of fact about how applications are handled, not a sales
   line, and it should read as the footnote a serious person wrote. */
.explainer {
  border: 1px solid var(--border-hair);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-2);
  background: var(--surface-card);
  padding: 18px 22px;
  margin: 0 0 26px;
  max-width: 74ch;
}
.explainer p {
  font: var(--w-light) var(--t-body-md)/1.6 var(--font-body);
  color: var(--text-primary);
  margin: 0 0 10px;
}
.explainer p:last-child { margin-bottom: 0; }
.explainer p.explainer-promise {
  font: var(--t-ui-md)/1.5 var(--font-ui);
  color: var(--text-secondary);
}

/* VIDEO IS A FILTER, NEVER A RANK -- see the comment in board.html
   above this nav. A chip changes which roles are shown, not their
   order relative to each other. */
.chips { display: flex; gap: 8px; margin-bottom: 22px; flex-wrap: wrap; }
.chip {
  font: var(--t-ui-sm)/1 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-secondary);
  text-decoration: none;
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-2);
  padding: 8px 14px;
}
.chip:hover { border-color: var(--accent); }
.chip-active {
  color: var(--accent-on-paper);
  border-color: var(--accent);
  background: var(--surface-inset);
}

/* --- search --------------------------------------------------------
   Two inputs, side by side on a laptop and stacked on a phone. They
   narrow the list; they never reorder it. */
.search {
  display: flex; gap: 14px; flex-wrap: wrap;
  margin: 0 0 22px;
}
.search-field { flex: 1 1 240px; display: block; }
.search-field > span {
  display: block;
  font: var(--t-ui-sm)/1 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-secondary);
  margin-bottom: 6px;
}
.search-field input {
  width: 100%;
  font: var(--t-body-lg)/1.45 var(--font-ui);
  padding: 12px 13px;
  background: var(--surface-card);
  color: var(--text-primary);
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-2);
}
.search-field input:focus {
  outline: 2px solid var(--accent); outline-offset: 1px;
}

.board-head {
  display: flex; align-items: baseline; justify-content: space-between;
  flex-wrap: wrap; gap: 10px 20px; margin-bottom: 18px;
}
.sort { display: flex; align-items: baseline; gap: 12px; }
.sort-label {
  font: var(--t-ui-sm)/1 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-tertiary);
}
.sort-link {
  font: var(--t-body-md)/1 var(--font-ui);
  color: var(--text-secondary); text-decoration: none;
}
.sort-link:hover { color: var(--accent-on-paper); }
.sort-active { color: var(--accent-on-paper); font-weight: var(--w-semi); }

.roles {
  list-style: none; margin: 0; padding: 0; display: grid; gap: 20px;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}
.role-card {
  background: var(--surface-card);
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-2);
  overflow: hidden;
}
.role-card:hover { border-color: var(--accent); }
.role-card-link {
  display: block; color: inherit; text-decoration: none; height: 100%;
}

/* A thumbnail rather than a live iframe on the list page -- a grid of
   thirty-five autoplaying-on-hover embeds is thirty-five outbound
   requests to YouTube for a page a reader is only scanning. The real
   player is one click away, on the role's own page. */
.role-media {
  position: relative; aspect-ratio: 16 / 9;
  background: var(--surface-dark);
}
.role-media img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
/* A ROLE WITH NO VIDEO carries Justin's card. See .role-media-card
   below -- the rules for it live beside the rest of the card furniture,
   and the `role-media-empty` pair that used to sit here described
   role-card-fallback.jpg, an asset deleted 2026-08-18. */

/* The industry, where the client's name would be. Not a badge and not a
   filter -- it is the same kind of fact as the location beside it. */
.sector { color: var(--text-secondary); }
.badge {
  position: absolute; top: 10px; left: 10px; z-index: 1;
  font: var(--w-semi) var(--t-ui-sm)/1 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  padding: 5px 9px;
  border-radius: var(--radius-1);
}
/* The play mark on a still. Drawn in CSS rather than shipped as an
   image: it is one triangle in a disc, and an extra network request for
   it on a page of thirty-five cards is thirty-five requests for a shape
   the browser can draw. */
.play {
  position: absolute; inset: 0; margin: auto;
  width: 54px; height: 54px; border-radius: 50%;
  background: rgba(178, 34, 52, .92);   /* --accent, over the still */
  border: 2px solid rgba(255, 255, 255, .9);
  pointer-events: none;
}
.play::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  width: 0; height: 0;
  border-left: 16px solid var(--white);
  border-top: 10px solid transparent;
  border-bottom: 10px solid transparent;
  transform: translateX(3px);   /* optical centre, not geometric */
}
.role-card:hover .play { background: var(--accent); }

.badge-video { background: var(--accent); color: var(--white); }
.badge-text {
  background: var(--surface-card);
  color: var(--text-secondary);
  border: 1px solid var(--border-hair);
}

.role-card-body { padding: 18px 20px; }
.role-card-body h2 {
  margin: 0 0 6px; font: var(--w-semi) 19px/1.25 var(--font-ui);
  color: var(--text-primary);
}
.role-card:hover .role-card-body h2 { color: var(--accent-on-paper); }
.meta {
  display: flex; flex-wrap: wrap; gap: 6px 16px;
  font-size: var(--t-body-md); color: var(--text-secondary); margin-bottom: 10px;
}
.confidential { color: var(--text-tertiary); font-style: italic; }
/* A field nobody filled in, shown rather than hidden. */
.unstated { color: var(--text-tertiary); font-style: italic; }
.summary { margin: 0; color: var(--text-secondary); max-width: 68ch; }

.empty {
  background: var(--surface-inset);
  border: 1px dashed var(--border-hair);
  border-radius: var(--radius-2);
  padding: 34px 26px;
  color: var(--text-secondary);
  max-width: 62ch;
}

/* --- a single role ------------------------------------------------- */
.back {
  font: var(--t-ui-sm)/1 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-tertiary); text-decoration: none;
  display: inline-block; margin-bottom: 22px;
}
.back:hover { color: var(--accent-on-paper); }

.section { margin: 34px 0 0; }
.section h3 {
  font: var(--t-ui-sm)/1 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-tertiary);
  margin: 0 0 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border-hair);
}
.section ul { margin: 0; padding-left: 1.1em; }
.section li { margin-bottom: 8px; color: var(--text-secondary); }
.section p { margin: 0 0 12px; color: var(--text-secondary); max-width: 68ch; }

/* The military translation, indented under its requirement so the
   pairing is visible without reading carefully. */
.reqs li { margin-bottom: 14px; }
.equiv {
  display: block;
  margin-top: 4px;
  padding-left: 12px;
  border-left: 2px solid var(--accent);
  font-size: var(--t-body-md);
  color: var(--text-tertiary);
}
.equiv-label {
  font: var(--t-ui-sm)/1 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--accent-on-paper);
  margin-right: 6px;
}

.apply {
  display: inline-block; margin-top: 30px;
  background: var(--surface-dark); color: var(--text-on-dark);
  text-decoration: none; padding: 14px 26px; border-radius: var(--radius-2);
  font: var(--w-semi) var(--t-ui-lg)/1 var(--font-ui);
}
.apply:hover { background: var(--blue-700); }

/* --- what sits under the video -------------------------------------
   The one-line summary, then the few facts the role actually carries.
   Both above the clips, so a reader who has just watched somebody
   describe the seat gets the shape of it before the highlight reel. */
.lede-summary {
  font: var(--w-light) var(--t-body-lg)/1.6 var(--font-body);
  color: var(--text-primary);
  /* FULL WIDTH, overriding the 68ch measure `.summary` sets. Justin,
     2026-08-15: "remove the natural left alignment for the role
     summary. let it fill in the entire rows."

     A reading measure is the right default for long prose, and it is
     wrong here: this paragraph sits directly above a facts card that
     spans the full column, so the short measure read as a column of
     text that had failed to fill its container rather than as a
     deliberate line length. */
  max-width: none;
  margin: 26px 0 0;
}

/* A bordered card of label/value pairs, not prose. Two columns where
   there is room, one where there is not -- the values are short and a
   long single column of four rows wastes the width. */
.glance {
  margin: 22px 0 0;
  padding: 4px 20px;
  border: 1px solid var(--border-hair);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-2);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 0 28px;
}
.glance-row {
  display: flex; align-items: baseline; gap: 12px;
  padding: 12px 0;
  border-bottom: 1px solid var(--rule-cool);
}
/* No hairline under the last row of either column. `:last-child` alone
   would only clear one of them. */
.glance-row:last-child, .glance-row:nth-last-child(2):nth-child(odd) {
  border-bottom: 0;
}
.glance dt {
  flex: 0 0 118px;
  font: var(--t-ui-sm)/1.4 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-tertiary);
}
.glance dd {
  margin: 0;
  font-size: var(--t-body-md);
  color: var(--text-primary);
}

/* --- body prose on a role page --------------------------------------
   Justin, 2026-08-15: "Remove left alignment or whatever to let it take
   up the entire rows. Also font and text formatting of those paragraphs
   to be easier to read. Formatting properly."

   Three things were wrong and only one of them was the width. The block
   was capped at a 68-character measure, so it ran as a narrow column
   down the left of a full-width page; it was set in the UI sans at the
   small body size; and it arrived as ONE paragraph, because the source
   contains no breaks anywhere in it.

   So: full width, the serif reading face at a larger size with open
   leading, and real paragraph spacing between the beats the text
   already has. `.prose p` overrides `.section p`, which keeps its
   measure for the short structured sections above. */
.prose p {
  max-width: none;
  font: var(--w-light) var(--t-body-lg)/1.75 var(--font-body);
  color: var(--text-primary);
  margin: 0 0 16px;
}
.prose p:last-child { margin-bottom: 0; }

/* --- the highlights ------------------------------------------------
   Performance objectives as numbered cards. Justin chose these as the
   highlights when asked, over anything this system would have had to
   invent. An outcome somebody will be measured on should not look like
   a bullet in a duties list. */
.highlights {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.highlight {
  background: var(--surface-card);
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-2);
  padding: 18px 20px;
}
.highlight-n {
  display: block;
  font: var(--w-semi) 22px/1 var(--font-mono);
  color: var(--accent);
  letter-spacing: var(--track-tight);
  margin-bottom: 10px;
}
/* aria-hidden on the numeral, and the list is an <ol>, so a screen
   reader counts the items itself rather than hearing "zero one" read
   out before every objective. */
.highlight p {
  margin: 0;
  color: var(--text-secondary);
  font-size: var(--t-body-md);
  line-height: var(--lh-body);
}

/* --- the action row ------------------------------------------------
   One tab and two buttons, at the top and the bottom of a role page.
   The tab marks where you are; the buttons are the two things you can
   do. They are deliberately not three tabs -- a control that navigates
   and a control that marks position should not look identical. */
.actions {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  border-bottom: 1px solid var(--border-hair);
  padding-bottom: 16px;
  margin: 24px 0 8px;
}
.actions[data-position="bottom"] {
  border-bottom: 0;
  border-top: 1px solid var(--border-hair);
  padding: 22px 0 0;
  margin: 38px 0 0;
}
.tab {
  font: var(--w-semi) var(--t-ui-md)/1 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-primary);
  padding-bottom: 6px;
  border-bottom: 2px solid var(--accent);
  margin-right: 4px;
}
.btn {
  display: inline-block;
  font: var(--w-semi) var(--t-ui-lg)/1 var(--font-ui);
  padding: 13px 22px;
  border-radius: var(--radius-2);
  text-decoration: none;
  cursor: pointer;
  border: 2px solid transparent;
}
/* Outlined in Old Glory Blue, filled in Old Glory Red: the secondary
   action reads as a frame, the primary as a solid. */
.btn-outline {
  background: transparent;
  color: var(--blue-700);
  border-color: var(--blue-700);
}
.btn-outline:hover { background: var(--surface-inset); }
.btn-solid {
  background: var(--accent);
  color: var(--white);
  border-color: var(--accent);
}
.btn-solid:hover { background: var(--red-500); border-color: var(--red-500); }
.btn:focus-visible { outline: 3px solid var(--blue-700); outline-offset: 2px; }

/* --- share this role -------------------------------------------------
   `<details>`, same as `.qtype` below: a real disclosure control with no
   script behind it, so the whole menu -- the link field, email, and
   every social link -- works if board.js never loads. */
.share { position: relative; display: inline-block; }
.share summary {
  list-style: none;
  /* .btn already does the outline-button look; this only removes the
     browser's own disclosure triangle, which .btn was never drawn for. */
}
.share summary::-webkit-details-marker { display: none; }
.share summary::marker { content: ""; }
.share[open] summary { background: var(--surface-inset); }
.share-menu {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 5;
  min-width: 260px;
  max-width: min(320px, calc(100vw - 32px));
  background: var(--surface-card);
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-3);
  box-shadow: 0 8px 24px rgba(26, 26, 34, .16);
  padding: 14px;
}
/* Anchored to the right on the bottom action row, so the menu opens
   toward the page rather than off its edge -- the bottom row sits at
   the foot of the content column, and a left-anchored menu there would
   hang mostly past the reading measure. */
.actions[data-position="bottom"] .share-menu { left: auto; right: 0; }
.share-copy { display: block; margin-bottom: 12px; }
.share-copy-label {
  display: block;
  font: var(--w-medium) var(--t-ui-sm)/1 var(--font-ui);
  color: var(--text-secondary);
  margin-bottom: 6px;
}
.share-copy-row { display: flex; gap: 8px; }
.share-copy-row input {
  flex: 1 1 auto;
  min-width: 0;
  font: var(--t-body-md)/1.3 var(--font-ui);
  color: var(--text-primary);
  background: var(--surface-page);
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-2);
  padding: 8px 10px;
}
.share-copy-row .btn { padding: 8px 14px; font-size: var(--t-ui-md); flex: 0 0 auto; }
.share-links {
  list-style: none;
  margin: 0; padding: 12px 0 0;
  border-top: 1px solid var(--border-hair);
  display: flex; flex-wrap: wrap; gap: 10px;
}

/* THE NETWORKS AS BUTTONS. Justin, 2026-08-18: "can you replace the
   hyperlinked text with those social media buttons?" Four words in a row
   read as fine print; the marks are recognised before they are read.

   EACH IN ITS OWN BRAND COLOUR rather than in the VHS palette, and that
   is the point of a share row: a person is scanning for the blue "in" or
   the black X they already know, not for a set of buttons in our navy
   that they have to read to tell apart. Email is ours, because it is not
   a brand -- it is the one that goes anywhere.

   44px, WHICH IS THE TOUCH TARGET rather than a size that looked right.
   These sit on a phone, inside a menu, next to each other; anything
   smaller and the wrong network opens. */
.share-btn {
  width: 44px; height: 44px;
  border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--white);
  text-decoration: none;
  transition: transform .12s ease, box-shadow .12s ease;
}
.share-btn svg { width: 20px; height: 20px; display: block; }
/* The Facebook mark is drawn tall on a 320x512 grid, so an equal box
   would set it visually smaller than the others. */
.share-btn-facebook svg { width: 15px; height: 22px; }
.share-btn:hover, .share-btn:focus-visible {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(26, 26, 34, .28);
}
/* Never suppressed: an icon-only control with no visible label is the
   one a keyboard user most needs to be able to see the focus on. */
.share-btn:focus-visible {
  outline: 2px solid var(--blue-900);
  outline-offset: 2px;
}
.share-btn-email    { background: var(--blue-900); }
.share-btn-linkedin { background: #0A66C2; }   /* LinkedIn brand blue */
.share-btn-facebook { background: #1877F2; }   /* Facebook brand blue */
.share-btn-x        { background: #000000; }   /* X is black, and only black */

/* WHAT A BLOCKED SHARE BUTTON SAYS. Written by board.js only when a
   network could not be opened -- inside the Wix embed a sandboxed frame
   blocks new windows outright (see the measurement in board.js). It sits
   under the marks, in the menu the reader already has open, rather than
   as an alert over the page. */
.share-note {
  margin: 10px 0 0;
  font: var(--t-body-sm)/1.4 var(--font-ui);
  color: var(--text-secondary);
}

/* --- the full job description, over the page ------------------------
   Slides up from the bottom over everything, including the video. See
   the note in role.html for why this is anchored to the viewport rather
   than to the video frame. */
/* IN THE FLOW OF THE PAGE, not fixed over it -- changed 2026-08-18, and
   the reason is in role.html above this element: the board is served
   inside a full-height cross-origin iframe on the Wix careers page, so
   `position: fixed` resolved to the bottom of the whole document. The
   reader clicked the button and the text opened thousands of pixels
   below them, behind a scrim that greyed out the entire page, with the
   body's scroll locked while they tried to reach it.

   There is no scrim, no scroll lock, no transform and no z-index here on
   purpose. Every one of those was a thing that only worked when this page
   owned the viewport, and this page does not. */
.jd {
  margin: 34px 0 0;
  border: 1px solid var(--border-hair);
  border-top: 3px solid var(--accent);
  border-radius: var(--radius-3);
  background: var(--surface-page);
}
.jd-summary {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 14px;
  padding: 16px 24px;
  cursor: pointer;
  list-style: none;                 /* the default triangle, replaced below */
}
.jd-summary::-webkit-details-marker { display: none; }
.jd-summary::after {
  content: "";
  margin-left: auto;
  width: 9px; height: 9px;
  border-right: 2px solid var(--accent-on-white);
  border-bottom: 2px solid var(--accent-on-white);
  transform: rotate(45deg);
  transition: transform .18s ease-out;
}
.jd[open] > .jd-summary::after { transform: rotate(-135deg); }
.jd[open] > .jd-summary { border-bottom: 1px solid var(--border-hair); }
.jd-summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.jd-summary-label {
  font: var(--t-ui-sm)/1 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-primary);
}
.jd-summary-hint {
  font: var(--w-light) var(--t-body-sm)/1.4 var(--font-body);
  color: var(--text-secondary);
}
/* LEFT, INSIDE A CENTRED PAGE, and this is the rebrand's own stated
   exception rather than an argument with it: "BODY COPY IS THE EXCEPTION
   AND STAYS AS IT IS. Anything longer than two lines is still set to
   read normally; centred prose is slower to read." The full posting is
   the longest body copy on the board -- up to ten thousand characters --
   and it inherited `main { text-align: center }` the moment this element
   moved into the flow of the page, which centred every line of it. */
.jd-body {
  padding: 24px;
  text-align: left;
}
.jd-summary { text-align: left; }
/* The reading column, per the handoff: serif body, generous leading,
   and a measure short enough to track across. */
.jd-body p {
  font: var(--w-light) var(--t-body-lg)/1.6 var(--font-body);
  color: var(--text-secondary);
  max-width: 68ch;
  margin: 0 auto 14px;
}
/* The posting's own headings, now that they are marked up rather than
   run together with the prose. Centred on the same measure as the body
   so the column reads as one column. */
.jd-body h3 {
  max-width: 68ch;
  margin: 26px auto 10px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border-hair);
  font: var(--t-ui-sm)/1 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--accent-on-white);
}
.jd-body h3:first-child { margin-top: 0; }
/* WHAT `emphasise` MARKS, and it marks three things: the field labels
   the posting already writes ("Reports To:"), the figures somebody is
   committing to (90 days, $10,000, 401(k)), and the sentences in
   quotation marks -- which in these documents are the hiring manager
   speaking.

   The body of this panel is deliberately grey-on-white at a light
   weight, so a bolded run is set in the ink colour as well: at 300
   weight, bold alone is a smaller jump than it looks in a mockup. */
.jd-body strong {
  font-weight: var(--w-semi);
  color: var(--text-primary);
}
.jd-body em {
  font-style: italic;
  color: var(--text-primary);
}
body.jd-locked { overflow: hidden; }

video, img { max-width: 100%; height: auto; border-radius: var(--radius-2); }

/* --- Video --------------------------------------------------------- */
/* An iframe has no intrinsic aspect ratio, so without this it collapses
   to whatever height the browser guesses -- typically 150px, which is a
   letterbox slot where a person's face should be. `aspect-ratio` on the
   wrapper reserves the right space before the frame loads, so the page
   does not jump when it does.

   16/9 for both the full conversation and the clips. A vertical Short
   played in a 16/9 frame is pillarboxed by YouTube rather than cropped,
   which keeps one shape on the page instead of a ragged mix -- and
   nothing of the speaker is cut off. */
.video-frame {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-2);
  overflow: hidden;               /* the iframe's corners follow the wrapper */
  background: var(--surface-dark); /* what shows for the moment before load */
}
.video-frame iframe {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  border: 0;
  display: block;
}

/* --- what you are about to watch ------------------------------------
   Justin, 2026-08-16: "this section needs to stand out more since we
   are the only company in the world doing this... whatever makes it
   stand out and people understand what they are about to watch."

   So it is set at display size in the reading face, with the flag's red
   beside it, and the first sentence -- the one that says what the video
   IS -- carries the weight. It was 13px grey UI text, which is the
   typography of a disclaimer. */
.video-lead {
  font: var(--w-light) 19px/1.5 var(--font-body);
  color: var(--text-primary);
  border-left: 3px solid var(--accent);
  padding-left: 18px;
  margin: 0 0 18px;
  max-width: 60ch;
  text-wrap: pretty;
}
.video-lead strong {
  display: block;
  font: var(--w-semi) var(--t-display-md)/1.2 var(--font-display);
  color: var(--surface-dark-2);
  margin-bottom: 4px;
}

/* THE FULL CONVERSATION AS A POSTER. Same shape as the player it
   becomes, so nothing moves when it is clicked, and the same play
   affordance as the story cards below it -- Justin, same day: "Format
   the VJD to look like and react the clips below it." */
.vjd {
  position: relative; display: block;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--radius-2);
  background: var(--surface-dark);
}
.vjd img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  border-radius: 0;
  opacity: .88;
  transition: opacity .18s ease;
}
.vjd:hover img, .vjd:focus-visible img { opacity: 1; }
.vjd:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Bigger than the cards' button, because this frame is four times the
   size and a 46px circle in the middle of it reads as an afterthought. */
.vjd .story-play {
  width: 74px; height: 74px; margin: -37px 0 0 -37px;
}
.vjd .story-play::after {
  left: 30px; top: 22px;
  border-top-width: 14px; border-bottom-width: 14px;
  border-left-width: 21px;
}

/* --- the story cards ------------------------------------------------
   The hiring manager's short answers, as 9:16 posters rather than as
   three more landscape players. Replaces `.clips`, which stretched a
   vertical clip across a 16/9 frame and loaded a YouTube player for
   each one on page load.

   `auto-fit` with a 240px minimum rather than a fixed three: a role
   with one clip gets one card at a sane width instead of a card
   stretched across the page, and a role with five wraps rather than
   shrinking into stamps. `max-width` on the track keeps three across
   from becoming three enormous columns on a wide screen. */
/* The same job as `.video-lead`, one size down: say what these are
   before somebody decides to watch one. The first sentence names the
   thing ("highlight clips from the larger conversation"); the second
   says why to bother. */
.stories-lead {
  margin: -2px 0 16px;
  font: var(--w-light) var(--t-body-lg)/1.55 var(--font-body);
  color: var(--text-secondary);
  max-width: 60ch;
}
.stories-lead strong {
  font-weight: var(--w-semi);
  color: var(--text-primary);
}
.stories-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  max-width: 960px;
}
.story {
  position: relative;
  display: block;
  aspect-ratio: 9 / 16;
  overflow: hidden;
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-2);
  background: #000;            /* what shows behind a thumbnail that
                                  never arrives -- a black card, not a
                                  broken-image icon */
  text-decoration: none;
}
.story img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;           /* hqdefault is 4:3 with the vertical
                                  frame centred; cropping the sides is
                                  what recovers the portrait shot */
  border-radius: 0;
  opacity: .82;
  transition: opacity .18s ease, transform .18s ease;
}
.story:hover img, .story:focus-visible img { opacity: 1; transform: scale(1.02); }
.story:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The wash under the text. Without it a white hook over a bright frame
   is unreadable, and which frame it is is not ours to choose. */
/* --- the popover: the posting, and a clip ---------------------------
   Justin, 2026-08-18: "we want a smooth experience for the users. pop up
   JD and clips instead of taking them to another page."

   ABSOLUTE, NOT FIXED, and anchored by board.js to the element the reader
   clicked. That is the one detail that makes a popup work here at all:
   the board is served inside a full-height cross-origin iframe on the Wix
   careers page, so "fixed" resolves against a six-thousand-pixel document
   rather than against the screen -- which is how the previous dialog came
   to open a thousand pixels away from the reader behind a page-wide grey
   scrim.

   THE BACKDROP PAINTS NOTHING. It catches a click outside so the popup
   closes like a popup, and it is transparent because a visible one over a
   document this tall IS the grey band that was objected to. */
.pop-catch {
  position: absolute; inset: 0;
  z-index: 40;
  background: transparent;
}
.pop {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  z-index: 41;
  width: min(760px, calc(100% - 32px));
  background: var(--surface-page);
  border: 1px solid var(--border-hair);
  border-top: 3px solid var(--accent);
  border-radius: var(--radius-3);
  /* The shadow is what separates it from the page, now that no scrim
     does. Deep enough to read as floating on a white background. */
  box-shadow: 0 18px 60px rgba(33, 32, 74, .34);
  overflow: hidden;
  animation: popIn .16s ease-out both;
}
@keyframes popIn {
  from { opacity: 0; transform: translateX(-50%) translateY(6px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}
.pop-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px;
  padding: 14px 20px;
  background: var(--surface-dark);
  color: var(--text-on-dark);
}
.pop-title {
  font: var(--t-ui-sm)/1.3 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-on-dark);
}
.pop-close {
  background: transparent; border: 0; cursor: pointer;
  color: var(--text-on-dark);
  font: 300 26px/1 var(--font-ui);
  padding: 0 4px;
}
.pop-close:hover { color: var(--accent-on-dark); }
.pop-close:focus-visible { outline: 2px solid var(--white); outline-offset: 2px; }

/* THE POSTING. A pixel cap rather than a `vh` one, deliberately: inside
   the embed `vh` is the height of the whole framed document, so `70vh`
   would be four thousand pixels of popup. 620px is a comfortable reading
   height on a laptop and still fits a short phone -- measured against the
   popup's own top edge sitting where the reader clicked, which is what
   decides whether the bottom of it lands on screen. */
.pop-jd .jd-body {
  max-height: 560px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

/* The popup's own way into the job. Only the board's clips carry it --
   see board.js. Full width under the player so it reads as the next step
   rather than as a caption. */
.pop-cta {
  display: block;
  margin: 0;
  border-radius: 0;
  text-align: center;
}

/* A CLIP IS PORTRAIT, and the popup narrows to it rather than framing a
   vertical video in a letterbox. */
.pop-clip { width: min(360px, calc(100% - 32px)); }
.pop-stage {
  position: relative;
  aspect-ratio: 9 / 16;
  background: var(--surface-dark);
}
.pop-stage iframe {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  border: 0;
}

.story-shade {
  position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(10,10,20,.92), rgba(10,10,20,.08) 58%);
}
/* RENDERED ONLY WHEN THE DATA EXISTS. `public_role_media` carries a
   label, a url and a sequence -- no category and no duration -- so
   neither of these appears today. They are styled and in the template
   so that adding the columns is a migration and a view change, with no
   design work and no second pass over this file. */
.story-tag {
  position: absolute; left: 10px; top: 10px;
  font: var(--t-label)/1 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--white);
  background: var(--accent);
  padding: 4px 7px;
  border-radius: var(--radius-1);
}
.story-play {
  position: absolute; left: 50%; top: 50%;
  width: 46px; height: 46px; margin: -23px 0 0 -23px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .16);
  border: 1.5px solid var(--white);
}
/* The triangle, drawn rather than shipped as an asset -- one less
   request on a page whose whole job is to load fast on a phone. */
.story-play::after {
  content: ""; position: absolute; left: 18px; top: 14px;
  border-top: 8px solid transparent;
  border-bottom: 8px solid transparent;
  border-left: 12px solid var(--white);
}
.story-foot { position: absolute; left: 12px; right: 12px; bottom: 12px; }
.story-hook {
  display: block;
  font: var(--w-bold) var(--t-ui-md)/1.35 var(--font-ui);
  color: var(--white);
}
.story-time {
  display: block; margin-top: 5px;
  font: var(--t-label)/1 var(--font-mono);
  letter-spacing: var(--track-tight);
  color: rgba(255, 255, 255, .72);
}

/* --- the portrait player -------------------------------------------
   The clip plays over the page rather than in the card: a 9:16 player
   240px wide is a phone screen held at arm's length. The stage is
   empty markup until board.js builds the iframe into it. */
.story-scrim {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(5, 5, 15, .88);
}
.story-modal {
  position: fixed; z-index: 51;
  left: 50%; top: 50%; transform: translate(-50%, -50%);
  /* Height-bound as well as width-bound. A 9:16 box sized only by width
     is taller than a laptop viewport, and the close control would sit
     off the top of the screen. */
  width: min(380px, 88vw);
  /* Height-bound as well: 42px for the close control above the stage,
     ~64px for the "See the full job posting" button below it on the
     board, and a margin so neither lands against a screen edge. */
  max-width: calc((100vh - 170px) * 9 / 16);
}
.story-stage {
  position: relative;
  aspect-ratio: 9 / 16;
  background: #000;
  border-radius: var(--radius-2);
  overflow: hidden;
}
.story-stage iframe {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  border: 0; display: block;
}
.story-close {
  position: absolute; right: 0; top: -42px;
  background: transparent; border: 0;
  color: var(--white); font-size: 30px; line-height: 1;
  padding: 0 4px; cursor: pointer;
}
.story-close:focus-visible { outline: 2px solid var(--white); outline-offset: 2px; }
/* THE ONLY CONTROL IN HERE THAT NAVIGATES. Shown on the board, where
   the clip is a trailer for a posting the reader has not opened;
   hidden on the role page, where they are already on it.

   `[hidden]` explicitly, because `display: block` below would otherwise
   beat the attribute and show a button with no destination. */
.story-cta {
  display: block; width: 100%; margin-top: 14px;
  text-align: center;
}
.story-cta[hidden] { display: none; }

footer {
  border-top: 1px solid var(--border-hair);
  margin-top: 60px; padding-top: 20px;
  font-size: var(--t-body-md); color: var(--text-tertiary);
}

/* --- the clip strip ------------------------------------------------
   Fixed to the bottom of the board, one instance, never at the top: the
   top of a phone viewport is where the title and the search inputs are,
   and a second bar up there competes with both.

   Old Glory Blue ground, Old Glory Red top edge -- the same two colours
   as the masthead, so the page is bracketed by the flag rather than
   ending in a floating grey bar. */
.clipstrip {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 20;
  background: var(--surface-dark-2);
  border-top: 2px solid var(--accent);
  overflow: hidden;
  padding: 10px 0;
}
.clipstrip-track {
  display: flex;
  width: max-content;
  /* --loop is set per page from the number of clips, so the strip moves
     at a constant ~50px/s whether it carries four tiles or twenty-four
     rather than sprinting when there are more. */
  animation: clipscroll var(--loop, 24s) linear infinite;
}
/* Half the track is one full copy of the clips, so translating by
   exactly -50% lands the second copy where the first began and the loop
   has no visible seam. */
/* 32px between items, up from 12px. Justin's clip-strip update,
   2026-08-16: the tiles now carry a hook and a role name beside the
   picture, and at the old spacing one tile's text ran straight into the
   next tile's thumbnail. The right padding matches the gap so the seam
   between the two halves of the loop is spaced like every other join --
   otherwise the marquee visibly stutters once per lap. */
.clipstrip-half { display: flex; gap: 32px; padding-right: 32px; }
@keyframes clipscroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
.clipstrip:hover .clipstrip-track,
.clipstrip:focus-within .clipstrip-track { animation-play-state: paused; }

/* A TILE IS NOW A PICTURE AND A SENTENCE. The link is the row; the
   thumbnail keeps its 16:9 box inside it, and the text sits beside it
   rather than on top of it -- a two-line label over a 72px-tall
   thumbnail was covering the face it was captioning. */
.clip-thumb {
  position: relative; display: flex; align-items: center; flex: none;
  gap: 10px;
  text-decoration: none;
}
.clip-shot {
  position: relative; display: block; flex: none;
  width: 128px; height: 72px;          /* 16:9 */
  border-radius: var(--radius-1);
  overflow: hidden;
  background: var(--blue-900);
}
/* The hook, and under it the role the clip belongs to. Width-capped so
   a long title wraps to two lines rather than stretching the item and
   pushing the loop out of time with the duration the server computed. */
.clip-text { display: block; width: 160px; }
.clip-hook {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
  font: var(--w-semi) var(--t-body-sm)/1.3 var(--font-ui);
  color: var(--white);
}
.clip-role {
  display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical;
  overflow: hidden;
  margin-top: 3px;
  font: var(--t-label)/1.3 var(--font-mono);
  letter-spacing: var(--track-tight);
  text-transform: uppercase;
  color: rgba(255, 255, 255, .66);
}
.clip-thumb img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  border-radius: 0;
  /* A faint darkening so white text over any frame stays legible -- the
     thumbnails are somebody else's lighting, not ours. */
  filter: brightness(.72);
}
.clip-thumb:hover img, .clip-thumb:focus-visible img { filter: brightness(1); }
.clip-thumb:focus-visible { outline: 2px solid var(--white); outline-offset: 2px; }
.clip-thumb-play {
  position: absolute; top: 6px; left: 6px;
  width: 0; height: 0;
  border-left: 11px solid var(--white);
  border-top: 7px solid transparent;
  border-bottom: 7px solid transparent;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .6));
}

/* The strip is fixed, so it sits on top of whatever the page ends with.
   Without this the last row of cards and the footer are underneath it. */
body.has-clipstrip main { padding-bottom: 130px; }

/* --- Applying ------------------------------------------------------ */
/* A form on a phone, in a parking lot, between shifts. One column, big
   targets, and every answer preserved on error -- somebody who mistypes
   an email and loses the message they just wrote closes the tab. */
main.narrow { max-width: 640px; }
.applying-for { margin: 0 0 4px; font-size: var(--t-body-lg); }

.f { display: block; margin: 22px 0; }
.f > span:first-child {
  display: block;
  font: var(--t-ui-sm)/1 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-secondary);
  margin-bottom: 6px;
}
.f > span:first-child em {
  font-style: normal; color: var(--accent-on-paper); margin-left: 6px;
}
.f input, .f select, .f textarea {
  width: 100%;
  font: var(--t-body-lg)/1.45 var(--font-ui);
  padding: 12px 13px;
  background: var(--surface-card);
  color: var(--text-primary);
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-2);
}
.f input:focus, .f select:focus, .f textarea:focus {
  outline: 2px solid var(--accent); outline-offset: 1px;
}
.f .note { display: block; margin-top: 5px; font-size: var(--t-body-md);
  color: var(--text-tertiary); }
.f .err { display: block; margin-top: 5px; font-size: var(--t-body-md);
  color: var(--alert-red); font-weight: var(--w-semi); }
.row { display: flex; gap: 14px; }
.row .f { flex: 1; }

/* --- resume_prefill.js's collapsed step ------------------------------
   ONLY EVER ADDED BY THE SCRIPT ITSELF. The server never renders this
   class, so a browser that never runs resume_prefill.js never has
   anything to hide -- #confirm-section stays exactly as visible as it
   was before this feature existed. */
.js-collapsed { display: none; }
.continue-btn { display: block; margin: 18px 0; }
.prefill-status { font-style: italic; }

/* --- the resume dropzone --------------------------------------------
   A real <input type="file"> laid over a drawn face, rather than a
   scripted dropzone. Dragging a file onto a file input already works in
   every browser; a div plus JavaScript would reimplement that and then
   fail for anybody whose script did not load, on the one field the
   application cannot be submitted without. */
.drop { position: relative; }
.drop input[type="file"] {
  position: absolute; inset: 0; top: 26px;   /* below the label text */
  width: 100%; height: calc(100% - 26px);
  opacity: 0; cursor: pointer;
}
.drop-face {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 38px 20px;
  border: 2px dashed var(--border-hair);
  border-radius: var(--radius-2);
  background: var(--surface-card);
  text-align: center;
}
.drop-lead {
  font: var(--w-medium) var(--t-ui-lg)/1.3 var(--font-ui);
  color: var(--text-primary);
}
.drop input[type="file"]:hover + .drop-face,
.drop input[type="file"]:focus + .drop-face {
  border-color: var(--accent);
  background: var(--surface-inset);
}
/* The browser draws the chosen filename inside the input, which is
   invisible here -- so the face keeps its instruction and the name
   appears in the input's own tooltip. Deliberate: the alternative is a
   script keeping a second copy of the filename in sync. */

/* --- the consent block ---------------------------------------------
   Bordered and set apart, because it is the one thing on this page a
   person is agreeing TO rather than telling us. */
.consent {
  display: flex; gap: 12px; align-items: flex-start;
  border: 1px solid var(--border-hair);
  border-left: 3px solid var(--blue-700);
  border-radius: var(--radius-2);
  padding: 16px 18px;
  background: var(--surface-card);
}
.consent input[type="checkbox"] {
  width: 20px; height: 20px; flex: none; margin-top: 2px;
  accent-color: var(--accent);
}
.consent-text {
  font-size: var(--t-body-md);
  line-height: var(--lh-body);
  color: var(--text-secondary);
}

/* --- "your own words" -- the two-question step -----------------------
   Rebuilt from a design that never shipped
   (docs/design/designs/HCS Journey Screens.dc.html:175-207) in the
   current Old Glory colors, per Justin's 2026-08-15 instruction. UI-only
   by his own choice: "Answer by phone" and "Answer on video" render so
   the shape of the eventual Phase 6 build is visible, but neither does
   anything yet -- only `.qtype`'s textarea is wired to the application. */
.qsection { margin: 30px 0; }
.qsection-label {
  display: block;
  font: var(--t-ui-sm)/1 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-secondary);
  margin-bottom: 8px;
}
.qsection-note {
  font-size: var(--t-body-md);
  color: var(--text-tertiary);
  max-width: 62ch;
  margin: 0 0 18px;
}
.qcard {
  border: 1px solid var(--border-hair);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-2);
  background: var(--surface-card);
  padding: 18px 20px;
  margin-bottom: 16px;
}
.qcard-label {
  font: var(--t-ui-sm)/1 var(--font-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--accent-on-paper);
  margin-bottom: 8px;
}
.qcard-text {
  font: var(--w-light) var(--t-body-lg)/1.5 var(--font-body);
  color: var(--text-primary);
  margin: 0 0 16px;
}
.qcard-actions { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
.qcard-actions .btn { padding: 9px 16px; font-size: var(--t-ui-md); }
/* Real buttons, not a note pretending to be one -- the shape Phase 6
   will eventually fill in. `disabled` refuses interaction; opacity is
   the only signal it needs, since the shared note above already says
   why. */
.qcard-soon[disabled] { opacity: .5; cursor: not-allowed; }
/* `<details>` rather than a script: revealing a textarea on click is
   something a browser already does with no JavaScript, on the one page
   here most likely to be opened on a phone with a slow connection. */
.qtype summary {
  font-size: var(--t-ui-md);
  color: var(--text-link);
  text-decoration: underline;
  cursor: pointer;
  list-style: none;
}
.qtype summary::-webkit-details-marker { display: none; }
/* Full width once opened, so the textarea drops to its own row below
   the two buttons rather than squeezing into whatever space is left
   beside them on the same flex line. */
.qtype[open] { flex: 1 1 100%; }
.qtype[open] summary { margin-bottom: 10px; }
.qtype textarea {
  width: 100%;
  font: var(--t-body-lg)/1.45 var(--font-ui);
  padding: 12px 13px;
  background: var(--surface-page);
  color: var(--text-primary);
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-2);
  margin-top: 4px;
}
.qtype textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

.form-errors {
  border: 1px solid var(--alert-red);
  border-radius: var(--radius-2);
  padding: 14px 16px; margin: 22px 0;
  color: var(--text-secondary);
}
.form-errors p { margin: 0; }

button.apply {
  border: none; cursor: pointer; margin-top: 26px; width: 100%;
}
.whats-next { margin: 26px 0; }
.whats-next p { color: var(--text-secondary); max-width: 62ch; }

/* --- the board on a phone ------------------------------------------
   Justin, 2026-08-14: "optimize the mobile experience."

   This surface needed less than the internal app did -- it is one
   column of cards inside a max-width, so it already reflowed -- but it
   is also the surface most likely to be READ on a phone. A veteran
   finds a role from a link in a message, not at a desk.

   The apply form's own comment already said where it gets used: "A form
   on a phone, in a parking lot, between shifts." Two things there were
   still built for a desktop: `.row` put first and last name side by
   side at any width, and the submit button was a normal-height target. */
@media (max-width: 640px) {
  main { padding: 26px 16px 60px; }
  .masthead { padding: 16px; }
  .masthead-inner { gap: 4px 12px; }
  /* Smaller, never dropped. The shield is the mark a veteran recognises
     from the message that sent them here. */
  .masthead .shield { height: 40px; width: 40px; }

  /* THREE PORTRAIT CARDS DO NOT FIT ON A PHONE, and shrinking them to
     make them fit produces three thumbnails nobody can read a hook off.
     One card at a time, swiped, with snap points so it lands square --
     and a peek of the next one at the right edge, which is the only
     thing that tells a reader there IS a next one. */
  .stories-grid {
    grid-auto-flow: column;
    grid-template-columns: none;
    grid-auto-columns: 76%;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 6px;
  }
  .stories-grid > li { scroll-snap-align: start; }
  .story-close { top: -38px; }

  h1 { font-size: var(--t-display-md); }
  .roles { grid-template-columns: 1fr; }
  .role-card-body { padding: 16px; }
  .role-card-body h2 { font-size: 17px; }
  .meta { gap: 4px 12px; }
  .board-head { flex-direction: column; align-items: flex-start; }

  /* Two 50%-width name fields on a 390px screen is two cramped boxes.
     One column, full width. */
  .row { flex-direction: column; gap: 0; }

  .f input, .f select, .f textarea { padding: 13px; }
  .apply { width: 100%; text-align: center; padding: 16px 22px; }
  .section { margin-top: 26px; }
  footer { margin-top: 40px; }

  /* Stacked, and full width -- two half-width search boxes on a 390px
     screen is the same mistake `.row` made with first and last name.

     `flex: 0 0 auto` matters and is not tidying. On a row, `flex-grow`
     on `.search-field` shares the WIDTH; the moment the container turns
     into a column, the main axis is vertical and the same rule stretched
     each field to fill the height -- which on a phone put a finger's
     worth of empty white between the keyword box and the location
     label. Caught in a 390px screenshot, not in the markup. */
  .search { flex-direction: column; gap: 12px; }
  .search-field { flex: 0 0 auto; }

  /* The two buttons go full width and sit under the tab. A 44px-tall
     target you can hit with a thumb, on the page where somebody decides
     to apply. */
  .actions { gap: 10px; }
  .actions .btn { flex: 1 1 100%; text-align: center; padding: 15px 20px; }
  .tab { flex: 1 1 100%; }
  /* `.share` is the flex item here, not its `summary.btn` -- the rule
     above stretches every direct-child button except this one, since a
     <details> wraps it. Matched by hand so "Share this role" lines up
     with "Apply for this role" instead of staying its natural width. */
  .share { flex: 1 1 100%; }
  .share summary { display: block; text-align: center; }
  .share-menu { left: 0; right: 0; }

  /* 96x54 per the handoff: still 16:9, still legible, less of a small
     screen given to a secondary strip. 20px between items rather than
     the desktop 32 -- the same instruction, scaled to a screen where
     32px of nothing is a tenth of the width. */
  .clip-shot { width: 96px; height: 54px; }
  .clipstrip-half { gap: 20px; padding-right: 20px; }
  .clip-text { width: 128px; }
  .clip-hook { font-size: 11px; -webkit-line-clamp: 2; }
  body.has-clipstrip main { padding-bottom: 110px; }

  .jd-body { padding: 18px 16px; }
}

/* Somebody who asked their operating system to stop things moving gets a
   strip that does not move. It still shows the clips -- the content is
   not the problem, the motion is -- so the track is simply parked and
   made scrollable by hand.

   Nothing needs saying about the full job description here any more: it
   is an in-flow disclosure since 2026-08-18 and has no transition to
   turn off. */
@media (prefers-reduced-motion: reduce) {
  .clipstrip-track {
    animation: none;
    width: auto;
    overflow-x: auto;
  }
  .clipstrip-half[aria-hidden="true"] { display: none; }
  .story img { transition: none; }
  .story:hover img, .story:focus-visible img { transform: none; }
}

/* Same reason as the internal app: under 16px, iOS zooms the page on
   focus and leaves it zoomed. On the apply form that lands a veteran
   mid-sentence on a page they now have to pinch back out of. */
@media (hover: none) and (pointer: coarse) {
  .f input, .f select, .f textarea { font-size: 16px; }
  .qtype textarea { font-size: 16px; }
}

/* ====================================================================
   THE 2026-08-18 REBRAND. Centred, Old Glory, and no masthead.

   WHY CENTRED. Justin, 2026-08-18: the left rag read as a document
   half-built. This board is embedded in a Wix page whose header is
   centred, and a left-aligned block under a centred header looks like
   two pages spliced together. The role CARDS stay centred too -- their
   content is three short facts, not prose, and a ragged column of
   three-word lines is worse than a centred one.

   BODY COPY IS THE EXCEPTION AND STAYS AS IT IS. Anything longer than
   two lines is still set to read normally; centred prose is slower to
   read and this board asks a veteran to read the explainer.
   ==================================================================== */

main { text-align: center; }

.eyebrow {
  color: var(--accent);
  letter-spacing: var(--track-eyebrow);
}

/* The tick under the eyebrow, replacing the gold bar the old palette
   opened a section with. Blue, because red is now the accent and the
   accent is already spent on the eyebrow above it. */
.eyebrow::after {
  content: "";
  display: block;
  width: 26px;
  height: 2px;
  background: var(--blue-700);
  margin: 12px auto 0;
}

/* THE HEADLINE IS THE PAGE. It is the one thing a visitor reads before
   deciding whether this board is different from the last one, so it is
   set as large as the column allows and it clamps rather than wrapping
   into four lines on a phone. */
.h1-lead {
  font-family: var(--font-display);
  font-weight: var(--w-bold);
  font-size: clamp(38px, 6.6vw, 82px);
  line-height: 1.02;
  color: var(--ink);
  /* BOTTOM MARGIN IS LOAD-BEARING HERE, and it was 0 in the handoff.
     The base rule is `h1 { margin: 0 0 8px }` and the 26px of air under
     the headline used to come from `.lede`, which this rebrand deletes.
     With a 0 bottom margin and no lede, an 82px display face sat its
     descenders straight through the explainer's blue top border --
     "before you apply!" touching the rule on desktop and crossing it on
     a phone. Verified in Chromium at 1280 and at 390 before and after.
     34px clears the descender at the largest clamp step. */
  margin: 22px auto 34px;
  max-width: 900px;
  text-wrap: balance;
}
.h1-blue { color: var(--blue-700); }
.h1-red  { color: var(--accent); font-style: italic; }

.explainer {
  text-align: center;
  max-width: 760px;
  margin-inline: auto;
  background: var(--surface-read);
  border: 1px solid var(--border-hair);
  border-top: 3px solid var(--blue-700);
  border-radius: 0;
  padding: 30px 34px;
}
.explainer > p:first-child {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: var(--w-semi);
  font-size: 23px;
  line-height: var(--lh-snug);
  color: var(--ink);
  margin: 0;
}
.explainer-lead {
  font-family: var(--font-display);
  font-weight: var(--w-semi);
  font-size: 19px;
  line-height: 1.45;
  color: var(--ink);
  margin: 18px auto 0;
  max-width: 600px;
  text-wrap: pretty;
}

.chips { justify-content: center; }

/* The chips are buttons now, not tags: square, bordered, and the
   active one carries a red underrule so the state survives a
   screenshot and does not depend on hue alone. */
.chip {
  border-radius: 0;
  border: 1px solid var(--gray-200);
  border-bottom: 3px solid var(--gray-200);
  padding: 13px 22px;
  letter-spacing: 0.16em;
}
.chip:hover { border-bottom-color: var(--accent); color: var(--blue-700); }
.chip-active {
  background: var(--blue-700);
  color: var(--white);
  border-color: var(--blue-700);
  border-bottom-color: var(--accent);
}

.search { justify-content: center; }
.search-field { text-align: center; }
.search-field > span { color: var(--blue-700); }
.search-field input,
.search-field select {
  text-align: center;
  text-align-last: center;
  border-radius: var(--radius-1);
}

/* THE SELECT INHERITED NONE OF THE INPUT'S BOX, which the handoff did
   not account for: `.search-field input` carries the width, padding,
   border and type, and the new `<select>` matched only on text-align.
   Side by side in the same flex row that rendered as a full-width
   bordered box next to a small native dropdown -- two controls that are
   peers looking like a field and an afterthought.

   Same declarations as the input rather than a shared selector up
   there, because that rule is above this appended block and this file
   is edited by appending. `appearance: none` plus a drawn chevron so
   the control is the same object in Safari, Firefox and Chrome; the
   chevron is a background image rather than a pseudo-element because a
   `<select>` cannot carry one. */
.search-field select {
  width: 100%;
  font: var(--t-body-lg)/1.45 var(--font-ui);
  padding: 12px 34px 12px 13px;
  background: var(--surface-card);
  color: var(--text-primary);
  border: 1px solid var(--border-hair);
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--blue-700) 50%),
                    linear-gradient(135deg, var(--blue-700) 50%, transparent 50%);
  background-position: calc(100% - 18px) calc(50% + 1px),
                       calc(100% - 13px) calc(50% + 1px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.search-field select:focus {
  outline: 2px solid var(--accent); outline-offset: 1px;
}

.board-head { justify-content: center; }
.sort-active { border-bottom: 2px solid var(--accent); }

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

.role-card { text-align: center; }
.role-card-link { text-align: center; }
.role-card {
  border-radius: 0;
  border-top: 3px solid var(--gray-200);
}
.role-card:hover { border-top-color: var(--accent); }

.role-card-body h2 { text-align: center; }

/* ---- share one role, from the list ------------------------------- */
/* Justin, 2026-08-18: "We need a share button for people to be able to
   share each specific role."

   THE CARD STOPS CLIPPING ITS OWN CONTENTS. `.role-card` carried
   `overflow: hidden` so a thumbnail respected the corner radius; the
   card's radius is 0 since the 2026-08-18 rebrand, so it was clipping
   nothing except -- as soon as this control existed -- the share menu,
   which opens below the button and outside the card's box. A menu that
   opens invisibly is worse than no menu.

   COLUMN, so the button sits at the foot of every card in a row whatever
   each summary's length. The link keeps the whole card clickable above
   it; the share control is deliberately NOT inside that link. */
.role-card { overflow: visible; display: flex; flex-direction: column; }

/* AND THIS IS WHY THE SEARCH DID NOTHING. Justin, 2026-08-19: "the text
   and state search bars are not connected to the jobs. nothing happens
   when I use either."

   They were connected. `board.js` was hiding the right cards and the
   count above the grid was updating -- "1 of 3 open" -- and every card
   stayed on the screen, because the line directly above this one gives
   `.role-card` a `display: flex`, and ANY author rule beats the browser's
   own `[hidden] { display: none }` whatever the specificity. The property
   was true the whole time; the computed display was `flex`.

   IDENTICAL TO THE DEFECT IN THE FULL-DESCRIPTION PANEL, found hours
   earlier the same day and recorded in test_public_board.py: `hidden`
   set, `display` overriding it, and every assertion on `el.hidden`
   passing while the page was wrong. Both were introduced by giving an
   element a display value for layout reasons, in a place far from the
   code that hides it.

   `.role-card[hidden]` is a class plus an attribute, so it outranks
   `.role-card` on specificity and does not need `!important` or a
   particular source order. */
.role-card[hidden] { display: none; }
.role-card-link { flex: 1 1 auto; height: auto; }
.role-card-actions { padding: 0 20px 18px; }
.role-card-actions .share summary { font-size: var(--t-ui-sm); padding: 8px 14px; }
/* Centred under a centred card, rather than anchored to one edge: the
   menu is about as wide as the card, so either edge would hang it half
   off the tile. */
.role-card-actions .share-menu {
  left: 50%;
  transform: translateX(-50%);
  text-align: left;
}

/* The hairline between the title and the facts. 22px, red, and the one
   piece of red on an otherwise quiet card. */
.role-card-body h2::after {
  content: "";
  display: block;
  width: 22px;
  height: 1px;
  background: var(--accent);
  margin: 10px auto;
}
.role-card-body .meta { justify-content: center; }
.role-card-body .summary { text-align: center; }

/* ---- the traditional-posting card -------------------------------- */
/* Justin's card, supplied 2026-08-18 and shipped as
   /static/role-card.svg. It replaces the four CSS rectangles that
   replaced role-card-fallback.jpg earlier the same day: a drawing of a
   document was an accurate way to say "no video here" and nothing else,
   and a tile whose whole message is an absence is not what a veteran
   scrolling this board should meet.

   COVER, and unlike the photograph it replaces that crops nothing --
   the artwork is 16:9 and so is this slot. The blue ground stays
   underneath so a card still reads as deliberate furniture in the
   moment before a 12KB file arrives, rather than as white space.

   The video cards keep their photograph. The contrast between a face
   and this card is the distinction this board is making. */
.role-media-card {
  background: var(--blue-900);
}

/* THE SAME PLATE AS A ROLE PAGE'S PLACEHOLDER, where the player sits on
   a role that has a video. Justin, 2026-08-18: "We need to put our logo
   there as a place holder."

   32:9, MATCHING THE ARTWORK EXACTLY, so nothing is ever cropped at any
   width. The first attempt cropped the 16:9 card into a 220px band and
   cut its outer ring; role-hero.svg is drawn for this band instead. A
   full 16:9 box here would be around 380px tall -- most of a laptop
   viewport spent on a placeholder, and it would push the summary Justin
   asked to be on the page "no matter what" below the fold. */
.role-hero {
  aspect-ratio: 32 / 9;
  border-radius: var(--radius-2);
  overflow: hidden;
  background: var(--blue-900);
}
.role-hero img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}

footer { text-align: center; }

/* The flag rule above the footer, replacing the plain hairline. Three
   equal bars, blue / white / red, and a hairline box around them so
   the white bar is visible on a white page. */
footer::before {
  content: "";
  display: block;
  width: min(300px, 60%);
  height: 3px;
  margin: 0 auto 22px;
  border: 1px solid var(--border-hair);
  background: linear-gradient(to right,
    var(--blue-700) 0 33.333%,
    var(--white)    33.333% 66.666%,
    var(--accent)   66.666% 100%);
}
