/* home.css — IPLG homepage styles (views/index.ejs only)
   Consolidated from the former hero.css + the homepage-specific rules of
   home2.css. Two logical blocks:
     1. Cinematic full-bleed hero (.iplg-hero*)
     2. Practice-card grid section (.practice-cards-section, .practice-card*)
   This file is self-sufficient for the homepage. The practice-card grid
   block below is DUPLICATED in demos.css, which the demo pages use. */

/* ===================================================================
   1. HERO — cinematic full-bleed hero
   Built to spec: .claude/workspace/specs/2026-05-20-cco-homepage-hero-redesign.md
   Full-bleed hero with darkened scrims, bottom-left rotating text carousel,
   and a slow Ken Burns image zoom. Scoped entirely under .iplg-hero so it
   cannot regress other page sections.
   =================================================================== */

/* 4.1 Hero container and height */
.iplg-hero {
  position: relative;
  /* BRIGHTNESS KNOB for the LOGO and the hero TEXT. Currently 1, so both
     render at full opacity (full brightness). Applied to the logo and to the
     .iplg-hero__content CONTAINER (not the individual leads) so it never fights
     the carousel crossfade, which animates each lead's own opacity 0<->1.
     Lower below 1 if a softer, slightly translucent look is ever wanted. */
  --hero-soft-alpha: 1;
  /* (Reverted) Old directional slide-fade travel distance for the rotating
     carousel text. The hero text now does a discreet fade-out-then-in with NO
     horizontal slide (see .iplg-hero__lead), so this knob is currently
     declared-but-unused. Left in place so the slide can be re-enabled later
     without re-deriving the value; raising it has no effect on its own. */
  --hero-slide-shift: 28px;
  /* HERO TEXT DRIFT (parallax with the Ken Burns image). The carousel text block
     (.iplg-hero__entries) slowly drifts toward the SAME corner the background
     image is panning to, so the foreground text reads as locked to the moving
     photo instead of sitting still over it. hero.js sets the per-slide direction
     (matching the randomly chosen image drift); these two knobs set the travel
     MAGNITUDE on each axis. Kept smaller than the image's pan so the text reads as
     a near foreground layer (a gentle parallax), not a 1:1 slide. Set both to 0px
     to park the effect. Magnitude over the (shorter) drift envelope below sets the
     drift SPEED. X is weighted well above Y so the motion reads mostly SIDEWAYS
     (more horizontal travel than vertical). */
  --hero-text-drift-x: 58px;
  --hero-text-drift-y: 26px;
  /* isolation:isolate makes the hero its own stacking context, so no
     z-index inside the hero (slides, scrims, content) can ever out-stack
     the nav, which is a separate sibling header (Task 2). */
  isolation: isolate;
  width: 100%;
  min-height: 100vh;           /* desktop: fill the screen */
  min-height: 100svh;          /* mobile-safe: small viewport unit so browser chrome never clips the hero */
  min-height: 100dvh;          /* dynamic viewport unit where supported */
  overflow: hidden;
  background-color: #0c1628;   /* brand navy fallback while images load */
  display: flex;
  flex-direction: column;
  /* The hero foreground (.iplg-hero__masthead) is now absolutely positioned to
     span the full hero height down the LEFT side, so it is not laid out by this
     flex container. justify-content:center is retained as a harmless default for
     any future in-flow child. */
  justify-content: center;
}

/* Visually hidden but accessible page heading. The hero shows no visible
   <h1>; this keeps the homepage from shipping with zero <h1>. */
.iplg-hero__h1 {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* 4.2 Backdrop — image layers */
.iplg-hero__stage {
  position: absolute;
  inset: 0;
  /* z-index + isolation give the stage its own stacking context, so the
     slide z-indexes (0/1) and the scrim z-index (2) are LOCAL to the stage
     and cannot leak into the section to fight the nav (Task 2). The stage
     itself sits below .iplg-hero__content (z-index:2 in the section). */
  z-index: 0;
  isolation: isolate;
  overflow: hidden;
}

.iplg-hero__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  /* Default stacking. The active/incoming slide is lifted above this via
     .is-active so the crossfade always fades IN on top, even on the
     last-to-first wrap where slide 0 sits first in DOM order (Fix 2). */
  z-index: 0;
  transition: opacity 2200ms ease-in-out;
  /* Promote the crossfading slide to its own compositor layer. Without this the
     opacity fade runs on the main thread and forces the transform-animating
     child image (.iplg-hero__img Ken Burns) to flatten and repaint every frame,
     which is the main source of transition choppiness. */
  will-change: opacity;
}
.iplg-hero__slide.is-active {
  opacity: 1;
  z-index: 1;
}

.iplg-hero__img {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  transform: scale(1);
  will-change: transform;
  /* Force a stable GPU layer for the Ken Burns transform so it never falls back
     to main-thread rasterization mid-drift. */
  backface-visibility: hidden;
  /* Darken every hero background image by 10 percent. brightness(0.9) sits on
     the image layer only, leaving the Ken Burns transform/animation, the scrims,
     and the slide opacity crossfade untouched. The first slide already uses a
     pre-muted image file; this uniform 10 percent CSS darken on top of it is
     intended and fine. */
  filter: brightness(0.9);
}
/* The DNA slide (first hero image) has hot/bright spots. Tone DOWN just those
   highlights while leaving shadows and midtones nearly untouched: multiply the
   image against a light gray. Multiply pulls bright pixels (near white) down toward
   the gray a lot, but dark pixels barely move (black * anything stays black), so the
   blown-out spots calm down without washing out or graying the whole image. The base
   brightness(0.9) still applies (this rule does not set filter). KNOB: lower the gray
   (e.g. #b8b8b8) to pull highlights down harder, raise it (toward #fff) to ease off. */
.iplg-hero__slide--dna .iplg-hero__img {
  /* Keep the multiply that holds the bright spots in check, then LIFT the overall
     image: brightness() raises luminance and contrast() < 1 compresses the range
     so the gain lands on the shadows/midtones rather than re-blowing the highlights.
     This brightens the image without just re-saturating the hot spots.
     WARMTH FIX: the DNA photo read too blue/cold. The multiply tint is now a warm
     cream (lower BLUE channel than red/green), so multiplying pulls the blue down and
     shifts the whole image warmer; saturate(0.9) takes a touch of intensity off the
     remaining blues. Push the tint cooler toward #f0f0f0 to undo, warmer (lower blue)
     to push further. */
  background-color: #f9f3ea;
  background-blend-mode: multiply;
  filter: brightness(1.16) contrast(1.05) saturate(0.97);
}
/* The courthouse slide reads dark; brighten it as a whole (overrides the base
   brightness(0.9) for this slide only). */
.iplg-hero__slide--courthouse .iplg-hero__img {
  filter: brightness(1.12);
}
/* The microchip slide: a slight overall lift (overrides the base brightness(0.9)). */
.iplg-hero__slide--microchip .iplg-hero__img {
  filter: brightness(1.12);
}
/* Ken Burns: the slide's image slowly scales up while on screen.
   Implemented as a keyframe (not a class-triggered transition).

   The zoom is driven by the .is-zooming class, NOT by .is-active. This
   decoupling fixes the outgoing-slide glitch: the zoom keyframe ends with
   transform: scale(1.06), so the moment a slide stops zooming its transform
   would snap back to the .iplg-hero__img base value scale(1). If that snap
   were tied to .is-active it would fire the instant a slide becomes inactive
   -- but the outgoing slide is still mid-crossfade (opacity 1500ms), so the
   viewer would see the still-visible image jump.

   Instead, .is-zooming is added to a slide when it becomes active and is held
   on the OUTGOING slide through its entire fade-out; hero.js only removes it
   after the 1500ms crossfade has finished, by which point the slide is fully
   transparent and the scale(1.06) -> scale(1) reset is invisible. On initial
   page load hero.js adds .is-zooming to slide 0 so the zoom plays from the
   first paint. hero.js restarts the keyframe on each incoming slide via a
   none/reflow/restore toggle (Fix 1, glitch fix). */
/* EIGHT-DIRECTION Ken Burns drift (4 corners + 4 cardinals). Each keyframe
   scales 1 -> 1.08 and pans toward one of eight compass directions: the four
   corners TL/TR/BL/BR and the four cardinals N (up), S (down), E (right),
   W (left). The horizontal component is 2% and the vertical component is a
   slight 3%, so every slide gets a horizontal and/or vertical direction.
   hero.js picks one direction at random per slide.
   SAFETY (no-edge-exposure): the 1.08 end-scale yields ~4% per-side overflow at
   the end of the animation. Both the scale overflow and each translate axis grow
   linearly from 0, so as long as each axis target stays at or under ~4% the
   overflow always covers the pan and no image edge is ever exposed mid-drift.
   2% horizontal and 3% vertical are both within that 4% per-axis bound. */
@keyframes iplgHeroZoomTL {
  from { transform: scale(1) translate(0, 0); }
  to   { transform: scale(1.08) translate(-2%, -3%); }
}
@keyframes iplgHeroZoomTR {
  from { transform: scale(1) translate(0, 0); }
  to   { transform: scale(1.08) translate(2%, -3%); }
}
@keyframes iplgHeroZoomBL {
  from { transform: scale(1) translate(0, 0); }
  to   { transform: scale(1.08) translate(-2%, 3%); }
}
@keyframes iplgHeroZoomBR {
  from { transform: scale(1) translate(0, 0); }
  to   { transform: scale(1.08) translate(2%, 3%); }
}
/* 4 corners only: hero.js picks one of these four corner keyframes at random
   per slide (see ZOOM_DIRS). The cardinal N/S/E/W keyframes were removed.
   Duration is kept a touch LONGER than the slide rotation interval (9000ms in
   hero.js) on purpose: the zoom is always still in motion when the slide
   changes, so it never finishes early and freezes on screen.
   Easing is LINEAR, not ease-out: ease-out front-loads most of the motion
   into the first ~2s and then decays ~10x, so the image reads as frozen for
   the back half of every slide hold. A constant-velocity (linear) drift
   stays perceptible across the entire 9000ms hold.
   'forwards' holds the final scale(1.06) so the outgoing image stays zoomed
   through its fade-out instead of snapping back while still visible. */
.iplg-hero__slide.is-zooming .iplg-hero__img {
  /* Slower, elegant drift. Kept LINEAR and well longer than the 9000ms slide
     interval so the motion never decelerates/stops before the slide changes.
     iplgHeroZoomBR is just the DEFAULT direction: hero.js overrides animation-name
     with a randomly chosen direction per slide (see ZOOM_DIRS in hero.js), so
     each slide drifts toward a different randomly picked compass direction. */
  animation: iplgHeroZoomBR 32000ms linear forwards;
}

/* 4.2 Scrims — the darkened backdrop. Two gradient layers above the images,
   below the content, guaranteeing text contrast over any of the three photos.
   z-index:2 keeps both scrims above EVERY slide: inactive slides sit at
   z-index:0 and the active/incoming slide at z-index:1, so 2 always wins and
   the darkened backdrop paints over the image through all transitions,
   including the slide-3-to-slide-1 wrap (Task 2). */
/* GRADIENT SCREEN - navy at BOTH edges fading to a transparent center. The hero
   foreground (logo, carousel text, timer bars) lives spread down the LEFT side of
   the hero. This element holds navy at the LEFT and RIGHT edges and fades to fully
   transparent through the CENTER, letting the background photo show through bright
   in the middle of the frame. It replaces the old translucent text box + backdrop
   blur. It sits above the image stage (slides at z-index 0/1) and below the
   foreground content (.iplg-hero__masthead at z-index 3).
   It reads as an edge-weighted navy screen: full navy at BOTH the left and the
   right edges, fading OUT from the left edge to transparent by --hero-screen-fade-start,
   holding a transparent band across the middle, then fading back IN to navy at the
   right edge starting from --hero-screen-fade-end. The background photo shows
   through bright in the center while the held navy edges keep the left-side text
   legible.
   Tunable knobs:
     --hero-screen-alpha      -> navy opacity at the held left and right edges
     --hero-screen-fade-start -> the % across the hero where the left navy finishes fading OUT
     --hero-screen-fade-end   -> the % across the hero where the right navy begins fading IN */
.iplg-hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  /* SUBTLE VERTICAL BARREL SCRIM. A nearly imperceptible black overlay that is
     very slightly darker across the vertical center and fades to fully
     transparent at the top and bottom edges. A gentle center band (40% to 60%)
     keeps it reading as a smooth barrel rather than a single dark line. It sits
     above the photo slides and below the foreground content/masthead (z-index:3),
     so it darkens the image only, never the logo, text, or separators. Tunable
     knob:
       --hero-barrel-alpha -> black opacity across the center band; raise for a
                              stronger barrel, set to 0 to disable. */
  display: block;
  --hero-barrel-alpha: 0.08;       /* center darkening; barely there by default */
  background: linear-gradient(to bottom, transparent 0%, rgba(0, 0, 0, var(--hero-barrel-alpha, 0.12)) 40%, rgba(0, 0, 0, var(--hero-barrel-alpha, 0.12)) 60%, transparent 100%);
}
/* Dark gradient banner across the TOP of the hero: navy at the very top fading to
   transparent before mid-hero. It frames the top of the frame and keeps the white
   top-nav links legible against bright sky. Knob: --hero-topnav-alpha sets how dark
   the top edge is; lower it to soften, set 0 to remove. The fade depth is the last
   gradient stop (transparent at 34%). */
.iplg-hero__scrim-bottom {
  position: absolute;
  inset: 0;
  z-index: 2;
  --hero-topnav-alpha: 0.6; /* darkness at the very top edge; 0 = off */
  background: linear-gradient(
    to bottom,
    rgba(8, 15, 28, var(--hero-topnav-alpha)) 0%,
    rgba(8, 15, 28, calc(var(--hero-topnav-alpha) * 0.5)) 12%,
    transparent 34%
  );
}

/* 4.3 Hero masthead - the foreground brand + carousel layout.
   The foreground content (logo, rotating carousel text, timer bars) spreads
   VERTICALLY down the LEFT side of the hero, on top of the gradient screen
   (.iplg-hero__scrim). No box, border, blur, or rounded panel. The masthead is a
   full-height, left-anchored flex container holding the content column. */
.iplg-hero__masthead {
  /* FULL-HEIGHT, LEFT-ANCHORED FOREGROUND COLUMN (no box chrome). The masthead no
     longer paints any background, blur, border-radius, or offset; the navy "screen"
     that sets the white text now lives on .iplg-hero__scrim (the right-to-left
     gradient) BEHIND this content. The masthead is absolutely positioned to span the
     full hero height down the LEFT side, occupying the left portion of the hero, so
     its content (logo, carousel text, ticks) can spread VERTICALLY via the column's
     space-between. z-index:3 keeps it above the gradient screen (z-index:2).
     Tunable knobs:
       --hero-foreground-width -> share of the hero width the foreground occupies
       --hero-nav-clearance    -> top offset that drops the whole foreground below the nav bar
       --hero-fg-top           -> top inset clearing the site nav
       --hero-fg-bottom        -> bottom inset clearing the stats area
       --hero-fg-x             -> SYMMETRIC horizontal inset (left == right) */
  position: absolute;
  /* NAV CLEARANCE: top offset for the whole foreground (logo + right-half dark screen).
     Set to 0 so the dark screen (.iplg-hero__textrow) now COVERS the nav area again,
     extending to the very top of the hero instead of starting below the nav band. Raise
     this knob (e.g. to the 120px nav height) if you want the screen to start below the
     nav instead. The hero bottom stays full-bleed (bottom:0 below). */
  --hero-nav-clearance: 0;
  top: var(--hero-nav-clearance, 0);
  bottom: 0;
  left: 0;
  z-index: 3;                     /* above the gradient screen (scrim, z-index:2) */
  /* Span the FULL hero width. The foreground is now full-width so the inner content
     box can be horizontally symmetric and a 3-column grid can place the hairline
     divider at the TRUE page center (50%). The gradient screen (.iplg-hero__scrim)
     extent is intentionally NOT changed here. */
  --hero-foreground-width: 100%;
  width: var(--hero-foreground-width);
  /* Clearance insets: clear the nav at top and the stats area at the bottom. The
     horizontal inset is now SYMMETRIC (equal left and right) so the content box is
     centered on the page; the old asymmetric left-only --hero-fg-left
     (clamp(3.5rem,10vw,13rem) left, 0 right) is replaced by a single symmetric knob
     so the divider's auto grid column lands on the page center. */
  /* FULL-BLEED CHANGE: the masthead padding is now ZEROED so the .iplg-hero__col
     fills the entire hero edge to edge (left, right, top, bottom). Each half then
     becomes a true full-bleed 50%, so the right-half dark screen
     (.iplg-hero__textrow) reaches the hero's right edge, top, and bottom, and the
     center seam. The old top/bottom nav/stats clearance that used to live here as
     OUTER padding is now restored as INNER content padding on each half (the
     textrow keeps its own generous padding for the centered carousel text; the
     brandmark centers the logo and adds its own top/bottom logo padding). The
     knobs below are kept declared for easy re-tuning but no longer feed the
     padding shorthand. */
  --hero-fg-top: 0;
  --hero-fg-bottom: 0;
  --hero-fg-x: 0;
  padding: 0;
  /* Flex container holding the content COLUMN (.iplg-hero__col), stretched to the
     full masthead height so the column can distribute the logo/text/ticks
     vertically via space-between. */
  display: flex;
  flex-direction: row;
  /* STRETCH the single content column to the masthead's FULL HEIGHT. Previously this was
     align-items:center, which collapsed .iplg-hero__col to its content height and centered
     it vertically, leaving the right-half dark screen (.iplg-hero__textrow) and the gold
     seam bar SHORT instead of full height. With stretch, the col fills the full masthead
     height (masthead is position:absolute top:0/bottom:0, so full hero height), and its two
     halves (brandmark + textrow, both align-self:stretch) become full height top-to-bottom.
     The lockup content stays centered because each half does its own centering via
     justify-content:center / align-items:center; the masthead no longer needs to center. */
  align-items: center;
  /* Center the now content-sized col (.iplg-hero__col shrinks to its lockup width)
     horizontally within the full-width masthead so the lockup stays page-centered. */
  justify-content: center;
  /* No full-hero darkening screen: the photo shows through cleanly. The logo and
     carousel text carry their own drop shadows, and the box behind the lockup
     (.iplg-hero__col) still provides local legibility backing. */
  background: transparent;
}

