/*
 * /careers
 *
 * Everything on this page comes from an internal source of truth and none of it
 * is invented, because a careers page is the one page a candidate reads
 * literally:
 *
 *   the four H's        highbrow/team/handbook.md, values are ratified
 *   the open role       highbrow/team/jd-product-ops-associate.md
 *   the stats           the same figures as the home page, checked 2026-08-27
 *
 * The handbook's "culture codes" are deliberately NOT here. They are marked
 * DRAFT, founders to ratify, so they are not ours to publish yet.
 *
 * Palette and fonts are the site's own: black, white, #d9d9d9, #4abf2c green,
 * Rajdhani for headings, Open Sans for body.
 */

/* Light, deliberately, and the only light page on the site.
 *
 * The rest of highbrowinteractive.com is dark because it is wrapped around game
 * art: full-bleed gameplay footage, character cutouts lit for dark backgrounds,
 * baked-dark card images. A light shell around that art reads as broken.
 *
 * Careers has none of that art. What it has is eight job descriptions, which is
 * a lot of reading, and long-form text is the one thing this site asks anyone to
 * actually read. So this page inverts.
 *
 * The nav and the footer stay dark and bracket the page. That is not a
 * compromise, it is the only option: there is a single logo asset, white on
 * transparent, so a light header would need artwork that does not exist.
 *
 * Greens are NOT the site's #30ff5d here. That colour is designed to glow on
 * black and fails contrast badly on white. Text greens below are darkened to
 * stay legible; #4abf2c survives as a border and accent only.
 */
:root {
  --c-bg: #ffffff;
  --c-panel: #f4f5f2;          /* section bands */
  --c-deep: #eef7ea;           /* green-tinted callouts */
  --c-text: #11150f;
  --c-muted: #3c423a;          /* body copy, not a light grey */
  --c-dim: #636a5f;
  --c-green: #4abf2c;          /* borders and accents only */
  --c-green-bright: #1d6b0c;   /* anything that is TEXT */
}

/* The nav is white-on-transparent and turns solid black on scroll. On a light
   page it would be white text on white until the user scrolls, so pin it solid
   in both states. Beats .navbar-transparent's own !important on specificity. */
body.careers-page .navbar.navbar-transparent,
body.careers-page .navbar.navbar-scrolled,
body.careers-page .navbar {
  background-color: #0d100c !important;
}

.careers-page {
  background: var(--c-bg);
  color: var(--c-text);
  font-family: 'Open Sans', sans-serif;
}

/* ---------- hero ---------- */

.careers-hero {
  padding: 190px 0 60px;
}

.careers-eyebrow {
  font-family: 'Rajdhani', sans-serif;
  text-transform: uppercase;
  letter-spacing: 2.5px;
  font-size: 15px;
  font-weight: 600;
  color: var(--c-green-bright);
  margin-bottom: 8px;
}

.careers-h1 {
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  font-size: clamp(2.3rem, 6vw, 4.2rem);
  line-height: 1.03;
  margin: 0 0 20px;
  max-width: 15ch;
}

.careers-lede {
  font-size: 1.12rem;
  line-height: 1.75;
  color: var(--c-muted);
  max-width: 62ch;
}

/* ---------- shared sections ---------- */

.careers-section {
  padding: 64px 0;
}

.careers-section--panel {
  background: var(--c-panel);
  border-top: 1px solid rgba(17, 21, 15, .07);
  border-bottom: 1px solid rgba(17, 21, 15, .07);
}

/* Section headings. Sat at 2.4rem against 1.72rem sub-headings, a ratio of
   1.4, which is close enough that the two levels read as the same weight.
   At 3.1rem the steps are 4.2 / 3.1 / 1.72 and the hierarchy is obvious.
   .apply-headline is the other h2 on the page and tracks this — change both. */
.careers-h2 {
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  font-size: clamp(2.1rem, 4.4vw, 3.1rem);
  line-height: 1.08;
  margin-bottom: 28px;
}

.careers-body p {
  color: var(--c-muted);
  line-height: 1.85;
  margin-bottom: 16px;
  max-width: 70ch;
}

/* ---------- the numbers strip ---------- */

.careers-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 22px;
  margin-top: 44px;
  padding-top: 34px;
  border-top: 1px solid rgba(17, 21, 15, .14);
}

.careers-stat b {
  display: block;
  font-family: 'Rajdhani', sans-serif;
  font-size: 2.1rem;
  font-weight: 700;
  color: var(--c-green-bright);
  line-height: 1.1;
  /* Rajdhani has proportional figures ("1" is 11px where "0" is 18px), so a
     uniform 1ch drum leaves visible air around narrow digits. Tighten the pitch
     between drums so "160" does not read as "1 60". */
  letter-spacing: -.05em;
}

/* Direct child only. As a descendant selector this also caught the .odo spans
   nested inside <b>, shrinking the odometer drums to .8rem so the digits
   rendered as subscripts next to a full-size "+" and "M". */
.careers-stat > span {
  font-size: .8rem;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--c-dim);
}

/* ---------- values ---------- */

.careers-values {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 18px;
}

