/* ============================================================================
   IDEAL overrides on the Industrial template.
   Everything here is a deliberate departure, numbered against the change
   register in the project: claude/ideal-change-register.md
   Loads after css/ideal-tokens.css, which holds the design system.
   ========================================================================= */

/* --- 1 + 88: the dark band. Uppercase, a step smaller, and both sides centred
   in it. The email row was 30px tall inside a 32px band and sat flush to the
   top, which is what made the right side look unbalanced. ------------------ */
.topbar{padding:0;font-size:var(--text-micro);line-height:1.4;
  letter-spacing:.1em;text-transform:uppercase}
/* Bootstrap's .row clearfix pseudos become flex items and break the split */
.topbar .container>.row:before,.topbar .container>.row:after{display:none}
.topbar .container>.row{display:flex;align-items:center;
  justify-content:space-between;min-height:34px}
.topbar .top-aside,.topbar .top-aside>*{float:none;line-height:1;max-width:none}
.topbar .top-nav,.topbar .top-contact{display:flex;align-items:center;margin:0}
.topbar .top-nav>li>a,.topbar .top-nav li a,.topbar .top-contact>li,
.topbar .top-contact span,.topbar .top-contact span a,
.topbar .top-contact .t-email span,.topbar .top-contact .t-phone span{
  font-size:var(--text-micro);line-height:1.4;padding-top:0;padding-bottom:0}
.topbar .top-contact>li:not(:last-child){margin-right:var(--space-4)}
.topbar .top-contact li{display:flex;align-items:center}
.topbar .top-contact .fa,.topbar.style-dark .top-contact .fa{
  font-size:12px;line-height:1;width:auto;height:auto;border:0;
  margin-right:var(--space-1);float:none}
/* 46 (replaces 39): the header band takes the company profile's own dark.
   Pages 2, 3 and 5 of the profile are 47 to 50 percent #181810, so charcoal is
   what IDEAL actually looks like in print. This reverses the no dark grounds
   rule from walkthrough 1, deliberately. White type at 16.66:1, and the icons
   take the profile's bright green, which only clears AA on a ground this dark. */
.topbar,.topbar.style-dark{background:var(--brand-anchor);color:#fff;
  border-bottom:none}