/* Content LOCKUP: a GROUPED flex row reading left-to-right as logo, a vertical hairline
   divider, then the carousel text. The three elements sit TOGETHER as one unit; the
   divider falls NATURALLY between the logo and the text and is no longer forced to the
   true page center. justify-content:center keeps the whole grouped lockup horizontally
   centered as a unit within the full-width masthead, and align-items:center
   VERTICALLY CENTERS the logo, divider, and text on a common horizontal centerline.
   column-gap supplies the breathing room on each side of the divider. */
.iplg-hero__col {
  /* CENTERED CONTENT LOCKUP: the col is now a content-sized horizontal group reading
     [logo] [divider] [text], centered as one unit on the full-hero dark screen (the
     screen now lives on .iplg-hero__masthead). It no longer stretches edge to edge as a
     50/50 split; width:fit-content shrinks it to the lockup so justify-content:center on
     the masthead can center the whole group. column-gap supplies the breathing room on
     each side of the divider between the logo and the text. */
  --hero-corner-len: 20px;        /* length of the corner lines / inner horizontals */
  flex: 0 0 auto;                 /* size to the lockup, do not fill the masthead */
  width: fit-content;             /* content-sized lockup group */
  min-width: 0;                   /* let the text shrink/wrap */
  display: flex;
  flex-direction: row;            /* logo | text, side by side */
  align-items: stretch;           /* both boxes the SAME height (the taller text box sets it) */
  justify-content: center;        /* keep the grouped lockup centered */
  column-gap: 0;   /* boxes are adjacent; the inner edges just have no border */
  /* No outline around the lockup. */
  border: none;
  border-radius: 0;
  /* No background screen behind the lockup - the photo shows through fully. */
  background: transparent;
  /* Anchor for the soft-edged box (::before) painted behind the lockup. This col
     lives inside the masthead (z-index:3), so the box, painted at the col level,
     renders above the hero photo. The lockup children are lifted to z-index:1
     (see below) so the logo/divider/text always paint ON TOP of the box. */
  position: relative;
  /* TILTED LOCKUP KNOB (COUNTER-ROTATED ELEMENTS). --hero-lockup-tilt is declared
     here AND consumed here: the col itself rotates, so the lockup (logo +
     carousel text) is PLACED along a diagonal axis as one group. BUT the logo and
     the carousel text are each COUNTER-rotated back to level by their own rules
     (rotate(calc(-1 * var(--hero-lockup-tilt))) on .iplg-hero__brandmark and
     .iplg-hero__textrow), so they render UPRIGHT while still sitting at their
     diagonal positions. The gold divider IS a child of this col, but absolutely
     positioned (.iplg-hero__lockup-divider), so it does not take part in the flex
     layout and is independent of the logo/text shifts. It CANCELS this col's
     inherited rotation and re-leans itself with a skewX instead, so it leans along
     the content diagonal but with FLAT (horizontal) top/bottom ends; it still shares
     the same pivot and its center is locked to the col's rotation axis. Net effect: a
     dramatic diagonal arrangement with a leaning (flat-ended) divider, but
     level/readable logo and text.
       - the dark glass screen is on .iplg-hero__masthead (NOT rotated, level),
       - the Ken Burns photo is in .iplg-hero__stage (NOT rotated),
       - the timer bars .iplg-hero__ticks are a separate sibling of the masthead
         (NOT a descendant of this col), so they stay centered + horizontal.

     ============================================================================
     HERO LOCKUP TUNING SET (six knobs):
       1. --hero-lockup-tilt  (THIS rule, below) -> the ONE angle. It (a) places
          the lockup on a diagonal and leans the gold divider, while (b) the logo
          and text are counter-rotated to STAY UPRIGHT automatically (their
          counter uses calc(-1 * this var); the divider also cancels the col's
          rotation and instead leans via a skewX defaulting to this same var).
          So changing this one value re-poses the whole effect and keeps the logo
          and text upright with no other edits. The gold divider tracks it too: by
          default --hero-divider-skew = this var, so the divider's lean follows the
          tilt (but as a flat-ended skew, see knob 6). positive leans the diagonal
          one way; flip the sign to lean it the other way; larger = more dramatic.
       2. --hero-logo-shift    (on .iplg-hero__brandmark) -> VERTICAL nudge of the
          (upright) logo, straight up/down on screen (negative = up, positive =
          down), within its counter-rotated frame. Default 0.
       3. --hero-logo-shift-x  (on .iplg-hero__brandmark) -> HORIZONTAL nudge of the
          (upright) logo, straight left/right on screen (positive = RIGHT,
          negative = LEFT). Default 0.
       4. --hero-text-shift    (on .iplg-hero__textrow)   -> VERTICAL nudge of the
          (upright) text, straight up/down on screen (negative = up, positive =
          down), within its counter-rotated frame. Default 0.
       5. --hero-text-shift-x  (on .iplg-hero__textrow)   -> HORIZONTAL nudge of the
          (upright) text, straight left/right on screen (positive = RIGHT,
          negative = LEFT). Default 0.
       6. THE GOLD DIVIDER is the short bar between the logo and the text, painted by
          .iplg-hero__content::before. It is centered by auto side margins and has its
          OWN nudge knobs (see that rule):
            --hero-divider-shift-x -> horizontal nudge, +RIGHT / -LEFT (default 0px)
            --hero-divider-shift-y -> vertical nudge,   +DOWN  / -UP   (default 0px)
            --hero-bar-w           -> bar length (default 96px)
            --hero-bar-gap         -> gap below the bar, before the text (default 1.5rem)
     NAMING: *-shift / *-shift-y = vertical (translateY), *-shift-x = horizontal
     (translateX); both act in the on-screen (world/upright) frame.
     Because the logo/text are counter-rotated to level, and both translates sit
     to the LEFT of the rotate() in each transform list (so rotate is applied to
     the point first, then the translates act in the parent frame), the shifts are
     plain on-screen horizontal/vertical nudges, NOT moves along the tilted axis.
     ============================================================================ */
  /* --hero-lockup-tilt: the ONE value that sets the diagonal placement angle of
     the lockup, consumed by the rotate() on this col (and counter-rotated off the
     logo/text, re-applied on the divider; see the tuning-set block above).
     CLIP HEADROOM: the col still rotates the GROUP by 16deg, so the rotated
     bounding box (the clip constraint) is unchanged from the all-tilted version.
     The logo/text are counter-rotated back to UPRIGHT inside that box; an upright
     wide text block at the diagonal position can reach a touch further toward the
     right edge than the tilted version did, but at common desktop widths
     (>=1280px) the upright text-end corner still clears the full-viewport
     .iplg-hero clip with comfortable room. Pushing past ~18-20deg starts bringing
     that corner toward the right edge on ~1280px viewports; ~16deg is the
     dramatic-but-safe default. If you raise the angle and the upright text clips,
     reduce .iplg-hero__lead max-width (~32em) slightly first. Phone zeroes it. */
  --hero-lockup-tilt: 0deg;
  /* Shift the WHOLE hero content (logo + text + buttons + divider) together.
     KNOBS: --hero-content-shift-y (positive = down), --hero-content-shift-x
     (positive = right). */
  --hero-content-shift-x: 20px;
  --hero-content-shift-y: -6vh;   /* lockup vertical position (lifted to leave room for the CTA below) */
  transform: translate(var(--hero-content-shift-x, 0), var(--hero-content-shift-y, 0)) rotate(var(--hero-lockup-tilt));
  transform-origin: center center;
}
/* SOFT-EDGED BOX BEHIND THE LOCKUP. Replaces the retired full-hero gradient
   screen (.iplg-hero__scrim, now display:none). This is a content-sized box: it
   is a pseudo-element on the lockup container, so it auto-wraps just the logo +
   divider + carousel text, NOT the full hero width. A generous blur feathers all
   four edges so the box fades softly into the photo rather than reading as a hard
   rectangle, and the negative inset gives a soft margin around the content while
   leaving enough solid core directly behind the text/logo (the blur eats into the
   fill, so the pad is intentionally large to keep the center adequately opaque).
   Tunable knobs:
     --hero-box-alpha  -> navy fill opacity (the photo still reads through a bit)
     --hero-box-blur   -> edge softness (larger = softer, more feathered edges)
     --hero-box-pad-x  -> horizontal soft margin around the lockup
     --hero-box-pad-y  -> vertical soft margin around the lockup
     --hero-box-radius -> shape of the box (50% renders an ellipse/circle) */

/* DNA-SLIDE-ONLY GRAY BACKING. A slight, soft-edged gray box behind the whole
   lockup (logo + carousel text). It is normally invisible (opacity 0) and fades
   in GRADUALLY only while the DNA photo (slide 2) is the active hero image,
   giving that busier photo a touch more legibility behind the lockup. Painted as
   a ::before on .iplg-hero__col so it auto-wraps the [logo | text] group; z-index
   0 keeps it BEHIND the lockup children (lifted to z-index:1). The fill is a
   solid rounded rectangle and the blur feathers its four edges into the photo,
   so it reads as a soft-edged BOX rather than a hard-cut panel.
   WHICH SLIDES show the box is controlled by gbx-on-* classes on the .iplg-hero
   element (set in index.ejs, toggled live by the #tune panel). For each slide,
   the box reveals only when BOTH its gbx-on-* class is present AND that slide is
   the active hero image (:has(...is-active)). Default set: dna + courthouse. The
   box crossfades in over --hero-graybox-fade (matched to the 2200ms image
   crossfade) and fades back out when the slide changes. Browsers without :has()
   simply never reveal it (it stays at opacity 0), so it degrades cleanly.
   KNOBS (all inherit, so the #tune slider panel or any ancestor can override them):
     --hero-graybox-gray   -> grayscale level 0-255 (0 = black, ~120 = medium gray)
     --hero-graybox-alpha  -> gray fill opacity (slight; raise for a denser box)
     --hero-graybox-blur   -> edge feather (larger = softer)
     --hero-graybox-pad-x  -> horizontal soft margin around the lockup
     --hero-graybox-pad-y  -> vertical soft margin around the lockup
     --hero-graybox-radius -> corner rounding
     --hero-graybox-fade   -> fade-in/out duration
   NOTE: pad/gray/blur/radius are consumed via var() with defaults and are NOT
   declared locally on this pseudo, so values set on an ancestor (.iplg-hero, by
   the tuner) inherit down and win. Only the fixed position shift is local. */
.iplg-hero__col::before {
  content: "";
  position: absolute;
  top: calc(-1 * var(--hero-graybox-pad-y, 32px));
  bottom: calc(-1 * var(--hero-graybox-pad-y, 32px));
  left: calc(-1 * var(--hero-graybox-pad-x, 48px));
  right: calc(-1 * var(--hero-graybox-pad-x, 48px));
  z-index: 0;
  /* Solid rounded-rectangle tint so it reads as a soft BOX behind the lockup.
     --hero-graybox-gray is a single grayscale level used for all three channels
     (0 = black, ~120 = medium gray); --hero-graybox-alpha sets the fill opacity;
     the blur feathers the four edges into the photo so the box has soft sides
     rather than a hard cut. Lower the blur for a crisper box. */
  background: rgba(
    var(--hero-graybox-gray, 36),
    var(--hero-graybox-gray, 36),
    var(--hero-graybox-gray, 36),
    var(--hero-graybox-alpha, 0.24));
  border-radius: var(--hero-graybox-radius, 25px);
  filter: blur(var(--hero-graybox-blur, 60px));
  /* Position nudge: negative X = left, positive Y = down; 0 = centered on the
     lockup. Consumed via var() with defaults and NOT declared locally, so the
     tuner (or any ancestor) can override the offsets by inheritance. */
  transform: translate(var(--hero-graybox-shift-x, 54px), var(--hero-graybox-shift-y, 0));
  opacity: 0;
  transition: opacity var(--hero-graybox-fade, 2200ms) ease-in-out;
  /* Cache the expensive 60px blur as a raster and animate only its opacity.
     Without this promotion the browser can recompute the large blur on every
     frame of the fade, which stalls the whole transition. */
  will-change: opacity;
  pointer-events: none;
}
/* Skyline (slide 1) carries no slide modifier class, so it is matched as the
   active .iplg-hero__slide whose class list has no "--" modifier. */
.iplg-hero.gbx-on-skyline:has(.iplg-hero__slide:not([class*="--"]).is-active) .iplg-hero__col::before,
.iplg-hero.gbx-on-dna:has(.iplg-hero__slide--dna.is-active) .iplg-hero__col::before,
.iplg-hero.gbx-on-courthouse:has(.iplg-hero__slide--courthouse.is-active) .iplg-hero__col::before,
.iplg-hero.gbx-on-microchip:has(.iplg-hero__slide--microchip.is-active) .iplg-hero__col::before {
  opacity: 1;
}

/* Row holding the carousel text and (via .iplg-hero__content) the ticks below it.
   In the horizontal lockup it is the RIGHT element, taking the remaining row width
   so the multi-line carousel sentences form a left-aligned block beside the divider. */
/* GLASS TEXT BOX. The carousel text container is the box. .iplg-hero__textrow was
   chosen over .iplg-hero__content because it is the element that cleanly encloses
   ONLY the rotating text (the ticks were moved out to be a direct child of
   .iplg-hero, so the textrow wraps just the carousel sentences), it is already a
   bottom-anchored flex block, and unlike .iplg-hero__content it does not carry the
   per-container opacity layer that drives the crossfade, so applying a backdrop
   filter here will not interact with the fade animation. The box encloses the text
   ONLY, never the logo (the logo is a separate sibling in the lockup row).

   The box is a VERY LIGHT frosted-glass panel: a near-transparent white fill plus a
   backdrop blur do most of the work. The gold line is NO LONGER painted here: the old
   text-anchored gold RULE (.iplg-hero__entries::before) is retired, and the gold divider
   is now an absolutely-positioned child of .iplg-hero__col (.iplg-hero__lockup-divider),
   centered on the lockup rotation axis, independent of this text. The --hero-gold-bar-* knobs below
   are therefore inert (kept for reference). The box is square on the LEFT, rounded RIGHT.

   Tunable knobs:
     --hero-glass-alpha        -> gray fill opacity (soft gray screen over the photo)
     --hero-glass-blur         -> frost strength of the backdrop blur
     --hero-gold-bar-w         -> (INERT) was the retired text-anchored gold rule thickness
     --hero-gold-bar-gap       -> (INERT) was the retired text-anchored gold rule gap
     --hero-glass-radius       -> right-corner rounding (left stays square) */
.iplg-hero__textrow {
  /* Carousel text directly under the logo, inside the centered vertical lockup. The
     gold divider bar (.iplg-hero__content::before) sits between the logo and this text.
     The --hero-text-shift knobs remain as optional nudges (default 0). */
  width: 100%;
  margin-left: 0;             /* divider sits right at the logo section's edge, so its gradient meets it */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;    /* center the text vertically within the full-height gray panel */
  --hero-text-shift: 6px;     /* row lockup: nudge the carousel text down a hair */
  --hero-text-shift-x: 0px;
  transform: translate(var(--hero-text-shift-x), var(--hero-text-shift));
  /* Background gradient removed: the single seamless gradient now lives on
     .iplg-hero__col so it flows uninterrupted across both boxes. */
  background: transparent;
  /* top | right | bottom | left - the LEFT padding (~2rem) approximately matches
     the logo's left inset (the logobox's 2rem frame padding) so the text/buttons
     sit in by the same distance the logo does. */
  padding: clamp(2rem, 5vh, 3.5rem) 1.25rem clamp(2rem, 5vh, 3.5rem) 3rem;
  position: relative;
}
/* Thin 1/2px divider between the logo box and the text box (the seam), inset from
   the top and bottom so there are gaps at each end. The rule softly FADES out at
   both ends via a vertical gradient (transparent -> white -> transparent) so it
   reads as a feathered rule rather than a hard-capped bar. KNOB: --hero-seam-inset. */
