/**
 * Careers pages — layout only.
 *
 * SEPARATE FILE ON PURPOSE. style.css is the main site's stylesheet and is under careful
 * management (a lint suite runs over it, and it carries the design tokens). Careers rules
 * live here so the main site's CSS does not grow for a page most visitors never see, and so
 * the Netlify handoff is a short file list.
 *
 * EVERY colour, font, radius and easing below comes from a style.css token. Nothing is
 * hard-coded, which is what guarantees this page cannot drift from V2. Load order is
 * style.css then this file — it depends on :root tokens defined there.
 *
 * Reused from style.css rather than redefined: .section, .inner, .btn/.btn-*, .eyebrow,
 * .section-heading, .section-sub, .fade-up, and the whole .v2-field form treatment (which
 * is why the apply form sits on a navy band — that is the background it was built for).
 */

/* ── Hero ──────────────────────────────────────────────────────────────────────
   Text-only, no photo. The listings are the point of the page, and a hero image would push
   the roles below the fold on a phone — where most applicants are. */
.careers-hero{
  background:var(--navy);
  padding:120px 48px 84px;   /* extra top padding clears the 68px fixed nav */
  color:#fff;
}

/* ── Hero team photo ──────────────────────────────────────────────────────────
   The photo sits BEHIND the copy with a navy scrim over it, rather than beside it: at 402px
   a side-by-side would shrink it to a thumbnail, and the copy has to stay first in the
   reading order.

   isolation:isolate creates a new stacking context so the negative z-indexes below stay
   trapped inside the hero. Without it they render behind the page background and the photo
   disappears entirely — the classic z-index:-1 mistake.

   The scrim is not decoration. White body copy sits at rgba(255,255,255,0.62); over bare
   photography that fails contrast badly. The gradient keeps the text side near-solid navy and
   lets the photo read on the open side. */
.careers-hero.has-photo{position:relative;isolation:isolate;overflow:hidden;}

.careers-hero-media{
  /* Frame is FLUSH to the hero. An earlier attempt overscanned this box (top:-7%;bottom:-7%) to
     give the parallax somewhere to travel, but with nothing yet driving the transform it just
     cropped harder at both edges and cut the head off the man at the back of the group.
     The travel room belongs on the IMG inside this frame, not on the frame itself. */
  position:absolute;inset:0;z-index:-2;
  display:block;margin:0;overflow:hidden;
}
/* Promoted only while the parallax script is driving it — a permanent will-change keeps a
   compositor layer alive for an element that is static most of the time. */
.careers-hero-media.is-parallax img{will-change:transform;}
.careers-hero-media img{
  width:100%;height:100%;
  /* Cover, not contain: the hero height is driven by the copy, so the photo has to fill
     whatever box that produces. 'center 32%' keeps faces in frame as the box gets shorter —
     centring vertically crops heads off on a tall phone hero. */
  /* cover + object-position, and the position matters more than usual here.
     The source is 1600x900 (1.78) and the hero box on a phone is roughly 375x714 (0.53), so
     covering scales to the HEIGHT and shows only a narrow vertical slice of the middle. At
     32% that slice landed on the building and car park above the crew. 48% centres it on the
     group itself. */
  /* The group sits vertically centred in this panorama, so a plain centre crop is correct on
     desktop, where the hero box is much wider than tall and cover trims top and bottom. */
  /* 116% tall, top-biased. Two jobs at once:
       - the extra 16% is the parallax travel, so the transform never exposes an edge;
       - object-position 26% (not centre) keeps the BACK ROW in frame. The source is 2000x839
         (2.38) and the desktop hero box is nearer 2.9, so cover scales to width and trims height
         — a centred trim takes roughly equal bites off the top and bottom, and the top bite lands
         exactly on the heads of the people standing at the back. Biasing the crop upward spends
         that trim on tarmac instead. */
  height:116%;
  object-fit:cover;object-position:center 26%;
  /* Full opacity — the scrim below does ALL the dimming.
     Dimming in both places multiplies: an 0.55 image under an 0.93 scrim leaves roughly 4% of
     the photo visible, which rendered as flat navy and looked like a failed image load. One
     dimming layer, tuned once. */
}
.careers-hero.has-photo::before{
  content:"";position:absolute;inset:0;z-index:-1;
  /* Heaviest over the copy, opening up on the right so the crew is actually visible.
     Values chosen to keep body copy — rgba(255,255,255,0.62) — legible over photography;
     the text column never sits under less than 0.88. */
  /* Stops chosen against a measurement, not by eye: the copy column ends at 51% of the
     viewport at 1440. The previous ramp was still 0.55 opaque at 80%, so it went on darkening
     photo long after there was any text left to protect — half the crew disappeared.

     Now it holds 0.86+ to just past the text and then falls away hard. 0.08 at the right edge
     means the far side of the photo is essentially unveiled. */
  /* SECOND PASS. The ramp above still held 0.80 at 54% and 0.30 all the way to 74%, so the
     middle of the panorama — the banner and the left half of the crew — stayed under heavy navy
     long after the text had ended. Jesse, twice: "we can't see half the team."

     The text column is the only thing the scrim exists to protect, and it is measurable rather
     than a guess: .careers-hero-copy is 680px inside a 1440px .inner, so the copy ends at ~47%
     of the viewport at 1440 and ~48% at 2000. Everything past that is protecting nothing.

     So: hold enough for text to ~48%, then fall off hard instead of tapering. Peak also comes
     down from 0.93 to 0.86, which is affordable because the body copy's own opacity went up
     (see .careers-hero-body) — the two were fighting each other, and dimming the text was
     forcing the background darker to compensate. */
  /* THIRD PASS, and a different shape entirely.
     The first two were directional ramps protecting a left column. The copy is centred now, so
     the scrim is centre-weighted instead: a soft ellipse behind the text, over a very light
     uniform wash. Jesse, twice: "we can't see half the team" — the wash is 0.20, down from a
     0.93 peak, so the crew at both edges is essentially unveiled.
     Text stays readable via the ellipse plus the text-shadows above, rather than by drowning
     the photograph. */
  background:
    radial-gradient(ellipse 62% 78% at 50% 48%,
      rgba(22,16,68,0.52) 0%,
      rgba(22,16,68,0.34) 55%,
      rgba(22,16,68,0.10) 100%),
    linear-gradient(rgba(22,16,68,0.20), rgba(22,16,68,0.20));
}
.careers-hero .inner{max-width:var(--max);margin:0 auto;}
.careers-hero-copy{max-width:680px;}   /* prose measure — brand rule */