/* Four identical pale cards was the plainest thing on the page, and it ignored
   the one idea the section's own name hands you: they all start with the same
   letter. The H is the graphic now, so the boxes are gone and a hairline does
   the separating. */
.careers-values li {
  background: none;
  border: 0;
  border-top: 2px solid rgba(17, 21, 15, .18);
  border-radius: 0;
  padding: 16px 0 0;
  color: var(--c-muted);
  line-height: 1.7;
}

.value-mark {
  display: block;
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  font-size: 5.6rem;
  line-height: .9;
  letter-spacing: -1px;
  /* Filled and soft by default. Only browsers that can actually draw an
     outline get one, because the outlined version needs a transparent fill
     and that would erase the letter everywhere else. */
  color: rgba(29, 107, 12, .2);
  transition: color .25s ease, transform .32s cubic-bezier(.34, 1.4, .64, 1);
}

@supports (-webkit-text-stroke: 2px currentColor) {
  .value-mark {
    color: transparent;
    -webkit-text-stroke: 3px var(--c-green-bright);
  }
}

/* Hovering fills the letter in and lifts it off its rule. */
.careers-values li:hover .value-mark {
  color: var(--c-green-bright);
  -webkit-text-stroke-color: var(--c-green-bright);
  transform: translateY(-4px);
}

.careers-values strong {
  display: block;
  font-family: 'Rajdhani', sans-serif;
  font-size: 1.45rem;
  color: var(--c-text);
  margin: 2px 0 6px;
}

@media (prefers-reduced-motion: reduce) {
  .value-mark {
    transition: color .25s ease;
  }

  .careers-values li:hover .value-mark {
    transform: none;
  }
}

/* ---------- the role ---------- */

/* The listing is a <details>. Collapsed it is a clickable row; open it is the
   full JD. No JS: <details> gives keyboard access and screen-reader semantics
   for free, and the text stays in the DOM while closed so it is still crawlable
   and findable with the browser's own find-in-page. */
.role {
  background: #ffffff;
  border-radius: 14px;
  border: 1px solid rgba(17, 21, 15, .12);
  transition: border-color .18s ease;
}

.role[open] {
  border-color: rgba(74, 191, 44, .75);
  box-shadow: 0 1px 3px rgba(17, 21, 15, .06);
}

.role-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 26px 30px;
  cursor: pointer;
  list-style: none;          /* Firefox */
  border-radius: 14px;
}

/* Chrome and Safari draw their own triangle; we use .role-toggle instead. */
.role-summary::-webkit-details-marker {
  display: none;
}

.role-summary:hover .role-title,
.role-summary:focus-visible .role-title {
  color: var(--c-green-bright);
}

/* <summary> is focusable, so the ring has to be visible and inside the card. */
.role-summary:focus-visible {
  outline: 2px solid var(--c-green-bright);
  outline-offset: -4px;
}

.role-summary-main {
  display: block;
}

/* The chevron. Rotates to point up once the listing is open. */
.role-toggle {
  flex: 0 0 auto;
  width: 13px;
  height: 13px;
  border-right: 2px solid var(--c-green-bright);
  border-bottom: 2px solid var(--c-green-bright);
  transform: rotate(45deg);
  transform-origin: center;
  transition: transform .2s ease;
  margin-right: 6px;
}

.role[open] .role-toggle {
  transform: rotate(-135deg);
}

.role-detail {
  padding: 0 30px 32px;
}

/* Flex rather than an inline icon nudged with vertical-align. The icon is a
   flex item centred against the title's line box, which is the alignment the
   eye is actually looking for; vertical-align was always going to be a guess. */
.role-title {
  display: flex;
  align-items: center;
  gap: .42em;
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  font-size: clamp(1.4rem, 3vw, 1.9rem);
  transition: color .18s ease;
}

/* The one-line version of the role, taken from the JD's own opening line, so
   the list can be read without opening anything. */
.role-line {
  display: block;
  margin-top: 6px;
  font-size: .95rem;
  line-height: 1.6;
  color: var(--c-muted);
  /* Wide enough that the one-liners mostly hold a single line; a two-line wrap
     put the facts column between the title and the description. */
  max-width: 78ch;
}

/* Openings and experience, off the left column entirely: they are the numbers
   you check after the title interests you, not while scanning for it. */
.role-facts {
  flex: 0 0 auto;
  font-size: .82rem;
  letter-spacing: .4px;
  color: var(--c-dim);
  white-space: nowrap;
}

.role-detail > p:first-child {
  margin-top: 0;
}

.role h4 {
  font-family: 'Rajdhani', sans-serif;
  font-size: 1.2rem;
  color: var(--c-green-bright);
  margin: 28px 0 12px;
  text-transform: uppercase;
  letter-spacing: 1.2px;
}

.role p,
.role li {
  color: var(--c-muted);
  line-height: 1.8;
}

.role ul {
  padding-left: 20px;
  margin: 0;
}

.role li {
  margin-bottom: 10px;
}

.role li strong,
.role p strong {
  color: var(--c-text);
}

/* The "what you will not own" block. Kept visually distinct because being
   plain about the limits of the role is the point of including it. */