.topbar a,.topbar.style-dark a,.topbar .top-nav li a,.topbar .top-contact span,
.topbar .top-contact span a{color:#fff}
.topbar a:hover,.topbar a:focus,.topbar .top-nav li a:hover,
.topbar .top-contact span a:hover{color:var(--accent-on-dark);
  text-decoration:none}
/* .topbar.style-dark .top-contact .fa sets currentColor and outranks a plain
   .topbar .fa, so it is named here. */
.topbar .fa,.topbar.style-dark .top-contact .fa,
.topbar .social li a,.topbar.style-dark .social li a{
  color:var(--accent-on-dark);border-color:var(--accent-on-dark)}

/* --- 2: logo comes down, header gains vertical air ------------------------ */
.site-header .navbar-brand img{max-height:36px}
.site-header .navbar-brand{padding-top:var(--space-3);padding-bottom:var(--space-3)}
/* 47: the white band. The nav list was sitting flush to the top of an 86px band
   with the slack all underneath, and the five menu items were bunched into the
   right half with a 420px dead gap between the logo and Home. The row is now a
   flex line centred in the band, and the menu spreads across the space it has
   instead of hugging the button. Floats off, because Bootstrap 3 floats cannot
   centre or distribute. */
@media (min-width:992px){
  /* Bootstrap's .nav:before/:after clearfix become flex items and steal the last
     slot from space-between, which is why the button stopped short of the edge. */
  .navbar-primary .container:before,.navbar-primary .container:after,
  .navbar-nav:before,.navbar-nav:after,
  .navbar-collapse:before,.navbar-collapse:after{display:none}
  /* zero the gutter so the logo and the button land on 135 and 1305, the same
     edges every other block on the page sits on */
  .navbar-primary .container{display:flex;align-items:center;
    justify-content:space-between;min-height:96px;padding-left:0;padding-right:0}
  .navbar-primary .container>.navbar-brand{flex:0 0 auto;margin:0;
    padding-top:0;padding-bottom:0;height:auto}
  .navbar-primary .container>.navbar-header{display:none}
  .navbar-primary .container>.navbar-collapse{flex:1 1 auto;display:flex;
    justify-content:flex-end;padding:0;margin:0;border:0}
  /* 77: the menu packs to the right, sitting just before the button, rather
     than distributing across the whole band. The air falls between the logo and
     the menu, not between the menu items. */
  .navbar-nav{display:flex;align-items:center;float:none;margin:0;
    flex:1 1 auto;justify-content:flex-end;gap:var(--space-8);padding-left:0}
  .navbar-nav>li{float:none;display:block}
  .navbar-nav>li>a:not(.btn){padding-top:var(--space-2);
    padding-bottom:var(--space-2);line-height:1.4}
  /* .header-s1 .navbar-nav > li.quote-btn carries margin-top:16px, which is
     what was pushing the button 8px below the centre line. */
  .navbar-nav>li.quote-btn,
  .header-s1 .navbar-nav>li.quote-btn{margin-left:var(--space-12);margin-top:0;
    display:flex;align-items:center}
  .navbar-nav>li.quote-btn .btn{margin:0}
}

/* 40: the well of white under the header. It was 40px of hero padding plus the
   slack that justify-content:center was splitting evenly above and below. The
   pictures now start 10px under the header and the text block centres itself in
   whatever is left, so the space falls below the headline, not above the photos. */
.navbar-primary{padding-bottom:0}
/* 86 (replaces 44): the header action is a filled green button again.
   .navbar-nav > li.quote-btn .btn strips the border, so it is named here too. */
.site-header .quote-btn .btn,
.site-header .navbar-nav>li.quote-btn .btn{background-color:var(--brand-accent);
  border:2px solid var(--brand-accent);color:#fff;
  /* 119: the compact size, off the one scale, not its own padding pair */
  font-size:var(--text-eyebrow);font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;padding:var(--space-2) var(--space-3);
  width:200px;min-height:48px;
  /* 150a: .navbar-nav > li > a makes this a block, so align-items and
     justify-content on .btn were inert and the label sat on the padding box,
     measured 5px above the centre of its own button and 5px above the nav
     baseline. Named here, where the selector is specific enough to win. */
  display:inline-flex;align-items:center;justify-content:center;line-height:1.2}
/* 150a: the hover was written to beat the template's #4A7020 and beat it with
   the same colour, so the button did not respond to the pointer at all.
   It also could not have won if it had tried: the rest state above is
   .site-header .navbar-nav>li.quote-btn .btn, four classes and a type, and a
   hover written as .site-header .quote-btn .btn:hover is four classes and no
   type, so the rest state outranked it. The hover now carries the same
   selector shape plus the pseudo class. */
.site-header .quote-btn .btn:hover,.site-header .quote-btn .btn:focus,
.site-header .navbar-nav>li.quote-btn .btn:hover,
.site-header .navbar-nav>li.quote-btn .btn:focus{
  background-color:var(--state-hover);color:#fff;border-color:var(--state-hover)}

/* --- 3 + 23 + 25 to 29: hero. On load the visitor sees the photographs, the
     headline and a downward cue. The buttons sit immediately past the fold, so
     the first scroll lands on them and the next on the client logos.
     Rail slides are shorter and sized so 2.5 show across the viewport. ------- */
.hero2{min-height:calc(100svh - var(--header-h));display:flex;flex-direction:column;
  justify-content:flex-start;padding:var(--space-2) 0 var(--space-18);position:relative}
.hero2>.container{margin-top:auto;margin-bottom:auto}
.hero2-rail{overflow:hidden;margin-bottom:var(--space-8)}
/* 36: the Elite geometry. Three frames across, flush, no gutter and no radius,
   running the full width of the page. The centre frame is twice the width of
   either flank, so one picture leads and two frame it. Widths animate, which is
   what moves the lead from frame to frame; the track only translates to keep the
   centre centred. With flanks at 25% and the centre at 50%, the translation for
   the frame at index i is exactly (i - 1) x 25% of the rail. */
.hero-track{display:flex;list-style:none;margin:0;padding:0;gap:0;
  transition:transform var(--t-slow) var(--ease);will-change:transform}
.hero-slide{flex:0 0 25%;max-width:25%;height:var(--rail-h);
  overflow:hidden;border-radius:0;background:var(--surface-soft);
  transition:flex-basis var(--t-slow) var(--ease),max-width var(--t-slow) var(--ease)}
.hero-slide.is-centre{flex:0 0 50%;max-width:50%}
.hero-slide img{width:100%;height:100%;object-fit:cover;display:block}
/* 171a: the flanks sit back so the eye lands on the centre frame. Scoped to
   rails that HAVE a centre frame. Item 151 replaced the hero's step carousel
   with a drift, which has no centre, so every hero frame was matching
   :not(.is-centre) and the whole rail was running at saturate(.82)
   brightness(.9) with nothing to be dimmer than. That is the fade. */
.hero2-rail:not(.drift-rail) .hero-slide:not(.is-centre) img{
  filter:saturate(.82) brightness(.9);
  transition:filter var(--t-slow) var(--ease)}

/* 26: headline uppercase and balanced across its lines */
.hero2-text{max-width:900px;margin:0 auto;text-align:center}
/* 37 + 41: the page title. Wider measure so it sets on two lines rather than
   three, one step up the scale, and the weight that stops it reading plain. */
.hero-h1{text-transform:uppercase;letter-spacing:.005em;text-wrap:balance;
  font-size:var(--text-display);max-width:860px;line-height:1.08;
  font-weight:600;margin:0 auto var(--space-4);color:var(--text-primary)}
@media (max-width:991px){.hero-h1{font-size:var(--text-h2);max-width:18ch;line-height:1.14}}

/* 85 (replaces 27): the eyebrow returns above the headline, white caps on the
   brand green rather than green type on white. White measures 5.79:1 on the
   accent. The countries line goes; the chip and the footer offices carry it. */
.hero-chip{display:inline-block;width:auto;margin:0 0 var(--space-4);
  /* 150b: the chip was charcoal on #DCDFD4. Every other eyebrow on the site is
     brand green, and green on #DCDFD4 measures 4.28:1, which fails. On the
     profile's own sand, #E8E8D8, the same green measures 4.67:1 and passes, so
     the chip moves to the sand and joins the rule the rest of the site follows. */
  background:var(--surface-band);color:var(--brand-accent);font-family:var(--font-body);
  font-size:var(--text-micro);font-weight:600;letter-spacing:.2em;
  text-transform:uppercase;padding:var(--space-1) var(--space-3) 3px;
  line-height:1.4;max-width:none}
.hero-chip i{font-style:normal;opacity:.6;padding:0 var(--space-1)}

/* 29: the cue, drawn from the chevron inside the IDEAL mark */
.hero-cue{position:absolute;left:50%;bottom:var(--space-6);width:24px;height:21px;
  margin-left:-12px;display:block;line-height:0;animation:heroCue 2.6s ease-in-out infinite}
.hero-cue svg{width:100%;height:100%;display:block;fill:var(--brand-accent)}
.hero-cue:hover svg{fill:var(--state-hover)}
@keyframes heroCue{0%,100%{transform:translateY(0)}50%{transform:translateY(8px)}}

/* 28: the two buttons carry equal width and sit centred, just past the fold */
.hero-cta-strip{padding:var(--space-12) 0 var(--space-6)}
.hero2-cta{display:flex;gap:var(--space-3);justify-content:center;flex-wrap:wrap}
.hero2-cta .btn{flex:0 0 300px;width:300px;max-width:300px;min-height:58px;
  text-align:center}

@media (max-width:767px){
  :root{--rail-h:clamp(190px,26svh,250px)}
  .hero2{--rail-h:clamp(190px,26svh,250px)}
  .hero-slide{flex:0 0 22%;max-width:22%}
  .hero-slide.is-centre{flex:0 0 56%;max-width:56%}
  .hero2{min-height:auto;padding-bottom:var(--space-12)}
  .hero-cue{display:none}
  .hero-cta-strip{padding:var(--space-8) 0}
  .hero2-cta .btn{flex:1 1 100%;width:100%;max-width:100%}
}
/* 35: on a short laptop screen the rail has to give way, or the buttons stop
   landing at the fold. Measured on 1366 by 768, where 46svh overshot by 67px. */
@media (min-width:768px) and (max-height:860px){
  /* the hero has to give way on a short screen or the buttons stop landing on
     the fold, and the other two rails follow so all three stay equal */
  :root{--rail-h:clamp(240px,40svh,360px)}
  .hero2{--rail-h:clamp(240px,40svh,360px)}
  .hero-slide.is-centre{flex:0 0 50%;max-width:50%}
  .hero2{padding-bottom:var(--space-12)}}
@media (prefers-reduced-motion:reduce){
  .hero-track{transition:none}.hero-cue{animation:none}}

/* --- 47: the client wall ---------------------------------------------------
   Thirteen marks, every one reduced to a single ink on transparent and sized by
   equal optical area rather than equal height, so a long lockup and a round
   crest carry the same weight. The track holds two identical copies and travels
   exactly half its own width, which is why the loop closes on an identical
   frame and shows no break. ------------------------------------------------ */
/* 81: the row was 58px inside a 236px band, a quarter of the room it sat in.
   Logos up about 40 percent and the dead air taken out, so they go from a
   quarter of the band to roughly two fifths. */
.client-strip{padding:var(--space-6) 0 var(--space-8)}
/* 71: the eyebrow was only centring its text inside a 40ch box that stayed
   pinned to the container's left edge, which is why TRUSTED BY sat 372px left
   of the logos it belongs to. The box itself has to centre. */
.center{text-align:center}
.heading-sm-lead.center{margin-left:auto;margin-right:auto;text-align:center}
.client-strip .heading-sm-lead{margin-bottom:var(--space-3)}
.client-strip .content.row{margin-bottom:var(--space-3)}
.mq{overflow:hidden;position:relative;-webkit-mask-image:linear-gradient(90deg,
  transparent,#000 180px,#000 calc(100% - 180px),transparent);
  mask-image:linear-gradient(90deg,transparent,#000 180px,#000 calc(100% - 180px),transparent)}
/* 174: the row keeps a fixed box so shrinking the marks turns into air rather
   than into a shorter strip. 65 is the tallest mark, 30 above and below. */
.mq-track{display:flex;align-items:center;width:max-content;list-style:none;
  margin:0;padding:0;will-change:transform;min-height:65px}
.mq{padding:var(--space-6) 0}
/* 72: slower. The track is 5164px and travels half of it per cycle, so 100s is
   about 26px a second, a reading pace rather than a conveyor. */
.mq-logos{gap:var(--space-18);animation:mqRun 128s linear infinite}
.mq-item{display:flex;align-items:center;justify-content:center;flex:0 0 auto}
/* 171b: the marks were at 72 percent. Already flattened to one ink by item
   47, so 72 percent on top of that is a second fade on the same element. */
.mq-item img{height:var(--h);width:auto;display:block;opacity:1;
  transition:opacity var(--t-base) var(--ease)}
.mq:hover .mq-logos{animation-play-state:paused}
.mq-item:hover img{opacity:1}
@keyframes mqRun{from{transform:translateX(0)}to{transform:translateX(-50%)}}

/* 62 (replaces 49): the atmospheric band is the same gallery as the hero, one
   frame centred and two flanking it, running the other way so the two rails are
   never mistaken for one another. The direction is set in js/script, not here. */
.atmos{padding:0}
.atmos-rail{margin:0}
/* 68: the atmospheric band shares the one rail height */
.atmos-rail .hero-slide{height:var(--rail-h)}

/* 48: the record. Off the green half band, onto a white strip under About, four
   columns divided by hairlines, counted up once on first sight. */
/* 61: the record strip takes the green. White numbers measure 5.79:1 on it and
   the labels sit at #E8EDE0, 4.85:1, both clear of AA. */
.figstrip{padding:var(--space-12) 0;background:var(--brand-accent);
  background-image:var(--band-gradient)}
.figrow{display:flex;flex-wrap:wrap}
.fig{flex:1 1 25%;text-align:center;padding:var(--space-2) var(--space-4);
  border-left:1px solid rgba(255,255,255,.28)}
.fig:first-child{border-left:none}
.fig b{display:block;font-family:var(--font-display);font-size:var(--text-h1);
  font-weight:600;line-height:1.05;color:#fff}
.fig b i{font-style:normal}
.fig-label{display:block;margin-top:var(--space-2);font-size:var(--text-caption);
  color:#E8EDE0;letter-spacing:.04em}
@media (max-width:767px){.fig{flex:1 1 50%;margin-bottom:var(--space-6)}
  .fig:nth-child(odd){border-left:none}
  .fig b{font-size:var(--text-h2)}
  .mq-logos{animation-duration:40s}}

/* --- 7: the picture's top corner meets the eyebrow top, its bottom corner
         meets the bottom of the button ---------------------------------------- */
.align-row{display:flex;flex-wrap:wrap;align-items:stretch}
.align-col{display:flex;flex-direction:column}
.align-cta{margin-top:auto;margin-bottom:0;padding-top:var(--space-4)}
.align-shot{flex:1 1 auto;overflow:hidden;border-radius:var(--radius);min-height:260px}
.align-shot img{width:100%;height:100%;object-fit:cover;display:block}
@media (max-width:767px){.align-row{display:block}.align-shot{min-height:220px;
  margin-top:var(--space-6)}}

/* --- 63: a centred section head. The roles keep their measures, the block just
   centres them, so a centred heading is still a heading and not a banner. --- */
.sec-head{text-align:center;margin:0 auto var(--space-8)}
.sec-head .heading-sm-lead,.sec-head h2,.sec-head p{margin-left:auto;margin-right:auto}
.sec-head p{max-width:62ch}
.sec-head 
/* --- 8 + 9 + 50: six disciplines, one row of six ---------------------------- */
.disc-head{margin-bottom:var(--space-6)}
/* 148: the strip went from six tiles at 178px to three at 377px, because 178
   is too small for the subject of the photograph to read. Two positions, and
   the arrows sit above the rail on the right rather than over a picture, which
   the site settled against. */
.disc-bar{display:flex;align-items:center;justify-content:flex-end;
  gap:var(--space-2);padding-bottom:var(--space-3)}
.disc-bar .disc-pos{font-family:var(--font-body);font-size:var(--text-eyebrow);
  font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  color:var(--text-muted);line-height:1;margin:0 var(--space-1) 0 0}
.disc-bar .rail-nav{position:static;margin:0;transform:none}
.disc-rail{overflow:hidden}
.disc-track{display:flex;margin:0 calc(var(--space-2)*-1);padding:0;list-style:none;
  transition:transform var(--t-slow) var(--ease)}
.disc-cell{flex:0 0 33.3333%;max-width:33.3333%;padding:0 var(--space-2)}
.dtile{display:block;text-decoration:none}
.dtile:hover,.dtile:focus{text-decoration:none}
.dtile-shot{aspect-ratio:3/4;overflow:hidden;border-radius:var(--radius);
  background:var(--neutral-200)}
.dtile-shot img{width:100%;height:100%;object-fit:cover;display:block;
  transition:transform var(--t-base) var(--ease)}
.dtile:hover .dtile-shot img{transform:scale(1.04)}
.dtile-empty{display:flex;align-items:center;justify-content:center;text-align:center;
  background:var(--surface-soft);border:1px dashed var(--border);padding:var(--space-3)}
.dtile-empty span{font-size:10px;letter-spacing:.1em;color:var(--text-muted);
  text-transform:uppercase;line-height:1.5}
/* 73: the label belongs to its picture, so it centres on it and carries enough
   weight to read as a label rather than a caption.
   103: carried as a p, not an h4. At 14px it was a sixth heading signature
   on a blueprint that allows five, and a label under a thumbnail is not a
   heading however bold it is set. */
.dtile-name{margin:var(--space-3) 0 0;font-size:var(--text-caption);
  line-height:1.35;color:var(--text-primary);font-family:var(--font-display);
  font-weight:600;max-width:none;text-align:center}
.dtile:hover .dtile-name{color:var(--brand-accent)}
/* two on a tablet and a phone, which is what the grid already did. One at a
   time would be six taps to see six disciplines. */
@media (max-width:991px){.disc-cell{flex:0 0 50%;max-width:50%}}

/* --- 12 + 51: selected projects. The hero rail again, travelling the same way,
   with the name and one line of detail on hover and on keyboard focus. ------- */
/* 66 + 74: both light bands carry IDEAL's own drawing underneath, the Zambezi
   River Prestige Extension One township layout. Projects shows the whole plan,
   About shows it close up, so the two read as one drawing at two scales rather
   than the same picture twice. The layer is 116 percent tall and starts 8 above,
   so a plus or minus 8 percent drift never exposes an edge. */
.proj-section{background:var(--neutral-50);padding-top:var(--section-y);
  padding-bottom:var(--space-18)}
.proj-frame{position:relative;z-index:1}
/* the three frames line up with the header: the logo's left edge at 135 and
   the header button's right edge at 1305. The rail pulls out past the
   container's 15px gutter plus the 10px it puts on each slide. */
.proj-frame .hero2-rail{overflow:hidden;
  margin-left:calc((var(--space-3) + var(--space-2)) * -1);
  margin-right:calc((var(--space-3) + var(--space-2)) * -1)}
/* three frames inside the 1170 container, so the rail's left edge lines up
   with the logo and its right edge with the header button */
.proj-frame .hero-slide,.proj-frame .hero-slide.is-centre{
  flex:0 0 33.3333%;max-width:33.3333%;padding:0 var(--space-2)}
.proj-frame .hero-slide>a,.proj-frame .hero-slide>figure{height:100%}
.proj-frame .rail-prev{left:var(--space-4)}
.proj-frame .rail-next{right:var(--space-4)}
.proj-section,.about-band{position:relative;overflow:hidden;isolation:isolate}
.bg-draw{position:absolute;left:-4%;right:-4%;top:-8%;height:116%;z-index:-1;
  background:url(../image/ideal/draw-watermark.png) center/cover no-repeat;
  opacity:.06;pointer-events:none;will-change:transform}
.about-band .bg-draw{background-image:url(../image/ideal/draw-detail.png);
  opacity:.05}
.proj-section>.container,.proj-section>.proj-frame,
.about-band>.container{position:relative;z-index:1}
@media (prefers-reduced-motion:reduce){.bg-draw{transform:none!important}}

.pj-over{position:absolute;inset:0;display:flex;flex-direction:column;
  justify-content:flex-end;padding:var(--space-6);text-decoration:none;
  color:#fff;opacity:0;transition:opacity var(--t-base) var(--ease);background-color:transparent;
  background-image:linear-gradient(180deg,rgba(29,31,23,0) 28%,
    rgba(29,31,23,.62) 52%,rgba(29,31,23,.95) 78%)}
.hero-slide{position:relative}
.hero-slide:hover .pj-over,.hero-slide:focus-within .pj-over{opacity:1}
.pj-over h3{color:#fff;margin:0 0 var(--space-1);font-size:var(--text-item);
  font-weight:700;text-transform:uppercase;letter-spacing:.06em;max-width:none}
.pj-over p{color:#E8EDE0;margin:0;font-size:var(--text-caption);line-height:1.5;
  max-width:36ch}
.pj-link{display:inline-block;margin-top:var(--space-3);font-size:var(--text-caption);
  font-weight:600;color:var(--accent-on-dark);letter-spacing:.06em;
  text-transform:uppercase}
.pj-over:hover{text-decoration:none}
@media (max-width:767px){.pj-over{opacity:1;padding:var(--space-3)}
  .pj-over p{display:none}}

/* --- 15: footer elements sit at balanced distances from each side ---------- */
.footer-widget{background:var(--surface-soft);color:var(--text-muted);
  border-top:1px solid var(--border);padding-top:var(--section-y);
  padding-bottom:var(--section-y)}
/* 21: the footer heading used to be 16px bold uppercase Poppins, which matched
   nothing else on the site. It now takes the same eyebrow treatment as every
   other label. The :not() guard is needed because the template's own rule
   carries an attribute selector and would otherwise win. */
.footer-widget .wgs-title,
.footer-widget .wgs-title:not([class*="color-"]){color:var(--brand-accent);
  font-family:var(--font-body);font-size:var(--text-eyebrow);font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;margin-bottom:var(--space-4)}
/* 22: the footer list chevrons were a mint green belonging to no system */
.footer-widget .wgs-menu .menu li a:before{color:var(--brand-accent)}
.footer-widget .contact-info .fa{color:var(--brand-accent)}
.footer-widget a{color:var(--text-body)}
.footer-widget a:hover{color:var(--brand-accent)}
.footer-widget .widget-row{display:flex;flex-wrap:wrap;justify-content:space-between}
/* 55: the four footer columns were 300, 200, 400 and 300. Evened. */
.footer-widget .footer-col{padding-left:var(--space-3);padding-right:var(--space-3);
  width:25%;flex:0 0 25%}
@media (max-width:767px){.footer-widget .footer-col{width:100%;flex:0 0 100%}}
/* 65: Offices ran to twice the height of the other three columns, which is what
   made the footer ragged. Two by two inside its own column levels the row. */
.footer-widget .offices{list-style:none;padding:0;margin:0;
  display:grid;grid-template-columns:1fr 1fr;gap:var(--space-4) var(--space-3)}
.footer-widget .offices li{margin-bottom:0}
.footer-widget .footer-col:nth-child(3){flex:0 0 32%;width:32%}
.footer-widget .footer-col:nth-child(1){flex:0 0 24%;width:24%}
.footer-widget .footer-col:nth-child(2){flex:0 0 16%;width:16%}
.footer-widget .footer-col:nth-child(4){flex:0 0 28%;width:28%}
@media (max-width:767px){.footer-widget .offices{grid-template-columns:1fr}
  .footer-widget .footer-col:nth-child(n){flex:0 0 100%;width:100%}}
.footer-widget .offices strong{color:var(--text-primary)}
.footer-widget .menu li{margin-bottom:var(--space-2)}

/* --- 18: the bottom bar goes green with white type ------------------------ */
/* 54: the bottom bar takes the anchor, which bookends the dark header and drops
   the page from three green blocks to two. */
.copyright{background:var(--brand-anchor);color:#fff;border-top:none;
  padding-top:var(--space-4);padding-bottom:var(--space-4);font-size:var(--text-caption)}
.copyright a{color:#fff;text-decoration:underline}
.copyright p{color:#fff;font-size:var(--text-caption);margin:0}

/* --- inner pages ---------------------------------------------------------- */
/* 89: the photographic banner is gone. IDEAL's photography is not strong enough
   to carry a full bleed hero on four pages, and saying so is better than
   pretending. The title sits on the brand gradient instead. */
.page-head{background:var(--brand-accent);background-image:var(--band-gradient);
  padding:var(--space-12) 0}
.ph-inner{text-align:center}
.ph-eyebrow{margin:0 auto var(--space-2);font-family:var(--font-body);
  font-size:var(--text-micro);font-weight:600;letter-spacing:.2em;
  text-transform:uppercase;color:#E8EDE0}
.page-head h1{color:#fff;margin:0 auto;text-transform:uppercase;
  letter-spacing:.005em;max-width:24ch}
@media (max-width:767px){.page-head{padding:var(--space-8) 0}}

/* 90: the About opening carries its own photograph, framed like the homepage */
.about-shot{overflow:hidden}
.about-shot img{width:100%;height:100%;object-fit:cover;display:block}

/* 91: the five letters of the name, each in its own frame, at a size that
   actually reads as the point of the section */
.val-row{display:grid;grid-template-columns:repeat(5,1fr);gap:var(--space-4);
  margin-top:var(--space-4)}
.valcard{text-align:center}
/* 98: the outlined frames read as empty boxes. Filled green with the letter
   reversed out, the five of them carry the name across the row. */
.val-letter{display:flex;align-items:center;justify-content:center;
  width:72px;height:72px;margin:0 auto var(--space-4);
  background:var(--brand-accent);color:#fff;
  font-family:var(--font-display);font-size:var(--text-h2);font-weight:600;
  line-height:1}
.valcard h4{margin:0 0 var(--space-2);max-width:none}
.valcard p{max-width:none}
@media (max-width:991px){.val-row{grid-template-columns:repeat(3,1fr);
  row-gap:var(--space-8)}}
@media (max-width:567px){.val-row{grid-template-columns:repeat(2,1fr)}}

/* 92: the directors centre, and their frames are square like everything else */
.team-list{display:flex;flex-wrap:wrap;justify-content:center}
.team-list .team-item,.team-list>[class*="col-"]{text-align:center}
.team-info .name,.team-info .sub-title,.team-info p,.team-info .quals{
  margin-left:auto;margin-right:auto;text-align:center}
.quals{text-align:center}

/* 93: the bench, which had no heading and no alignment at all */
/* 96: three names in a two column list read as leftovers under the
   directors. As cards with a monogram they are people, and the line
   underneath accounts for the other twenty two. */
.bench-row{margin-top:var(--space-8)}
/* the bench heading is six words and was breaking after the first */
.bench-head h2{max-width:none}
.bench-card{text-align:center;padding:var(--space-8) var(--space-6) var(--space-6);
  background:var(--neutral-0);border-top:3px solid var(--brand-accent);
  height:100%}
.bench-mono{display:flex;align-items:center;justify-content:center;
  width:56px;height:56px;margin:0 auto var(--space-4);
  background:var(--brand-accent);color:#fff;font-family:var(--font-display);
  font-size:var(--text-h4);font-weight:600;letter-spacing:.04em;line-height:1}
.bench-card h4{margin:0 0 var(--space-2);font-size:var(--text-item);
  font-weight:700;text-transform:uppercase;letter-spacing:.06em;
  color:var(--text-primary);max-width:none}
.bench-disc{margin:0 0 var(--space-3);font-size:var(--text-eyebrow);
  letter-spacing:.14em;text-transform:uppercase;color:var(--brand-accent);
  font-weight:600}
.bench-card .small{margin:0;color:var(--text-muted);max-width:none}
.bench-rest{margin:var(--space-8) auto 0;max-width:62ch;text-align:center;
  font-size:var(--text-caption);color:var(--text-muted)}
@media (max-width:767px){.bench-card{margin-bottom:var(--space-6)}}

.cap-tile{margin-bottom:var(--space-8)}
.cap-tile .fbox-photo{aspect-ratio:3/2;overflow:hidden;margin-bottom:var(--space-4);
  border-radius:var(--radius)}
.cap-tile .fbox-photo img{width:100%;height:100%;object-fit:cover}
.cap-tile .fbox-empty{background:var(--surface-soft);border:1px dashed var(--border);
  display:flex;align-items:center;justify-content:center;color:var(--text-muted);
  font-size:var(--text-eyebrow);letter-spacing:.12em;text-transform:uppercase}
.wideshot{aspect-ratio:21/9;overflow:hidden;border-radius:var(--radius)}
.wideshot img{width:100%;height:100%;object-fit:cover}
.gshot{aspect-ratio:4/3;overflow:hidden;margin-bottom:var(--space-2);
  border-radius:var(--radius)}
.gshot img{width:100%;height:100%;object-fit:cover}
.facts th{width:90px;font-weight:600;color:var(--brand-accent);
  text-transform:uppercase;font-size:var(--text-eyebrow);letter-spacing:.1em}
/* .team-member .team-photo img sets width:100% and outranks a plain
   .team-photo img, which is why the portraits were 370px wide. */
.team-photo img,.team-member .team-photo img{width:180px;height:180px;
  object-fit:cover;margin:0 auto var(--space-4);border-radius:0;max-width:180px}
.team-photo{text-align:center}
.quals{list-style:none;padding:0;color:var(--text-muted)}
.quals li{margin-bottom:var(--space-1);font-size:var(--text-caption)}
.col-md-2-4{width:20%;float:left;padding:0 var(--space-3)}
@media (max-width:991px){.col-md-2-4{width:50%}}
.office-box{margin-bottom:var(--space-6)}
.form-box{padding:var(--space-6);background:var(--surface-soft);
  border-radius:var(--radius)}
.form-control{min-height:44px;border-color:var(--border)}
.minih{font-family:var(--font-body);font-size:var(--text-eyebrow);letter-spacing:.14em;
  font-weight:600;text-transform:uppercase;color:var(--brand-accent);margin:var(--space-6) 0 var(--space-1)}
.creds{margin-top:var(--space-6)}
.creds div{padding:var(--space-3) 0;border-top:1px solid var(--border)}
.creds b{font-family:var(--font-display);font-size:var(--text-h4);
  color:var(--brand-accent);display:block}
.creds span{font-size:var(--text-caption);color:var(--text-muted)}
.register td,.register th{padding:var(--space-3)}

/* --- fixes found in the render pass -------------------------------------- */
/* the CTA band inherits body ink, which lands dark green on green */
/* 67: the green bands carry a gradient, the brand green across a broad centre
   and the logo's own hue at the two edges. Held inside the legible range: every
   stop carries white type at 4.6:1 or better. */
.call-action.bg-primary,.section-projects.bg-primary{
  background-image:var(--band-gradient)}
.call-action.bg-primary,.call-action.bg-primary p,.call-action.bg-primary h3,
.section-projects.bg-primary,.section-projects.bg-primary h2{color:#fff}
/* 119: this was 13px uppercase while every other Secondary was 14px sentence
   case. Same role, same object. Only the colour reverses. */
.call-action.bg-primary .btn.btn-outline{color:#fff;border-color:#fff;
  background:transparent;font-size:var(--text-caption);font-weight:700;
  letter-spacing:.02em;text-transform:none}
.call-action.bg-primary .btn.btn-outline:hover,
.call-action.bg-primary .btn.btn-outline:focus{background:#fff;color:var(--brand-accent);
  border-color:#fff}
/* principle 3, alignment: the loop's first item lines up with the container edge */
.ploop-track{padding-left:max(var(--space-3),calc((100vw - var(--container)) / 2));
  padding-right:max(var(--space-3),calc((100vw - var(--container)) / 2))}

/* --- 20: the template shipped a second green (#76A53B) in 61 places and a
     third (#36D371) in the footer. Both are gone from css/style-ideal.css.
     Only the hover state is allowed to differ from the brand green. -------- */
a:hover,a:focus,a:active{color:var(--state-hover)}
.btn:hover,.btn:focus{background-color:var(--state-hover);
  border-color:var(--state-hover);color:#fff}
.site-header .quote-btn .btn:hover{background-color:var(--state-hover)}
.copyright a:hover,.copyright a:focus{color:var(--accent-on-dark)}
.footer-widget a:hover,.footer-widget a:focus{color:var(--state-hover)}

/* --- 34: the About heading balances across its lines like the hero ------- */
.heading-lead{text-wrap:balance}

/* --- 38 to 42, blueprint enforcement -------------------------------------
   The template carries a handful of heading rules whose specificity beats the
   five role selectors in css/ideal-tokens.css. Each one is mapped back to its
   role here rather than left to win. This is the whole list; there is no
   sixth heading treatment anywhere on the site. */
.feature h3,.feature h4,
.feature h3:not([class*="font-w-"]),.feature h4:not([class*="font-w-"]),
.feature-row .col-sm-4 .feature h3,.feature-row .col-sm-4 .feature h4,
.feature-row .col-md-4 .feature h3,.feature-row .col-md-4 .feature h4,
.feature-project h3,.feature-project h4,.feature-product h3,.feature-product h4,
.feature-modern .title,.feature-modern .title:not([class*="font-w-"]){
  font-size:var(--text-item);font-weight:700;color:var(--text-primary);
  text-transform:uppercase;letter-spacing:.06em;max-width:30ch;
  font-family:var(--font-display)}
.call-action h1,.call-action h2,.call-action h3,.call-action h4{
  font-weight:400;line-height:1.25}
/* 167: the band heading takes the Section role rather than Block. It was
   Poppins 25 / 400 while every section heading on the site is 31 / 600, so
   the one heading that carries the conversion read lighter than the three
   above it. Same role as the others now, reversed to white on the green. */
.call-action.bg-primary h3,.call-action.bg-primary .cta-sameline h3{
  color:#fff;font-size:var(--text-h2);font-weight:600;letter-spacing:-.005em;
  line-height:var(--lh-section);max-width:none}
/* the band gives this Block its own full width, so the 34ch measure that keeps
   a card heading honest is not what should break this line */
.call-action .cta-sameline{display:flex;align-items:center;flex-wrap:wrap;
  gap:var(--space-4) var(--space-8);justify-content:space-between;
  padding:var(--space-12) 0}
.call-action .cta-text{flex:1 1 560px;min-width:0}
.call-action .cta-sameline h3{margin:0 0 var(--space-1)}
.call-action .cta-sameline p{margin:0;color:#E8EDE0;max-width:none}
.call-action .cta-sameline .btn{flex:0 0 auto;float:none;margin:0}
.banner-text h1,.banner-text h2,.banner-inner h1{font-size:var(--text-h1)}
.wgs-box.wgs-menus ul.list>li.list-heading span{color:var(--brand-accent)}
/* .team-member .team-info .name is (0,3,0) and puts the names in green, which
   breaks rule 1. The sub-title carried #aaa, a grey from no ramp of ours. */
h4.name,.team h4,.team .name,
.team-member .team-info h4,.team-member .team-info .name,
.team-profile .team-member .name{color:var(--text-primary)}
.team-member .team-info .sub-title,.sub-title{color:var(--text-muted);
  font-size:var(--text-caption);margin:0 0 var(--space-2)}

/* 42: the cue, redrawn from the IDEAL mark itself rather than approximated.
   Outer path is the arrowhead with its flared base and notched foot; the inner
   path is the counter. Inverted, because the mark points up and a scroll cue
   points down. 42px, which is where the counter still reads. */
.hero-cue{width:42px;height:37px;margin-left:-21px}

/* --- 68: the projects rail is driven by its arrows, not by a clock. The hero
   and the atmospheric band move on their own because nothing is being read off
   them; this one carries captions, so the visitor decides when it moves. ---- */
.proj-rail{position:relative}
.rail-nav{position:absolute;top:50%;margin-top:-26px;width:52px;height:52px;
  border:2px solid #fff;border-radius:0;background:rgba(29,31,23,.45);
  color:#fff;padding:0;display:flex;align-items:center;justify-content:center;
  cursor:pointer;z-index:3;transition:background-color var(--t-fast) var(--ease),
  border-color var(--t-fast) var(--ease)}
.rail-nav svg{width:22px;height:22px;display:block}
.rail-nav:hover,.rail-nav:focus-visible{background:var(--brand-accent);
  border-color:var(--brand-accent)}
.rail-prev{left:var(--space-6)}
.rail-prev svg{transform:scaleX(-1)}
.rail-next{right:var(--space-6)}
@media (max-width:767px){.rail-nav{width:42px;height:42px;margin-top:-21px}
  .rail-prev{left:var(--space-2)}.rail-next{right:var(--space-2)}}

/* --- 99: the Also Delivered grid. Every photo kept its own aspect ratio, so
   the three cards in a row started at different heights and the grid
   staggered. Locked to 3:2 like the Capability tiles, cards equal height. -- */
.feature-row{display:flex;flex-wrap:wrap}
.feature-row>[class*="col-"]{display:flex}
/* 109: --space-5 does not exist, so the whole padding shorthand was being
   dropped and the copy ran edge to edge of the card. Picture full bleed at
   the top, copy inset 30px, 30px at the foot. */
.other-tile{display:flex;flex-direction:column;width:100%;
  background:var(--neutral-0);padding:var(--space-6);
  margin-bottom:var(--space-8)}
.other-tile .fbox-photo{aspect-ratio:3/2;overflow:hidden;
  margin-bottom:var(--space-6);border-radius:var(--radius)}
/* 114: the copy was inset but the picture still ran to the card's top and
   sides, so the card had padding on three sides out of four. One value all
   the way round now, picture included. */
.other-tile .title,.other-tile p,.other-tile .fbox-photo{padding-left:0;padding-right:0}
.other-tile .fbox-photo img{width:100%;height:100%;object-fit:cover;display:block}
.other-tile .title{margin:0 0 var(--space-2)}
.other-tile .title{margin:0 0 var(--space-2);font-size:var(--text-item);
  font-weight:700;text-transform:uppercase;letter-spacing:.06em;
  color:var(--text-primary)}
.other-tile p:last-child{margin-bottom:0}
/* the township layout is a drawing, not a photograph. On the warm sand ground
   and fitted rather than cropped, it reads as a deliverable instead of a
   picture that failed to load. */
.other-tile .fbox-photo.is-drawing{background:var(--surface-band);
  display:flex;align-items:center;justify-content:center}
.other-tile .fbox-photo.is-drawing img{object-fit:contain;padding:var(--space-2)}
/* green belongs to eyebrows. This line is metadata. */
.other-meta{margin:0 0 var(--space-3);font-size:var(--text-caption);
  color:var(--text-muted)}

/* === 101: the Projects page, rebuilt ====================================== */
/* One padding value for every section on the site, off the token rather than
   the template's hardcoded 90px, so the rhythm cannot drift again. */
.section-pad{padding-top:var(--space-18);padding-bottom:var(--space-18)}
.section-pad-sm{padding-top:var(--space-12);padding-bottom:var(--space-12)}

/* the three flagships as rows: picture one side, text the other, flipping
   each time. Each row is about a third of what the old case study block cost. */
.feat-list{margin-top:var(--space-12)}
.feat-row{display:flex;align-items:center;gap:var(--space-12);
  padding:var(--space-12) 0;border-top:1px solid var(--border)}
.feat-row:first-child{border-top:0;padding-top:0}
.feat-row.is-flip{flex-direction:row-reverse}
.feat-media{flex:0 0 56%;max-width:56%}
.feat-text{flex:1 1 auto}
.feat-hero{aspect-ratio:16/9;overflow:hidden;border-radius:var(--radius)}
.feat-hero img{width:100%;height:100%;object-fit:cover;display:block}
.feat-strip{display:flex;gap:var(--space-2);margin-top:var(--space-2)}
.feat-thumb{flex:1 1 0;aspect-ratio:4/3;overflow:hidden;border-radius:var(--radius)}
.feat-thumb img{width:100%;height:100%;object-fit:cover;display:block}
/* role: block. 25/500/ink/28ch, same as every other h3 on the site */
.feat-name{margin:0 0 var(--space-2)}
.feat-meta{margin:0 0 var(--space-4);font-size:var(--text-caption);
  color:var(--text-muted)}
.feat-text .btn{margin-top:var(--space-4)}
@media (max-width:767px){
  .feat-row,.feat-row.is-flip{flex-direction:column;gap:var(--space-6);
    padding:var(--space-8) 0}
  .feat-media{flex:0 0 100%;max-width:100%;width:100%}
}

/* the register band, and the next/previous foot on a case page */
/* The band sat green directly above the green CTA, which broke the rule that
   no two grounds of a kind touch. White between two rules does the same job
   and keeps the page alternating white, tint, white, green. */
.reg-band{background:var(--neutral-0);padding:var(--space-12) 0;
  border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
.reg-inner{display:flex;align-items:center;justify-content:space-between;
  gap:var(--space-8);flex-wrap:wrap}
.reg-copy{max-width:52ch}
.reg-band h3{margin:0 0 var(--space-2)}
.reg-band p{margin:0;font-size:var(--text-caption);color:var(--text-muted)}
/* 119: this is Primary. It needed no role of its own, only no wrapping. */
.btn-ghost{width:300px;min-height:58px}
/* a row with no thumbnail strip gets a taller picture so the two columns
   still balance */
.feat-row.no-strip .feat-hero{aspect-ratio:4/3}
.case-nav{padding:var(--space-12) 0;border-top:1px solid var(--border)}
.case-nav .reg-inner{justify-content:space-between}

/* the case page hero, and its one sub heading */
.case-hero{aspect-ratio:21/9;overflow:hidden;border-radius:var(--radius)}
.case-hero img{width:100%;height:100%;object-fit:cover;display:block}
.case-sub{margin:var(--space-8) 0 var(--space-3)}
.gal-row .gshot{margin-bottom:var(--space-2)}
.gal-row .small{color:var(--text-muted)}
/* the page-head eyebrow sits on the green band, so rule 2 applies */
.page-head .ph-eyebrow{font-size:var(--text-eyebrow);text-transform:uppercase;
  letter-spacing:.14em;font-weight:700;color:#fff;opacity:.85;
  font-family:var(--font-body);margin:0 0 var(--space-2);max-width:none}

/* 110: the rows stagger left and right, so a ragged right edge on one side
   and a ragged left on the other reads as drift. Justified, with hyphens on
   so the word spacing does not open up into rivers. */
.feat-text p{text-align:justify;hyphens:auto;-webkit-hyphens:auto}
.feat-text .heading-sm-lead,.feat-text .feat-meta{text-align:left;hyphens:manual}

/* 108: .sec-head sets margin:0 auto, whose shorthand cancels any Bootstrap
   col-md-offset on the same element. Every centred section head was sitting
   165px left of centre because of it. The head now carries its own measure
   and centres itself, with no grid offset to be cancelled. */
.sec-head.center{max-width:780px}
.sec-head.center p{margin-left:auto;margin-right:auto;max-width:62ch}

/* 111: the case page gallery moves into the left column under the facts, so
   the column balances the body copy instead of leaving a well of white. */
.case-gal{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-4) var(--space-2)}
.case-gal .cg-cell{margin:0}
.case-gal .cg-cell:first-child{grid-column:1 / -1}
.case-gal img{width:100%;aspect-ratio:4/3;object-fit:cover;display:block;
  border-radius:var(--radius)}
.case-gal .cg-cell:first-child img{aspect-ratio:16/9}
.case-gal figcaption{font-size:var(--text-micro);line-height:1.45;
  color:var(--text-muted);margin-top:var(--space-1)}

/* 115: the onward move is solid, the way back is see through.
   119: the way back is now plain Secondary. It had a grey border of its own,
   a third border colour doing the job of the one we already had. */
.btn-ink{background:var(--brand-anchor);border:2px solid var(--brand-anchor);
  color:#fff;border-radius:var(--radius)}
.btn-ink:hover,.btn-ink:focus{background:var(--brand-accent);
  border-color:var(--brand-accent);color:#fff}
/* rule 2: on the dark ground the onward button takes the profile's brighter
   green with ink type, which clears AA at 5.37:1 where white would not. */
.bg-primary .btn-ink,.reg-band.on-ink .btn-ink{background:var(--accent-on-dark);
  border-color:var(--accent-on-dark);color:var(--brand-anchor)}
.ideal-arrow{width:13px;height:11px;flex:0 0 auto;fill:currentColor;
  transform:rotate(-90deg)}

/* 120: the template's .form-message .btn (0,2,0) sets uppercase and 1px
   tracking, which is why the one submit button on the site was the only
   button still shouting. */
.form-quote .btn,.form-message .btn,.form-signup .btn,.form-login .btn,
.form-subscribe .btn{text-transform:none;letter-spacing:.02em;
  font-size:var(--text-caption);font-weight:700;width:240px;min-height:52px}

/* 124: the figures were four white numbers with nothing holding them. The
   mark sits above each one in ink, the one colour on that band that is
   neither white nor the green itself. 3.1:1 against the green, which is the
   bar for a graphic rather than for type. */
.fig-mark{display:block;margin:0 auto var(--space-3);width:24px;height:20px}
.fig-mark svg{width:100%;height:100%;fill:var(--surface-band);display:block;
  transform:rotate(180deg)}   /* 132: the profile's warm sand, 7.3:1 on the
    green. Off white rather than white, so it reads as a colour of its own
    without adding one to the palette. */

/* 126: the CTA action converts, so it is Primary, which on a green ground
   reverses to a solid white with green type. It had been an outline. */
.call-action.bg-primary .btn.btn-cta{background:#fff;border-color:#fff;
  color:var(--brand-accent);font-size:var(--text-caption);letter-spacing:.02em;
  text-transform:none}
.call-action.bg-primary .btn.btn-cta:hover,
.call-action.bg-primary .btn.btn-cta:focus{background:var(--brand-anchor);
  border-color:var(--brand-anchor);color:#fff}

/* 131: the last three off-system labels.
   the chip and the discipline label were each carrying a weight and a size
   of their own; the hover link on a project frame was 14/600 in the profile
   green. All three are the eyebrow, at the size their place allows. */
.hero-chip{font-size:var(--text-micro);font-weight:700;letter-spacing:.18em}
.dtile-name{font-size:var(--text-caption);font-weight:700;
  text-transform:uppercase;letter-spacing:.06em;font-family:var(--font-display)}
.pj-link{font-size:var(--text-eyebrow);font-weight:700;letter-spacing:.14em;
  color:var(--accent-on-dark)}
.bench-mono{font-size:var(--text-item);font-weight:700;letter-spacing:.06em}

/* 131: the eyebrow consolidation has to sit here, not in the tokens file.
   Five earlier rules in this file set 12/600 with four tracking values, and
   this file loads after the tokens, so the tokens version never won. */
.ph-eyebrow,.bench-disc,.minih,.register th,.facts th,.pj-link,
.footer-widget .wgs-title,.footer-widget .wgs-title:not([class*="color-"]){
  font-family:var(--font-body);font-size:var(--text-eyebrow);font-weight:700;
  text-transform:uppercase;letter-spacing:.14em;line-height:1.5}
/* the discipline label is Caption at bold, in the body face. It had been
   Poppins at 14/600, which was a seventh signature for no reason. */
.dtile-name{font-family:var(--font-body);font-size:var(--text-caption);
  font-weight:700;text-transform:uppercase;letter-spacing:.06em}

/* ===========================================================================
   133: the footer. Edu was right and the per-element audit was the wrong
   check. Every element in here already used a declared role, so the audit
   passed it. What it could not see is that the block mixes two of them:
   three columns run on Caption at 14px and the Contact column runs on Body
   at 16px, side by side, so the right hand column is visibly larger. On top
   of that the block carried three greys at once: #3C4336 on the links,
   #3C3C3C on the office names and #656C5D on the addresses.

   The whole footer is Caption now, with two colours and not three: body ink
   for everything, anchor ink for the names that act as labels.
   ======================================================================== */
.footer-widget,.footer-widget p,.footer-widget li,.footer-widget a,
.footer-widget .wgs-content,.footer-widget .offices li{
  font-size:var(--text-caption);line-height:1.7;color:var(--text-body)}
.footer-widget strong,.footer-widget .offices strong{
  font-size:var(--text-caption);font-weight:700;color:var(--text-primary)}
.footer-widget a:hover,.footer-widget a:focus{color:var(--state-hover)}

/* === 135: the motion system ============================================== */
/* rule 1. The template sets `transition: all` on twelve selectors, which
   reaches 152 elements. Each is replaced by the properties it actually
   needed. */
a,.btn,.icon-box,.gallery-lists li,
.header-s1.is-transparent .navbar-nav>li>a:not(.btn):before,
.site-onepage .navbar-nav>li>a:not(.btn):before{
  transition:color var(--t-fast) var(--ease),
             background-color var(--t-fast) var(--ease),
             border-color var(--t-fast) var(--ease)}
.feature .fbox-over,.gallery .photo-link .links,
.photo-item-link .link-block,.photo-item-link:after{
  transition:opacity var(--t-base) var(--ease)}
.feature-s4 .feature .fbox-photo img,.feature-s5 .feature .fbox-photo img,
.gallery .photo img{
  transition:transform var(--t-base) var(--ease)}

/* the site's own transitions, folded onto the three steps */
.rail-nav{transition:background-color var(--t-fast) var(--ease),
  border-color var(--t-fast) var(--ease)}
.pj-over,.dtile-over{transition:opacity var(--t-base) var(--ease)}
.hero2-rail:not(.drift-rail) .hero-slide:not(.is-centre) img{
  transition:filter var(--t-slow) var(--ease)}
.hero-slide{transition:flex-basis var(--t-slow) var(--ease),
  max-width var(--t-slow) var(--ease)}
.hero2-track,.proj-frame .hero2-track{transition:transform var(--t-slow) var(--ease)}
.form-control{transition:border-color var(--t-fast) var(--ease)}

/* rule 4, the signature. The arrowhead is already the scroll cue, the four
   figure marks and the onward button. It is the one thing that moves. */
.btn .ideal-arrow{transition:transform var(--t-base) var(--ease)}
.btn:hover .ideal-arrow,.btn:focus-visible .ideal-arrow{
  transform:rotate(-90deg) translateY(-4px)}   /* the svg is already turned a
    quarter, so its own Y is the page's X */

/* the only place the site gave no signal that a picture is a link */
.proj-frame .hero-slide img{transition:transform var(--t-slow) var(--ease)}
.proj-frame .hero-slide:hover img,
.proj-frame .hero-slide:focus-within img{transform:scale(1.015)}

/* 136: at rest the homepage had four things moving at once. The scroll cue
   is the one that earns it least: it pulses every 2.6 seconds forever, next
   to two rails that are already travelling, and its whole job is done the
   moment you scroll. It stops on the first scroll and does not come back. */
.hero-cue.is-done{animation:none}

/* reduced motion. The JavaScript already checked for this; the CSS covered
   one selector out of 152. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important;scroll-behavior:auto!important}
  .bg-draw{transform:none!important}
}
/* the last two off-scale transitions, both from the template: a colour fade
   on every nav and footer link at 0.3s ease, and a three-property fade on the
   Capability and Also Delivered tiles at 0.35s ease. */
.topbar .top-nav li a,.navbar-nav>li>a,.pagination li a,
.wgs-box.wgs-menus ul.list>li.list-heading span,
.wgs-box.wgs-search .form-group button.search-btn{
  transition:color var(--t-fast) var(--ease)}
.feature-s1 .feature,.feature-s2 .feature,
.feature-s1 .feature h3,.feature-s1 .feature h4,
.feature-s1 .feature h3 a,.feature-s1 .feature h4 a,
.feature-s2 .feature h3,.feature-s2 .feature h4,
.feature-s2 .feature h3 a,.feature-s2 .feature h4 a{
  transition:color var(--t-fast) var(--ease),
             background-color var(--t-fast) var(--ease),
             border-color var(--t-fast) var(--ease)}

/* === 138 to 140: the phone ================================================
   Everything in this session was measured at 1440. At 390 two things today's
   work had broken: the hero headline ran off the screen, and the projects
   rail still showed three frames across a 390px row, 123px each, with the
   captions spilling out of them.

   The overflow check did not catch either, because the template sets
   overflow-x:hidden on html and body. The page clips instead of scrolling,
   so scrollWidth read 390 on all nine pages and the number looked clean.
   ========================================================================= */
@media (max-width:767px){
  /* 139: one project per row, and the rail steps by one */
  .proj-frame .hero-slide,.proj-frame .hero-slide.is-centre{
    flex:0 0 100%;max-width:100%}
  .proj-frame .hero2-rail{margin-left:0;margin-right:0}
  .proj-frame .rail-prev{left:var(--space-2)}
  .proj-frame .rail-next{right:var(--space-2)}

  /* 140: the top bar carried the full email address off the right edge.
     The phone number stays, because it is tappable and it is the one thing
     a visitor on a phone actually wants from that strip. */
  .topbar .top-contact .t-email{display:none}
  .topbar .top-nav{display:none}
  .topbar .container>.row{justify-content:flex-end}

  /* the hero buttons go full width rather than keeping the 300px Large */
  .hero2-cta .btn,.reg-inner .btn,.case-nav .btn{width:100%;max-width:100%}
  .reg-inner,.case-nav .reg-inner{flex-direction:column;align-items:stretch}
}

/* === 141 to 143: three things that read as unfinished on a phone ==========
   Found by walking the live site at 390 rather than counting. */
@media (max-width:767px){
  /* 141: the marks are sized by equal optical area for a 1440 strip, which
     put them at 186, 215 and 230px wide on a 390 screen. One logo took more
     than half the width and the next was cut in half. The scale is capped
     here: --h is the authored height, and max-width holds the widest marks
     (an 11:1 ministry lockup) to a quarter of the screen. */
  .mq-item img{height:auto;max-height:calc(var(--h) * 0.62);max-width:96px;
    width:auto;object-fit:contain}
  .mq-logos{gap:var(--space-8)}
  /* 174: less air on a phone, where vertical space is the scarce thing */
  .mq{padding:var(--space-4) 0}
  .mq-track{min-height:40px}
  .mq{-webkit-mask-image:linear-gradient(90deg,transparent,#000 48px,
    #000 calc(100% - 48px),transparent);
    mask-image:linear-gradient(90deg,transparent,#000 48px,
    #000 calc(100% - 48px),transparent)}

  /* 142: the hero pair ran 330px from x=30 and the CTA button 240px from
     x=15. Same page, two widths, two left edges. Every button on a phone is
     now full width inside the one gutter. */
  .call-action .cta-sameline .btn,.btn-ghost,.form-message .btn,
  .hero2-cta .btn,.reg-inner .btn,.case-nav .btn{
    width:100%;max-width:100%;flex:0 0 auto}
  .call-action .cta-sameline{display:block}

  /* 143: one gutter for the whole phone layout. The discipline grid sits at
     x15 w360, so the project frame and the hero buttons match it rather than
     each carrying an inset of their own. */
  .proj-frame .hero-slide{padding:0}
  .proj-frame .hero2-rail{margin-left:calc(var(--space-3) * -1);
    margin-right:calc(var(--space-3) * -1)}
  .hero2-cta{padding-left:0;padding-right:0;gap:var(--space-2)}
  .hero-cta-strip>.container{padding-left:var(--space-3);
    padding-right:var(--space-3)}
}

/* 145 (D7): the hero rail to chip gap measured 43px, a value on no scale.
   It is .hero2-rail's 40px bottom margin plus 3px of leading above the chip.
   Cutting the margin to 20px lands it on 24, which is on both the 4 and the
   5 grid, and tightens the fold by 19px as a side effect. */
.hero2 .hero2-rail{margin-bottom:var(--space-4)}
.hero-chip{margin-top:0}

/* ============================================================================
   144 (D4) continued. Pinning line height in the tokens file was not enough,
   because this file loads after it and the template after that. More useful:
   the first pass assumed a role is a family, a size and a weight. It is not.
   Several genuinely different roles share all three:

     Poppins 31/600  is both the Section heading and a stat figure
     Poppins 18/700  is both the Item label and a monogram in a box
     Roboto  14/700  is both a button and bold caption text

   So four roles that were never declared are declared here, each with its own
   line height, rather than pretending they are drift.
   ========================================================================= */
/* role: button. One line, a control, not prose. The compact header button was
   sitting at line-height 2, which is why it looked taller than it measured. */
.btn,a.btn,button.btn,.site-header .quote-btn .btn,
.site-header .navbar-nav>li.quote-btn .btn{line-height:1.2}
/* role: figure. A number that is the point of its block. */
.fig b,.fig b i,.fig .count,span.h2,span.h3,.creds b,.stat-fig{line-height:1.05}
/* role: monogram. A glyph centred in a box, so it carries no leading. */
.val-letter,.bench-mono{line-height:1}
/* role: table cell. Denser than body copy on purpose; a 31 row register at
   1.7 runs 300px longer for no gain in legibility. */
.register td,.register th,.facts th,.facts td,.table td,.table th{line-height:1.5}
/* role: navigation. Also a control rather than prose. */
.navbar-nav>li>a,.topbar .top-nav li a,.footer-widget .wgs-menu .menu li a{
  line-height:1.4}
/* the three that were genuine drift */
.fig-label{line-height:var(--lh-caption)}
.dtile-name{line-height:var(--lh-eyebrow)}
.register strong,.footer-widget strong{line-height:var(--lh-caption)}

/* 150c: the hero gallery only, taller. 58svh capped at 540 put it at 522px on a
   900px screen. 62svh capped at 580 puts it at 558, which is 36px more picture
   and still leaves the headline and the cue above the fold. The atmospheric
   band keeps the old height, because it is a band and not the first thing
   anybody sees. */
/* 154: the rail is sized off what has to fit under it, not off a share of the
   screen. 62svh looked right on a tall window and pushed the headline and the
   cue off the bottom on a laptop. The furniture below the pictures, the chip,
   the headline at two lines and the cue, plus the header above them, measures
   400px, so the pictures take whatever the window has left. Floor 260, ceiling
   580, and on a very tall screen the ceiling is what holds. */
@media (min-width:992px){.hero2{--rail-h:clamp(260px,calc(100svh - 400px),580px)}}

/* 151: the hero drifts. It used to step one frame every 6 seconds with a wide
   centre frame, so at 1440 the centre was 720px and the flanks 360px and no
   photograph was ever seen whole. Equal frames now, and the strip never stops
   or snaps.
   The track is sized to its content, so translating it 50% is exactly one copy
   of the set. 200 seconds for a full pass is 14.4px a second at 1440, which is
   a frame crossing the screen in just over 25 seconds. The old step was the
   equivalent of 45px a second.
   Hover or keyboard focus pauses it, because a picture you are looking at
   should hold still. Reduced motion stops it outright. */
.drift-rail .hero-track{width:max-content;transform:none;transition:none;
  animation:heroDrift 200s linear infinite}
.drift-rail .hero-slide{flex:0 0 25vw;max-width:25vw}
.drift-rail:hover .hero-track,
.drift-rail:focus-within .hero-track{animation-play-state:paused}
@keyframes heroDrift{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@media (max-width:991px){.drift-rail .hero-slide{flex:0 0 40vw;max-width:40vw}}
@media (max-width:767px){.drift-rail .hero-slide{flex:0 0 62vw;max-width:62vw}
  .drift-rail .hero-track{animation-duration:150s}}
@media (prefers-reduced-motion:reduce){
  .drift-rail .hero-track{animation:none}}

/* 153: the atmospheric band joins the hero rather than keeping its own clock.
   It drifts the other way and slower, 260s against the hero's 200, so the two
   never read as one strip and never line up. The projects rail keeps its
   arrows and moves only when somebody asks it to. One continuous family, one
   rail under the visitor's hand. */
.atmos-rail .hero-track{animation-name:heroDriftR;animation-duration:260s}
@keyframes heroDriftR{from{transform:translateX(-50%)}to{transform:translateX(0)}}
@media (max-width:767px){.atmos-rail .hero-track{animation-duration:200s}}



/* 155: the header menu was the only text on the site in its own signature,
   Roboto 14 / 400 / uppercase with no letter spacing, and the only uppercase
   run anywhere with no tracking. The top bar carries .10em, the header button
   .12em, the eyebrow .14em, the tile label and the discipline title .06em.
   The nav now takes the tile label's signature exactly, Roboto 14 / 700 /
   uppercase / .06em, so it adds nothing to the blueprint.
   The footer menu is the same family, size and weight but sentence case, which
   left the site's two navigations disagreeing on case. It joins the header. */
.navbar-nav>li>a:not(.btn),
.footer-widget .wgs-menu .menu li a{font-family:var(--font-body);
  font-size:var(--text-caption);font-weight:700;letter-spacing:.06em;
  text-transform:uppercase}

/* 158: In the news reads as an insights index. Four equal cards in three
   columns, no lead post, the grid running all the way down. A card carries
   only what a visitor needs to decide whether to follow the link: the source,
   the date, the headline as it was printed, one line on IDEAL's part in the
   project, and the flag where the report and this site disagree. The bullet
   summaries are gone, they were the bulk.

   The source mark is the publication's initials in a circle, not its logo.
   Using The Namibian's or New Era's actual masthead would need their files and
   their permission, and inventing one is worse than initials.

   The headline is Poppins 25 / 600, which the site already sets on the
   capability figures. Block is 25 / 400 and reads too light for a headline. */
/* align-items:start, because the grid was stretching every card to the tallest
   one and the Aus card carries a six line flag. Card three was 300px of white
   above its own link. A blog grid that is slightly ragged beats one that is
   padded out with nothing. */
.post-list{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-8) var(--space-5);
  align-items:start}
.post{display:flex;flex-direction:column;background:var(--neutral-0);
  border:1px solid var(--border);
  transition:border-color var(--t-fast) var(--ease)}
.post:hover,.post:focus-within{border-color:var(--brand-accent)}
.post-shot{display:block;overflow:hidden;background:var(--neutral-200);aspect-ratio:16/10}
.post-shot img{width:100%;height:100%;object-fit:cover;display:block;
  transition:transform var(--t-base) var(--ease)}
.post-shot:hover img,.post-shot:focus img{transform:scale(1.04)}
.post-body{padding:var(--space-6);display:flex;flex-direction:column;flex:1 1 auto}
/* 160: the source line, no logo and no circle. The publication sits in a small
   sand chip in the brand green, the date beside it in muted ink, both on the
   eyebrow role the site already uses everywhere. The circle was carrying an
   invented mark where a masthead would go, which is the wrong thing to invent.
   One line, 12px, so the headline stays the loudest thing on the card. */
.post-src{display:flex;align-items:baseline;flex-wrap:wrap;gap:var(--space-2);
  margin:0 0 var(--space-3);font-family:var(--font-body);font-size:var(--text-eyebrow);
  font-weight:700;letter-spacing:.14em;text-transform:uppercase;line-height:1.5}
.src-name{background:var(--surface-band);color:var(--brand-accent);
  padding:3px var(--space-2)}
.src-date{color:var(--text-muted)}
.post-title{font-family:var(--font-display);font-size:var(--text-h3);
  font-weight:600;line-height:1.25;letter-spacing:-.01em;max-width:none;
  margin:0 0 var(--space-4)}
.post-title a{color:var(--text-primary)}
.post-title a:hover,.post-title a:focus{color:var(--brand-accent)}
.post-role{margin:0 0 var(--space-5)}
.post-more{margin:auto 0 0}
.post-more a{font-family:var(--font-body);font-size:var(--text-caption);font-weight:700;
  color:var(--brand-accent);display:inline-flex;align-items:center;gap:var(--space-2)}
.post-more a .ideal-arrow{width:13px;height:11px;fill:currentColor}
@media (max-width:991px){.post-list{grid-template-columns:repeat(2,1fr)}}
@media (max-width:567px){.post-list{grid-template-columns:1fr}}

/* 159: Careers is the page title band, open positions, and the offices.
   The why-here section, the six discipline cards and the three application
   steps are gone: the disciplines already have their own page and a careers
   page is not where somebody reads them again. */
.vac-empty{max-width:720px;margin:0 auto;text-align:center;
  border:1px dashed var(--border);background:var(--neutral-0);padding:var(--space-8)}
.vac-empty p{margin:0 0 var(--space-4)}
.vac-empty .proj-all{margin:0}
.off-card{background:var(--neutral-0);border:1px solid var(--border);
  padding:var(--space-5);height:100%;margin-bottom:var(--space-5)}
.off-card p{margin:0}
.off-city{font-family:var(--font-body);font-size:var(--text-caption);font-weight:700;
  letter-spacing:.06em;text-transform:uppercase;margin:0 0 2px}
.off-kind{font-family:var(--font-body);font-size:var(--text-eyebrow);font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;color:var(--brand-accent);
  margin:0 0 var(--space-3)}
@media (max-width:767px){.off-card{margin-bottom:var(--space-4)}}

/* 161: the two disagreements come off the cards. On a card each one was six
   lines of caveat under a two line headline, which is not what a news index is
   for and it made the Aus card half as tall again as its neighbours. They sit
   in one block at the foot of the page instead, still live, still unresolved,
   and now readable as what they are: notes for IDEAL rather than part of the
   story. */
.notes{max-width:820px;margin:0 auto}
.note-list{margin:var(--space-6) 0 0}
.note-list dt{font-family:var(--font-body);font-size:var(--text-caption);font-weight:700;
  letter-spacing:.06em;text-transform:uppercase;color:var(--brand-accent);
  margin:0 0 var(--space-2)}
.note-list dd{margin:0 0 var(--space-6);padding-left:var(--space-4);
  border-left:3px solid var(--border)}
.note-list dd:last-child{margin-bottom:0}

/* 168: Also Delivered on a phone. Six cards stacked full width came to 3,753px
   on their own, the single tallest block on the site, and each card was a
   200px photograph above 206px of text.
   Side by side from 568 to 767, where the text column is wide enough: the
   photograph takes a 132px column at 3:4 and the title, meta and description
   stack beside it. Card 520px to 222px there.
   Below 568 it stays stacked. I built the side by side at 390 first and it
   was worse than what it replaced: a 208px text column wrapped the title to
   four lines and the description to eight, and the card only came down to
   485px. Narrow columns do not save height, they trade it for raggedness. */
@media (min-width:568px) and (max-width:767px){
  .other-tile{display:grid;grid-template-columns:132px 1fr;
    column-gap:var(--space-4);align-items:start}
  .other-tile .fbox-photo{grid-column:1;grid-row:1 / span 3;aspect-ratio:3/4;
    height:auto;overflow:hidden}
  .other-tile .fbox-photo img{width:100%;height:100%;object-fit:cover}
  .other-tile .fbox-photo.is-drawing{aspect-ratio:1/1}
  .other-tile .title{grid-column:2;grid-row:1;margin:0 0 var(--space-1)}
  .other-tile .other-meta{grid-column:2;grid-row:2;margin:0 0 var(--space-2)}
  .other-tile p:last-child{grid-column:2;grid-row:3;margin:0}
}

/* 168b: below 568 the photograph goes from 16:10 to 16:9, which is the only
   height a stacked card can give back without losing anything. */
@media (max-width:567px){
  .other-tile .fbox-photo{aspect-ratio:16/9;overflow:hidden}
  .other-tile .fbox-photo img{width:100%;height:100%;object-fit:cover}
}

/* === 175: the section rhythm on phones =================================== */
/* Item 101 fixed every section at --space-18, 90px at every width, to stop the
   rhythm drifting. It also bypassed --section-y, whose 40px phone value never
   reached a page: measured 29 October, every section-pad on About was 90/90
   at 390. On phones the rhythm drops to 40/40. Desktop is untouched at 90. */
@media (max-width:767px){
  .section-pad{padding-top:var(--space-8);padding-bottom:var(--space-8)}
}

/* === 176: the footer on phones ============================================ */
/* Four columns stacked full width ran the footer to 1595px at 390, a fifth of
   About. The two link lists sit side by side, which they are short enough for.
   Offices stays full width and takes back its two by two from item 65: a 165px
   office column wraps every address (168: narrow columns trade height for
   raggedness). Contact stays full width. Desktop is untouched. */
@media (max-width:767px){
  .footer-widget .footer-col:nth-child(1),
  .footer-widget .footer-col:nth-child(2){flex:0 0 50%;width:50%}
  .footer-widget .offices{grid-template-columns:1fr 1fr}
  .footer-widget .footer-col:nth-child(4) a{overflow-wrap:anywhere}
}