.iplg-hero__textrow::before {
  content: "";
  position: absolute;
  left: 0;
  top: var(--hero-seam-inset, 28px);
  bottom: var(--hero-seam-inset, 28px);
  width: 1px;
  background: linear-gradient(
    to bottom,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.85) 15%,
    rgba(255, 255, 255, 0.85) 85%,
    rgba(255, 255, 255, 0) 100%
  );
  pointer-events: none;
}
/* Vertical divider between the logo (left) and the text box: a white rule on the
   text box's left edge, INSET from the top and bottom so it is shorter than the
   full box height. KNOB: the top/bottom insets set how much shorter it is. */

/* Hero CTA buttons row (Contact us / Read more), below the carousel text inside
   the text box. The buttons are white-outline on the dark panel and invert to a
   white fill with navy (readable) text on hover/focus. */
/* CTA block (prompt line + two buttons), centered horizontally and parked roughly
   HALFWAY between the centered lockup ("middle content") and the slide-timer bars
   at the bottom. Absolutely positioned so it does not affect the lockup centering.
   KNOB: --hero-cta-bottom sets the vertical spot. */
.iplg-hero__cta-wrap {
  position: absolute;
  left: 50%;
  bottom: var(--hero-cta-bottom, 14%);
  transform: translateX(-50%);
  width: 90%;
  max-width: 44rem;
  text-align: center;
}
.iplg-hero__cta-prompt {
  font-family: 'Cormorant Garamond', Georgia, serif;
  font-size: clamp(1.4rem, 2.6vw, 1.9rem);
  font-weight: 500;
  line-height: 1.3;
  color: #ffffff;
  margin: 0 0 1.5rem;
  text-shadow: 0 2px 5px rgba(0, 0, 0, 0.6);
}
.iplg-hero__cta {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;        /* space between the Contact us and Read more buttons */
  justify-content: center;   /* center the buttons under the prompt */
}
.iplg-hero__btn {
  display: inline-block;
  background: rgba(255, 255, 255, 0.015); /* uniform white screen behind the label - nearly fully transparent */
  border: 2px solid #ffffff;
  border-radius: 0;
  color: #ffffff;
  font-family: 'Montserrat', sans-serif;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 2px;
  text-transform: uppercase;
  padding: 0.75rem 1.5rem;
  text-decoration: none;
  text-shadow: 0 2px 5px rgba(0, 0, 0, 0.6);   /* keep the label legible over the photo */
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
  transition: background-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
}
.iplg-hero__btn:hover,
.iplg-hero__btn:focus {
  background-color: #ffffff;
  color: #163861;   /* navy text on the white fill - readable */
  text-shadow: none; /* no shadow once the label is navy-on-white */
  text-decoration: none;
}
.iplg-hero__btn-arrow {
  margin-left: 0.5em;   /* gap between the "Read more" label and its right arrow */
}

/* RETIRED SEAM BAR. The gold rule used to live here as a full-height vertical bar at
   left:0 of the right half (the panel seam between the brandmark half and this screen),
   far from the centered carousel text. The firm wanted the gold line RIGHT NEXT TO the
   quote, reading as part of the text group, not stranded out at the seam. So this
   pseudo-element is now inert (content:none) and the gold rule is painted instead by
   .iplg-hero__entries::before, which is anchored to the TEXT block and hugs its left
   edge. The --hero-gold-bar-* knobs are reused there. The dark glass screen still fills
   the full right half (the .iplg-hero__textrow background/blur are untouched); only the
   gold bar moved off the seam and over to the text. */

/* RETIRED: the "- IPLG" brand signature. This ::after used to paint a small
   "- IPLG" attribution in normal flow below the rotating carousel text so the
   lines read as attributed quotes. Removed per firm direction; content:none turns
   the pseudo-element off entirely so it paints nothing and takes no space. The
   rest is left inert for reference. */

/* RETIRED: the old text legibility box ::before. It used to paint a content-sized
   translucent box behind the masthead content. The navy "screen" now lives on
   .iplg-hero__scrim (the right-to-left gradient) behind the foreground, so this
   pseudo-element is kept declared but inert (no content/background) and nothing
   double-paints. The markup in index.ejs is unchanged. */

/* The large IPLG brand mark. flex:0 0 auto so the logo row sizes to the logo and
   never shrinks. It sits directly above the carousel text as part of the
   vertically centered stack (the column uses justify-content: center with a gap).
   --hero-logo-drop is kept as a tunable top-margin knob but defaults to 0, since
   the stack is centered rather than top-anchored. */
.iplg-hero__brandmark {
  /* LOGO ELEMENT of the lockup. Sized to the logo (flex:0 0 auto), not a half. It
     centers the logo and sits immediately left of the divider in the centered lockup. */
  flex: 0 0 auto;
  display: flex;
  /* STACK the brandmark as a column so the logo sits on top and the gold ::after
     line lands directly BELOW it (a flex row would place the ::after line beside
     the logo). The logo (and gold bar under it) are now centered in both axes. */
  flex-direction: column;
  align-items: center;          /* center the logo (and the gold line below it) horizontally within the half */
  justify-content: center;      /* center the stacked logo block */
  /* Stretch to the same height as the text box (the logo stays vertically centered
     inside via justify-content: center). */
  align-self: stretch;
  /* Background gradient removed: the single seamless gradient now lives on
     .iplg-hero__col so it flows uninterrupted across both boxes. */
  background: transparent;
  /* Lift the logo ABOVE the soft box (.iplg-hero__col::before, z-index:0) so the
     brandmark always paints on top of the box fill. */
  position: relative;
  z-index: 1;
  /* NOTE: the small upward nudge (--hero-logo-offset-y) is applied to the logo IMG
     itself (.iplg-hero__logo), NOT here, so it does not also drag the ::before panel
     up off the row. The brandmark box stays flush with the row (and thus the glass
     text box) while only the glyph shifts. */
  /* Symmetric top/bottom padding so the logo sits truly centered in its box.
     Tunable via the knobs (keep them equal to keep the logo centered). */
  padding-top: var(--hero-logo-pad-top, 0);
  padding-bottom: var(--hero-logo-pad-bottom, 1.5rem);
  padding-right: 0;
  /* HERO LOCKUP TUNING KNOB 2 of 3 (see --hero-lockup-tilt on .iplg-hero__col).
     --hero-logo-shift nudges the WHOLE logo block. The logo is COUNTER-ROTATED
     here (rotate(calc(-1 * var(--hero-lockup-tilt)))) to cancel the col's tilt,
     so the logo glyph renders UPRIGHT/level even though the col places it along
     the diagonal axis. Because the counter-rotation un-tilts this element's
     frame, --hero-logo-shift is now a PLAIN vertical nudge (straight up/down on
     screen) within the upright logo frame:
       negative = moves the logo up,
       positive = moves it down.
     Default 0 = no shift. Keep to modest nudges: large values push the logo
     toward a hero edge and can approach a clip. The counter-rotation tracks
     --hero-lockup-tilt automatically via calc(-1 * ...), so changing the angle
     keeps the logo upright. The fine per-glyph nudge lives on .iplg-hero__logo
     and composes under this. --hero-logo-shift-x is the HORIZONTAL companion
     (translateX): positive = moves the logo RIGHT, negative = LEFT, straight
     across the screen. Because translateX/translateY sit to the LEFT of the
     rotate() in the transform list, the rotate is applied to the point first
     (defining the upright orientation) and the two translates then act in the
     PARENT (world/on-screen) frame, so both shifts move the logo straight
     horizontally/vertically on screen, NOT along the tilted axis. */
  --hero-logo-shift: 0px;     /* no vertical nudge, so the logo box top aligns with the text box top */
  --hero-logo-shift-x: 0px;
  transform: translateX(var(--hero-logo-shift-x)) translateY(var(--hero-logo-shift)) rotate(calc(-1 * var(--hero-lockup-tilt)));
  transform-origin: center center;
}

/* CONTENT-WIDTH WRAPPER AROUND THE LOGO. The brandmark is a full 50% half that
   centers its content, so a bar anchored to the brandmark's left edge would pin to
   the panel edge, not the logo. This box hugs the logo (width: fit-content == the
   logo's rendered width) and is centered as a group in the half by the brandmark's
   align-items/justify-content: center. The gold bar is a ::after on THIS box, so it
   can anchor to the LOGO's left edge (plus an inset) and travel with the logo. */
.iplg-hero__logobox {
  display: flex;
  flex-direction: column;        /* logo on top, gold bar (::after) directly below it */
  width: fit-content;            /* shrink to the logo's width so the box's left edge == the logo's left edge */
  align-items: stretch;          /* the bar's own margins control its length and left anchoring, not stretch */
  /* Vertical offset for the whole logo group (logo + gold bar ::after) within the
     left section. Reset to 0 so the group relies on the brandmark's centering;
     tunable via the knob (negative translateY moves it up). */
  transform: translateY(var(--hero-logobox-offset-y, 0));
  /* Corner-bracket frame: only the TOP-LEFT and BOTTOM-RIGHT corners are drawn
     (L-shaped brackets via the ::before / ::after below), not a full border. The
     padding holds the brackets off the glyph; position:relative anchors them.
     KNOBS: --hero-logo-border (thickness), --hero-logo-frame-pad (inset),
     --hero-logo-corner (bracket arm length). */
  position: relative;
  padding: var(--hero-logo-frame-pad, 1rem) var(--hero-logo-frame-pad, 2rem);
}

/* Corner brackets removed per firm direction: the logo now reads as a clean
   glyph with no L-shaped frame. The pseudo-elements are turned off (content:none
   generates nothing and takes no space); the logobox padding is kept so the
   logo still has breathing room within the lockup. */
.iplg-hero__logobox::before,
.iplg-hero__logobox::after {
  content: none;
}

.iplg-hero__logo {
  display: block;
  width: auto;
  /* Larger than the 88px nav logo. Height drives the size; width follows the
     ~1.486:1 aspect ratio. Sized so the brandmark reads large in the upper area of
     the left foreground column while staying within the masthead's top/left
     clearance insets. */
  height: clamp(165px, 21vh, 290px);  /* LOGO SIZE knob: height drives it, width follows the ~1.486:1 aspect; tune within this band */
  /* OPTICAL LEFT-EDGE ALIGNMENT KNOB. The logo IMAGE carries a little built-in
     transparent padding on its left side, so its VISIBLE glyph edge sits slightly
     inset from the box's left edge. Because the box's left edge already lines up
     with the masthead's left padding (and therefore with the text row below),
     that internal whitespace makes the logo read as nudged RIGHT of the text. A
     small NEGATIVE left margin pulls the logo box left by exactly the image's
     internal pad, so the visible glyph edge lands on the same vertical line as
     the carousel text beneath it. --hero-logo-inset is the tunable
     compensation (negative = pull left); start modest and fine-tune to taste.
     This is desktop/tablet only - the brandmark is display:none below 600px, so
     the negative margin never applies on mobile. The value is small enough that
     the logo stays well inside the panel's left padding (clamp 2.25rem..10rem). */
  --hero-logo-inset: 0;          /* neutralized: the logo is now CENTERED in the left band, so the old left-edge optical pull no longer applies */
  margin-left: var(--hero-logo-inset);
  /* Logo is fully opaque (decoupled from --hero-soft-alpha). */
  opacity: 1;
  /* Drop shadow matching the carousel text treatment (.iplg-hero__lead
     text-shadow: 0 2px 5px / 0 1px 14px black), so the logo and text read as a
     lifted, matched set. The trailing blur(--hero-logo-soften) softens the logo
     edges slightly; raise it for a softer logo, set to 0 for fully sharp. */
  filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.75)) drop-shadow(0 1px 14px rgba(0, 0, 0, 0.45)) blur(var(--hero-logo-soften, 0.5px));
  /* Paint the logo image ABOVE the brandmark panel. The brandmark is
     position:relative; z-index:1, and its ::before panel sits at z-index:0 within
     that context; lifting the img to z-index:1 here guarantees the glyphs render
     ON TOP of the panel fill rather than under it. */
  position: relative;
  z-index: 1;
  /* Optical vertical nudge of JUST the glyph (kept here off the brandmark so it
     does not drag the ::before panel up off the row). Reset to 0 so the logo relies
     on the container's centering; --hero-logo-offset-y stays tunable (negative
     values move the logo up within its centered half). */
  transform: translateY(var(--hero-logo-offset-y, -6px));
}

/* 4.5 Carousel content column: the rotating text + ticks inside the
   .iplg-hero__textrow. It does not own the left offset or the bottom anchoring
   (the masthead/screen does); it is just the flex column that takes the
   textrow width. */
.iplg-hero__content {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  padding: 0;
  /* Constant layer alpha UNDER the per-lead crossfade (never set opacity on the lead
     itself or it fights the crossfade). */
  opacity: var(--hero-soft-alpha);
  /* Optional static vertical nudge knob (kept; default 0). */
  transform: translateY(var(--hero-text-offset-y, 0));
}

/* The rotating lead paragraphs — all stacked in the same spot,
   only the active one is visible. */
.iplg-hero__entries {
  /* The leads are stacked into a SINGLE grid cell (each placed at grid-area 1/1 in
     the lead rule), so the cell is always as tall as the tallest sentence and there
     is no resize/jump on the slide-3-to-slide-1 wrap. */
  display: grid;
  justify-items: center;
  width: 100%;
  /* Capped narrower than the old stacked measure so the side-by-side
     [logo | line | text] lockup stays balanced and does not blow out to the
     hero edges on wide desktops. Tablet/phone blocks below narrow it further. */
  max-width: var(--hero-text-measure, min(34rem, 42vw));
  margin-inline: auto;
  text-align: left;
  /* KEN BURNS PARALLAX. hero.js sets an inline transform per slide (translate
     toward the corner the image is panning to, magnitude from --hero-text-drift-*).
     This transition GLIDES the whole text block to that corner over a 26s envelope
     with a DRAMATIC ease-in-out curve (cubic-bezier easeInOutCubic): the text gently
     accelerates off its current spot, drifts, and eases down again, instead of the
     old constant-speed (linear) glide that read as mechanical and fast. Because the
     12s slide interval redirects it mid-curve, each slide re-eases from rest toward
     the new corner -- a soft settle-and-glide pulse synced to the carousel, with no
     snap-back. Two SPEED/feel knobs: the duration (longer = slower, gentler peak) and
     the easing curve (more aggressive bezier = more dramatic in/out). transform-only,
     so it composes cleanly under the per-lead opacity crossfade. */
  transition: transform 26000ms cubic-bezier(0.65, 0, 0.35, 1);
  will-change: transform;
}

/* RETIRED: the text-anchored gold rule. This ::before used to paint a short
   vertical gold bar hugging the LEFT edge of the carousel text block, anchored
   INSIDE the entries wrapper so it moved with the text. The gold divider is now
   an absolutely-positioned child of .iplg-hero__col (.iplg-hero__lockup-divider),
   centered on the col's rotation axis, so it no longer tracks the logo or the text.
   content:none turns this pseudo-element off entirely so it paints nothing and
   takes no space; the rest of the rule is left inert for reference. The old
   --hero-divider-shift knob that drove it is gone (replaced by the independent
   --hero-divider-shift-x / --hero-divider-shift-y on the standalone divider). */