.role-limits {
  background: var(--c-deep);
  border-left: 3px solid var(--c-dim);
  border-radius: 8px;
  padding: 20px 24px;
  margin-top: 26px;
}

.role-limits h4 {
  color: var(--c-muted);
  margin-top: 0;
}

/* ---------- apply ---------- */

/* The close. Same construction as the one green work board — green fill, white
   keyline, green outer ring — because this is the other thing on the page that
   is meant to stop you, and it is where the signal in the left margin goes
   green. The old version was a pale outline box, which made the conversion
   point the quietest element on the page. */
.apply-panel {
  background: var(--c-green-bright);
  border: 2px solid #ffffff;
  box-shadow: 0 0 0 2px var(--c-green-bright);
  border-radius: 12px;
  padding: 46px 48px 42px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.apply-eyebrow {
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  font-size: .74rem;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .87);
  margin-bottom: 10px;
}

.apply-headline {
  margin: 0 0 14px;
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  font-size: clamp(2.1rem, 4.4vw, 3.1rem);
  line-height: 1.08;
  letter-spacing: .2px;
  color: #ffffff;
}

.apply-body {
  margin: 0;
  max-width: 68ch;
  font-size: 1rem;
  line-height: 1.8;
  color: rgba(255, 255, 255, .9);
}

/* A button, not a link in a paragraph. It is the only thing on the page a
   candidate has to act on. */
.apply-cta {
  display: inline-block;
  margin-top: 28px;
  padding: 15px 28px;
  border-radius: 8px;
  background: #ffffff;
  color: var(--c-green-bright);
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  font-size: 1.22rem;
  letter-spacing: .4px;
  text-decoration: none;
  transition: transform .22s cubic-bezier(.34, 1.4, .64, 1), box-shadow .22s ease;
}

.apply-cta:hover,
.apply-cta:focus-visible {
  color: var(--c-green-bright);
  transform: translateY(-2px);
  box-shadow: 0 10px 22px rgba(17, 21, 15, .22);
}

.apply-foot {
  margin-top: 22px;
  font-size: .82rem;
  letter-spacing: .4px;
  color: rgba(255, 255, 255, .87);
}

@media (max-width: 767px) {
  .apply-panel {
    padding: 32px 24px 30px;
  }

  .apply-cta {
    align-self: stretch;
    text-align: center;
  }
}

@media (max-width: 767px) {
  .careers-hero {
    padding: 150px 0 44px;
  }

  .careers-section {
    padding: 44px 0;
  }

  .role-summary {
    padding: 20px 22px;
  }

  .role-detail {
    padding: 0 22px 26px;
  }
}

/* Space between listings once there is more than one. */
.role + .role {
  margin-top: 14px;
}

/* ---------------------------------------------------------------------------
   Redesign, 2026-08-28. Six changes, all aimed at routing rather than reading.
   The page's job is to get the right person into the right listing, and nine
   identical rows did not do that.
--------------------------------------------------------------------------- */

/* Discipline headings above each cluster of listings. */
.role-group {
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  font-size: 1.26rem;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--c-green-bright);
  margin: 40px 0 15px;
  display: flex;
  align-items: baseline;
  gap: 12px;
}

.role-group:first-of-type {
  margin-top: 26px;
}

.role-group span {
  font-family: 'Open Sans', sans-serif;
  font-weight: 400;
  font-size: .8rem;
  letter-spacing: .6px;
  text-transform: none;
  color: var(--c-dim);
}

.careers-intro {
  color: var(--c-muted);
  line-height: 1.8;
  max-width: 62ch;
  margin-bottom: 22px;
}

/* What happens after you apply. Cheap to write, rarely offered, and the thing
   candidates are most anxious about. */
.careers-steps {
  list-style: none;
  padding: 0;
  margin: 0;
  max-width: 76ch;
}

.careers-steps li {
  position: relative;
  padding-left: 52px;
  margin-bottom: 20px;
  color: var(--c-muted);
  line-height: 1.8;
}

/* The step marker is a kilometre stone rather than a numbered circle. The
   hiring process is a road and the candidate is moving along it, which is a
   better fit for this studio than a generic ordered list. */
.step-stone {
  position: absolute;
  left: 0;
  top: -4px;
  width: 34px;
  height: 44px;
  display: block;
}

.step-stone svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* The numeral is centred in the WHITE body of the stone, not in the stone as a
   whole. In the 0 0 44 56 viewBox the yellow band runs to y=24 and the body
   from y=24 to y=52, so the white area is 43% to 93% of the height. Insetting
   to exactly that and centring inside it puts the digit on the middle of the
   white rather than drifting toward the base. */
.step-stone b {
  position: absolute;
  left: 0;
  right: 0;
  top: 43%;
  bottom: 7%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  font-size: 1.05rem;
  color: #11150f;
  line-height: 1;
}


/* Each step's lead is the heading for that step, so it is set as one rather
   than left at body size in bold. */
/* Set to the same step as .work-board__value — same size, line-height and
   tracking — so "A first conversation." and "Open to everyone" read as the same
   level of heading rather than merely similar. Change them together. */
.careers-steps strong {
  display: block;
  margin-bottom: 6px;
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  font-size: 1.72rem;
  line-height: 1.15;
  letter-spacing: .2px;
  color: var(--c-text);
}