/* ── Centred hero, photo variant ────────────────────────────────────────────────
   Jesse's call, against the live Zoho page: "centered is fine as a hero for this imo."

   Centring is not just an alignment change here — it invalidates the old scrim. A 100deg
   directional gradient exists to protect a LEFT-HAND text column; with the copy centred it
   darkens the crew for no reason. Both had to change together.

   Measure goes a little wider than the 680px left-aligned version because centred text reads
   worse at a long measure but the headline needs room to sit on two lines rather than three. */
.careers-hero.has-photo .careers-hero-copy{
  max-width:820px;margin-inline:auto;text-align:center;
}
.careers-hero.has-photo .eyebrow{justify-content:center;}
.careers-hero.has-photo .careers-hero-cta{justify-content:center;}
/* Legibility insurance for the smaller copy. The scrim is now light enough to show the whole
   team, which means the body text can land on the bright signed banner in the middle of the
   photo — the worst-contrast spot on the image. A shadow costs nothing and rescues exactly that
   case without darkening the photo for everyone. */
.careers-hero.has-photo .careers-hero-sub,
.careers-hero.has-photo .careers-hero-body{
  text-shadow:0 1px 14px rgba(10,7,32,0.75), 0 1px 3px rgba(10,7,32,0.6);
}
.careers-hero.has-photo h1{text-shadow:0 2px 22px rgba(10,7,32,0.6);}
.careers-hero .eyebrow{color:var(--brown-light);}
.careers-hero h1{
  font-family:var(--heading);font-weight:800;
  font-size:clamp(32px,5vw,52px);line-height:1.08;letter-spacing:-0.01em;
  color:#fff;margin:0 0 18px;
}
/* The gold line is its own block so the two sentences read as two statements rather than one
   run-on headline. margin-top rather than a looser line-height on the h1: line-height would also
   space out the lines WITHIN each sentence, which at 52px would sprawl. em-based so the gap
   scales with the clamped font size instead of fixing at one viewport. */