.iplg-hero__lead {
  margin: 0;
  /* LEFT-ALIGNED carousel text. With the gold rule now sitting beside the text, a rule
     against center-aligned lines looks detached (short lines pull away from the rule). So
     the carousel lines are left-aligned and the rule hugs a clean left text edge; the
     whole rule+text group is still centered in the right half (see .iplg-hero__entries
     width:max-content + margin-inline:auto). */
  text-align: left;
  justify-self: center;
  /* Hero subtext now uses the firm page-title serif, Cormorant Garamond, the
     same family used by .iplg-page-title (weight 500, see style.css). The font
     is loaded site-wide via views/partials/head.ejs (weights 300-700), so no
     @import or link is needed here. Cormorant has a smaller x-height than Karla
     and reads lighter/smaller at the same px, so the weight is bumped to 500
     (matching the page-title treatment) and the size raised to ~2rem so the
     visual size is comparable to, or a touch larger than, the old Karla 1.55rem.
     font-size and font-weight are the obvious tunables. The .iplg-hero__entries
     grid cell auto-sizes to the tallest sentence and the lead is bottom-anchored
     (align-self: end), so the size cannot clip or shift. */
  font-family: 'Cormorant Garamond', serif;
  font-size: 1.9rem;
  font-weight: 500;
  line-height: 1.4;
  color: #fff;
  max-width: 100%;

  /* Directional drop shadow for contrast on the bright photo, matching the logo
     and divider treatment so the three read as a lifted, matched set. The two
     soft 0 0 ... halo layers (the evenly-spread glow) were removed; a single
     downward-offset shadow remains so the text holds against the photo without
     the halo. Offset, blur, and alpha are tunable. */
  text-shadow: 0 2px 5px rgba(0, 0, 0, 0.75), 0 1px 14px rgba(0, 0, 0, 0.45);

  /* Fix 6: all three leads are placed in the SAME single grid cell so they
     stack on top of one another and each contributes to the cell's height
     (see .iplg-hero__entries). This replaces the old position:absolute,
     which removed inactive leads from layout and let the box resize on
     every slide change. */
  grid-area: 1 / 1;
  /* Fix 7 (gap fix, follows the Fix 6 grid-stacking fix): bottom-anchor every
     lead inside the locked grid cell. The cell is always as tall as the
     TALLEST sentence (slide 3, ~6 lines); the leads used to be top-aligned in
     it, so a SHORT sentence (slide 1, ~2 lines) sat at the top and left dead
     space between itself and the .iplg-hero__ticks row below. align-self:end
     pins each lead to the bottom edge of the cell (the edge nearest the
     ticks), so the gap to the ticks is the same consistent margin on every
     slide and the leftover space moves ABOVE the short sentences, into the
     invisible dark hero area. The cell still auto-sizes to the tallest
     sentence, so there is still no resize and no jump. justify-self and the
     left/40em horizontal behavior are intentionally left untouched.
     CHANGED end -> center: each sentence is now vertically centered in the locked
     cell, so short slides no longer sit at the bottom. Trade-off: the gap down to
     the slide ticks varies slightly per slide instead of staying constant. */
  align-self: center;
  /* DISCREET FADE-OUT-THEN-IN (inactive = exit end state AND entry start state).
     The directional horizontal slide was reverted: the only transform left here
     is the vertical lift (--hero-lead-lift), which moves the text up/down
     WITHOUT moving the "- IPLG" signature (.iplg-hero__entries::after is a
     separate pseudo-element that flows BELOW this leads cell; a transform here
     only repaints within the cell and does not change the cell's layout height,
     so the signature stays put). The --hero-slide-shift knob is intentionally
     left declared-but-unused (see the :root block) so the old slide can be
     re-enabled later without re-deriving the value. */
  transform: translateY(var(--hero-lead-lift, 0));
  z-index: 0;
  opacity: 0;
  /* The OUTGOING sentence crossfades OUT while the incoming one fades IN, matching
     the background image crossfade (opacity 2200ms ease-in-out, simultaneous). Only
     opacity is transitioned, so the text never drifts sideways. */
  transition: opacity 2200ms ease-in-out;
  /* Raster the glyphs once and animate only opacity, so the text is not
     re-rendered on the main thread every frame of the crossfade. */
  will-change: opacity;
  pointer-events: none;
}
.iplg-hero__lead.is-active {
  /* Fix 6: no position swap. With all leads sharing one grid cell every lead
     already contributes to the box height, so the active lead no longer has
     to take layout space on its own; it only needs to paint on top. */
  z-index: 1;                  /* incoming lead always crossfades on top (Fix 2) */
  opacity: 1;
  /* Rest position: vertical lift retained, no horizontal shift any more. */
  transform: translateY(var(--hero-lead-lift, 0));
  /* The INCOMING sentence crossfades IN over 2200ms ease-in-out, simultaneous with
     the outgoing fade-out, matching the background image crossfade. */
  transition: opacity 2200ms ease-in-out;
  pointer-events: auto;
}
/* First-load priming. The first lead is born with .is-active in the HTML, so
   without help it would render at its end-state (opacity 1) with no fade on
   first paint. hero.js adds .iplg-hero--primed for one frame (pinning the active
   lead to opacity 0), then removes it so the first entry fades in. Only opacity
   is pinned now; the slide transform was reverted (Fix 4). */
.iplg-hero--primed .iplg-hero__lead.is-active {
  opacity: 0;
}
/* The 600ms fade-IN delay must NOT apply to the very first on-load fade-in, or
   the hero would sit blank for ~600ms right after priming is removed. hero.js
   adds the permanent .iplg-hero--loaded class only on the FIRST auto-advance
   (after the initial fade-in has already played), so the initial fade-in runs
   with zero delay (this rule wins until loaded is set) while every later
   slide-to-slide handoff keeps the 600ms out-then-in delay from .is-active. */
.iplg-hero:not(.iplg-hero--loaded) .iplg-hero__lead.is-active {
  transition-delay: 0s;
}

/* The progress ticks, one per slide, the active tick fills over the interval.
   The ticks are a DIRECT CHILD of .iplg-hero (moved out of .iplg-hero__content
   in the markup), so they are absolutely positioned near the BOTTOM of the hero.
   The lockup (logo + text) is now centered as one group on the full hero, so the
   ticks center on the WHOLE hero: left:50% / translateX(-50%). Because .iplg-hero is
   position:relative, this centers the ticks at the bottom-middle of the full frame.
   z-index:3 keeps them above the scrim (z-index:2). The carousel JS still targets
   .iplg-hero__tick by class, so the rotation / progress fill is unaffected.
   --hero-ticks-bottom is the clearance from the hero's bottom edge. The phone
   breakpoint also uses left:50% (same centering on the narrow stacked layout). */
.iplg-hero__ticks {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  --hero-ticks-bottom: clamp(1.5rem, 4vh, 3rem);
  bottom: var(--hero-ticks-bottom);
  z-index: 3;
  display: flex;
  gap: 8px;
  justify-content: center;
}
.iplg-hero__tick {
  position: relative;
  flex: 0 0 auto;
  width: 56px;
  height: 2px;
  background-color: rgba(255, 255, 255, 0.14);
  /* Clickable slide selector. Reset button chrome and keep the exact 56x2 bar;
     the ::after below enlarges only the hit area (no visual/layout change). */
  border: 0;
  padding: 0;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
/* No outline on mouse click; keep a visible ring for keyboard users only. */
.iplg-hero__tick:focus {
  outline: none;
}
.iplg-hero__tick:focus-visible {
  outline: 3px solid rgba(255, 255, 255, 0.7);
  outline-offset: 4px;
}
/* Modest hit area: a little taller than the 2px bar for clickability, but
   small enough not to feel like a large invisible target. */
.iplg-hero__tick::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -6px;
  bottom: -6px;
}
.iplg-hero__tick > i {
  display: block;
  height: 100%;
  width: 0%;
  background-color: rgba(255, 255, 255, 0.55);
}
.iplg-hero__tick.is-active > i {
  width: 100%;
  /* The fill duration is DERIVED from the rotation interval: hero.js publishes
     the interval it is actually running as --hero-tick-fill on .iplg-hero, so the
     bar always finishes exactly as the slide changes and the two can never desync
     (see the note in hero.js). The 9000ms literal is only the no-JS fallback and
     should match the current `interval` value in hero.js. */
  transition: width var(--hero-tick-fill, 9000ms) linear;
}
/* Ticks are laid out by flex (each takes its own slot, no overlap), so they
   have no last-to-first stacking issue like the slides and leads. The active
   tick is still raised for consistency and to keep its fill bar crisp. */
.iplg-hero__tick.is-active {
  z-index: 1;
}

/* The stats bar used to be absolutely positioned over the bottom of the hero.
   It has been relocated to a standalone section directly under the brand bar
   (see index.ejs and the .iplg-stats-bar rules later in this file), so the
   former hero-overlay positioning, white-on-image text colors, and mobile-hide
   have been removed. .iplg-hero keeps position:relative for its own stage. */
.iplg-hero { position: relative; }

/* Four-offices rollup: light band on the homepage. The full-photo cards below
   carry their own city/address/CTA text overlaid on the photo (with a dark
   gradient for legibility), so the section behind them is kept light - a clean
   white close that matches the rest of the site and keeps the heading and city
   names readable. home.css loads only on the homepage, so these overrides do
   not affect the office-rollup anywhere else. */
.iplg-office-rollup {
  background-color: #ffffff;   /* light */
}
.iplg-office-rollup-heading {
  color: #0a1e3c;   /* dark navy, readable on the light band */
}
.iplg-office-rollup .iplg-section-lead {
  color: rgba(10, 30, 60, 0.75);
}

/* Full-splash office cards. The shared light cards (#f4f6fa body, navy text)
   glow too bright on the black section, so on the homepage each card becomes a
   single darkened photo with the city, address, and CTA overlaid at the bottom.
   The cards are smaller (a short landscape splash) and the grid is tighter. */
.iplg-office-rollup-grid {
  /* 12-col track grid so spans land on exact halves/thirds. Silicon Valley
     (1st child) takes the centered middle 6 cols on row 1 (= half width, same
     as the old 2-up card width); Fresno / Washington D.C. / San Diego each
     take 4 cols on row 2 (= one third, a "services card" width). */
  grid-template-columns: repeat(12, 1fr);
  gap: 30px;                                /* match the service-card gutter */
}
.iplg-office-rollup-item:nth-child(1) { grid-column: 4 / 10; grid-row: 1; } /* Silicon Valley: middle 6 cols, centered, row 1 */
.iplg-office-rollup-item:nth-child(2) { grid-column: 1 / 5;  grid-row: 2; } /* Fresno: cols 1-4 (one third) */
.iplg-office-rollup-item:nth-child(3) { grid-column: 5 / 9;  grid-row: 2; } /* Washington, D.C.: cols 5-8 (one third) */
.iplg-office-rollup-item:nth-child(4) { grid-column: 9 / 13; grid-row: 2; } /* San Diego: cols 9-12 (one third) */
.iplg-office-rollup-item {
  background: #0a0a0a;
  border: none;
}
.iplg-office-rollup-item:hover {
  box-shadow: 0 14px 30px rgba(0, 0, 0, 0.55);
}
.iplg-office-rollup-link {
  position: relative;
  display: block;
  /* Desktop 12-col grid: give all four cards one shared, fixed height instead
     of a width-derived aspect-ratio. A width-derived 4/3 ratio diverges between
     the two widths - the half-width Silicon Valley card (~555px) would compute
     to ~416px tall while the third-width cards (~360px) hit the 280px min-height
     floor (their 4/3 height ~270px is below it). Locking every card to the same
     280px the bottom row already renders at makes Silicon Valley match them
     exactly, with no responsive drift, and leaves the bottom row unchanged.
     The mobile block below restores a width-appropriate ratio when stacked. */
  aspect-ratio: auto;
  height: 280px;
  min-height: 0;
}
/* Photo fills the whole card instead of sitting in a fixed-ratio strip. */
.iplg-office-rollup-media {
  position: absolute;
  inset: 0;
  aspect-ratio: auto;
  overflow: hidden;            /* clip the overfilled image at every scale */
  background: #0a0a0a;
}
.iplg-office-rollup-image {
  /* Overfill the media box by 1px on every side so neither the rest state nor
     the hover scale can ever expose the dark #0a0a0a container edge (the
     sub-pixel seam, most visible along the bottom). Base style.css sets
     width/height: 100% and object-fit: cover on a static element; we override
     to absolute and oversize, keeping object-fit: cover so the photo still
     crops to fill rather than stretch. */
  position: absolute;
  inset: -1px;
  width: calc(100% + 2px);
  height: calc(100% + 2px);
  object-fit: cover;
  filter: brightness(0.82);    /* lightly dimmed, not dark */
  /* Promote to a GPU layer for a clean hover scale. */
  transform: scale(1);
  transform-origin: center;
  backface-visibility: hidden;
  will-change: transform;
  transition: transform 0.5s ease, filter 0.3s ease;
}
.iplg-office-rollup-item:hover .iplg-office-rollup-image {
  transform: scale(1.08);
  filter: brightness(0.92);
}
/* Fresno's skyline is a thin band between a big sky and the freeway foreground,
   so at the shared cover crop the city reads small. Oversize the image inside
   its clipped media box (top-biased so more sky is trimmed than foreground) to
   push the city front, centered, and larger. Sizing, not transform, so the
   hover scale above still animates. Scoped to Fresno via its filename. */
.iplg-office-rollup-image[src*="fresno"] {
  top: -28%;
  left: -20%;
  width: 140%;
  height: 140%;
}
/* Bottom gradient for text legibility, lighter overall so the photo stays bright. */
.iplg-office-rollup-media::after {
  background: linear-gradient(
    to top,
    rgba(0, 0, 0, 0.8) 0%,
    rgba(0, 0, 0, 0.3) 40%,
    rgba(0, 0, 0, 0.05) 100%
  );
}
.iplg-office-rollup-body {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  display: block;
  text-align: left;
  padding: 1.1rem 1.25rem 1.2rem;
}
.iplg-office-rollup-city {
  color: #ffffff;
  font-size: 1.3rem;
  margin: 0 0 0.35rem;
}
.iplg-office-rollup-address {
  color: rgba(255, 255, 255, 0.82);
  margin: 0 0 0.7rem;
}
.iplg-office-rollup-cta {
  color: #ffffff;
}

/* Mobile: collapse the 12-col homepage layout back to a single stacked column.
   Matches the base style.css office-rollup mobile breakpoint (575px) so all
   four cards stack in office order; the desktop nth-child spans are reset so
   they do not leak into the single-column flow. */
@media (max-width: 575px) {
  .iplg-office-rollup-grid {
    grid-template-columns: 1fr;
  }
  .iplg-office-rollup-item:nth-child(1),
  .iplg-office-rollup-item:nth-child(2),
  .iplg-office-rollup-item:nth-child(3),
  .iplg-office-rollup-item:nth-child(4) {
    grid-column: auto;
    grid-row: auto;
  }
  /* Stacked full-width cards: drop the fixed desktop height and let the photo
     keep a sensible proportion at the full column width. */
  .iplg-office-rollup-link {
    height: auto;
    aspect-ratio: 4 / 3;
  }
}

/* 5. Responsive - hero foreground reflow. */

/* INTERMEDIATE (601px - 1200px): the content-sized lockup [logo | divider | ~32em
   text] grows wide enough that its outer edges (logo left, text right) crowd the
   viewport edges with little breathing room. Two additive fixes, both of which
   PRESERVE the column-gap (divider clearance) on .iplg-hero__col:
     1. Constrain the centered col to a max-width inset from the viewport edges by an
        --hero-edge-pad on EACH side, kept centered with margin-inline:auto, so the
        lockup can never reach the screen edges.
     2. Narrow the carousel TEXT measure (.iplg-hero__entries / .iplg-hero__lead
        max-width, 32em -> ~26em) so the whole lockup gets narrower (the text wraps to
        more lines) and fits inside that edge-padded width. The squeeze is absorbed by
        the TEXT, NOT by pulling the logo and text inward over the divider; the col's
        column-gap is untouched here, so the logo<->divider<->text spacing is intact.
   This range only narrows/pads; the stacked band (<=1319px) and the medium horizontal
   band (1320-1400px) below in the cascade refine the lockup layout itself, and the
   <=600px phone (where the logo is hidden, the divider shows as a short horizontal
   accent above the text, and the text centering is tuned) is untouched here. */
@media (max-width: 1200px) {
  .iplg-hero__col {
    /* EDGE PADDING for the lockup: cap the centered col so its left/right edges always
       clear the viewport by --hero-edge-pad, and keep it centered. fit-content still
       shrink-wraps the lockup when it is narrower than this cap; the cap only bites once
       the lockup would otherwise reach the edges. The column-gap (divider clearance) is
       NOT changed - the cap narrows the TEXT (below), not the gap. KNOB: --hero-edge-pad. */
    --hero-edge-pad: clamp(1.5rem, 4vw, 3rem);
    max-width: calc(100vw - 2 * var(--hero-edge-pad));
    margin-inline: auto;
  }
  /* Narrow the carousel text MEASURE so the lockup fits within the edge-padded width.
     Both the entries cap and the lead cap drop together (they must match so neither
     re-caps the box) from 32em to 26em; the text wraps to more lines instead of pushing
     the lockup to the screen edges. KNOB: this max-width is the text measure. */

  /* MEDIUM-RANGE RIGHT BREATHING ROOM. The base textrow padding is 0; on medium
     screens give the carousel text box a RIGHT-only pad so the (center-aligned,
     measure-capped) text block is not flush to the box's right edge. RIGHT ONLY:
     no left pad is added (that would push the text toward the centered divider)
     and the col's column-gap / divider clearance is untouched. This is outside
     the text measure cap above, so it adds breathing room without narrowing the
     text. The <=600px phone block's full `padding:` shorthand comes later in the
     cascade and resets all four sides, so the phone keeps its own symmetric pad.
     KNOB: padding-right below. */

}

/* Tablet: the left foreground half-width is getting tight, so widen it toward
   most of the hero and trim the left inset. The gradient screen (.iplg-hero__scrim)
   keeps its right-to-left fade; widen the screen's transparent point a little so it
   does not crowd the now-wider text column. The full-height column still distributes
   logo (top) and text/ticks (bottom) via space-between. */