/* ---------------------------------------------------------------------------
   Character cutouts, 2026-08-28.

   The game screenshots that used to sit here were rectangles of someone else's
   composition dropped into the page. These are the transparent character
   cutouts the site already ships, placed so they break the page edge rather
   than sit inside a box, and drifting slightly against the scroll.

   Parallax is done with CSS scroll-driven animation rather than a scroll
   listener. No JavaScript on this page at all, and where the browser does not
   support it the figures simply hold still, which still reads as intentional
   because the composition does not depend on the movement.
--------------------------------------------------------------------------- */

/* Figures bleed past the viewport, so the page must not gain a horizontal
   scrollbar. `clip` rather than `hidden`: it does not create a scroll
   container, so it cannot break scrolling or anchoring inside the page.
   
   It has to be set on the ROOT as well as the body. Setting it on body alone
   leaves html at `visible` and the page still scrolls sideways, measured at
   71.5px before this was added. Scoped with :has so this stylesheet, which is
   only loaded by /careers, cannot affect any other page. */
html:has(body.careers-page),
.careers-page {
  overflow-x: clip;
}

.careers-hero,
.careers-section {
  position: relative;
}

/* Content sits above the art. Without this the cutouts would intercept the
   listings, which are clickable. */
.careers-hero > .container-fluid,
.careers-section > .container-fluid {
  position: relative;
  z-index: 1;
}

/* --flip: -1 mirrors a figure so it faces INTO the page. A character on the
   right edge must look left and one on the left edge must look right, or the
   art points the reader off the page. Three of the four needed it.
   
   rst_char gets a second benefit: the asset ships mirrored, so the cap reads
   "SIVAD". Flipping it corrects the facing and the lettering at once.
   
   The flip has to live in the same transform as the parallax or one overwrites
   the other, hence the shared scaleX(var(--flip)) in both the base rule and the
   keyframes. */
.careers-figure {
  position: absolute;
  z-index: 0;
  pointer-events: none;
  user-select: none;
  height: auto;
  will-change: transform;
  transform: scaleX(var(--flip, 1));
}

/* Two figures, not four, and both on the right.

   Four was wrong for a reason that only shows up once you measure it. The
   content column here is col-lg-10, which leaves 138px of margin, and these
   figures are 260-300px wide. Anywhere the section holds full-width cards, the
   Open roles list and the four H's grid, the character necessarily sits behind
   them. One of those was also taller than its own section and poked up into the
   band above it.

   They survive in the hero and in How we work because the prose there is capped
   at 62-70ch and ends around x=670, so the right side is genuinely empty and
   the figure occupies space nothing else wants.
   
   They started bled well past the edge, which cropped a third off each one and
   cut through faces. Pulled fully inside: no character is clipped horizontally
   now, they simply sit near the margin. The parallax is what gives them life,
   not the crop.
   
   They are not bled downward either. The following section paints over them and
   cut every figure off mid-shin, the same amputation the game cards had. They
   sit on the section boundary, and the drift runs upward only so nothing pushes
   a figure back below its own edge. */
.fig--hero {
  --flip: -1;          /* was reaching off the right edge with the phone */
  right: 18px;
  bottom: 0;
  width: 300px;
  opacity: .95;
}

.fig--work {
  /* no flip: he already looks left, which is into the page from this edge */
  right: 18px;
  bottom: 0;
  width: 260px;
  opacity: .9;
}

/* Parallax. Amplitudes differ per figure so they separate in depth rather than
   moving as one sheet. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .careers-figure {
      animation: figure-drift linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }

    .fig--hero { --drift: 40px; }
    .fig--work { --drift: 70px; }

    /* Upward-only: a symmetric drift would push each figure below its own
       section edge on the way in and clip the feet again. */
    @keyframes figure-drift {
      from { transform: translateY(0) scaleX(var(--flip, 1)); }
      to   { transform: translateY(calc(var(--drift, 60px) * -1)) scaleX(var(--flip, 1)); }
    }
  }
}

/* Below large screens the content column takes the full width and the figures
   would sit under the text. */
@media (max-width: 1199px) {
  .careers-figure {
    display: none;
  }
}

/* ---------------------------------------------------------------------------
   Tilt pills.

   Three words in the hero, set as objects rather than text. Each rests at its
   own small angle, set inline per pill, so the row looks placed by hand rather
   than laid out by a grid. On hover the pill rotates the other way, lifts, and
   takes the green: the twist is the point, and it only reads if the resting
   state is already off-square.

   Angles are per-pill because a single shared angle looks like a mistake and
   alternating +/- looks like a pattern. Uneven values look deliberate.
--------------------------------------------------------------------------- */

.tilt {
  display: inline-block;
  padding: 1px 12px 3px;
  margin: 0 1px;
  border-radius: 100px;
  background: var(--c-deep);
  border: 1px solid rgba(74, 191, 44, .45);
  color: #14500a;
  font-weight: 600;
  white-space: nowrap;
  transform: rotate(var(--a, 0deg));
  /* Slight overshoot on the way out, so it settles rather than stops. */
  transition: transform .28s cubic-bezier(.34, 1.56, .64, 1),
              background-color .2s ease,
              border-color .2s ease,
              box-shadow .28s ease;
}