.careers-hero h1 em{
  display:block;
  /* px, not em. This was 0.34em, which resolved against the INHERITED 52px while this element
     had no font-size of its own — so the moment it got one, the gap silently collapsed from
     ~11px to ~7px on mobile. An em-based margin on an element whose font-size you are about to
     change is a trap; an absolute value cannot move underneath you like that.
     Desktop value first (the file's idiom is max-width overrides), more room below 900px. */
  margin-top:10px;
  /* DM Sans, not the display face. Poppins is for titles; this is a secondary line and would
     inherit the h1's family unless told otherwise. It was also italic, which the single-weight
     display face has no true italic for — so the browser synthesised an oblique, which looked
     mechanical, and the brand mockup has this line upright anyway. Weight 400 because DM Sans at
     500 next to a 600-weight title reads heavy. */
  font-family:var(--sans);
  font-style:normal;font-weight:400;color:var(--brown-light);
  /* Its own font-size, which it did not have. It was inheriting the h1's 52px and only
     overriding family and weight — so the subordinate line was set at full display size and
     took 112px of the h1's 186px, making the secondary statement 1.5x the visual mass of the
     actual headline. Two blocks at the same size is not a hierarchy, and the bigger one was
     winning, which is why the header read as having no authority.

     30px is not a taste pick: it is the largest size at which this copy still sets on ONE line
     inside the 680px prose measure (it needs 656px; at 34px it needs 743px and wraps). A display
     line that wraps stops being a line and becomes a paragraph, which is the other half of the
     problem. Retighten this number if the copy length changes.

     Ratio to the headline is 0.58, and the vw term keeps it there as the h1 clamps down. */
  font-size:clamp(20px,2.9vw,30px);line-height:1.25;letter-spacing:0;
}
.careers-hero-sub{
  font-family:var(--sans);font-size:clamp(16px,2vw,19px);line-height:1.6;
  /* Raised from 0.78. These opacities were tuned against a much heavier scrim; once the scrim
     came down to show the crew, dim text over photography stopped being readable. Contrast is
     the constraint on how light the scrim can go, so the text gets the headroom instead. */
  color:rgba(255,255,255,0.90);margin:0 0 14px;
}
.careers-hero-body{
  font-family:var(--sans);font-size:15px;line-height:1.7;
  /* Raised from 0.62 — the weakest text in the hero and therefore the thing that was dictating
     how dark the background had to be. */
  color:rgba(255,255,255,0.80);margin:0 0 30px;
}
.careers-hero-cta{display:flex;gap:12px;flex-wrap:wrap;}
/* On navy, the outline button's dark border disappears — restore it in white. */
.careers-hero .btn-outline{border:1.5px solid rgba(255,255,255,0.28);color:#fff;background:none;}
.careers-hero .btn-outline:hover{border-color:#fff;background:rgba(255,255,255,0.08);}

/* ── Openings ─────────────────────────────────────────────────────────────── */

/* Gap must EXCEED the cards' own internal padding, or they stop reading as separate objects.
   This was 14px against 24px of card padding, which made four distinct roles look like one
   continuous slab — Jesse: "looks super close together".

   28px, not 22: the cards carry 24px of padding, and the hover state lifts one by 2px which eats
   into the gap above it. 22px satisfied neither, so the cards still read as a slab mid-hover. */
.role-list{
  display:grid;gap:28px;
  /* REGRESSION FIX. .section-sub (in style.css) carries no margin-bottom — it relied entirely on
     the "OTTAWA · 4 ROLES" count eyebrow that used to sit between it and this list, which
     contributed 17px of its own height plus 26px of margin. Removing that eyebrow removed 43px
     of stacking and nothing replaced it, so the first card's border ran straight into the last
     line of the intro paragraph on both mobile and desktop.
     Fixed here rather than by adding margin-bottom to .section-sub, because that rule is
     site-wide and other sections already space themselves after it — this is scoped to the one
     list that lost its spacer. */
  margin-top:40px;
}

/* One card per role. The whole card is the link rather than a "read more": it is one
   destination, and a full-width tap target is the difference between a phone applicant
   getting in and mis-tapping. */
.role-card{
  display:block;text-decoration:none;
  background:var(--white);
  border:1px solid var(--border-warm);
  border-radius:16px;
  padding:24px 26px;
  transition:transform 0.22s var(--ease),box-shadow 0.22s var(--ease),border-color 0.22s var(--ease);
}
.role-card:hover{
  transform:translateY(-2px);
  border-color:var(--brown);
  box-shadow:0 10px 28px rgba(11,8,42,0.08);
}
.role-card:focus-visible{outline:2px solid var(--brown);outline-offset:3px;}

.role-card-top{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;}
.role-card-title{
  font-family:var(--heading);font-size:19px;font-weight:700;
  color:var(--navy);margin:0 0 8px;line-height:1.25;
}
.role-card:hover .role-card-title{color:var(--brown-dark);}
.role-card-arrow{
  flex:0 0 auto;width:34px;height:34px;border-radius:50%;
  border:1.5px solid var(--border-warm);
  display:inline-flex;align-items:center;justify-content:center;
  color:var(--navy);transition:background 0.22s var(--ease),border-color 0.22s var(--ease),color 0.22s var(--ease);
}
.role-card:hover .role-card-arrow{background:var(--brown);border-color:var(--brown);color:#fff;}

/* Meta chips: location, type, pay, posted. Pills rather than a run-on line so they stay
   scannable when they wrap on a narrow phone. */
.role-meta{display:flex;flex-wrap:wrap;gap:7px;margin:0 0 12px;}
.role-chip{
  font-family:var(--sans);font-size:11.5px;font-weight:500;
  color:var(--text-2);background:var(--cream-raised);
  border-radius:999px;padding:4px 11px;white-space:nowrap;
}
.role-chip.is-pay{background:var(--brown-pale);color:var(--brown-dark);font-weight:600;}
.role-summary{
  font-family:var(--sans);font-size:14.5px;line-height:1.62;
  color:var(--text-2);margin:0;max-width:62ch;
}

/* Empty state. Reachable in production if RI returns zero open roles — without it the
   section renders as a heading over nothing, which reads as a broken page. */
.role-empty{
  background:var(--cream-raised);border:1px dashed var(--border-warm);
  border-radius:16px;padding:34px 28px;text-align:center;
}
.role-empty p{font-family:var(--sans);font-size:15px;color:var(--text-2);margin:0 0 6px;}

/* ── Talent network band ──────────────────────────────────────────────────────
   On Zoho this is a FAKE job posting sitting in the roles list. Here it is its own band, so
   it never appears as a vacancy and never carries JobPosting schema. */
.talent-band{
  background:var(--cream-raised);
  border:1px solid var(--border-warm);
  border-radius:16px;
  padding:34px 32px;
  display:flex;align-items:center;justify-content:space-between;gap:26px;flex-wrap:wrap;
}
.talent-band-copy{max-width:560px;}
.talent-band h2{
  font-family:var(--heading);font-size:22px;font-weight:700;
  color:var(--navy);margin:0 0 8px;
}
.talent-band p{font-family:var(--sans);font-size:14.5px;line-height:1.6;color:var(--text-2);margin:0;}

/* ── Role detail: centred hero + stat row ─────────────────────────────────────
   Replaces a left-aligned header plus a sticky right-hand facts panel. Four facts is not enough
   content to justify a sidebar, and the sidebar put compensation — the field applicants look
   for first — below the fold in a right-hand column. */
.role-head{background:var(--navy);padding:104px 48px 64px;color:#fff;}
.role-head .inner{max-width:var(--max);margin:0 auto;}

.role-back{
  display:inline-flex;align-items:center;gap:7px;
  font-family:var(--sans);font-size:11px;font-weight:700;letter-spacing:0.08em;
  text-transform:uppercase;color:rgba(255,255,255,0.55);text-decoration:none;
}
.role-back:hover{color:#fff;}

/* The hero column. max-width rather than a grid: everything inside is centred text, and a
   measure keeps the title from running to 1440px on a wide monitor. */
.role-head.is-centred .role-hero{
  max-width:820px;margin:0 auto;text-align:center;
  padding-top:38px;
}

.role-hero-eyebrow{
  font-family:var(--sans);font-size:12px;font-weight:700;letter-spacing:0.14em;
  text-transform:uppercase;color:var(--brown-light);margin-bottom:20px;
}

.role-head.is-centred h1{
  font-family:var(--heading);font-weight:800;
  /* Big, but capped: "Property Maintenance Crew Member" at 64px would run to four lines. */
  font-size:clamp(34px,5.6vw,62px);line-height:1.02;letter-spacing:-0.022em;
  color:#fff;margin:0 auto 34px;max-width:16ch;
  /* balance keeps a 3-word title from leaving one word alone on the last line. Progressive —
     browsers without it just wrap normally. */
  text-wrap:balance;
}

/* ── Stat row ──────────────────────────────────────────────────────────────────
   Four across on desktop with hairline dividers between, 2x2 on a phone. Built as a <dl> of
   dt/dd pairs rather than divs: these are genuinely name/value pairs, and a screen reader then
   announces "Compensation, CA$38 to CA$42 per hour" as one unit. */
.role-stats{
  display:grid;grid-template-columns:repeat(4,1fr);
  margin:0 0 36px;padding:0;
  border-top:1px solid rgba(255,255,255,0.14);
  border-bottom:1px solid rgba(255,255,255,0.14);
}
.role-stat{
  padding:22px 16px;
  /* Divider on the left of every cell except the first, so there is no trailing rule at the
     end of the row. box-shadow rather than border-left: a border would add width to three of
     four cells and break the 1fr alignment. */
  box-shadow:inset 1px 0 0 rgba(255,255,255,0.14);
}
.role-stat:first-child{box-shadow:none;}
.role-stats dt{
  font-family:var(--sans);font-size:12.5px;font-weight:700;
  letter-spacing:0.02em;color:#fff;margin:0 0 6px;
}
.role-stats dd{
  font-family:var(--sans);font-size:15px;line-height:1.4;
  color:rgba(255,255,255,0.66);margin:0;
}
/* Compensation is the row people scan for — gold, and it gets to be the loud one. */
.role-stat.is-pay dd{color:var(--brown-light);font-weight:600;}

.role-hero-cta .btn{min-width:200px;justify-content:center;}

/* ── Load-in ───────────────────────────────────────────────────────────────────
   Staggered rather than one block, so the eye lands on the title, then the facts, then the
   button — the order you want them read in. --i is set per element in the template.

   A CSS animation, NOT the site's .fade-up (which is IntersectionObserver-driven). This content
   is above the fold, and an observer for above-fold content is both pointless and fragile: if
   the observer never fires the element stays at opacity 0 and the page looks empty. An
   animation always completes.

   forwards holds the end state, so nothing snaps back after it runs. */
@keyframes role-in{
  from{opacity:0;transform:translateY(14px);}
  to{opacity:1;transform:none;}
}
.role-head.is-centred .role-hero > *{
  opacity:0;
  animation:role-in 0.62s var(--ease) forwards;
  animation-delay:calc(90ms * var(--i, 0) + 60ms);
}

/* Someone who has asked their OS for less motion gets the content, immediately, with no
   movement — and critically still gets opacity:1, which a naive prefers-reduced-motion block
   that only kills the animation would not. */
@media(prefers-reduced-motion:reduce){
  .role-head.is-centred .role-hero > *{
    animation:none;opacity:1;transform:none;
  }
}

/* ── Job copy ──────────────────────────────────────────────────────────────── */
/* Left-aligned within .inner, NOT centred.
   margin:0 auto put the job copy at 380px while the apply form below it and every other
   section on the site sit at 48px — the same 680px measure, two different left edges, which
   read as two different pages stacked. Matches .apply-wrap exactly now, so the JD, the form
   and the rest of V2 share one left margin.
   The hero above stays centred on purpose: that is a deliberate feature block, not body copy. */
.role-body{max-width:680px;}   /* prose measure, left-aligned in .inner */

.role-prose h2,
.role-prose h3{
  font-family:var(--heading);font-weight:700;color:var(--navy);
  font-size:19px;line-height:1.3;margin:34px 0 12px;
}
/* The section-opening h2 is a size up from the h3s inside the copy, so the outline reads
   visually as well as semantically. */
.role-prose > h2:first-child{
  font-size:clamp(22px,2.6vw,27px);letter-spacing:-0.01em;margin:0 0 18px;
}
.role-prose h2:first-child,
.role-prose h3:first-child{margin-top:0;}
.role-prose p{font-family:var(--sans);font-size:15.5px;line-height:1.72;color:var(--text-2);margin:0 0 14px;}
.role-prose ul,
.role-prose ol{margin:0 0 18px;padding-left:22px;}
.role-prose li{
  font-family:var(--sans);font-size:15.5px;line-height:1.68;color:var(--text-2);
  margin-bottom:8px;padding-left:2px;
}
.role-prose li::marker{color:var(--brown);}
.role-prose strong{color:var(--navy);font-weight:600;}
.role-prose a{color:var(--brown-dark);text-decoration:underline;text-underline-offset:2px;}

.role-section{border-top:1px solid var(--border-warm);margin-top:36px;padding-top:32px;}

/* ── Apply form ───────────────────────────────────────────────────────────────
   Navy band so it inherits .v2-field verbatim from style.css — that treatment is built for
   white-on-navy, and reusing it is what keeps this form visually identical to the site's
   contact form rather than a lookalike. */
.apply-band{background:var(--navy);padding:84px 48px;}
.apply-band .inner{max-width:var(--max);margin:0 auto;}
.apply-wrap{max-width:680px;}
.apply-band h2{
  font-family:var(--heading);font-size:clamp(24px,3.2vw,32px);font-weight:800;
  color:#fff;margin:0 0 10px;letter-spacing:-0.01em;
}
.apply-band .eyebrow{color:var(--brown-light);}
.apply-intro{font-family:var(--sans);font-size:15px;line-height:1.65;color:rgba(255,255,255,0.7);margin:0 0 30px;}

.apply-grid{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-bottom:18px;}
.apply-grid .is-full{grid-column:1 / -1;}
.v2-field .req{color:var(--brown-light);}
.v2-field .hint{
  font-family:var(--sans);font-size:11.5px;font-weight:400;
  color:rgba(255,255,255,0.45);margin-top:2px;
}

/* File input. Native file controls cannot be styled consistently, so the real input is
   visually hidden but stays FOCUSABLE and in the accessibility tree (clip, not display:none,
   which would remove it entirely) and a styled label drives it. */
.apply-file{
  border:1px dashed rgba(255,255,255,0.24);border-radius:8px;
  padding:18px;text-align:center;transition:all 0.2s var(--ease);
}
.apply-file.is-over{border-color:var(--brown-light);background:rgba(167,120,69,0.12);}
.apply-file input[type=file]{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
}
.apply-file label{
  display:inline-flex;align-items:center;gap:8px;cursor:pointer;
  font-family:var(--sans);font-size:11.5px;font-weight:700;letter-spacing:0.06em;
  text-transform:uppercase;color:#fff;
  background:rgba(255,255,255,0.1);border:1px solid rgba(255,255,255,0.2);
  border-radius:8px;padding:10px 18px;
}
.apply-file label:hover{background:rgba(255,255,255,0.18);}
/* Focus must show on the LABEL when the hidden input has focus, or keyboard users cannot
   tell the control is selected. */
.apply-file input[type=file]:focus-visible + label{outline:2px solid var(--brown-light);outline-offset:2px;}
.apply-file-name{
  display:block;font-family:var(--sans);font-size:12.5px;
  color:rgba(255,255,255,0.6);margin-top:10px;
}
.apply-file-name.is-set{color:var(--brown-light);font-weight:600;}

/* Honeypot. Invisible to humans but NOT display:none — some bots skip hidden fields.
   Screen readers are told to ignore it via aria-hidden + tabindex="-1" in the markup. */
.apply-hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden;}

.apply-actions{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-top:24px;}
.apply-submit{position:relative;min-width:190px;justify-content:center;}
.apply-submit[disabled]{opacity:0.6;cursor:not-allowed;}
.apply-spinner{
  width:14px;height:14px;border-radius:50%;
  border:2px solid rgba(255,255,255,0.35);border-top-color:#fff;
  animation:apply-spin 0.7s linear infinite;display:none;
}
.apply-submit.is-busy .apply-spinner{display:inline-block;}
@keyframes apply-spin{to{transform:rotate(360deg);}}

/* AI disclosure — required next to the submit button. Deliberately legible rather than fine
   print: it is a compliance statement, not a footnote. */
.apply-disclosure{
  font-family:var(--sans);font-size:12.5px;line-height:1.6;
  color:rgba(255,255,255,0.58);
  border-left:2px solid var(--brown);padding-left:14px;margin:26px 0 0;max-width:60ch;
}

.apply-error{
  font-family:var(--sans);font-size:13.5px;line-height:1.55;color:#fff;
  background:rgba(214,69,69,0.18);border:1px solid rgba(214,69,69,0.45);
  border-radius:8px;padding:12px 14px;margin-top:18px;
}
.apply-error:empty{display:none;}

/* Success. Replaces the form in place rather than redirecting — a redirect on a static host
   loses which role was applied for, and a page load after a slow upload reads as failure. */
.apply-done{
  background:rgba(255,255,255,0.06);border:1px solid rgba(167,120,69,0.5);
  border-radius:16px;padding:34px 32px;
}
.apply-done h2{margin:0 0 10px;}
.apply-done p{font-family:var(--sans);font-size:15px;line-height:1.65;color:rgba(255,255,255,0.74);margin:0 0 8px;}
.apply-done .btn{margin-top:18px;}

/* ── Mobile ───────────────────────────────────────────────────────────────────
   Single column, facts BEFORE the job copy (order:-1) so pay and location come first.
   Every input stays 16px+ or iOS zooms on focus — checked by dev/test-css-traps.php. */
@media(max-width:900px){
  /* More air between the headline and the gold line on phones and iPad portrait. At these
     widths the headline itself wraps to 2+ lines, so the two statements sit closer together
     optically than the same margin implies on desktop. */
  .careers-hero h1 em{margin-top:18px;}

  /* 2x2, which is why the divider is drawn with an inset shadow on the left of every cell
     except the first-of-row rather than a plain border — see the desktop rule. */
  .role-stats{grid-template-columns:1fr 1fr;}
  .role-stat:nth-child(odd){box-shadow:none;}
  .role-stat:nth-child(n+3){box-shadow:inset 1px 0 0 rgba(255,255,255,0.14);}
  .role-stat:nth-child(3){box-shadow:inset 0 1px 0 rgba(255,255,255,0.14);}
  .role-stat:nth-child(4){box-shadow:inset 1px 0 0 rgba(255,255,255,0.14), inset 0 1px 0 rgba(255,255,255,0.14);}

  /* The sticky "I'm interested" pill is the mobile CTA — see the note on the same breakpoint
     further up. Two identical controls a scroll apart is the bug this avoids. */
  .role-hero-cta{display:none;}
}
@media(max-width:768px){
  .careers-hero{padding:100px 24px 60px;}
  /* Copy spans the full width here, so a left-to-right scrim protects nothing. Switch to a
     vertical one that is heaviest where the text is and lightest at the base. */
  /* ── Mobile: the photo becomes a BAND, not a background ─────────────────────
     A background does not work at this aspect ratio and no object-position can rescue it.
     The source is 16:9; the hero box on a phone is roughly 0.53:1. object-fit:cover scales to
     the height, so it renders the FULL height of a strip about 30% of the image's width — and
     the vertical object-position has no overflow left to move, which is why nudging it did
     nothing. You get a random vertical slice: in this shot, a building and a car park.

     So on a phone the photo sits below the copy as a full-bleed 16:9 band. The whole crew is
     visible, uncropped, at the size it was shot for. Copy stays on solid navy above it, which
     also removes the contrast problem the scrim existed to solve. */
  .careers-hero.has-photo{display:flex;flex-direction:column;}
  .careers-hero .inner{order:1;}
  .careers-hero-media{
    order:2;position:static;
    /* Cancel the section's 24px side padding so the band reaches both edges. */
    margin:34px -24px -60px;
    /* The photo's own ratio, not 16/9. It is a 2000x839 panorama (2.38); forcing 16/9 would
       crop roughly a quarter of the width off — and on a group shot that means cropping
       people out at both ends. Written as the pixel pair so it stays obviously tied to the
       asset if the asset is ever replaced. */
    aspect-ratio:2000 / 839;
    width:auto;
  }
  .careers-hero-media img{height:100%;object-position:center 50%;}
  /* No scrim: nothing sits on top of the photo any more. */
  .careers-hero.has-photo::before{display:none;}
  /* padding-bottom:0 — with the hero CTA hidden on mobile, 44px of navy sat below the stat row
     with nothing in it. The navy-to-cream colour change is the section boundary, so the stat
     row's own bottom border is redundant too: two horizontal lines a few pixels apart read as a
     mistake. Drop the border, let the colour change close the block. */
  .role-head{padding:92px 22px 0;}
  .role-head.is-centred .role-hero{padding-top:26px;}
  .role-head.is-centred h1{max-width:none;margin-bottom:26px;}
  .role-stats{margin-bottom:0;border-bottom:0;}
  .role-stat{padding:18px 12px 22px;}
  .apply-band{padding:60px 24px;}
  .apply-grid{grid-template-columns:1fr;gap:14px;}
  .role-card{padding:20px;}
  .talent-band{padding:26px 22px;}
  .role-facts{padding:20px 20px 6px;}
}

/* ── Stepped apply form ────────────────────────────────────────────────────────
   Pattern borrowed (numbered dots on a filling track), colours and proportions are NorthCo's.
   The reference used #3498db on white; this sits on the navy apply band.

   AUDIENCE NOTE: this page serves applicants, roughly 18-45 — not the 40-65 homeowner ICP the
   rest of the site is tuned around. Stepped flows are familiar to that group. The same pattern
   would be the wrong call on the estimator, which faces clients. */

.apply-progress{
  /* Not display:none by default — see .apply-step. Only the JS-driven state hides things, so a
     no-JS form still renders every field. The bar itself is harmless when stepping is off. */
  display:none;
  position:relative;
  justify-content:space-between;align-items:center;
  list-style:none;margin:0 0 30px;padding:0;
  max-width:360px;
}
form.is-stepped .apply-progress{display:flex;}

/* Track and fill are absolutely positioned siblings rather than ::before/::after on the <ol>,
   because the fill has to animate its width independently of the track. z-index keeps both
   behind the dots without needing negative z-index (which would escape to the page background
   the way the hero photo did). */
.apply-progress-track,
.apply-progress-fill{
  position:absolute;top:50%;left:0;transform:translateY(-50%);
  height:3px;border-radius:2px;
}
.apply-progress-track{width:100%;background:rgba(255,255,255,0.16);z-index:0;}
.apply-progress-fill{
  width:0;background:var(--brown);z-index:1;
  transition:width 0.42s var(--ease);
}

.apply-dot{
  position:relative;z-index:2;
  width:34px;height:34px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  font-family:var(--sans);font-size:13px;font-weight:700;
  /* Solid, not translucent: the fill runs behind these, and a see-through dot would show the
     bar through the number. */
  background:var(--navy);color:rgba(255,255,255,0.45);
  border:2.5px solid rgba(255,255,255,0.18);
  transition:all 0.32s var(--ease);
}
.apply-dot.is-active{border-color:var(--brown);color:#fff;}
/* A completed step reads as done rather than current — filled, not just outlined. */
.apply-dot.is-done{background:var(--brown);border-color:var(--brown);color:#fff;}

.apply-stepnote{
  display:none;
  font-family:var(--sans);font-size:11px;font-weight:700;
  letter-spacing:0.1em;text-transform:uppercase;
  color:var(--brown-light);margin:0 0 18px;
}
form.is-stepped .apply-stepnote{display:block;}

/* Steps are visible until JS says otherwise. This is the progressive-enhancement hinge: get it
   backwards and a JS failure leaves a form nobody can complete. */
.apply-step{border:0;margin:0 0 18px;padding:0;min-width:0;}
.apply-step legend{
  font-family:var(--sans);font-size:17px;font-weight:700;
  color:#fff;padding:0;margin:0 0 16px;
}
form.is-stepped .apply-step{display:none;}
form.is-stepped .apply-step.is-current{display:block;}
/* The legend is redundant once the progress bar and step note say the same thing. */
form.is-stepped .apply-step legend{
  position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;
}

.apply-nav{min-width:120px;justify-content:center;}
/* On navy the outline button's dark border vanishes. */
.apply-band .btn-outline{border:1.5px solid rgba(255,255,255,0.3);color:#fff;background:none;}
.apply-band .btn-outline:hover{border-color:#fff;background:rgba(255,255,255,0.08);}
.apply-band .btn-outline[disabled]{opacity:0.4;cursor:not-allowed;}

@media(max-width:768px){
  .apply-progress{max-width:100%;}
  .apply-nav{flex:1 1 40%;min-width:0;}
  .apply-submit{flex:1 1 100%;}
}

/* ── Apply-band eyebrow must not wrap ─────────────────────────────────────────
   "LET'S GO!" broke to "LET'S / GO!" on desktop. .eyebrow is an inline-flex whose text is an
   anonymous flex item, so it shrinks and wraps whenever the flex line is under pressure — and
   at 0.14em tracking the break is glaring.

   style.css already solves this with .eyebrow.nowrap, but that would mean remembering to add a
   class at every call site. The apply band has exactly one eyebrow and it is always short, so
   it is set here once instead. flex:0 0 auto stops the text item shrinking; the ::before dash
   keeps its own fixed basis. */
.apply-band .eyebrow{white-space:nowrap;display:flex;}
.apply-band .eyebrow::before{flex:0 0 32px;}

/* Turnstile's own iframe carries its sizing; this just gives it room and keeps the layout from
   jumping when the widget appears on step 4. min-height matches the widget's rendered height so
   the Submit button does not shift down as it loads. */
.apply-captcha{margin:4px 0 2px;min-height:66px;}

/* ── [hidden] must actually hide ────────────────────────────────────────────────
   The UA stylesheet's [hidden]{display:none} is a (0,1,0) author-weight-equivalent rule that
   ANY author display declaration beats — and .btn sets display:inline-flex. So hidden="hidden"
   on Back/Next/Submit did nothing: Submit rendered on step 1, and the no-JS fallback would have
   shown Back and Next with no handlers attached.

   Scoped selector rather than !important: .apply-actions [hidden] is (0,2,0) and wins on
   specificity, which is easier to reason about later than an override. */
.apply-actions [hidden]{display:none;}

/* ── Openings → talent band spacing ───────────────────────────────────────────
   These two sections were 160px apart (.section's 96px bottom + .section.tight's 64px top),
   which read as the page having ended. The talent band is not a new topic — it is the "no fit?"
   answer to the list directly above it, so it should sit close enough to be read as a reply.

   Scoped to the ids rather than changing .section, which is used site-wide. */
#openings{padding-bottom:48px;}
#talent{padding-top:0;}

@media(max-width:768px){
  #openings{padding-bottom:32px;}
}

/* ── Content indent on the role page ──────────────────────────────────────────
   .section already supplies the site's 48px gutter; this adds 40px on top so the reading column
   sits off the viewport edge rather than hard against it.

   Applied to BOTH the job copy and the apply form, together, so the single left margin they now
   share is preserved — indenting one would undo the alignment fix. Desktop only: at phone widths
   there is no room to give away, and .section drops to 22-24px there for the same reason. */
@media(min-width:901px){
  .role-body,
  .apply-band .apply-wrap{margin-left:40px;}
}

/* The "Description" eyebrow sits tighter to its heading than a standalone section eyebrow. */
.role-prose > .eyebrow{margin-bottom:10px;}

/* ── Opaque nav over the navy careers heroes ──────────────────────────────────
   The nav is normally rgba(250,247,242,0.88) with a backdrop blur, which is right over
   photography — it picks up whatever is beneath and reads as frosted glass. Over a flat navy
   hero it just picks up navy and goes muddy grey, losing the crisp cream-on-navy edge.

   Both careers heroes are flat navy, so on these two templates the nav is solid. The
   border-bottom already provides separation once the page scrolls onto cream, so nothing is lost
   by dropping the transparency here.

   -webkit- prefix included: without it Safari keeps blurring and the fix appears not to work
   on exactly the browser most of this audience uses. */
body.page-template-careers .nav,
body.page-template-careers-role .nav{
  background:var(--cream);
  backdrop-filter:none;
  -webkit-backdrop-filter:none;
}

/* ── Standalone careers bar (Netlify build only) ───────────────────────────────
   The V2 nav is a flex row with three CTAs and a mega menu; this is a logo and nothing else, so
   it needs its own sizing rather than inheriting a layout built for content it does not have.
   Shorter than the 68px site nav because it carries no buttons — but html{scroll-padding-top}
   is still 84px, which only over-scrolls anchors slightly and is harmless. */
.nav.nav-minimal{
  height:64px;
  justify-content:flex-start;
  background:var(--cream);
  backdrop-filter:none;
  -webkit-backdrop-filter:none;
  border-bottom:1px solid var(--border-warm);
}
.nav.nav-minimal .nav-logo{display:inline-flex;align-items:center;}

/* The heroes sit under a fixed bar, so their top padding has to match its height. 64 + breathing
   room, rather than the 68px nav's allowance. */
.nav-minimal ~ main .careers-hero{padding-top:112px;}
.nav-minimal ~ main .role-head{padding-top:96px;}
@media(max-width:768px){
  .nav-minimal ~ main .careers-hero{padding-top:96px;}
  .nav-minimal ~ main .role-head{padding-top:88px;}
}

/* ── Buttons are UI, not titles ────────────────────────────────────────────────
   Poppins is the title face; everything else is DM Sans. .btn lives in style.css and is used on
   every page, so this override is SCOPED to the careers templates rather than changed globally —
   the homepage has already been signed off and should not be restyled as a side effect of a
   careers decision.

   If the rule is meant site-wide, the correct fix is one line in style.css (.btn font-family →
   var(--sans)) and this block gets deleted. Flagged rather than assumed. */
body.page-template-careers .btn,
body.page-template-careers-role .btn,
/* Footer column labels (MENU / SERVICES / CONTACT) are labels, not titles — same rule, and
   also in the shared footer, so also scoped rather than changed globally. */
body.page-template-careers .footer-col-title,
body.page-template-careers-role .footer-col-title{
  font-family:var(--sans);
  font-weight:700;
  letter-spacing:0.05em;   /* 0.07em was tuned for the display face; loose for DM Sans here */
}

/* ── Skill keyword chips (talent-network form) ──────────────────────────────────
   Hidden in the markup and revealed by careers.js, so a no-JS page shows the plain text input
   with no empty container above it. */
.skill-tags{
  display:flex;flex-wrap:wrap;gap:8px;margin-bottom:10px;
}
.skill-tag{
  display:inline-flex;align-items:center;gap:7px;
  font-family:var(--sans);font-size:13px;font-weight:500;line-height:1;
  padding:8px 10px 8px 12px;border-radius:999px;
  background:var(--brown-pale);color:var(--brown-dark);
  border:1px solid rgba(167,120,69,0.28);
}
.skill-tag-x{
  /* 24px hit area at 13px type — this is a phone-first form and a 10px x is unhittable.
     The visual chip stays small; the button just overhangs its own glyph. */
  appearance:none;border:0;background:none;cursor:pointer;
  width:18px;height:18px;display:inline-flex;align-items:center;justify-content:center;
  font-family:var(--sans);font-size:16px;line-height:1;
  color:var(--brown-dark);opacity:0.62;border-radius:50%;
  padding:0;
}
.skill-tag-x:hover{opacity:1;background:rgba(167,120,69,0.16);}
.skill-tag-x:focus-visible{outline:2px solid var(--brown);outline-offset:1px;opacity:1;}

/* ── Minimal footer (standalone careers build) ──────────────────────────────────
   The full .footer grid is a 4-column sitemap; this is a single row. Reuses .footer's own
   background and .footer-bottom/.fcopy so the two versions cannot drift apart visually. */
.footer-minimal .footer-min-inner{
  max-width:var(--max);margin:0 auto;
  display:flex;align-items:center;justify-content:space-between;gap:24px;flex-wrap:wrap;
  padding:38px 0 30px;
}
.footer-minimal .footer-min-brand{display:inline-flex;align-items:center;}
.footer-minimal .footer-min-links{
  display:flex;align-items:center;gap:26px;flex-wrap:wrap;
  font-family:var(--sans);font-size:14px;
}
@media(max-width:700px){
  /* Stacked and left-aligned on a phone — the row version centres the logo over the links,
     which reads as a broken grid rather than a deliberate layout. */
  .footer-minimal .footer-min-inner{
    flex-direction:column;align-items:flex-start;gap:20px;padding:30px 0 24px;
  }
  .footer-minimal .footer-min-links{gap:14px 20px;}
}