@media (max-width: 991px) {
  .iplg-hero__scrim {
    --hero-screen-fade-end: 82%;   /* fade later so the wider text stays on navy */
  }
  .iplg-hero__masthead {
    /* The foreground is already full-width with a symmetric horizontal inset at the
       base rule, so the 1fr auto 1fr grid keeps the divider centered here. Tighten the
       symmetric horizontal inset a touch for the narrower viewport. */
    --hero-fg-x: clamp(1.25rem, 4vw, 3rem);
  }
  .iplg-hero__logo {
    /* Scaled down a touch on the narrower viewport, but still large to fill the
       centered left half of the two-half split. */
    height: clamp(180px, 26vh, 300px);
  }
  .iplg-hero__col {
    /* TABLET: ease the lockup tilt. The viewport is narrower here, so the wide
       [logo | divider | ~32em text] lockup, now rotated as a WHOLE group, has
       less headroom before its corners approach the hero edges. Soften (and keep
       the same positive direction as the desktop default) so the rotated bounding
       box stays comfortably inside the full-viewport clip. */
    --hero-lockup-tilt: 0deg;
    /* The lockup is level here (tilt 0), so the desktop 20px horizontal nudge
       (--hero-content-shift-x) no longer makes sense and was reading as the box
       being shifted right. Zero it for all narrow viewports. */
    --hero-content-shift-x: 0px;
    /* The --hero-edge-pad cap + margin-inline:auto from the <=1200px block still apply
       here (inherited), keeping the lockup off the viewport edges; the column-gap is
       unchanged so the divider clearance is preserved at tablet too. */
  }
  /* TABLET: narrow the carousel text MEASURE one more step (26em -> 22em) so the
     lockup keeps comfortable edge padding even near the bottom of this range, where
     the logo is still ~180-300px wide. The text absorbs the squeeze (wraps to more
     lines); the divider gap is untouched. KNOB: this max-width is the text measure. */

}

/* MID-SMALL STACKED REFLOW (601px - 1319px). AUDIT: the desktop lockup is a
   horizontal row [logo | divider | text] whose three parts are spread apart by
   large WORLD-FRAME shifts tuned for wide desktop (--hero-logo-shift-x:-200px on
   the brandmark, --hero-text-shift-x:241px on the textrow, --hero-divider-shift-x:
   100px on the divider). The gold divider is absolutely positioned at the col
   center (+ --hero-divider-shift-x) and reserves NO flex space, so when the
   horizontal lockup is cramped the wide carousel-text column crosses left over the
   line and the text clips the gold divider. This was visible through the old medium
   band: the text measure reverts to 32em above 1200px, making the lockup widest
   (most cramped) exactly where the half-spread shifts could not clear the divider.

   FIX: extend this clean STACKED band UP to 1319px (was 1100px) so the calm
   centered layout - hero logo on TOP, carousel text centered BELOW, gold divider as
   a SHORT HORIZONTAL bar between them - covers every cramped width where the desktop
   vertical divider could be clipped. The horizontal desktop lockup now only engages
   at >=1320px, where the viewport is wide enough to seat the (measure-capped) text
   clear to the right of the (vertical) divider. The hero logo stays visible (branding
   is always present; the homepage nav logo stays hidden >600px, the stacked hero logo
   is the brand here). All three world-frame shifts are zeroed so nothing is dragged
   toward the edges; the column simply stacks and centers, with the divider restyled
   as an in-flow horizontal rule (see the divider override below). This sits between
   the phone band (<=600, hero logo hidden / nav logo shown, centered text with the
   divider as an accent above it) and the desktop horizontal lockup (>=1320, vertical
   divider), giving a smooth, clip-free three-regime transition. */
@media (min-width: 601px) and (max-width: 1319px) {
  /* Logo sizing for this range; the carousel text is the global bottom band, so there
     is nothing to restack here. */
  .iplg-hero__logo {
    height: clamp(130px, 18vw, 200px);
    --hero-logo-inset: 0;
  }
  .iplg-hero__lead {
    font-size: clamp(1.5rem, 2.4vw, 1.9rem);
  }
}

/* MEDIUM HORIZONTAL-LOCKUP RANGE (1320px - 1400px). AUDIT: just above the stacked
   band the lockup snaps back to the horizontal row [logo | divider | text] and the
   full wide-desktop WORLD-FRAME shifts resume (--hero-logo-shift-x:-200px drags the
   logo toward the LEFT edge, --hero-text-shift-x:+241px drags the text toward the
   RIGHT edge, --hero-divider-shift-x:+100px). On a viewport this side of 1400px the
   screen is not yet wide enough to absorb that ~440px total spread, so the logo's
   left edge and the text's right edge sit too close to the viewport edges, AND - the
   bug this band exists to prevent - the carousel TEXT column (absolutely-positioned
   divider reserves no space) can cross LEFT over the gold divider and clip it.

   FIX: this band starts at 1320px (was 1101px). The clean stacked layout now covers
   everything below 1320px (divider hidden), so the horizontal lockup only shows where
   there is room. In this band we (1) SCALE DOWN the logo/divider shifts so the lockup
   keeps edge clearance, (2) KEEP the text MEASURE capped at 26em up here (it would
   otherwise revert to 32em above the <=1200px block and balloon back over the
   divider), and (3) push the text shift well to the RIGHT (+170px) so the
   (measure-capped) text column's LEFT edge stays clearly to the right of the divider
   (at +45px) with a visible gap - wherever the divider is shown, the text never
   crosses it. This is a single calm step between the zeroed stacked band (<=1319) and
   the full -200/+241/+100 spread (>=1400, where the viewport is wide enough), giving a
   smooth handoff with no sudden jump at either boundary. KNOBS: the shift values and
   the 26em text measure below. */
@media (min-width: 1320px) and (max-width: 1400px) {
  .iplg-hero__brandmark {
    /* -200px -> -90px: the logo's left edge clears the viewport edge with breathing
       room across the band; pull is in the same (negative/left) direction as desktop. */
    --hero-logo-shift-x: 0px;
  }
  /* KEEP the text measure capped at 26em through this band. Above the <=1200px block
     the entries/lead caps revert to the base 32em, which widens the text column back
     over the divider. Re-capping at 26em here keeps the column narrow so the rightward
     text shift below clears the divider with room. Both caps must match so neither
     re-caps the box smaller. KNOB: this measure. */

  .iplg-hero__textrow {
    /* +241px -> +170px: tamed from the full desktop spread for edge clearance, but
       pushed far enough RIGHT that the 26em-capped text column's LEFT edge sits clearly
       to the right of the gold divider (at +45px) with a visible gap, so the text never
       clips the divider anywhere the divider is shown. Same (positive/right) direction
       as desktop. */
    --hero-text-shift-x: 0px;
  }
  .iplg-hero__lockup-divider {
    /* +100px -> +45px: scaled down so the divider tracks the narrower spread and stays
       in the gap between the logo and the text. With the text shifted to +170px and
       capped at 26em, the divider at +45px sits clearly LEFT of the text's left edge. */
    --hero-divider-shift-x: 45px;
  }
}

/* Phone: the left-anchored half-width column is too cramped. Let the foreground go
   full-width and turn the gradient screen into an overall darkening so the stacked
   white text stays legible across the whole frame. The brandmark is hidden (the
   nav-bar logo takes over), so the column holds only the carousel text + ticks. */
@media (max-width: 600px) {
  .iplg-hero {
    /* Taller on phones so the stacked logo + carousel text + slide ticks have
       room to breathe instead of reading squished. */
    min-height: 92svh;
  }
  /* Phone: drop the vertical divider entirely - it reads as clutter on the narrow
     stacked layout. */
  .iplg-hero__textrow::before {
    display: none;
  }
  .iplg-hero__scrim {
    /* SCREEN OFF ON PHONES TOO. The scrim is display:none at the base rule (the
       soft box behind the lockup now provides the legibility backing). The old
       phone full-frame navy wash override is neutralized here so the box-only
       treatment is consistent on phones and the scrim never double-treats. */
    background: none;
  }
  .iplg-hero__masthead {
    /* Full-width foreground (already the base) with a tighter symmetric horizontal
       inset for the phone; the content stacks left-aligned. */
    --hero-fg-top: clamp(4.5rem, 9vh, 6rem);
    --hero-fg-bottom: clamp(4rem, 8vh, 6rem);
    --hero-fg-x: clamp(1.25rem, 6vw, 2rem);
    /* PHONE: drop the carousel text LOW, close to the slide-timer bars, instead of
       centering it over the full hero height. Bottom-align the content and keep
       only a small bottom gap so the text sits just above the ticks. The top
       padding just keeps it clear of the nav if the hero is short. */
    align-items: flex-end;
    padding-top: clamp(4rem, 13vh, 7rem);
    /* Lifted a bit: larger bottom inset pushes the bottom-anchored carousel text
       up off the slide-timer bars so it reads higher in the frame on phones. */
    padding-bottom: clamp(4rem, 11vh, 5.5rem);
    /* Symmetric horizontal inset so the text BOX (.iplg-hero__col) has a left/right
       margin instead of spanning edge to edge. The full-hero tint (this masthead's
       own background) still goes edge to edge; only the inner box is inset. */
    padding-left: clamp(1rem, 5vw, 1.75rem);
    padding-right: clamp(1rem, 5vw, 1.75rem);
  }
  .iplg-hero__col {
    /* Phone: the col spans the FULL viewport width with a small symmetric inset
       (padding-inline), so the carousel text box reads full-width with a margin to
       the screen edges rather than hugging the text. box-sizing keeps the inset
       inside the 100% width. */
    width: 100%;
    max-width: 100%;
    margin-inline: auto;
    padding-inline: clamp(1rem, 5vw, 1.5rem);
    box-sizing: border-box;
    /* PHONE: level the lockup. The col consumes --hero-lockup-tilt via its own
       rotate(); a dramatic rotation would clip badly in the narrow stacked
       layout, so zero the variable here and the whole lockup (logo + divider +
       text) sits level/upright on phones. */
    --hero-lockup-tilt: 0deg;
    /* Phone fallback: the 3-column logo | divider | text grid is too cramped on a
       narrow viewport, so drop back to a single-column stacked flow. The brandmark is
       hidden below (the nav-bar logo takes over); the divider shows as a short
       horizontal gold accent above the text, so the column holds that accent plus the
       carousel text. Override display:grid back to flex so no 3-column track is forced.
       The text is VERTICALLY CENTERED on phones (justify-content:center) rather than
       bottom-weighted: with the logo gone the content is the divider accent plus the
       carousel quote, so centering it in the hero gives a balanced stacked read and
       keeps comfortable breathing room above the ticks instead of crowding them. The
       masthead's --hero-fg-top/-bottom insets keep it clear of the nav and the ticks. */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }
  /* Phone tuning for the soft box behind the stacked lockup. The ::before still
     wraps the now-vertical content (logo hidden, carousel text stacked); trim the
     horizontal pad and the blur so the negative-inset + feathered edge stay inside
     the viewport and never cause a horizontal overflow on the narrow column. */
  .iplg-hero__col::before {
    --hero-box-alpha: 0.25;    /* match desktop lightening so the phone box is a touch lighter too */
    --hero-box-pad-x: 1.75rem; /* a bit larger, still inside the narrow centered col to avoid horizontal overflow */
    --hero-box-pad-y: 2rem;
    --hero-box-blur: 28px;     /* softer edges, kept modest so the feathered inset stays within the viewport */
  }
  /* Show the lockup divider on phones as a SHORT HORIZONTAL gold accent ABOVE the
     centered carousel text. The hero logo is hidden here (the nav-bar logo takes
     over), so the bar (order:1) sits above the text (order:2) as a centered accent
     rather than between two elements. Same conversion as the stacked band: in-flow
     (position:static), transform cleared, square-ended gold rule, centered with auto
     side margins and vertical breathing room. It is short and centered, so it cannot
     reintroduce horizontal overflow at phone widths. KNOBS: width/height/margins. */
  /* Hide the standalone lockup divider on phones. The orange rule now hugs the LEFT
     edge of the carousel text (painted by .iplg-hero__entries::before below) so the
     text keeps its gold rule on the left as one [bar | text] group, rather than the
     rule becoming a separate horizontal accent above centered text. */
  .iplg-hero__lockup-divider {
    display: none;
  }
  .iplg-hero__logo {
    height: 110px;               /* fixed; still ~1.25x the 88px nav logo */
  }
  .iplg-hero__content {
    max-width: 100%;
  }
  /* The base text measure (min(34rem, 42vw)) collapses to ~42vw on a phone, which
     squeezes the carousel text into a tiny column. Let it fill the full text box
     width so the lines wrap naturally instead of being crammed. */
  .iplg-hero__entries {
    max-width: 100%;
  }
  /* Phone tuning for the carousel text container (.iplg-hero__textrow). The dark glass
     screen now lives on the masthead (covers the full hero), so this container is
     transparent on phones too; it just holds the stacked carousel text. Keep
     max-width:100% so it never overflows the narrow viewport and give it a little
     compact padding for breathing room. The gold rule thickness/gap knobs are tuned
     thinner/tighter for the narrow column. */
  .iplg-hero__textrow {
    /* Phone: the col is a stacked single-column flow (brandmark hidden), so the text
       container plus the gold divider are the children. flex:0 0 auto + width:100%
       fills the narrow column; max-width:100% keeps it inside the viewport. order:2
       keeps the text BELOW the divider accent (order:1). */
    flex: 0 0 auto;
    order: 2;
    width: 100%;
    max-width: 100%;
    margin-left: 0;   /* reset the desktop 8px gutter so the full-width box does not overflow */
    /* Phone: the lockup is stacked and level (tilt 0), so the along-the-line
       shift no longer makes sense; zero it so phone stays clean regardless of
       whatever desktop value the user dials. The HORIZONTAL companion
       (--hero-text-shift-x, +241px on desktop) must ALSO be zeroed here, or the
       centered phone text is painted translateX(241px) and runs off the right
       edge of the viewport (box-sizing:border-box from bootstrap-reboot keeps the
       width:100% + padding inside the column, so the only off-screen cause is
       this paint-only shift). */
    --hero-text-shift: 0px;
    --hero-text-shift-x: 0px;
    /* Compact, SYMMETRIC padding for breathing room around the stacked text. The
       carousel quote is centered on phones (see the .iplg-hero__lead override below),
       so the old right-heavy inset (which propped a left-aligned block off the right
       edge) is gone; equal left/right insets keep the centered text visually balanced
       in the narrow column. 4-value shorthand: top right bottom left. */
    /* Reduced padding on phones: tighter top/bottom and a small horizontal inset
       (the box's right gap comes from margin-right above, not padding). */
    padding: clamp(0.85rem, 3vh, 1.5rem) clamp(0.85rem, 4vw, 1.25rem);
  }
  /* Phone: SIZE + ALIGNMENT of the carousel text. The base rule sizes the lead at the
     desktop 1.9rem (Cormorant Garamond, small x-height) and left-aligns it next to the
     now-retired gold rule; both read oversized/off-center in the narrow stacked column.
     Scale the size to the viewport and CENTER the text so it reads as deliberately
     placed. clamp(1.3rem, 4.6vw, 1.7rem): clearly smaller than 1.9rem on a typical phone
     and a touch smaller than the old 5vw/1.8rem so the LONGEST quote (the ~165-char
     "We build strong relationships..." sentence, ~7 lines at ~360px) has breathing room
     and does not crowd; still legible, capped under the desktop size on wider phones.
     This is the knob to tweak for bigger/smaller mobile text. */
  .iplg-hero__lead {
    font-size: clamp(1.2rem, 4.8vw, 1.55rem);    /* slightly larger on phones */
    text-align: left;                            /* keep the hero text left-aligned on mobile */
    justify-self: start;
    max-width: 100%;
    width: auto;
  }
  /* Left-align the carousel text block on mobile (the grid cell fills the width). */
  .iplg-hero__entries { justify-items: start; }
  /* Smaller CTA buttons on mobile: the desktop size reads oversized on a phone. */
  .iplg-hero__cta { gap: 0.75rem; }
  .iplg-hero__btn {
    font-size: 0.7rem;
    letter-spacing: 1px;
    padding: 0.6rem 1.05rem;
  }
  /* Fix 6: the entries box no longer needs a mobile min-height reservation.
     The single-cell grid (see .iplg-hero__entries) always sizes to the
     tallest sentence at whatever the current viewport width is, including
     this 600px-and-down column, so the old hard-coded 16.5em floor is
     redundant and has been removed. */
  /* Mobile: the desktop base rule centers the ticks in the LEFT half (left:25%),
     but on phones the layout stacks to a single column (the brandmark logo is
     hidden below), so override back to left:50% to center on the narrow stacked
     layout. Also tighten the bottom clearance a touch for the narrower viewport. */
  .iplg-hero__ticks {
    left: 50%;
    --hero-ticks-bottom: 1.5rem;
  }
  .iplg-hero__tick {
    width: 40px;
  }
  /* Mobile logo swap: hide the large hero brandmark so the nav-bar logo
     can take over (restored in style.css). The masthead becomes a single-
     child carousel column; the existing padding and gap are sufficient. */
  .iplg-hero__brandmark {
    display: none;
    /* Phone: brandmark is hidden and the stacked lockup is level (tilt 0); zero
       the along-the-line shift so phone stays clean regardless of the desktop
       value the user dials. Zero the HORIZONTAL companion too (--hero-logo-shift-x,
       -200px on desktop) for safety even though display:none hides the logo here. */
    --hero-logo-shift: 0px;
    --hero-logo-shift-x: 0px;
  }
}