.tilt:hover,
.tilt:focus-visible {
  /* Twist against the resting angle rather than to zero: straightening reads as
     a correction, over-rotating reads as the object being nudged. */
  transform: rotate(calc(var(--a, 0deg) * -1.9)) translateY(-2px) scale(1.04);
  background: rgba(74, 191, 44, .18);
  border-color: rgba(74, 191, 44, .85);
  box-shadow: 0 4px 14px rgba(17, 21, 15, .1);
}

/* The lede and the roles intro are body copy, so the pills must not disturb the
   line rhythm. A rotated inline-block still occupies its unrotated box, which is
   why the line height holds even at these angles. */
.careers-lede .tilt,
.careers-intro .tilt {
  font-size: .97em;
}

@media (prefers-reduced-motion: reduce) {
  .tilt {
    transform: none;
    transition: background-color .2s ease, border-color .2s ease;
  }

  .tilt:hover,
  .tilt:focus-visible {
    transform: none;
  }
}

/* ---------------------------------------------------------------------------
   Five pieces of road and rail language. All CSS: this page still ships no
   JavaScript, so the letter spans and digit strips are written into the markup
   and every animation is a scroll-driven timeline.
--------------------------------------------------------------------------- */

/* --- 4. the location chip as a number plate: REMOVED -------------------
   Removed with the chip it styled. Every listing said "Chennai" and
   "Full-time, in-office" identically, so nine rows carried eighteen chips that
   never varied - texture, not information. It is stated once above the list
   instead, and the rows now show only what differs between them.

   Note the rule was ".role-meta span:first-child". Left in place it would have
   promoted whatever chip fell first next - the openings count - to a number
   plate, which is a worse bug than the noise it was drawn to fix. */

/* --- 5. the barrier as the expand indicator ---------------------------- */
.role-toggle {
  width: 58px;
  height: 21px;
  margin-right: 14px;
  border: 0;            /* drops the old chevron borders */
  transform: none;
  transform-origin: left center;
  transition: transform .3s cubic-bezier(.34, 1.3, .64, 1);
}

.role-toggle svg {
  width: 100%;
  height: 100%;
  display: block;
  overflow: visible;
}

/* Closed the boom is down across the road; open it is raised, as at a level
   crossing letting you through. */
.role[open] .role-toggle {
  transform: rotate(-58deg);
}

/* --- 6. split-flap headings: REMOVED ----------------------------------
   Dropped deliberately, not lost in a refactor. The stagger was built with
   animation-delay on a view() timeline, and progress-based timelines ignore
   time-based delays - the offset has to come from animation-range-start. So
   every letter flipped in unison, which is a single card turning, not a
   departure board. The sequential ripple was the whole idea.

   It also cost 37 spans across four headings and split the heading text into
   per-letter nodes, which is worth avoiding for a half-second seen once. */

/* --- 7. odometer digits ------------------------------------------------ */
.odo {
  display: inline-block;
  height: 1em;
  overflow: hidden;
  vertical-align: bottom;
  line-height: 1;
}

.odo i {
  display: block;
  font-style: normal;
  /* One glyph per line, so translating by -Nem lands on digit N. The strip is
     one character wide and breaks anywhere, which stacks 0-9 vertically.
     white-space: pre was here and defeated the whole thing: it suppresses the
     wrap, so all ten digits sat on one line and the drum read sideways. */
  width: 1ch;
  line-height: 1;
  word-break: break-all;
  white-space: normal;
  /* Centre the glyph in its cell so the leftover advance is symmetric; letter-
     spacing is reset because it would otherwise offset the centring. */
  text-align: center;
  letter-spacing: normal;
  transform: translateY(calc(var(--d) * -1em));
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .odo i {
      animation: odo-roll 1s cubic-bezier(.2, .8, .2, 1) both;
      animation-timeline: view();
      animation-range: entry 0% entry 70%;
    }

    @keyframes odo-roll {
      from { transform: translateY(0); }
      to   { transform: translateY(calc(var(--d) * -1em)); }
    }
  }
}

/* --- 8. horizon strip -------------------------------------------------- */
.horizon {
  height: 90px;
  overflow: hidden;
  pointer-events: none;
  position: relative;
}

.horizon svg {
  width: 100%;
  height: 100%;
  display: block;
}

.horizon-far  { fill: rgba(17, 21, 15, .07); }
.horizon-mid  { fill: rgba(17, 21, 15, .16); }
.horizon-near { fill: rgba(74, 191, 44, .38); }