/* Awards / recognition strip: WHITE background on the homepage. The shared
   component (.iplg-awards-strip in style.css) defaults to a light-gray
   (#f4f6fa) band, which is kept for the other pages that use it (attorneys, the
   IP landing, the landing-page demos). home.css loads only on the homepage, so
   this override repaints the strip white here without touching those pages. The
   strip text is navy and the badge logos read fine on white. */
.iplg-awards-strip {
  background-color: #ffffff;
}

/* A bit more vertical breathing room on the homepage's stacked sections so the
   page feels less cluttered. The brand-bar marquee and awards strip are shared
   components (style.css, padding: 4rem 0) used on other pages too, so the extra
   padding is scoped to the homepage via the `main`-qualified selectors below
   (these sections sit inside <main> on index.ejs). Homepage-specific sections
   (.practice-cards-section, .loc-pills-section) are bumped directly in this file
   where they're defined. */
main .iplg-brand-bar-marquee {
  /* Symmetric breathing room: the removed awards strip used to supply the space
     below the marquee, so the marquee now carries a matching bottom gap itself. */
  padding-top: 10rem;
  padding-bottom: 10rem;
}
/* Awards strip has its own paddings. The gold divider bar that used to frame this
   gap was removed, so the awards header now needs real top padding of its own. */
main .iplg-awards-strip {
  padding-top: 8rem;       /* breathing room above the awards header */
  padding-bottom: 10rem;    /* generous bottom gap below the section, unchanged */
}

/* Firm stats relocated INTO the dark (#111) firm-CTA text panel, directly under
   the "Speak with an attorney" button (see .home-cta-stats rules in the firm-CTA
   block lower in this file). The former standalone white .iplg-stats-bar band was
   removed from the homepage; the shared stats-bar partial keeps its own defaults
   for the demo/elements pages, which do not load home.css. */

/* ===================================================================
   2. PRACTICE-CARD GRID — homepage "Practice Areas" section
   DUPLICATED BLOCK: these rules also appear in demos.css, which the
   demo/preservation pages (style-home2/3/4, demo/practice-cta-variants)
   load. Both files are kept self-sufficient by design; keep the two
   copies in sync if either is edited. Originally from home2.css.
   =================================================================== */

/* Light backdrop behind the homepage practice-card grid. Was a near-black
   vertical gradient; now a clean very-light-gray neutral so the section reads
   as a bright band. The photo practice cards carry their own dark overlays, so
   they still read on the light background; the embedded CTA card uses the BLACK
   variant (set in index.ejs) as the dark anchor here. */
.practice-cards-section {
  background: var(--iplg-bg-gray);            /* slight-gray page tint (matches the body
                                    background, var(--iplg-bg-gray)) so this section reads the
                                    same gray as the inter-section separator gaps
                                    where the gold .iplg-section-divider lines sit */
  padding: 2.4rem 0 5.5rem;         /* no top padding (per directive); bottom kept */
}
/* The awards strip sits directly above this section and the locations section
   directly below. The shared .iplg-section-elevated utility paints an inset top
   highlight (inset 0 1px 0) plus a 1px bottom hairline and a soft drop shadow,
   which together read as an unwanted divider line on this now-white section.
   Neutralize the utility's shadow here (homepage-scoped only) so there is no
   visible divider line; the utility is left intact for other pages. */
.practice-cards-section.iplg-section-elevated {
  box-shadow: none;
}
/* The homepage nav sits over the full-bleed hero, so no drop shadow here (the
   shadow stays on interior pages via style.css; home.css loads homepage-only). */
.nav-variant-tall-solid-navy {
  box-shadow: none;
}
/* The locations section is wrapped in <div class="iplg-section-elevated
   home-locations-band"> in index.ejs (the home-locations-band hook is added there
   so this targets ONLY the homepage locations wrapper, not other
   .iplg-section-elevated users like the brand marquee or awards strip). Paint it
   the slight-gray page tint (var(--iplg-bg-gray)) so it reads the same gray as the
   inter-section separator gaps and the practice-cards section above. The utility's
   `0 1px 0` bottom-hairline stop (plus its inset top highlight and soft drop
   shadow) read as an unwanted divider line right above the firm CTA band that
   follows it, so neutralize the utility's shadow here too (homepage-scoped) for a
   seamless blend. The utility stays intact elsewhere. */
main .iplg-section-elevated.home-locations-band {
  background: var(--iplg-bg-gray);
  box-shadow: none;
}
.practice-cards-heading {
  font-family: 'Karla', sans-serif;
  font-size: 1.75rem;
  color: #0a1e3c;                /* dark navy, reads on light gray */
  text-align: center;
  margin: 0 0 2.25rem;
}
.practice-cards-section .iplg-section-lead {
  color: rgba(10, 30, 60, 0.78); /* dark navy, reads on light gray */
}

/* HOMEPAGE practice CTA card -> SOLID BLACK. The shared partial
   (views/partials/elements/practice-cta-card.ejs) paints the --black variant a
   TRANSLUCENT black (rgba(0,0,0,0.8)) via its inline <style>. On the homepage
   that card sits over this light-gray .practice-cards-section, so the 0.8 alpha
   lets the gray bleed through and the card reads as dark-gray. Force it to a
   SOLID #000 here, scoped to the homepage only (home.css loads only on the
   homepage, and the `main`-qualified selectors out-specify the partial's inline
   single-class rules). Other pages that use this card (location.ejs, demos)
   keep the translucent 0.8 version. The card's own white heading/body and
   white-outline button stay legible on solid black. */
main .practice-cards-section .practice-cta-card,
main .practice-cards-section .practice-cta-card--black {
  --iplg-cta-black-alpha: 1;                 /* knob: fully opaque on the homepage */
  background-color: #000000;                 /* solid black, no gray bleed-through */
  background-image: none;
}

/* 3-ACROSS practice card grid (homepage). The deck has 6 items (5 photo
   practice cards + the black CTA card where the `cleantech` slug renders), so
   3 per row = 2 even rows. The shared Bootstrap base (style.css) already sets
   `.card-deck { flex-wrap: wrap }` and a 3-across flex-basis at >=1200px; this
   homepage-scoped block adds the VERTICAL row spacing between the two wrapped
   rows (Bootstrap card-deck only spaces cards horizontally), and re-asserts the
   one-third flex-basis so it travels with the 15px-per-side base gutter. The
   1400px / 1600px gutter overrides further down restate the flex-basis for their
   wider gutters. Below 1200px the base collapses the deck to a single stacked
   column (tablet + mobile), which is left intact here. Row-gap is the tunable. */
@media (min-width: 1200px) {
  .home-main .practice-cards-section .card-deck .card {
    flex: 0 0 calc(33.333% - 30px);   /* 15px per side = 30px gutter between cards */
    max-width: calc(33.333% - 30px);
    margin-bottom: 30px;              /* vertical gap between the two wrapped rows */
  }
}

/* Practice cards — fade-reveal on hover */
.practice-card {
  position: relative;
  min-height: 350px;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  overflow: hidden;
  cursor: pointer;
  border: none;
  border-radius: 0;
}

.practice-card .card-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  /* Per-card overlay alpha: set on the .practice-card element via inline
     style from data/practice-cards.js to balance perceived brightness
     across images of different intrinsic luminance. */
  background: rgba(0, 0, 0, var(--card-overlay-alpha, 0.40));
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding: 2rem;
  transition: background 0.6s cubic-bezier(0.25, 0.1, 0.25, 1.0);
}

.practice-card:hover .card-overlay {
  background: rgba(0, 0, 0, var(--card-overlay-hover-alpha, 0.72));
}

/* Subtle background zoom on hover */
.practice-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: inherit;
  background-size: cover;
  background-position: center;
  transition: transform 0.6s cubic-bezier(0.25, 0.1, 0.25, 1.0);
  z-index: 0;
}

.practice-card:hover::before {
  transform: scale(1.04);
}

.practice-card .card-overlay {
  z-index: 1;
}

/* Card-corner positioning for the shared .iplg-review-mark badge when it
   appears inside a practice-card. The visual treatment (fill, color,
   typography) comes from .iplg-review-mark in style.css; here we only
   layer absolute corner placement and override the inline margin. */
.practice-card .iplg-review-mark {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 2;
  margin-left: 0;
  pointer-events: none;
}

/* Card title — absolutely centered so it does not move during the hover
   transition. Only opacity animates: title fades out in place. */
.practice-card .card-title {
  position: absolute;
  top: 50%;
  left: 2rem;
  right: 2rem;
  transform: translateY(-50%);
  margin: 0;
  text-align: center;
  color: #fff;
  font-family: 'Karla', sans-serif;
  font-weight: normal;
  z-index: 2;
  opacity: 1;
  transition: opacity 0.5s cubic-bezier(0.25, 0.1, 0.25, 1.0);
}

.practice-card:hover .card-title {
  opacity: 0;
}

/* Card details — absolutely placed in the upper region of the card with the
   body text vertically centered within that region. The CTA button sits
   below in its own slot, so the details box does not need to leave room
   for it via padding. Slides up + fades in on hover. */
.practice-card .card-details {
  position: absolute;
  top: 2rem;
  bottom: 6rem;          /* leaves a fixed slot for the .card-cta button */
  left: 2rem;
  right: 2rem;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  opacity: 0;
  transform: translateY(16px);
  transition: transform 0.6s cubic-bezier(0.25, 0.1, 0.25, 1.0),
              opacity 0.5s cubic-bezier(0.25, 0.1, 0.25, 1.0) 0.12s;
}

.practice-card:hover .card-details {
  opacity: 1;
  transform: translateY(0);
}

/* Learn-more CTA — anchored to a fixed slot at the bottom of every card so
   the button sits at the same vertical position across all 6 cards regardless
   of body-copy length. */
.practice-card .card-cta {
  position: absolute;
  left: 2rem;
  right: 2rem;
  bottom: 3.25rem;       /* raised a little so the button sits higher in the card */
  display: flex;
  justify-content: center;
  opacity: 0;
  transform: translateY(10px);
  transition: transform 0.5s cubic-bezier(0.25, 0.1, 0.25, 1.0) 0.12s,
              opacity 0.5s cubic-bezier(0.25, 0.1, 0.25, 1.0) 0.12s;
}

.practice-card:hover .card-cta {
  opacity: 1;
  transform: translateY(0);
}

.practice-card .card-details p {
  color: #f0f0f0;
  font-size: 0.95rem;
  line-height: 1.5;
  margin: 0;
}

/* Standard outline button, matching .iplg-consultation-cta-btn (navy variant):
   2px white border on the dark card; fills white with navy text on hover. */