/* Layers move at different rates, which is the whole trick: the far hills
   barely shift, the poles pass, the track runs. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .horizon-far, .horizon-mid, .horizon-near {
      animation: pan linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }

    .horizon-far  { --pan: 14px; }
    .horizon-mid  { --pan: 56px; }
    .horizon-near { --pan: 104px; }

    @keyframes pan {
      from { transform: translateX(calc(var(--pan) * .5)); }
      to   { transform: translateX(calc(var(--pan) * -.5)); }
    }
  }
}

@media (max-width: 767px) {
  .horizon { height: 62px; }
}

/* --- 10. the truck arriving on the hero baseline ------------------------
   It drives in from the left when the page loads, brakes, dips on its
   suspension and parks. The hero's bottom edge is the ground line: the white
   hero meets the grey Open roles panel there, so the truck needs no drawn road
   under it.

   The parked transform is on the base rule, not only in the keyframes, so that
   when animations never run - reduced motion, or a browser that skips the
   animation entirely - the truck is simply parked rather than stranded
   off-screen to the left. */
.hero-truck {
  position: absolute;
  bottom: 0;
  left: 0;
  z-index: 0;
  width: clamp(120px, 13vw, 182px);
  height: auto;
  overflow: visible;
  pointer-events: none;
  user-select: none;
  --park: 44vw;
  transform: translateX(var(--park));
}

/* The hero's bottom padding was 60px, which is less than the truck is tall, so
   it would have sat behind the stats row. This gives it its own band. */
.careers-hero {
  padding-bottom: 100px;
}

@media (prefers-reduced-motion: no-preference) {
  .hero-truck {
    animation: truck-arrive 2s cubic-bezier(.16, .84, .3, 1) .25s both;
  }

  /* fill-box so each wheel turns about its own centre rather than the SVG's. */
  .truck-wheel {
    transform-box: fill-box;
    transform-origin: center;
    animation: truck-wheel .5s linear .25s 4 both;
  }

  /* Only the body dips; the wheels stay on the ground. Positive rotation is
     clockwise, which drops the front of a right-facing vehicle - a brake dive. */
  .truck-body {
    transform-box: fill-box;
    transform-origin: 62% 100%;
    animation: truck-settle .55s ease-out 2.05s both;
  }
}

@keyframes truck-arrive {
  from { transform: translateX(-240px); }
  to   { transform: translateX(var(--park)); }
}

@keyframes truck-wheel {
  to { transform: rotate(360deg); }
}

@keyframes truck-settle {
  0%   { transform: rotate(0deg); }
  40%  { transform: rotate(1.6deg); }
  100% { transform: rotate(0deg); }
}

/* Below 768px the stats grid wraps to two rows and the hero gets tight; the
   truck would land on top of it. The figures already stop at 1199px. */
@media (max-width: 767px) {
  .hero-truck {
    display: none;
  }

  .careers-hero {
    padding-bottom: 44px;
  }
}

/* --- 3. level chips as route shields: REMOVED --------------------------
   Levels came off the listings: L2 is internal currency and means nothing to
   a candidate, the years band already carries the seniority signal, and the
   Pay block promises the band on the first call, which is the right moment
   for it. Publishing level and years together also meant publishing two views
   of one fact that had to agree, and one of them did not.

   The shield went with the chip it styled. Nothing else uses it. */

/* --- 1. the road down "How we hire": REMOVED ---------------------------
   The carriageway is gone; the kilometre stones stay. The gutter it opened up
   goes back with it — the list was padded to 74px and the stones pushed out to
   24px to clear the lane, and both revert. */

/* --- 2. the signal reads your scroll position ---------------------------
   A three-aspect colour-light signal pinned to the left margin. Red near the
   top, yellow through the roles, green by Apply.

   Base state is green lit and steady. That is the state anywhere the scroll
   timeline does not run — an unsupporting browser, or reduced motion — and a
   signal showing a clear aspect is a sensible static prop, where one stuck on
   red would read as an error. */
/* The rail spans the page above the footer. Sticky rather than fixed, so the
   signal rides the viewport floor for the whole page and is then carried up
   and out by the rail's own bottom edge when the footer arrives, instead of
   hovering over it. */
.page-rail {
  position: relative;
}

.signal-rail {
  /* The shared footer partial carries margin-top: 30px, which left the signal
     hanging that far above it. The rail is extended through the margin so the
     post stands on the footer's top edge. Tied to the partial: if that margin
     changes, change this. */
  --footer-gap: 30px;
  position: absolute;
  left: 34px;
  top: 0;
  bottom: calc(-1 * var(--footer-gap));
  width: 56px;
  z-index: 2;
  pointer-events: none;
  /* The signal's static position has to sit at the BOTTOM of the rail. Sticky
     with a bottom inset pins a box that would otherwise scroll off the bottom
     of the scrollport; it does not drag a box down to the floor. Parked at the
     rail's top it simply scrolled away, which is what happened first time. */
  display: flex;
  align-items: flex-end;
}

.rail-signal {
  position: sticky;
  bottom: 0;            /* stands on the screen floor */
  display: block;
  width: 56px;
  height: auto;
  user-select: none;
}

.rail-signal .lamp {
  opacity: .16;
}

.rail-signal .lamp--green {
  opacity: 1;
  filter: drop-shadow(0 0 5px rgba(74, 191, 44, .95));
}

/* Green is not a scroll percentage — it is tied to the Apply section actually
   appearing, so the signal clears exactly when the thing it is clearing you for
   comes into view. The section is not an ancestor of the signal, so the name is
   published into .page-rail's scope. */
.page-rail {
  timeline-scope: --apply-tl;
}

.careers-section--apply {
  view-timeline-name: --apply-tl;
}

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .rail-signal .lamp {
      animation-duration: auto;
      animation-timing-function: steps(1, end);
    }

    /* Red is pure scroll depth: lit until you are into the roles. */
    .rail-signal .lamp--red {
      animation-name: aspect-red;
      animation-timeline: scroll(root block);
      animation-fill-mode: both;
    }

    /* Amber runs two animations on two different timelines — it comes on by
       scroll depth and goes off when Apply arrives.

       amber-clear uses `forwards`, not `both`, deliberately. With a backwards
       fill it would apply its opening frame from the very top of the page and
       hold amber lit underneath the red. Listed last so it wins once its own
       range begins. */
    .rail-signal .lamp--amber {
      animation-name: aspect-amber, amber-clear;
      animation-timeline: scroll(root block), --apply-tl;
      animation-fill-mode: both, forwards;
      animation-range: normal, entry 0% entry 30%;
    }

    .rail-signal .lamp--green {
      animation-name: aspect-green;
      animation-timeline: --apply-tl;
      animation-fill-mode: both;
      animation-range: entry 0% entry 30%;
    }
  }
}

/* The paired offsets define the hold bands; the switch itself is instant and
   comes from the steps() timing function above. */
@keyframes aspect-red {
  0%, 26%   { opacity: 1; filter: drop-shadow(0 0 5px rgba(229, 72, 77, .95)); }
  30%, 100% { opacity: .16; filter: none; }
}

@keyframes aspect-amber {
  0%, 26%   { opacity: .16; filter: none; }
  30%, 100% { opacity: 1; filter: drop-shadow(0 0 5px rgba(242, 178, 27, .95)); }
}

@keyframes amber-clear {
  from { opacity: 1; filter: drop-shadow(0 0 5px rgba(242, 178, 27, .95)); }
  to   { opacity: .16; filter: none; }
}

@keyframes aspect-green {
  from { opacity: .16; filter: none; }
  to   { opacity: 1; filter: drop-shadow(0 0 5px rgba(74, 191, 44, .95)); }
}

/* Needs a clear left margin to sit in. Below this the content column reaches
   the edge and the signal would sit on top of the text. */
@media (max-width: 1399px) {
  .rail-signal {
    display: none;
  }
}

/* --- WASD driving -------------------------------------------------------
   W and S are throttle and reverse, A and D steer. The truck turns because it
   is moving rather than pivoting on the spot, so it drives like a vehicle
   instead of a cursor.

   Two trucks, not one: the hero truck is drawn side-on, which is right for a
   thing parked on a baseline and wrong for a thing being steered around a
   page. Starting to drive swaps to the overhead one, which reads as the camera
   lifting rather than as a different truck.

   Nothing in here shifts layout. Disturbed content is moved with transforms
   and springs back to exactly where it was. */
.drive-truck {
  position: fixed;
  left: 0;
  top: 0;
  width: 168px;
  height: auto;
  z-index: 40;
  pointer-events: none;
  user-select: none;
  transform-origin: 50% 50%;
  opacity: 0;
  visibility: hidden;
  filter: drop-shadow(0 12px 16px rgba(17, 21, 15, .3));
}

.is-driving .drive-truck {
  opacity: 1;
  visibility: visible;
}

/* The parked one steps aside while you are driving its overhead counterpart. */
.is-driving .hero-truck {
  opacity: 0;
  transition: opacity .22s ease-out;
}

.drive-hint {
  position: absolute;
  bottom: 104px;
  left: calc(44vw + 4px);
  z-index: 1;
  padding: 4px 10px;
  border: 2px solid #11150f;
  border-radius: 4px;
  background: #fff;
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  font-size: .72rem;
  letter-spacing: 1.4px;
  color: #11150f;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
  .drive-hint {
    animation: hint-show 9s ease-out 3.4s both;
  }
}

@keyframes hint-show {
  0%        { opacity: 0; transform: translateY(4px); }
  8%, 78%   { opacity: 1; transform: translateY(0); }
  100%      { opacity: 0; transform: translateY(0); }
}

/* Once you are driving the hint has done its job. */
.is-driving .drive-hint {
  animation: none;
  opacity: 0;
}

/* No keyboard, or motion turned down: the truck stays parked and the hint
   never appears, so nothing advertises a control that does nothing. */
@media (max-width: 1023px), (pointer: coarse), (prefers-reduced-motion: reduce) {
  .drive-hint {
    display: none;
  }
}

/* --- "How we work" as sign boards --------------------------------------
   Each board leads with the hard fact and demotes the sentence, because the
   sentence is the part nobody reads.

   One board is green, not five. Five solid slabs was a wall of colour that
   took over the page and flattened its own hierarchy - every point shouting
   equally is the same as none of them shouting. Ownership carries the green
   because it is the headline claim and spans the width; the rest are quiet
   boards that read as supporting signage under it. */
/* Bento rather than a banner over a 2x2. Three columns, with Ownership held
   as a 2x2 anchor and the rest falling in around it by auto-placement:
   Cadence and Decisions stack down the third column beside it, then
   Measurement takes two and Detail takes one on the bottom row.

   No explicit row or column numbers anywhere - the spans alone produce the
   layout, so reordering the list cannot leave a hole in the grid. */