.btn-ghost {
  background-color: transparent;
  border: 2px solid #fff;
  color: #fff;
  font-family: 'Montserrat', sans-serif;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 2px;
  border-radius: 0;
  padding: 0.75rem 2.25rem;
  width: auto;            /* size to content like the CTA button (was a fixed 18rem) */
  max-width: 100%;
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.btn-ghost:hover,
.btn-ghost:focus,
.btn-ghost:active {
  background-color: #fff;
  color: #0b336e;
  border-color: #fff;
}

/* On the practice cards the learn-more button spans the card, ALMOST full width:
   the .card-cta wrapper already insets 2rem from each card edge (its left/right:2rem),
   so a 100% button there reads as full width with a comfortable, even margin on both
   sides. This overrides the content-width default above (.btn-ghost width:auto), which
   left the button looking too narrow under the wide card. Scoped to .practice-card so
   the demo/CTA-card uses of .btn-ghost keep their content-width sizing. */
.practice-card .card-cta .btn-ghost {
  width: 100%;
}

/* Mobile: no hover on touch, so revert to a static stacked layout. Title,
   details, and CTA are all in the flex flow, centered as a group. */
@media (max-width: 767px) {
  /* Tighten the vertical gutter between the stacked practice cards on phones.
     Both card types carry Bootstrap's `mb-4` (1.5rem !important), so the override
     needs !important too. Covers .practice-card and the .practice-cta-card. */
  .home-main .practice-cards-section .card-deck .card {
    margin-bottom: 0.75rem !important;
  }

  .practice-card .card-overlay {
    background: rgba(0, 0, 0, 0.5);
  }

  .practice-card .card-title {
    position: static;
    transform: none;
    opacity: 1;
    margin-bottom: 1rem;
  }

  .practice-card .card-details {
    position: static;
    transform: none;
    opacity: 1;
    display: block;
  }

  .practice-card .card-cta {
    position: static;
    transform: none;
    opacity: 1;
    margin-top: 1rem;
  }
}

/* Firm CTA section ("Speak with an IPLG attorney"): a clean WHITE split-panel
   section on the homepage - navy/dark text panel on one side, consultation photo
   on the other. The shared .landing-ip-closing component (landing.css) is a gray
   (#4a4d53) full-bleed band; that reads well on the IP landing page, but the
   homepage wants this to read as a normal white section above the footer.

   IMPORTANT cascade note: index.ejs loads landing.css AFTER home.css, so a bare
   `.landing-ip-closing { background:#fff }` here LOSES to landing.css's
   `.landing-ip-closing { background:#4a4d53 }` (same specificity, later file
   wins). These overrides are therefore qualified with `main` (the CTA sits inside
   <main> on the homepage) so they out-specify the shared rules regardless of
   stylesheet order. Scoped to the homepage; the gray version on
   /practice-areas/intellectual-property is untouched (home.css loads only here).

   DESIGN: the section and the bounded text panel are both plain white (#ffffff),
   matching the firm's other clean light sections. The copy that was built light
   for the old dark band is darkened here: heading + stat figures use the firm navy
   (#0b336e), the lead + stat labels use the near-black/dark-gray body colors, and
   the outline button is overridden from white to navy so all of it reads on white.

   The IMAGE half (.landing-ip-closing__media) is intentionally LEFT OUT of the
   fill override so its split-panel photo (set inline in index.ejs, sized by
   landing.css) renders. `align-items-stretch` on the row keeps both halves the
   same height. */
main .landing-ip-closing {
  /* FULL-BLEED WHITE SECTION (homepage only): the section background is now a
     normal white (#ffffff) and spans the entire viewport width (it is a
     block-level section, so it already stretches edge to edge). The bounded
     container below keeps the CTA content at a readable centered width while this
     white background fills the gutters on both sides, so the section runs edge to
     edge with no off-color strips beside it. White (not the body's var(--iplg-bg-gray) tint)
     matches the firm's other clean light sections (the elevated cards / services
     panel use #ffffff). Qualified with `main` so only the homepage is affected;
     landing.css's gray base on demo/landing pages is untouched. */
  background-color: #ffffff;   /* normal white, full-bleed section background */
  background-image: none;
  /* Vertical breathing room above and below the CTA content inside the section. */
  padding-top: 1.5rem;
  /* Bottom padding keeps the CTA off the footer. Homepage-scoped via `main`, so
     demo/landing pages on the landing.css base are unaffected. The bottom white
     space below the CTA is now PADDING (pure #ffffff section color), not margin,
     so it no longer shows the body's var(--iplg-bg-gray) tint. The 5.75rem folds in the old
     2.75rem padding plus the previous 3rem margin gap, preserving total spacing. */
  padding-bottom: 5.75rem;
}

@media (max-width: 991.98px) {
  main .landing-ip-closing {
    padding-bottom: 5rem;   /* folds in the old margin gap; pure white footer space on mobile */
  }
}
main .landing-ip-closing__bounded,
main .landing-ip-closing__panel,
main .landing-ip-closing .row.g-0 > .landing-ip-closing__panel {
  background-color: #ffffff;       /* normal white to match the full-bleed section background */
  background-image: none;          /* no photo behind the text panel */
}
/* WIDTH-MATCH the firm CTA CONTENT to the services cards panel above it. The
   practice cards section (.practice-cards-section) holds its grid inside a
   Bootstrap 4 `.container`, whose effective content width is 1140px at the
   largest breakpoint. landing.css bounds this CTA at 1170px; override it here
   (homepage-scoped via `main`) to 1140px so the firm CTA content lines up to the
   same width as the services section. Kept centered via auto margins. NOTE: this
   bounds only the CONTENT width; the surrounding section background is now solid
   black full-bleed (see the `main .landing-ip-closing` rule above), so the black
   still runs edge to edge across the viewport while the text/photo stay centered
   at this readable width. */
main .landing-ip-closing__bounded {
  max-width: 1140px;
  margin-left: auto;
  margin-right: auto;
}
/* ANCHOR STATS TO BOTTOM (homepage only):
   The base panel is `display:flex; align-items:center` which vertically centers
   the single inner block. To anchor the stats to the BOTTOM edge of the box we
   instead stretch the inner block to the full panel height and lay it out as a
   vertical column; `margin-top:auto` on the stats then pushes them to the bottom
   while the heading/body/button stay at the top. The bottom padding from the
   panel keeps the stats off the very bottom edge. */
main .landing-ip-closing__panel,
main .landing-ip-closing .row.g-0 > .landing-ip-closing__panel {
  align-items: stretch;            /* let the inner block fill the panel height */
}
main .landing-ip-closing__inner {
  display: flex;
  flex-direction: column;
  height: 100%;                    /* fill the full (stretched) panel height */
}
/* Heading + body + button group: grow to fill the space above the
   bottom-anchored stats (.home-cta-stats keeps its margin-top:auto) and
   vertically center their contents within that space. Horizontal alignment
   stays left as before. */
main .landing-ip-closing__copy {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
main .landing-ip-closing__heading {
  color: #0b336e;    /* firm navy heading on white, matches the other homepage section headings */
}
main .landing-ip-closing__lead {
  color: #1a1a1a;    /* near-black body copy on white, matches the homepage light-section body color */
}
/* The button markup is .btn-outline-light (white outline + white text), built for
   the old dark band; on white it would be invisible. Override it to the firm's
   navy outline (#0b336e, the same accent used by the homepage location pills and
   service cards) that fills navy on hover and flips the label to white. Scoped to
   the homepage CTA so the shared landing.css base is untouched. */
main .landing-ip-closing .landing-ip-closing__btn.btn-outline-light {
  color: #0b336e;
  border-color: #0b336e;
}
main .landing-ip-closing .landing-ip-closing__btn.btn-outline-light:hover,
main .landing-ip-closing .landing-ip-closing__btn.btn-outline-light:focus {
  color: #ffffff;
  background-color: #0b336e;
  border-color: #0b336e;
}

/* FIRM-CTA STATS (homepage only):
   The four firm stats now live INSIDE the black (#111) CTA text panel, directly
   under the "Speak with an attorney" button. On DESKTOP they sit in ONE
   horizontal row of four compact stat items (number stacked over its label),
   four-across within the ~half-card panel width. On MOBILE the panel goes
   full-width and the row wraps to a tidy 2x2 grid so nothing is cramped.
   Dark navy figures + muted dark-gray labels to read on the white panel. */
main .landing-ip-closing .home-cta-stats {
  list-style: none;
  /* `margin-top:auto` pushes this row to the BOTTOM of the vertical-column inner
     block so the stats anchor to the bottom edge of the box (with at least 2rem
     of breathing room above the button when the panel is short). The panel's own
     bottom padding keeps the stats off the very bottom edge. */
  margin: 2rem 0 0;
  margin-top: auto;
  padding-top: 2rem;          /* minimum breathing room above the stats row */
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  display: flex;
  flex-wrap: nowrap;          /* desktop: keep all four on a single line */
  align-items: flex-end;      /* bottom-align the four stat stacks so labels line up */
  gap: 1.25rem;
}
main .landing-ip-closing .home-cta-stats__item {
  flex: 1 1 0;                /* equal-width columns, four-across */
  min-width: 0;
  display: flex;
  flex-direction: column;     /* number stacked over its label */
}
main .landing-ip-closing .home-cta-stats__value {
  color: #0b336e;             /* firm navy figures on white, matches the heading */
  font-family: 'Cormorant Garamond', Georgia, serif;
  font-weight: 500;
  font-size: 2rem;            /* compact so four fit on one line */
  line-height: 1.05;
  letter-spacing: 0.01em;
  font-variant-numeric: lining-nums tabular-nums;
  font-feature-settings: "lnum" 1, "tnum" 1;
}
/* The "+" renders as a plain default browser superscript (small, raised). No
   custom size/position/weight; it inherits the navy color from the value. */
main .landing-ip-closing .home-cta-stats__label {
  color: rgba(26, 26, 26, 0.66);      /* muted dark-gray labels on white */
  font-weight: 400;
  font-size: 0.72rem;
  line-height: 1.25;
  letter-spacing: 0.02em;
  /* Label now renders ABOVE the number (markup order: label then value), so the
     small gap sits BELOW the label, between header and figure. */
  margin-bottom: 0.35rem;
}

@media (max-width: 991.98px) {
  /* Panel stacks full-width on mobile: wrap the four stats to a 2x2 grid so the
     figures and labels have room instead of squeezing onto one cramped line. */
  main .landing-ip-closing .home-cta-stats {
    flex-wrap: wrap;
    gap: 1.5rem 1rem;
  }
  main .landing-ip-closing .home-cta-stats__item {
    flex: 1 1 40%;            /* two per row -> 2x2 */
  }
  main .landing-ip-closing .home-cta-stats__value {
    font-size: 2.1rem;        /* a touch larger now there is room */
  }
  main .landing-ip-closing .home-cta-stats__label {
    font-size: 0.78rem;
  }
}

/* FULL-HEIGHT / SQUARE FIX (homepage only):
   The shared base gives the media half min-height:400px, which over two equal
   col-lg-6 halves produces a wide, short rectangle. With background-size:cover
   that wide-short box crops the consultation photo (the right side of the frame
   got cut off and the image read as "chopped"). Two changes fix it:

   1) Make the split TALL so each half is a near-square box, not a short strip.
      Both halves stretch equally via align-items-stretch on the row, so raising
      the media half's min-height pulls the black text panel up to the same full
      height. ~560px over a 585px-wide half (1170px bounded card / 2) is close to
      square, revealing much more of the photo than the old 400px strip did.

   2) Re-frame the photo. The image now sits on the RIGHT of the split panel
      (the text panel is on the left), so the horizontal focus shifts toward the
      LEFT of the photo where the three attorneys facing the camera sit; "30%"
      vertical keeps their faces in view rather than centering on torsos. (When
      the photo was on the left this was "right 30%" to avoid cropping the right
      edge.) The card stays bounded (max-width:1170px, centered) on the white
      page - the intentional "card sitting above the footer" look. */
main .landing-ip-closing__media {
  /* Image height knob: was 560px (too tall, bloated the closing section). Lowered
     to 440px so the photo is shorter and the whole closing CTA section is less
     tall. `align-items-stretch` on the row lets the media half match the text
     panel, so the section only shrinks down to whichever half is taller; trim
     this value further (toward the panel's natural height) to shorten more. */
  min-height: 440px;
  /* Horizontal is pinned to `left` (0% / leftmost) and this rule (specificity
     0,1,1) already beats the landing.css base `.landing-ip-closing__media` rule
     (`background-position: center`, specificity 0,1,0), so `left` IS the effective
     value - there was no cascade conflict to fix.

     With `background-size: cover` on the tall 560px / ~585px-wide half the photo
     was zoomed in hard, so even at `left` only a thin sliver of the leftmost
     content showed. To genuinely reveal MORE of the LEFT of the frame we zoom out
     horizontally: `background-size: auto 100%` fits the photo to the box HEIGHT
     and lets its natural width run off the right edge, while `left` anchors the
     leftmost pixels in view. The result shows a wider left portion of the source
     image than `cover` could. The 25% vertical keeps the attorneys' faces framed. */
  background-size: auto 100%;
  background-position: right 25%;
}

@media (max-width: 991.98px) {
  /* Stacked on mobile: a 560px image above the text would be absurdly tall, so
     cap it back to a normal banner height. The panel sits below and sizes to its
     own copy. background-position center keeps the framing sensible when stacked. */
  main .landing-ip-closing__media {
    min-height: 280px;
    /* Restore cover on the short stacked banner: `auto 100%` from the desktop
       rule would leave horizontal gaps on a wide phone viewport. */
    background-size: cover;
    background-position: center 35%;
  }
}

/* ===================================================================
   LOCATIONS PILL-NAV (views/partials/elements/locations-pills.ejs)
   Light-theme replacement for the old dark office-rollup band. A
   horizontal-scrolling row of office pills (the shared .iplg-tabs strip)
   toggles one office detail card at a time.
   =================================================================== */
/* Black/white + navy-accent system. The section is a clean white band; text is
   near-black; the single accent color is NAVY (#0b336e), used on the heading,
   pills, links, dividers, and the "View office" button. */
.loc-pills-section {
  background-color: var(--iplg-bg-gray);    /* slight-gray page tint (matches the body
                                    background and the practice-cards section) so
                                    the locations band reads the same gray as the
                                    inter-section separator gaps */
  padding: 2.5rem 0 7rem;       /* top trimmed to 2.5rem: less total
                                    space above the section header; bottom bumped
                                    from 5.5rem to 7rem for a slightly more
                                    generous gap below the section */
}
.loc-pills-heading {
  font-family: 'Karla', sans-serif;
  font-weight: 600;
  color: #0a1e3c;            /* dark navy (was #fff, invisible on the light-gray band) */
  text-align: center;
  margin-bottom: 0.75rem;
}
.loc-pills-lead {
  color: #1a1a1a;            /* near-black body copy */
  text-align: center;
  max-width: 46rem;
  margin: 0 auto 2rem;
}

/* Horizontal scroller: the pill row scrolls sideways on narrow screens rather
   than wrapping, so every office stays reachable with a swipe. */
.loc-pills-scroller {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;            /* Firefox: hide the bar */
  /* overflow-x:auto also clips the Y axis, which cut off the pills' hover lift +
     shadow at the top. Top/side padding gives that lift room inside the box. */
  padding: 8px 4px 4px;
  margin-top: 1.5rem;
  margin-bottom: 2.5rem;
}
.loc-pills-scroller::-webkit-scrollbar { display: none; }   /* WebKit: hide */

/* The pill row itself uses the shared .iplg-tabs strip (style.css); the carousel
   (home-locations.js) drives which office is active. */
.loc-pills__panel[hidden] { display: none; }

/* Mobile office nav: navy prev/next chevrons flanking a row of dots (one per
   office). Hidden on desktop, where the pill row is used instead. */
.loc-dotsnav { display: none; }
@media (max-width: 575px) {
  .loc-pills-scroller { display: none; }
  .loc-dotsnav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    margin: 2.25rem 0 2rem;   /* sits a little lower */
  }
  .loc-dotsnav__arrow {
    background: none;
    border: none;
    outline: none;             /* no focus/click ring */
    -webkit-tap-highlight-color: transparent;
    padding: 0.35rem 0.7rem;   /* larger tap target */
    font-size: 2.75rem;        /* bigger, easier to tap */
    line-height: 1;
    color: #0b336e;            /* navy chevrons */
    cursor: pointer;
  }
  .loc-dotsnav__arrow:disabled {
    opacity: 0.25;
    cursor: default;
  }
  /* Kill the focus/active ring some mobile browsers (and any global button:focus
     rule) draw as a black box on tap. */
  .loc-dotsnav__arrow:focus,
  .loc-dotsnav__arrow:focus-visible,
  .loc-dotsnav__arrow:active,
  .loc-dotsnav__dot:focus,
  .loc-dotsnav__dot:focus-visible,
  .loc-dotsnav__dot:active {
    outline: none;
    box-shadow: none;
  }
  .loc-dotsnav__dots {
    display: flex;
    align-items: center;
    gap: 0.2rem;
    margin-top: 0.85rem;       /* nudge the dots lower than the chevron centerline */
  }
  .loc-dotsnav__dot {
    /* 11px visible circle with transparent padding to enlarge the tap target to
       ~25px (background-clip keeps only the content box filled). */
    width: 11px;
    height: 11px;
    padding: 7px;
    box-sizing: content-box;
    border: none;
    outline: none;
    -webkit-tap-highlight-color: transparent;
    border-radius: 50%;
    background: #cfd4da;       /* light gray, filled (no outline) */
    background-clip: content-box;
    cursor: pointer;
    transition: background-color 0.15s ease;
  }
  .loc-dotsnav__dot.is-active {
    background: #0b336e;       /* navy filled active dot */
    background-clip: content-box;
  }
}

/* Direction-dependent slide transition between offices (home-locations.js adds
   the transient --in-next / --in-prev class to the panel being shown, then
   removes it on a timer). Going to the NEXT office the incoming panel slides in
   from the RIGHT (content moves right-to-left); going to the PREVIOUS office it
   slides in from the LEFT. translateX is GPU-friendly (~600ms slide).
   overflow-x on the section clips the panel while it is off to the side so it
   never causes a horizontal scrollbar. Only one panel is ever in flow (the
   others are [hidden]), so there is no overlap/jump and the resting state is
   unchanged. Guarded by prefers-reduced-motion below (instant swap, no slide). */
.loc-pills-section { overflow-x: hidden; }
@keyframes locSlideInNext {
  from { transform: translateX(100%); opacity: 0.4; }
  to   { transform: translateX(0);    opacity: 1; }
}
@keyframes locSlideInPrev {
  from { transform: translateX(-100%); opacity: 0.4; }
  to   { transform: translateX(0);     opacity: 1; }
}
/* Slide duration (600ms) is kept in sync with SLIDE_MS in
   /javascript/home-locations.js, which clears the transient slide class on a
   matching timer. Change both together. */
.loc-pills__panel--in-next { animation: locSlideInNext 600ms ease both; }
.loc-pills__panel--in-prev { animation: locSlideInPrev 600ms ease both; }
@media (prefers-reduced-motion: reduce) {
  .loc-pills__panel--in-next,
  .loc-pills__panel--in-prev {
    animation: none;
  }
}

/* Office detail layout: a wrapper-less, two-column arrangement (photo + details)
   that sits OPEN/FLUSH on the section background - no card chrome (no card
   background, border, box-shadow, radius, or navy left-border divider). Keeps
   the two-column photo+details layout; stacks on narrow screens. Constrained
   and centered under the pill row. */
/* Full-bleed office photo behind a dark screen, with the city details + button
   overlaid in white on top. */
.loc-card {
  position: relative;
  overflow: hidden;
  border-radius: 0;             /* square corners */
  min-height: 460px;            /* height for the full-bleed background image */
  box-shadow: 0 12px 32px rgba(11, 51, 110, 0.10);
  max-width: 960px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;    /* the text block (with its own screen) sits at the bottom */
  align-items: flex-start;      /* hug the left so the screen wraps the content, not the full width */
}
/* The dark screen is no longer over the whole photo; it now sits BEHIND THE TEXT
   ONLY, as the background of .loc-card__body (below). */
.loc-card__media {
  /* Full-bleed background photo filling the whole card, behind the screen + content. */
  position: absolute;
  inset: 0;
  z-index: 0;
  background: #0c1628;
}
.loc-card__image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Slight navy screen in front of the office photo (under the text panel, which
   sits at z-index:2). Just enough tint to settle the image and match the screen
   on the other location-image surfaces. */
.loc-card__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(12, 28, 51, 0.22);
  pointer-events: none;
}
.loc-card__body {
  position: relative;
  z-index: 2;                /* above the full-bleed photo */
  flex: 1 1 auto;            /* fill the card height: the screen runs TOP TO BOTTOM */
  align-self: flex-start;    /* left side; do not stretch to the full card width */
  width: fit-content;
  max-width: 46%;            /* about half the card width, or less */
  margin: clamp(0.5rem, 1.2vw, 0.85rem);  /* small inset from the card edges */
  padding: 1.15rem 1.4rem;
  color: #ffffff;            /* white text over the screen */
  display: flex;
  flex-direction: column;
  justify-content: center;   /* center the text + button vertically in the tall panel */
  /* A clean, flat uniform screen - no gradient (gradients banded/splotched over the
     photo). Single solid translucent navy reads cleanly. */
  background: rgba(7, 16, 30, 0.62);
}
/* Phone: a shorter card so the full-bleed photo is not too tall. */
@media (max-width: 680px) {
  .loc-card {
    min-height: 380px;
  }
  /* The ~46% left text panel is far too narrow on a phone-width card and crushes
     the address/contacts. Let the dark text panel span the full card width so the
     details read cleanly on mobile, and drop the inset margin so the screen covers
     the ENTIRE card edge to edge (no photo border peeking around it). */
  .loc-card__body {
    max-width: none;
    width: auto;
    align-self: stretch;
    margin: 0;
  }
}
.loc-card__city {
  font-family: 'Karla', sans-serif;
  font-weight: 600;
  color: #ffffff;
  font-size: 1.5rem;
  margin: 0 0 0.35rem;
}
.loc-card__subtitle {
  color: rgba(255, 255, 255, 0.82);
  margin: 0 0 1rem;
}
.loc-card__address {
  font-style: normal;
  color: rgba(255, 255, 255, 0.88);
  line-height: 1.5;
  margin: 0 0 1rem;
}
.loc-card__contacts {
  list-style: none;
  padding: 0;
  margin: 0 0 1.5rem;
}
.loc-card__contacts li {
  margin-bottom: 0.35rem;
  color: rgba(255, 255, 255, 0.88);
}
.loc-card__contacts a { color: #ffffff; }   /* white links on the dark screen */
.loc-card__label {
  display: inline-block;
  min-width: 3.5rem;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.6);
  text-transform: uppercase;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
}
.loc-card__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.25rem;
}
/* "View office" button: the site's standard black/white outline button
   (Bootstrap .btn-outline-dark). Transparent with a dark border, fills dark
   on hover. We keep a slightly heavier font-weight to match the other CTAs
   in this section but otherwise defer to the Bootstrap convention. Label is
   uppercased via CSS (markup keeps the readable "View office" text) with a
   little tracking to match the site's other uppercase buttons. */
/* Look + hover come from the shared .iplg-btn / .iplg-btn--black component;
   only the full-width layout (spanning the card's text panel) stays here. */
.loc-card__btn {
  width: 100%;
}
/* Locations carousel: every office card sits in one horizontal track; the active
   card is centered and its neighbours peek at the left/right edges. The side arrows
   step through; home-locations.js sets the track's translateX to center the active. */
.loc-carousel {
  /* Arrows FLANK the viewport so the peeking neighbour cards sit BETWEEN the two
     arrows; align-items:center keeps the arrows vertically centered on the cards. */
  position: relative;
  margin: 2.25rem auto 0;
  /* Overall carousel width = the VISIBLE area. Wider here reveals MORE of the
     peeking neighbour cards on each side; the active card is held to roughly the
     same on-screen width by lowering .loc-carousel__slide width to compensate
     (see below), so widening this does not enlarge the cards themselves. */
  max-width: 1320px;
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.loc-carousel__viewport {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  /* Edge fade on the peeking out-of-view cards. A WIDER ramp (the #000 stops
     pulled further in from the edges) makes the fade more gradual, so the
     neighbour cards dissolve softly over a larger distance instead of clipping. */
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 13%, #000 87%, transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 13%, #000 87%, transparent 100%);
}
.loc-carousel__track {
  display: flex;
  gap: 3rem;
  transition: transform 0.55s cubic-bezier(0.25, 0.1, 0.25, 1);
  will-change: transform;
}
.loc-carousel__slide {
  flex: 0 0 auto;
  /* Lowered from 74% so the active card stays ~the same on-screen width even
     though the viewport (.loc-carousel max-width) was widened; the extra viewport
     width goes to the peeking neighbours, which now show more of themselves. */
  width: 57%;
  opacity: 1;                 /* peeking neighbours show at full strength too */
  transition: opacity 0.55s ease;
}
.loc-carousel__slide.is-active {
  opacity: 1;                 /* the centered card reads full strength */
}
.loc-carousel__slide .loc-card {
  max-width: none;            /* fill the slide (override the 960px cap) */
  margin: 0;
}
.loc-carousel__arrow {
  /* Large dark-gray chevron flanking the viewport, no background. */
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 4rem;
  font-size: 3rem;
  font-weight: 500;             /* slightly thinner chevron */
  line-height: 1;
  color: #9a9a9a;
  background: none;
  border: 0;
  cursor: pointer;
  transform: scaleY(1.15);      /* stretch the chevron a touch taller (not wider) */
  transition: color 0.15s ease, opacity 0.15s ease;
}
.loc-carousel__arrow:hover,
.loc-carousel__arrow:focus {
  color: #6f6f6f;
  outline: none;
}
@media (max-width: 680px) {
  /* Mobile: one full-width card (with the container's side margin), no peeking
     neighbours, no flanking arrows. The office pills above drive the selection. */
  .loc-carousel__arrow { display: none; }
  .loc-carousel { gap: 0; }
  .loc-carousel__slide { width: 100%; }
  .loc-carousel__viewport {
    -webkit-mask-image: none;
            mask-image: none;
  }
}
.loc-card__maps {
  color: #0b336e;            /* navy link */
  font-weight: 600;
  text-decoration: underline;
}

/* Carousel-style navigation below the active office: prev/next arrows flanking
   a row of dot indicators. Same black/white + navy (#0b336e) accent system as
   the pills. Driven by home-locations.js; shares the pills' active state. */
.loc-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Deliberately wider spread so the prev/next arrows sit well apart from the
     dot row and the whole control cluster reads wider. align-items:center
     (above) preserves the arrow/dot vertical alignment fix. */
  gap: 2.5rem;
  margin: 2.25rem auto 0;
  max-width: 960px;
}
/* Bare chevron arrows: no circle, border, or background - just the navy glyph
   at a larger size so the prev/next controls read as plain arrows flanking the
   dot row. The .loc-nav row uses align-items:center, so the tall glyphs and the
   small dots share a common vertical center line. A controlled line-height:1
   (not 0) gives the glyph a clean, predictable line box; the inner <span> is
   then nudged up a hair (translateY) to cancel the chevron font metrics that
   otherwise let the glyph's optical center sit BELOW the dot row's center. */
.loc-nav__arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  font-size: 2.6rem;
  line-height: 1;
  color: #0b336e;
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 0;
  cursor: pointer;
  transition: color 0.15s ease, opacity 0.15s ease;
}
/* The &lsaquo;/&rsaquo; glyphs carry internal top/bottom bearing that parks
   their optical center low; lift the glyph ~0.08em so its visual midpoint lands
   exactly on the dot row's center line. align-items:center on both the arrow and
   the .loc-nav row does the coarse centering; this nudge does the optical part. */
.loc-nav__arrow > span {
  display: inline-block;
  line-height: 1;
  transform: translateY(-0.2em);
}
.loc-nav__arrow:hover,
.loc-nav__arrow:focus {
  color: #0b336e;
  opacity: 0.6;
  outline: none;
}
.loc-nav__arrow:focus-visible {
  outline: 2px solid #0b336e;
  outline-offset: 3px;
}
/* Nudge each bare chevron glyph a touch toward the dots for optical centering
   (the &lsaquo;/&rsaquo; glyphs sit slightly off-center). */
.loc-nav__arrow--prev > span { margin-right: 1px; }
.loc-nav__arrow--next > span { margin-left: 1px; }
.loc-nav__dots {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}
/* Smaller, calmer dots with a consistent hairline border; the active dot is a
   solid navy fill (no scale jump), matching the pills' single-accent system. */
.loc-nav__dot {
  display: inline-block;
  flex: 0 0 auto;
  width: 0.55rem;
  height: 0.55rem;
  padding: 0;
  border-radius: 999px;
  background: #ffffff;
  border: 1px solid rgba(11, 51, 110, 0.35);
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
.loc-nav__dot:hover,
.loc-nav__dot:focus {
  border-color: #0b336e;
  background: #cfdcec;
  outline: none;
}
.loc-nav__dot:focus-visible {
  outline: 2px solid #0b336e;
  outline-offset: 3px;
}
.loc-nav__dot.is-active {
  background: #0b336e;        /* navy active dot, solid fill */
  border-color: #0b336e;
}

@media (max-width: 575px) {
  .loc-card__body { padding: 1.5rem 1.25rem; }
  /* Stacked mobile layout: the photo is full-width above the details. The 3:2
     landscape box and max-height (from the base rule) carry over, so the image
     crops to a sensible, not-too-tall landscape strip at the column's full
     width; no override needed. */
}

/* ==========================================================================
   Homepage headers: firm page-title serif (Cormorant Garamond)
   --------------------------------------------------------------------------
   Switches the homepage section headings to the firm's page-title serif, the
   same family used by .iplg-page-title (Cormorant Garamond, weight 500, navy,
   in style.css). The font is loaded site-wide via views/partials/head.ejs, so
   no @import or link is added here.

   These selectors are scoped under .home-main (the class added to the <main>
   element in views/index.ejs) on purpose. Several of these heading classes are
   produced by SHARED partials (brand-bar-marquee, awards-strip, locations-pills)
   that also render on other pages, and home.css is additionally linked by the
   gated /demo hero preview views. Scoping under .home-main guarantees the serif
   swap applies to the HOMEPAGE only and never leaks to those other pages.

   This is a font-family-only swap. The global h1-h4 = Karla rule in style.css
   is the site default and is left untouched. weight is nudged to 500 to match
   the page-title treatment so the serif does not read too light; size is left
   as each heading's existing size. font-family / font-weight are the tunables. */
.home-main .practice-cards-heading,
.home-main .iplg-brand-bar-marquee-heading,
.home-main .iplg-awards-strip-heading,
.home-main .loc-pills-heading,
.home-main .landing-ip-closing__heading {
  font-family: 'Cormorant Garamond', serif;
  font-weight: 500;
  /* Extra space between each homepage section header and its subheader/lead.
     One knob for all five sections; raise/lower to taste. */
  margin-bottom: var(--home-header-subheader-gap, 3.25rem);
}

/* ==========================================================================
   Homepage section heading separators (gold line ABOVE each section header)
   --------------------------------------------------------------------------
   The standalone between-section .iplg-section-divider divs were retired from
   views/index.ejs. The small gold line now renders directly ABOVE each homepage
   section heading via a ::before, so the separator travels with the header it
   introduces. The rules are scoped under .home-main so they only fire on the
   homepage <main>; the same heading classes are reused by shared partials on
   other pages (brand marquee, awards strip, locations pills, landing CTA) and
   must NOT pick up the line there.
   The line look matches the retired divider: about an inch wide (96px), 3px,
   square ends, firm accent gold #c7a559 (same gold as the hero accent). Width
   and thickness stay exposed through the original knobs (--iplg-section-divider-w
   / --iplg-section-divider-h) so they remain tunable.

   BAR PLACEMENT (the gold line floats in the gap ABOVE the header):
   The bar no longer hugs the heading. It is now seated with BOTH a top margin
   and a bottom margin so it sits roughly midway in the vertical space between
   the section top and the header text, biased a touch LOWER (closer to the
   header). Two tunable knobs drive the two gaps:
     --iplg-header-bar-gap-above -> the bar's own top margin (space added on top
                                    of the section's existing top padding, which
                                    is now ~3.5rem on four of the five sections,
                                    so the real space ABOVE the bar is that top
                                    padding PLUS this value)
     --iplg-header-bar-gap-below -> the gap from the bar down to the header text
   With the trimmed defaults below, the four 3.5rem-top-padded sections (practice
   cards, brand marquee, awards strip, locations) read: ~4.5rem above the bar
   (3.5rem section pad + 1rem) and 2.5rem below it, so the bar still floats in the
   gap and lands just past the midpoint of that ~7rem run, slightly low. The
   bar-to-header gap (2.5rem) is identical on all five sections, which keeps the
   placement reading consistent even though the CTA panel's fixed top padding
   (section 1.5rem + panel 3rem = ~4.5rem) is close to the other four; there its
   run is ~5.5rem above / 2.5rem below, still slightly low, so no per-section
   override is needed. The CTA panel also vertically centers its copy block, which
   softens the exact section-top reference, but the equal 2.5rem below-gap keeps
   the visual read in line with the other four. Overall this leaves noticeably
   LESS empty room above each header than before while the bar still floats.
   ========================================================================== */

/* CENTERED headings: the line is centered with margin-inline:auto to match the
   heading's own centered text-align (.practice-cards-heading and the awards-strip
   inner are text-align:center). The brand-bar-marquee heading and the locations
   (loc-pills) heading were REMOVED from this group per user request, so neither
   renders a gold line anymore (no ::before content). The Practice Areas heading was
   also removed from this group per user request, so the only remaining gold bar on
   the homepage is above the Awards strip (which takes its OWN margin override below
   to float mid-gap). */

/* AWARDS-STRIP separator: own margin override so the gold bar floats at the
   MIDPOINT of the white band between the brand-bar section above and the awards
   heading text, WITHOUT affecting the Practice Areas bar (which keeps the
   defaults above). The band is framed symmetrically:
     space ABOVE the bar = brand-bar bottom padding (3.5rem) + awards-strip top
                           padding (0) + bar top margin (0.75rem)  = 4.25rem
     space BELOW the bar = bar bottom margin (4.25rem)             = 4.25rem
   so the bar lands dead-center in the band. Tunables: nudge
   --iplg-header-bar-gap-below UP to push the bar toward the brand-bar (higher),
   DOWN to drop it toward the awards header; keep it roughly equal to
   (brand-bar bottom padding + awards top padding + --iplg-header-bar-gap-above)
   to stay centered. */

/* LEFT-aligned heading: the firm CTA heading sits in a flex-start panel with no
   text-align, so its text is left-aligned; pin the line to the left edge with
   margin-left:0 to match.
   HUGGING placement (per user request): unlike the other four section headers,
   which float the bar in the gap above the header, the bottom landing CTA bar
   sits DIRECTLY ABOVE its header text the way it did before the floating change.
   The two gap knobs are pinned tight here: a minimal top margin so almost no
   space is added above the bar, and a small bottom margin so the bar lands just
   above the header rather than floating midway. The four other headers keep the
   floated defaults (1rem above / 2.5rem below) untouched. */
.home-main .landing-ip-closing__heading::before {
  content: "";
  display: block;
  width: var(--iplg-section-divider-w, 96px);   /* about 1 inch (96px is ~1 CSS inch) */
  border-top: var(--iplg-section-divider-h, 3px) solid #c7a559;  /* 3px firm gold line, matches the hero accent */
  border-radius: 0;                              /* square ends */
  margin-left: 0;                                /* left-align to match the left-aligned heading */
  --iplg-header-bar-gap-above: 0.25rem;          /* minimal space above the bar (hugs the header, not floated) */
  --iplg-header-bar-gap-below: 1rem;             /* bar sits just above the header text */
  margin-top: var(--iplg-header-bar-gap-above, 1rem);      /* tight top so the bar does not float in the gap */
  margin-bottom: var(--iplg-header-bar-gap-below, 2.5rem); /* small gap so the bar hugs the header below */
}

/* ===================================================================
   MEDIUM-DESKTOP WIDENING (homepage only)
   The band between Bootstrap's xl breakpoint (>=1200px) and the large-desktop
   block below (>=1400px) used the default fixed 1140px `.container`, so the
   3-across practice cards read narrow on common 1280/1366px laptops. These two
   progressive steps widen the homepage container (and the width-matched CTA
   band) within that range. Each max-width is held below the band's low-end
   viewport so it never overflows: 1170px is safe at 1200px, 1260px at 1300px.
   Gutters stay at the base 15px-per-side default here; the >=1400px block takes
   over and opens them further. SCOPE: `.home-main` only; nothing else changes.
   =================================================================== */
@media (min-width: 1200px) {
  .home-main .container {
    max-width: 1170px;
  }
  main .landing-ip-closing__bounded {
    max-width: 1170px;
  }
}
@media (min-width: 1300px) {
  .home-main .container {
    max-width: 1260px;
  }
  main .landing-ip-closing__bounded {
    max-width: 1260px;
  }
}

/* ===================================================================
   LARGE-DESKTOP BREATHING ROOM (homepage only)
   On wide screens (>=1400px) the homepage content sat inside Bootstrap 4's
   `.container` (capped at 1140px), so the sections read small and squished in
   the middle of the viewport with large empty side margins, and the practice
   cards (now 3 across, flex:0 0 calc(33.333% - gutter)) got narrow. These blocks widen the homepage
   section containers and loosen the spacing on large desktops only.

   SCOPE: every selector is qualified with `.home-main` (the class on the
   homepage <main>), so shared components on other pages keep their current
   width. NOTHING here fires below 1400px, so tablet/mobile/medium are untouched.
   Tunable knobs: the container max-width and card-deck gutter per breakpoint.
   =================================================================== */
@media (min-width: 1400px) {
  /* Widen the homepage section content. The CTA band is bounded separately
     (.landing-ip-closing__bounded, default 1140px) so it is widened in step to
     keep lining up with the cards above it. */
  .home-main .container {
    max-width: 1380px;
  }
  main .landing-ip-closing__bounded {
    max-width: 1380px;
  }
  /* Open the practice card-deck gutters a touch (Bootstrap default is 15px per
     side = 30px between cards) so the wider, now-larger cards are not tight. */
  .home-main .practice-cards-section .card-deck {
    margin-right: -20px;
    margin-left: -20px;
  }
  .home-main .practice-cards-section .card-deck .card {
    margin-right: 20px;
    margin-left: 20px;
    /* 3-across at this gutter: 20px per side = 40px between cards, so the
       flex-basis subtracts the full 40px gutter from each one-third column. */
    flex: 0 0 calc(33.333% - 40px);
    max-width: calc(33.333% - 40px);
  }
  /* A little more vertical air around the practice section at this width. */
  .home-main .practice-cards-section {
    /* top padding comes from the base .practice-cards-section rule (single source) */
    padding-bottom: 6.5rem;
  }
}

@media (min-width: 1600px) {
  /* Even wider viewports: let the content use a bit more of the screen and
     open the card gutters another step so the sections never feel crammed. */
  .home-main .container {
    max-width: 1500px;
  }
  main .landing-ip-closing__bounded {
    max-width: 1500px;
  }
  .home-main .practice-cards-section .card-deck {
    margin-right: -26px;
    margin-left: -26px;
  }
  .home-main .practice-cards-section .card-deck .card {
    margin-right: 26px;
    margin-left: 26px;
    /* 3-across at this gutter: 26px per side = 52px between cards. */
    flex: 0 0 calc(33.333% - 52px);
    max-width: calc(33.333% - 52px);
  }
}