/* Four columns across the full content width, so the block is wider than it is
   tall. Three tiles span two columns each and the other six take one, which is
   twelve cells - exactly four columns by three rows, with no gaps.

   The DOM order is what staggers them: the wide tiles land in a different pair
   of columns on each row (1-2, then 2-3, then 3-4) rather than stacking down
   one side, which is what stops it reading as a table with merged cells. */
.work-boards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  /* dense so a two-column tile that cannot fit the tail of a row is passed over
     and a later single backfills the gap. Without it the grid holds at four
     columns but opens a hole at three, where a wide tile lands with one slot
     left. The tiles are independent statements, so the small reordering dense
     can cause costs nothing. */
  grid-auto-flow: dense;
  gap: 14px;
}

/* Wide, not tall. As a 2x2 the anchor was a column of mostly empty green. */
.work-board--anchor {
  grid-column: span 2;
}

.work-board {
  display: flex;
  flex-direction: column;
  /* Centred, not top-aligned. A bento cell is sized by the tallest tile in its
     row, so a short tile top-aligned leaves a pool of dead space under it that
     reads as a mistake. Centred, the same slack becomes padding. */
  justify-content: center;
  gap: 8px;
  padding: 24px;
  border-radius: 8px;
  background: #ffffff;
  border: 1px solid rgba(17, 21, 15, .16);
}

.work-board--wide {
  grid-column: span 2;
}

/* Still the loudest tile, but it is a wide one now rather than a slab. */
.work-board--anchor {
  gap: 10px;
  padding: 26px;
}

.work-board--anchor .work-board__value {
  font-size: 2.1rem;
}

/* The one green board. White keyline set inside a green outer ring, the same
   construction as the level shields on the listings, so the plate, the shield
   and this board read as one signage system. */
.work-board--sign {
  background: var(--c-green-bright);
  border: 2px solid #ffffff;
  box-shadow: 0 0 0 2px var(--c-green-bright);
}

.work-board__label {
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  font-size: .74rem;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--c-green-bright);
}

.work-board__value {
  font-family: 'Rajdhani', sans-serif;
  font-weight: 700;
  font-size: 1.72rem;
  line-height: 1.15;
  letter-spacing: .2px;
  color: var(--c-text);
}

.work-board__note {
  font-size: .95rem;
  line-height: 1.75;
  color: var(--c-muted);
}

/* Chips get their own row rather than running inline with the sentence, which
   read as one cramped line. */
.work-board__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 2px 0;
}

/* On the green board everything inverts. */
.work-board--sign .work-board__label {
  color: rgba(255, 255, 255, .87);
}

.work-board--sign .work-board__value {
  color: #ffffff;
}

.work-board--sign .work-board__note {
  color: rgba(255, 255, 255, .88);
}

.work-board--sign .tilt {
  background: rgba(255, 255, 255, .12);
  border-color: rgba(255, 255, 255, .75);
  color: #ffffff;
}

.work-board--sign .tilt:hover {
  background: #ffffff;
  color: var(--c-green-bright);
}

/* Two columns first: the anchor keeps its 2x2 and everything else goes full
   width under it, which still reads as a bento rather than a plain stack. */
/* Twelve cells divide cleanly by 4, 3, 2 and 1, so every step keeps a full
   grid with no gaps: 3 rows, then 4, then 6, then a stack. */
@media (max-width: 1439px) {
  .work-boards {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (max-width: 1023px) {
  .work-boards {
    grid-template-columns: repeat(2, 1fr);
  }

  .work-board--anchor,
  .work-board--wide {
    grid-column: span 2;
  }
}

@media (max-width: 767px) {
  .work-boards {
    grid-template-columns: 1fr;
  }

  .work-board--anchor,
  .work-board--wide {
    grid-column: span 1;
  }

  .work-board--anchor .work-board__value {
    font-size: 1.72rem;
  }
}

/* Role icons ------------------------------------------------------------
   One per listing, sitting in the row's left gutter. currentColor on the SVG
   means the icon takes the row's colour, so it greens with the title on hover
   without a second rule.

   .role-summary is space-between, so the main block has to take the free space
   or the icon would drift toward the middle instead of hugging the left. */
.role-summary-main {
  flex: 1 1 auto;
  min-width: 0;
}

/* Sized in em so it tracks the title, which is a clamp() and changes with the
   viewport. A fixed px icon drifted out of step with the text beside it. */
.role-icon {
  display: block;
  flex: 0 0 auto;
  width: .95em;
  height: .95em;
  color: var(--c-dim);
  transition: color .18s ease;
}

.role-icon svg {
  width: 100%;
  height: 100%;
  display: block;
  overflow: visible;
}

.role-summary:hover .role-icon,
.role-summary:focus-visible .role-icon {
  color: var(--c-green-bright);
}



/* Below this the facts would squeeze the title, so they drop under the row. */
@media (max-width: 767px) {
  .role-summary {
    flex-wrap: wrap;
  }

  .role-facts {
    order: 3;
    width: 100%;
  }
}
