/* ==========================================================================
   AI / ENTERPRISE — Lowes Digital Twin
   Case-study stylesheet, consolidated the same way css/switching.css is: one
   file per case study, loaded alongside css/footer.css and nothing else.

   Assembled from, in order:
     1. TOKENS      — this page's design tokens (Figma node 694:12015)
     2. MAIN        — reset, a11y helpers, layout primitives
     3. CHROME      — site header + case-study sub-nav, lifted verbatim from
                      css/switching.css so all four case studies share one
                      header rather than each re-deriving it
     4. COMPONENTS  — meta rail, captioned figure, pull quote, video
     5. CASE STUDY  — the page's own sections
   ========================================================================== */

/* ==========================================================================
   TOKENS
   Design tokens extracted from the Figma frame
   "AI / ENTERPRISE" — Lowes Digital Twin (node 694:12015) in
   PORTFOLIO_CASE-STUDY-LAYOUT (file 4a4AKXypAI56ESXq89ZJ1q).

   Values come from get_variable_defs + the frame's design context. Nothing
   in the other stylesheets should hardcode a hex value or a raw type size —
   reach for a token instead.

   Figma published only a thin set of variables for this frame:
     font size/13 · font size/20 · font size/40 · font family/Font 1 (Inter)
     font weight/400 · 500 · 700 · line height/30 · line height/36
     stroke weight/1 · color/grey/15 (#282626)
   Everything else below was read off the frame's design context and is
   annotated with the node it came from.
   ========================================================================== */

:root {
  /* ---------------------------------------------------------------------
     COLOR
     --------------------------------------------------------------------- */

  /* Surfaces */
  --color-bg: #fafafa;              /* page background (694:12015) */
  --color-surface-grey: #e1e1e1;    /* ROLE band (725:9443) */
  --color-header: #323237;          /* top header bar (706:12989) */
  --color-footer: #303032;          /* footer bar (725:9447) */
  --color-hero-blue: #1958e5;       /* hero art base colour, behind the bitmap */

  /* Ink (dark → light) */
  --color-ink: #242427;             /* rail labels, column headings */
  --color-ink-overview: #2a2724;    /* emphasised lead of the overview */
  --color-grey-15: #282626;         /* color/grey/15 — pull-quote rule */
  --color-rule: #686765;            /* ROLE column rules (722:8553) */
  --color-body: #686765;            /* ROLE body copy */
  --color-muted: #8a857f;           /* section labels, captions, footer */
  --color-nav-inactive: #454545;    /* case-study sub-nav, resting state */
  --color-nav-active: #1d1d1b;      /* active sub-nav underline (694:12522) */
  --color-focus: #1e9aff;           /* focus ring on the video scrubber */
  --color-quote-ink: #303032;       /* closing pull-quote, over the photo (743:15739) */

  /* Ink on dark surfaces */
  --color-on-dark: #ededea;         /* nav tabs, hero title, footer value */
  --color-white: #ffffff;
  --color-hero-eyebrow: rgba(255, 255, 255, 0.75);
  --color-hero-lede: rgba(255, 255, 255, 0.88);

  /* ---------------------------------------------------------------------
     TYPOGRAPHY
     font family/Font 1 = Inter. The frame uses six weights.
     --------------------------------------------------------------------- */
  --font-primary: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI",
    Helvetica, Arial, sans-serif;

  /* Weights */
  --fw-light: 300;
  --fw-regular: 400;    /* font weight/400 */
  --fw-medium: 500;     /* font weight/500 */
  --fw-semibold: 600;
  --fw-bold: 700;       /* font weight/700 */
  --fw-extrabold: 800;

  /* Static sizes — used where the design does not need to scale. */
  --fs-12: 0.75rem;     /* 12px — TEAM / SHIPPED rail labels */
  --fs-13: 0.8125rem;   /* 13px — font size/13, hero eyebrow */
  --fs-14: 0.875rem;    /* 14px — nav tabs, sub-nav, ROLE rail label */
  --fs-15: 0.9375rem;   /* 15px — rail values */

  /* Fluid sizes. Upper bound is the value measured in the 1920 frame; lower
     bound is the value chosen for a 375px viewport. Both ends are clamped so
     type never drops below the small-screen minimum or overshoots the frame. */
  --fs-body: clamp(1rem, 0.939rem + 0.259vw, 1.25rem);            /* 16 → 20 */
  --fs-column-heading: clamp(1.125rem, 1.095rem + 0.129vw, 1.25rem); /* 18 → 20 */
  --fs-section-label: clamp(1.125rem, 1.034rem + 0.388vw, 1.5rem);   /* 18 → 24 */
  --fs-footer: clamp(0.875rem, 0.845rem + 0.129vw, 1rem);         /* 14 → 16 */
  --fs-wordmark: clamp(1.25rem, 1.068rem + 0.777vw, 2rem);        /* 20 → 32 */
  --fs-hero-title: clamp(2rem, 1.788rem + 0.906vw, 2.875rem);     /* 32 → 46 */
  --fs-hero-lede: clamp(1rem, 0.939rem + 0.259vw, 1.25rem);       /* 16 → 20 */
  --fs-overview: clamp(1.5rem, 1.257rem + 1.036vw, 2.5rem);       /* 24 → 40 */

  /* Line heights — unitless so they scale with the fluid sizes above. */
  --lh-hero-title: 1.08;   /* 49.68 / 46 */
  --lh-overview: 1.4;      /* 56 / 40 */
  --lh-body: 1.5;          /* line height/30 ÷ font size/20 */
  --lh-label: 1.5;         /* line height/36 ÷ 24 */
  --lh-rail: 1.4;          /* 21 / 15 */
  --lh-footer: 1.25;       /* 20 / 16 */
  --lh-normal: normal;     /* nav tabs + rail labels use Figma "normal" */

  /* Letter spacing */
  --ls-overview: -1px;
  --ls-hero-title: -0.92px;
  --ls-tight-44: -0.44px;  /* wordmark, section labels, ROLE rail label */
  --ls-0: 0;
  --ls-10: 0.1px;          /* column headings */
  --ls-12: 0.12px;         /* TEAM / SHIPPED rail labels */
  --ls-15: 0.15px;         /* rail values */
  --ls-30: 0.3px;          /* footer */
  --ls-eyebrow: 1.82px;    /* hero eyebrow */

  /* ---------------------------------------------------------------------
     SPACE
     --------------------------------------------------------------------- */
  --space-3xs: 4px;
  --space-2xs: 8px;
  --space-xs: 12px;
  --space-sm: 16px;
  --space-md: 24px;
  --space-lg: 40px;
  --space-xl: 64px;
  --space-2xl: 80px;

  /* Section rhythm. The frame uses exactly three vertical gaps between its
     major blocks, measured edge to edge off the node coordinates:

       40px   hero (ends 873) → overview (913)
              innovation captions (end 1983) → pull quote (2023)
       94px   AI panels (end 3859) → closing photo (3953)
       110px  overview (ends 1305) → INNOVATION (1415)
              pull quote (ends 2113) → ROLE band (2223)
              ROLE band (ends 2810) → AI POWERED (2920)

     The closing photo sits flush against the footer (both at y=4453). */
  --section-gap-sm: clamp(24px, 2.08vw, 40px);
  --section-gap: clamp(48px, 4.9vw, 94px);
  --section-gap-lg: clamp(64px, 5.73vw, 110px);

  /* ---------------------------------------------------------------------
     LAYOUT
     The frame is 1920 wide with 80px gutters. Content sits in two columns:
     a label rail starting at x=80 and a main column starting at x=401.
     The hero and the closing photo bleed past the right gutter to x=1920.
     --------------------------------------------------------------------- */
  /* Two the shared case-study chrome expects, carried over from
     css/switching.css so the header and sub-nav match the other case
     studies exactly rather than being re-derived here. */
  --cs-page-gutter: clamp(1.25rem, 0.33981rem + 3.88350vw, 5rem);
  --cs-subnav-gap: clamp(1.5rem, 1.13594rem + 1.55340vw, 3rem);

  --layout-max: 1920px;
  --gutter: clamp(20px, 4.17vw, 80px);
  /* 401 − 80 at the reference width. Fluid below it: at 1024 a fixed 321px
     rail would leave the hero too narrow to hold its lockup on one line. */
  --rail-width: clamp(200px, 16.72vw, 321px);
  --content-max: 1439px;  /* overview measure at the 1920 reference width */

  /* Chrome heights, for reference. The shared header block below owns the
     actual layout and holds both bars at a fixed 90 + 51 = 141px at every
     width, so nothing here should drive the offset. */
  --header-height: 90px;
  --case-nav-height: 51px;

  /* Gap between nav items — 48px in the frame. Fluid below it: seven primary
     tabs plus the wordmark do not fit on one line at 1024 with a fixed 48. */
  --nav-gap: clamp(20px, 2.5vw, 48px);

  /* Gap between the wordmark, the nav, and the social icon — 70px at the
     reference width puts the last tab's right edge at x=1726 and the icon's
     at x=1840, exactly where the frame has them. */
  --header-gap: clamp(32px, 3.65vw, 70px);

  /* The case-study sub-nav is right-aligned in the frame: its last item ends
     at x=1730, i.e. 190px in from the right edge, lining up with the main
     nav's right edge. Fluid for the same reason as --nav-gap. */
  --subnav-inset-right: clamp(0px, 9.9vw, 190px);

  /* ---------------------------------------------------------------------
     BORDERS, RADII, SHADOWS
     --------------------------------------------------------------------- */
  --stroke-1: 1px;        /* stroke weight/1 — active sub-nav underline */
  --stroke-3: 3px;        /* ROLE column rules, pull-quote rule */
  --shadow-page: 0 20px 60px 0 rgba(0, 0, 0, 0.12);

  /* Minimum touch target (WCAG 2.5.5 / mobile guidance) */
  --touch-target: 44px;

  /* ---------------------------------------------------------------------
     BREAKPOINTS
     Single source of truth. CSS cannot evaluate custom properties inside a
     media query, so the literals below are repeated in @media rules — but
     only these five values are ever used, and js/main.js reads them back
     from here via getComputedStyle so the two stay in step.

       Mobile        base styles      →  480px
       Large mobile  481px            →  767px
       Tablet        768px            → 1023px
       Desktop       1024px           → 1439px
       Large desktop 1440px and up  (the 1920 Figma frame is the reference)
     --------------------------------------------------------------------- */
  --bp-mobile-max: 480px;
  --bp-large-mobile: 481px;
  --bp-tablet: 768px;
  --bp-desktop: 1024px;
  --bp-large-desktop: 1440px;

  /* Not a layout breakpoint — the width at which the primary nav stops being a
     hamburger and goes inline. The seven tabs plus the wordmark and the social
     icon measure ~1020px, which does not fit inside 1024's gutters, so the
     collapsed pattern is held until 1200. Every layout breakpoint above is
     unaffected: the rail, hero and panel grids still switch at 1024. */
  --bp-nav: 1200px;
}




/* ==========================================================================
   MAIN
   Reset, element defaults, and the layout primitives shared by every page.
   Load order: tokens.css → main.css → components.css → case-study.css
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. RESET
   -------------------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  /* Anchor links land below the whole sticky chrome (header + sub-nav) rather
     than under it. Tracks both heights so it stays correct across breakpoints. */
  /* The shared chrome is a fixed 141px at every width (90 header + 51
     sub-nav), so anchors land below it rather than under it. */
  scroll-padding-top: calc(5.625rem + 3.1875rem);
  -webkit-text-size-adjust: 100%;
  /* Backstop against a stray wide child producing a sideways scrollbar.
     `clip` rather than `hidden`, and on <html> rather than <body>: `hidden`
     turns the element into a scroll container, which would make the sticky
     header and sub-nav stick to it instead of to the viewport. */
  overflow-x: clip;
}

body {
  margin: 0;
  background-color: var(--color-bg);
  color: var(--color-ink);
  font-family: var(--font-primary);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  /* The shared header is fixed and carries both bars, so replace the space it
     no longer occupies in the flow — the same offset the other case studies
     use, and the chrome holds it at 141px across every breakpoint. */
  padding-top: calc(5.625rem + 3.1875rem);   /* header 90 + sub-nav 51 */
}

h1,
h2,
h3,
h4,
p,
figure,
blockquote,
dl,
dd {
  margin: 0;
}

dd {
  margin-inline-start: 0;
}

ul,
ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

img,
picture,
svg,
video {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

/* --------------------------------------------------------------------------
   2. ACCESSIBILITY HELPERS
   -------------------------------------------------------------------------- */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Skip link — first thing in the tab order, revealed on focus. */
.skip-link {
  position: absolute;
  top: var(--space-2xs);
  left: var(--space-2xs);
  z-index: 100;
  padding: var(--space-xs) var(--space-sm);
  background: var(--color-header);
  color: var(--color-on-dark);
  font-size: var(--fs-14);
  font-weight: var(--fw-semibold);
  transform: translateY(-200%);
}

.skip-link:focus-visible {
  transform: translateY(0);
}

/* One visible focus ring for the whole page. */
:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
  border-radius: 2px;
}

/* Respect a reduced-motion preference for anything animated later. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* --------------------------------------------------------------------------
   3. LAYOUT PRIMITIVES

   The page is one centred container capped at the frame width. Inside it,
   `.rail-layout` is the two-column grid the whole case study hangs off:

       │← gutter →│← rail (321) →│← main column ────────→│← gutter →│
       x=0         x=80           x=401                   x=1840

   Below the desktop breakpoint the rail collapses and both columns stack.
   -------------------------------------------------------------------------- */

.page {
  max-width: var(--layout-max);
  margin-inline: auto;
  background-color: var(--color-bg);
  box-shadow: var(--shadow-page);
}

/* Horizontal padding for anything that sits inside the page container. */
.container {
  padding-inline: var(--gutter);
}

.rail-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-md);
  padding-inline: var(--gutter);
}

@media (min-width: 1024px) {
  .rail-layout {
    grid-template-columns: var(--rail-width) minmax(0, 1fr);
    gap: 0;
  }
}

/* The label that sits in the rail ("INNOVATION", "AI POWERED", "ROLE").
   Above the desktop breakpoint it sits in column 1, aligned to the top of
   the content beside it. Below it, it reads as a plain section heading. */
.rail-label {
  grid-column: 1;
  margin: 0;
  color: var(--color-muted);
  font-size: var(--fs-section-label);
  font-weight: var(--fw-medium);
  line-height: var(--lh-label);
  letter-spacing: var(--ls-tight-44);
}

/* Main column of the rail grid. */
.rail-content {
  grid-column: 1;
  min-width: 0;
}

@media (min-width: 1024px) {
  .rail-content {
    grid-column: 2;
  }
}

/* Sections that carry no rail label still line up with the main column, so
   the overview and the pull quote start at x=401 like everything else. */
.content-column {
  padding-inline: var(--gutter);
}

@media (min-width: 1024px) {
  .content-column {
    padding-inline-start: calc(var(--gutter) + var(--rail-width));
  }
}

/* --------------------------------------------------------------------------
   4. SECTION RHYTHM
   -------------------------------------------------------------------------- */

.section--sm {
  margin-block-start: var(--section-gap-sm);
}

.section {
  margin-block-start: var(--section-gap);
}

.section--lg {
  margin-block-start: var(--section-gap-lg);
}


/* ==========================================================================
   CHROME (shared with the other case studies — see css/switching.css)
   ========================================================================== */

/* --------------------------------------------------------------------------
   SITE HEADER
   Dark bar (#323237, 90px tall in the frame) carrying the wordmark, the
   primary nav, and the LinkedIn link.
   -------------------------------------------------------------------------- */

/* Values copied verbatim from the shared system (tokens.css / components.css)
   so this header renders pixel-identically to the other case studies.
   Keep in step if the shared system changes. */
.site-header {
  /* Fixed, carrying BOTH bars (dark header + case-study strip) as one unit,
     exactly as the other case studies do. body carries a matching padding-top
     to replace the space it no longer occupies in the flow. */
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 20;
  background-color: #323237;          /* --color-surface-header */
}

/* Shared page shell: 1760 content + gutters, centred. */
.site-header .shell {
  width: 100%;
  max-width: calc(1760px + var(--cs-page-gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--cs-page-gutter);
}

.site-header__inner {
  display: flex;
  align-items: center;
  gap: 1rem;                          /* --space-16 */
  min-height: 5.625rem;               /* --header-height, 90 flat */
}

.site-header__wordmark {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  min-height: 44px;                   /* --tap-target */
  color: #fafafa;                     /* frame 715:8183 */
  /* Both header frames set this at a flat 32 — the desktop header (728:9841)
     and the mobile one (742:15539) — so it reaches 32 by the nav breakpoint
     and holds there rather than growing on to 1920. */
  font-size: clamp(1.375rem, 1.09056rem + 1.21359vw, 2rem);  /* 22 -> 32 at 1199 */
  font-weight: 500;
  line-height: 1.125;
  letter-spacing: -0.44px;            /* --ls-eyebrow */
  white-space: nowrap;
}

/* Hamburger toggle — mobile and tablet only. */
/* Drawn to match the frame's menu icon (742:15548): three 22.5 rules, 2.5
   thick with round caps, 7.5 apart. Kept as CSS bars rather than the exported
   SVG so they can still cross into an X when the panel opens. Sizing and
   placement come from the .site-header__actions group above. */
.nav-toggle__box {
  display: block;
  position: relative;
  width: 22.5px;
  height: 17.5px;
}

.nav-toggle__bar,
.nav-toggle__bar::before,
.nav-toggle__bar::after {
  position: absolute;
  left: 0;
  width: 22.5px;
  height: 2.5px;
  border-radius: 1.25px;              /* the frame's round line caps */
  background-color: currentColor;
  transition: transform 0.2s ease, opacity 0.2s ease;
}

.nav-toggle__bar {
  top: 7.5px;
}

.nav-toggle__bar::before,
.nav-toggle__bar::after {
  content: "";
}

.nav-toggle__bar::before {
  top: -7.5px;
}

.nav-toggle__bar::after {
  top: 7.5px;
}

/* Open state — the bars cross into an X.
   TODO (interactions phase): js/main.js toggles aria-expanded on
   .nav-toggle and the `is-open` class on .primary-nav. */
.nav-toggle[aria-expanded="true"] .nav-toggle__bar {
  background-color: transparent;
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bar::before {
  transform: translateY(7.5px) rotate(45deg);
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bar::after {
  transform: translateY(-7.5px) rotate(-45deg);
}

/* Primary nav — a collapsible panel below the header bar on small screens. */
.primary-nav {
  display: none;
  position: absolute;
  inset-inline: 0;
  /* The header is the containing block and carries both bars, so 100% drops
     the panel clear of the sub-nav — same as the other case studies. */
  top: 100%;
  padding: var(--space-xs) var(--gutter) var(--space-md);
  background-color: var(--color-header);
  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.24);
}

.primary-nav.is-open {
  display: block;
}

.primary-nav__list {
  display: flex;
  flex-direction: column;
  list-style: none;
}

.primary-nav__link {
  display: flex;
  align-items: center;
  min-height: var(--touch-target);
  font-size: var(--fs-14);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-normal);
  color: var(--color-on-dark);
  text-transform: uppercase;
}

.primary-nav__link:hover {
  color: var(--color-white);
}

/* LinkedIn mark. Aligned to the end, not centred: the frame sets the mark's
   right edge on the page margin. The 44px touch target grows inward. */
/* --- Header actions: mail · LinkedIn · menu ------------------------------
   Figma 742:15540. The frame sets 40 between the icon boxes and puts the
   group's right edge on the page margin. Each control is a 44 touch target
   with its glyph centred, so the gap between BOXES is the frame's 40 less
   the padding those targets add either side.
   ------------------------------------------------------------------------ */
.site-header__actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: clamp(0.0625rem, -0.57752rem + 2.73058vw, 1.46875rem);
  margin-inline-start: auto;
  /* The frame puts the LAST glyph's right edge on the page margin, but a 44
     target centred on a 22.5 menu icon holds it ~11 short of that. Pulling
     the group out by that padding lands the glyph on the margin without
     shrinking the target or disturbing the gaps between the icons. */
  margin-inline-end: -10.75px;
}

.site-header__mail,
.site-header__social,
.nav-toggle {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 44px;                        /* --tap-target */
  height: 44px;
  color: var(--color-on-dark);
}

.site-header__mail-icon {
  width: 25px;
  height: 20px;
}

.site-header__mail:hover { color: var(--color-white); }

.site-header__social img {
  width: 2rem;                        /* 32 */
  height: 2rem;
  max-width: none;
}

/* From the shared nav breakpoint (1200) the row sits inline and right-aligned
   and the toggle disappears — same breakpoint and metrics as the other pages. */
@media (min-width: 1200px) {
  /* Only the menu goes. The mail icon has replaced the row's "Get in touch"
     item (frame 715:8183), so it is shown at every width now. */
  .nav-toggle { display: none; }

  /* Frame 715:8183 across the 1760 content box: the wordmark sits left and
     the cluster is hard against the right margin -
       nav row (658 at its natural width) | 41 | mail 30 | 38 | LinkedIn 32
     The icon anchors stay 44px touch targets with their glyphs centred, so
     the gaps between BOXES are the frame's gaps less the padding those
     targets add: 41 - 9.5 = 34 before the group, 40.5 - 9.5 - 6 = 25 inside
     it, and the trailing target overhangs the margin by 6. */
  .site-header__inner {
    display: flex;
    align-items: center;
    gap: 34px;
  }
  .site-header__wordmark { flex: 0 0 auto; }
  .primary-nav { flex: 0 0 auto; margin-inline-start: auto; }
  .primary-nav__list { max-width: none; margin-inline: 0; }
  .site-header__actions {
    flex: 0 0 auto;
    margin-inline-start: 0;
    gap: 25px;
    margin-inline-end: -6px;
  }

  .primary-nav {
    display: block;
    position: static;
    flex: 1 1 auto;
    min-width: 0;
    padding: 0;
    background: none;
    box-shadow: none;
  }

  .primary-nav__list {
    flex-direction: row;
    align-items: center;
    justify-content: flex-end;
    gap: clamp(1.5rem, -1rem + 3.33333vw, 3rem);   /* 24 -> 48 */
    /* No cap: frame 715:8183 lets the six items sit at their natural 658
       and pins the row to the right margin. */
    max-width: none;
    min-width: 0;
    margin-inline: 0;
  }

  .primary-nav__link {
    min-height: 66px;
    white-space: nowrap;
    text-align: center;
  }




  .primary-nav__list {
    margin-inline: 0;
  }
}

/* --------------------------------------------------------------------------
   CASE-STUDY SUB-NAV
   Light 51px strip listing the six case studies, with an underline marking
   the current one. Scrolls horizontally when it cannot fit.
   -------------------------------------------------------------------------- */

/* These values are copied verbatim from the shared system (tokens.css,
   base.css, components.css) that the other case studies use, so this bar
   renders pixel-identically to the ones on Boarding App and WayFinder News.
   Keep them in step if the shared system changes. */
.case-nav {
  /* Sits inside the fixed .site-header, so the two bars pin together as one
     unit — the same arrangement the other case studies use. */
  background-color: #fafafa;          /* --cs-subnav-bg */
}

/* Shared page shell: 1760 content + gutters, centred. */
.case-nav .shell {
  width: 100%;
  max-width: calc(1760px + var(--cs-page-gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--cs-page-gutter);
}

.case-nav__inner {
  display: flex;
  align-items: center;
  min-height: 3.1875rem;              /* --cs-subnav-h, 51 flat */
  /* Below the nav breakpoint the labels are wider than the bar, so it scrolls
     sideways rather than wrapping or shrinking the type. */
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}

.case-nav__inner::-webkit-scrollbar {
  display: none;
}

.case-nav__list {
  display: flex;
  align-items: center;
  gap: var(--cs-subnav-gap);
  min-width: max-content;             /* keeps the labels on one line */
  list-style: none;
}

.case-nav__link {
  display: flex;
  align-items: center;
  min-height: 44px;                   /* --tap-target */
  color: #454545;                     /* --cs-subnav-ink */
  font-size: 0.875rem;                /* --fs-nav, 14 fixed */
  font-weight: 600;
  line-height: normal;
  text-transform: uppercase;
  white-space: nowrap;
  text-decoration-thickness: 1px;     /* --cs-subnav-underline */
  text-decoration-color: #1d1d1b;     /* --cs-subnav-underline-ink */
  text-underline-offset: 7px;     /* frame: 7px under the baseline */
}

a.case-nav__link:hover {
  color: #2f2e2e;                     /* --cs-ink */
  text-decoration-line: underline;
}

/* The project this page is. A <span>, not a link — there is nowhere for it to
   go — carrying the underline that marks it as selected. The frame keeps every
   label at --cs-subnav-ink; only the rule marks the current one. */
.case-nav__link--current {
  text-decoration-line: underline;
}

/* At the frame's own width the row sits where the frame puts it. Below the
   nav breakpoint the bar scrolls, so it starts from the left instead. */
@media (min-width: 1200px) {
  .case-nav__list {
    margin-inline-start: auto;
    margin-inline-end: 6.25%;         /* --cs-subnav-inset-end */
  }
}

/* --------------------------------------------------------------------------


/* ==========================================================================
   COMPONENTS
   The pieces this case study adds on top of the shared chrome: the meta
   rail, captioned figures, the pull quote, and the video player.

   The site header, primary nav and case-study sub-nav come from the shared
   block above; the footer comes from css/footer.css.

   Page-specific composition lives in the CASE STUDY section below.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. META RAIL  (Figma 726:9483 — Role / Team / Shipped)

   A description list. Stacks as a row of columns on small screens so it
   does not eat vertical space above the hero, then becomes the left rail
   at the desktop breakpoint.
   -------------------------------------------------------------------------- */

.meta-rail {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md) var(--space-xl);
}

.meta-rail__item {
  min-width: 0;
}

.meta-rail__term {
  margin: 0 0 var(--space-2xs);
  color: var(--color-ink);
  font-size: var(--fs-12);
  font-weight: var(--fw-bold);
  line-height: var(--lh-normal);
  letter-spacing: var(--ls-12);
  text-transform: uppercase;
}

/* "Role" is set a step larger than TEAM / SHIPPED in the frame
   (14px / −0.44px vs 12px / 0.12px). Kept faithful. */
.meta-rail__term--lg {
  font-size: var(--fs-14);
  letter-spacing: var(--ls-tight-44);
}

.meta-rail__desc {
  color: var(--color-muted);
  font-size: var(--fs-15);
  font-weight: var(--fw-regular);
  line-height: var(--lh-rail);
  letter-spacing: var(--ls-15);
}

.meta-rail__desc--medium {
  font-weight: var(--fw-medium);
}

@media (min-width: 1024px) {
  .meta-rail {
    flex-direction: column;
    flex-wrap: nowrap;
    gap: var(--space-md);
    /* Matches the 45px gap between Role and Team in the frame. */
    padding-inline-end: var(--space-sm);
  }
}


/* --------------------------------------------------------------------------
   2. CAPTIONED FIGURE

   Every image on the page is a <figure> with a real <figcaption> — including
   the three AI-POWERED panels, whose captions were baked into the Figma
   export and have been re-set as live text.
   -------------------------------------------------------------------------- */

.figure {
  margin: 0;
  min-width: 0;
}

.figure__media {
  display: block;
  width: 100%;
  height: auto;
}

.figure__caption {
  margin-block-start: var(--space-md);
  color: var(--color-muted);
  font-size: var(--fs-body);
  font-weight: var(--fw-regular);
  line-height: var(--lh-body);
}

/* --------------------------------------------------------------------------
   3. PULL QUOTE  (Figma 697:12694 — 3px rule, #282626)
   -------------------------------------------------------------------------- */

.pull-quote {
  border-inline-start: var(--stroke-3) solid var(--color-grey-15);
  padding-inline-start: var(--space-md);
  color: var(--color-muted);
  font-size: var(--fs-body);
  font-weight: var(--fw-regular);
  line-height: var(--lh-body);
}

/* --------------------------------------------------------------------------
   4. VIDEO
   A clip that fills its slot exactly the way an image would, with the controls
   laid over the foot of it: play/pause, a scrubbable progress bar,
   elapsed/total time, and a mute toggle.

   This is the same .video component the other case studies use (the Alaska
   boarding and WayFinder heroes), ported to this page's tokens so the markup,
   class names and JS behaviour are identical.

   Not in the Figma frame — the frame draws the clip's poster frame and nothing
   else — so the control bar is composed from existing tokens and overlaid
   rather than given its own space, which keeps the frame's layout untouched.
   -------------------------------------------------------------------------- */

.video {
  position: relative;
  overflow: hidden;
  background-color: var(--color-bg);
}

.video__el {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* --- Control bar -------------------------------------------------------- */

.video__controls {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-2xs) var(--space-sm);
  /* A scrim, so white controls hold up over a bright frame of the clip. */
  background-image: linear-gradient(
    to top,
    rgba(0, 0, 0, 0.55),
    rgba(0, 0, 0, 0)
  );
}

.video__toggle,
.video__mute {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: var(--touch-target);
  height: var(--touch-target);
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-white);
  cursor: pointer;
}

.video__icon {
  width: 24px;
  height: 24px;
}

/* Which glyph shows is driven by the state on the wrapper, so the button's
   markup never changes and its accessible name is the only thing JS swaps. */
.video__icon--pause { display: none; }
.video[data-playing="true"] .video__icon--play  { display: none; }
.video[data-playing="true"] .video__icon--pause { display: block; }

/* Same trick for the speaker pair. Muted is the default — this page starts
   silent — so the crossed-out speaker shows until the reader turns sound on. */
.video__icon--unmuted { display: none; }
.video[data-muted="false"] .video__icon--muted   { display: none; }
.video[data-muted="false"] .video__icon--unmuted { display: block; }

.video__time {
  flex: 0 0 auto;
  margin: 0;
  color: var(--color-white);
  font-size: var(--fs-14);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

/* --- Scrubber ------------------------------------------------------------
   A real <input type="range">, so keyboard seeking, screen-reader semantics
   and touch dragging all come for free. --p is the played fraction, set by
   js/main.js, and paints the filled part of the track.
   ------------------------------------------------------------------------ */

.video__scrub {
  flex: 1 1 auto;
  min-width: 0;
  height: var(--touch-target);   /* full-height grab area, hairline track */
  margin: 0;
  padding: 0;
  background: none;
  cursor: pointer;
  -webkit-appearance: none;
          appearance: none;
}

.video__scrub:focus { outline: none; }

.video__scrub:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

/* WebKit / Blink */
.video__scrub::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;
  background-image: linear-gradient(
    to right,
    var(--color-white) 0 calc(var(--p, 0) * 100%),
    rgba(255, 255, 255, 0.35) calc(var(--p, 0) * 100%) 100%
  );
}

.video__scrub::-webkit-slider-thumb {
  -webkit-appearance: none;
          appearance: none;
  width: 12px;
  height: 12px;
  margin-top: -4px;              /* centres the 12px thumb on the 4px track */
  border: 0;
  border-radius: 50%;
  background-color: var(--color-white);
}

/* Firefox */
.video__scrub::-moz-range-track {
  height: 4px;
  border-radius: 2px;
  background-color: rgba(255, 255, 255, 0.35);
}

.video__scrub::-moz-range-progress {
  height: 4px;
  border-radius: 2px;
  background-color: var(--color-white);
}

.video__scrub::-moz-range-thumb {
  width: 12px;
  height: 12px;
  border: 0;
  border-radius: 50%;
  background-color: var(--color-white);
}


/* ==========================================================================
   CASE STUDY — AI / ENTERPRISE (Lowes Digital Twin)

   Page-specific composition. Everything reusable lives in components.css.
   Section order follows the Figma frame top to bottom:
     hero → overview → innovation → pull quote → role → ai powered → closing
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. INTRO  (hero + meta rail, Figma y=141)

   Desktop: the rail sits at x=80 and the hero starts at x=401 and bleeds off
   the right edge of the frame to x=1920.

   Below 1024px the grid collapses to one column. The hero leads in the DOM so
   it stays ABOVE the meta there, matching the other case studies; the rail is
   put back into column 1 with explicit grid placement rather than by ordering
   the markup.
   -------------------------------------------------------------------------- */

.intro {
  /* No horizontal padding: the hero is edge-to-edge on small screens, and at
     the desktop breakpoint only the left gutter comes back so the hero still
     runs off the right edge exactly as it does in the frame (x=401 → 1920). */
  padding-inline: 0;
}

/* Wrapped under the hero on small screens. */
.intro .meta-rail {
  padding: var(--space-lg) var(--gutter) 0;
}

@media (min-width: 1024px) {
  .intro {
    padding-inline-start: var(--gutter);
    align-items: start;
  }

  /* Explicit placement, so the hero can lead in the markup while the rail
     still sits in column 1 beside it. */
  .intro .meta-rail {
    grid-column: 1;
    grid-row: 1;
    padding: 0 var(--space-sm) 0 0;
  }

  .intro .hero {
    grid-column: 2;
    grid-row: 1;
  }
}

/* --- Hero (714:8051 — 1519×732) ----------------------------------------- */

.hero {
  /* Query container for the title below: the "LOWES / DIGITAL TWIN" lockup is
     sized against the hero's own width, not the viewport's, so both lines stay
     unwrapped at every width instead of outrunning the copy column. */
  container-type: inline-size;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  padding: var(--space-lg) var(--gutter);
  background-color: var(--color-hero-blue);
  background-image: url("../assets/ai-enterprise/hero-background.jpg");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  color: var(--color-on-dark);
}

.hero__copy {
  min-width: 0;
}

.hero__eyebrow {
  margin: 0 0 var(--space-sm);
  color: var(--color-hero-eyebrow);
  font-size: var(--fs-13);
  font-weight: var(--fw-bold);
  line-height: var(--lh-normal);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
}

.hero__title {
  margin: 0;
  font-size: var(--fs-hero-title);
  line-height: var(--lh-hero-title);
  letter-spacing: var(--ls-hero-title);
}

/* "LOWES" is Extra Bold, "DIGITAL TWIN" Regular — two lines of one heading. */
.hero__title-brand {
  display: block;
  font-weight: var(--fw-extrabold);
}

.hero__title-product {
  display: block;
  font-weight: var(--fw-regular);
}

.hero__lede {
  margin-block-start: var(--space-sm);
  max-width: 412px;
  color: var(--color-hero-lede);
  font-size: var(--fs-hero-lede);
  font-weight: var(--fw-regular);
  line-height: var(--lh-body);
}

.hero__figure {
  margin: 0;
  min-width: 0;
}

.hero__image {
  width: 100%;
  height: auto;
}

@media (min-width: 768px) {
  .hero {
    padding-block: var(--space-xl);
  }
}

@media (min-width: 1024px) {
  .hero {
    flex-direction: row;
    align-items: center;
    gap: 47px;
    min-height: 732px;
    /* var(--gutter) resolves to the frame's 80px at 1920. */
    padding: var(--space-md) var(--gutter);
  }

  /* 3.385cqi === 46px when the hero's content box is 1359px wide, which is
     what it measures at the 1920 reference width. */
  .hero__title {
    font-size: min(3.385cqi, 2.875rem);
  }

  /* 411 : 901 — the split measured in the frame. */
  .hero__copy {
    flex: 411 1 0;
  }

  .hero__figure {
    flex: 901 1 0;
  }

  .hero__eyebrow {
    margin-block-end: 15px;
  }

  .hero__lede {
    margin-block-start: 17px;
  }
}

/* --------------------------------------------------------------------------
   2. OVERVIEW  (726:9463 — 40/56, 1439 wide)

   One paragraph in two tones: the opening sentence in near-black, the rest
   in the muted grey.
   -------------------------------------------------------------------------- */

.overview__text {
  max-width: var(--content-max);
  margin-inline-start: auto;
  margin-inline-end: auto;
  color: var(--color-muted);
  font-size: var(--fs-overview);
  font-weight: var(--fw-regular);
  line-height: var(--lh-overview);
  letter-spacing: var(--ls-overview);
}

.overview__lead {
  color: var(--color-ink-overview);
}

@media (min-width: 1024px) {
  .overview__text {
    margin-inline: 0;
  }
}

/* --------------------------------------------------------------------------
   3. INNOVATION  (726:9478 + 726:9477)

   Two figures side by side at 510 : 886, stacking below the tablet
   breakpoint. Captions sit tight under the images in the frame (~10px),
   closer than the 24px used elsewhere.
   -------------------------------------------------------------------------- */

.innovation__figures {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

.innovation .figure__caption {
  margin-block-start: var(--space-xs);
}

/* Left figure is cropped in the frame: the 1264×702 screenshot is shown in a
   510×498 window centred ~48% across. object-fit reproduces that crop while
   staying fluid. */
.innovation__figure--crop .figure__media {
  aspect-ratio: 510 / 498;
  object-fit: cover;
  object-position: 48% center;
}

/* The clip fills the slot the frame's poster occupied. 886/501 is the frame's
   own ratio (1.7685) and the 1280x724 source is 1.7680, so nothing is cropped. */
.innovation__figure--video .video {
  aspect-ratio: 886 / 501;
}

@media (min-width: 768px) {
  .innovation__figures {
    flex-direction: row;
    align-items: flex-start;
    gap: clamp(20px, 2.84%, 41px);
  }

  .innovation__figure--crop {
    flex: 510 1 0;
  }

  .innovation__figure--video {
    flex: 886 1 0;
  }
}

/* --------------------------------------------------------------------------
   4. PULL QUOTE  (697:12694)

   The quote itself is a component (.pull-quote); here it only needs to be
   dropped into the main column, which `.content-column` handles in the markup.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   5. ROLE  (725:9443 — full-bleed #e1e1e1 band)

   Two columns of prose, each introduced by a heading and marked with a 3px
   rule down the left edge.
   -------------------------------------------------------------------------- */

.role {
  margin-block-start: var(--section-gap-lg);
  padding-block: var(--space-lg) var(--space-xl);
  background-color: var(--color-surface-grey);
}

@media (min-width: 1024px) {
  .role {
    padding-block: 49px 90px;
  }
}

.role__columns {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-lg);
  margin-block-start: var(--space-md);
}

@media (min-width: 768px) {
  .role__columns {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 46px;
  }
}

@media (min-width: 1024px) {
  .role__columns {
    margin-block-start: 0;
  }
}

.role__heading {
  /* Sits at the same indent as the ruled body text below it. */
  margin: 0 0 var(--space-md);
  padding-inline-start: calc(var(--stroke-3) + var(--space-md));
  color: var(--color-ink);
  font-size: var(--fs-column-heading);
  font-weight: var(--fw-medium);
  line-height: var(--lh-label);
  letter-spacing: var(--ls-10);
}

.role__body {
  border-inline-start: var(--stroke-3) solid var(--color-rule);
  padding-inline-start: var(--space-md);
  color: var(--color-body);
  font-size: var(--fs-body);
  font-weight: var(--fw-regular);
  line-height: var(--lh-body);
}

/* One blank line of leading between paragraphs, as in the frame. */
.role__body p + p {
  margin-block-start: 1.5em;
}

/* DISCOVERY / EXPLORATION & TESTING lead-ins. */
.role__term {
  font-weight: var(--fw-semibold);
}

/* --------------------------------------------------------------------------
   6. AI POWERED  (726:9501)

   The frame ships this as one flattened 2708×1877 export with its three
   captions burned into the bitmap. It has been split back into three panels
   so the captions can be live text that scales and reads out.

   Panel widths in the frame: 764 : 203.5 : 200, gaps ~93px.
   -------------------------------------------------------------------------- */

.ai-powered__panels {
  /* The panel row stops short of the column's right edge in the frame. */
  max-width: 1354px;
  display: grid;
  /* Two columns from the smallest screen up: the wide planning screen takes a
     row of its own and the two tall panels stay paired beside each other. They
     scale down together rather than ever stacking into a single column. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  /* 6.87% === the frame's ~93px gap when the row is 1354 wide. */
  column-gap: clamp(14px, 6.87%, 93px);
  row-gap: var(--space-lg);
  align-items: start;
}

.ai-powered__panel--wide {
  grid-column: 1 / -1;
}

/* At the desktop breakpoint all three sit in one row at the frame's
   764 : 203.5 : 200 split. */
@media (min-width: 1024px) {
  .ai-powered__panels {
    grid-template-columns: 764fr 204fr 200fr;
  }

  .ai-powered__panel--wide {
    grid-column: auto;
  }
}

/* --------------------------------------------------------------------------
   7. CLOSING  (743:15739 — 1920×500, full bleed)

   The store photo with a pull quote set over it. The quote stays on the photo
   at every width — it never drops below — so the type, the gaps and the
   padding are one continuous fluid system rather than two layouts either side
   of a breakpoint. Every clamp lands on the frame's value at 1920 and floors
   at something readable on a phone:

     quote      40 → 18      padding-top   51 → 16
     credit     18 → 13      padding-inline  var(--gutter): 80 → 20
     gap        24 → 12      measure       600 → the full column

   cqw is measured against .closing, which is full-bleed, so the ratios track
   the photo itself.
   -------------------------------------------------------------------------- */

.closing {
  /* 94px in the frame, tighter than the 110px used between text sections. */
  margin-block-start: var(--section-gap);
  position: relative;
  container-type: inline-size;
}

.closing__image {
  width: 100%;
  height: auto;
  aspect-ratio: 1920 / 500;
  /* The 1920:500 band is only 98px tall at 375 — far too short to carry the
     quote. The floor lets the photo crop instead, and above ~768 the ratio
     takes over again. */
  min-height: 200px;
  object-fit: cover;
  /* Biased left so the crop keeps the pale wall the quote is set on, rather
     than centring on the associates' dark shirts. */
  object-position: 22% center;
}

.closing__quote {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(12px, 1.25cqw, 24px);
  /* 51 / 156 against the frame's 1920. */
  padding-block: clamp(16px, 2.656cqw, 51px) clamp(16px, 8.125cqw, 156px);
  padding-inline: var(--gutter);
  color: var(--color-quote-ink);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-normal);
}

/* A scrim under the text only. The frame can guarantee the quote sits on the
   white wall because it is drawn at one width; a cropped photo cannot, so
   this keeps the dark ink legible wherever the crop lands. It fades out well
   before the associates so the photograph still reads as the subject. */
.closing__quote::before {
  content: "";
  position: absolute;
  inset: 0;
  /* Hold full strength to wherever the text measure actually ends, then fade
     out over a fixed 300px. At 1920 that is 680px → 980px, so it clears the
     associates entirely and is invisible over the white wall the frame puts
     the quote on. On a phone the measure runs nearly the full width, so the
     photo reads as a muted backdrop and the quote stays the subject. */
  --scrim-end: min(calc(var(--gutter) + 600px), 90%);
  background-image: linear-gradient(
    to right,
    rgba(250, 250, 250, 0.93) 0,
    rgba(250, 250, 250, 0.9) var(--scrim-end),
    rgba(250, 250, 250, 0) calc(var(--scrim-end) + 300px)
  );
}

.closing__text,
.closing__cite {
  position: relative;   /* above the scrim */
  /* The frame's 600px measure; the full column once there is less than that. */
  max-width: 600px;
}

.closing__text {
  margin: 0;
  /* 40 against a 1920 frame. */
  font-size: clamp(1.125rem, 2.0833cqw, 2.5rem);
}

.closing__cite {
  display: flex;
  flex-direction: column;
  /* 11 against a 1920 frame. */
  gap: clamp(6px, 0.5729cqw, 11px);
  /* 18 against a 1920 frame. */
  font-size: clamp(0.8125rem, 0.9375cqw, 1.125rem);
  font-style: normal;
}

.closing__role {
  max-width: 454px;
}


/* --- Below the nav breakpoint: the mobile header (Figma 742:15538) -------
   A taller bar — 114 against the desktop header's 90 — carrying the wordmark
   and the three icons and nothing else. This stylesheet writes the chrome
   offsets as literals rather than reading a token, so the bar height, the
   body offset that replaces it in the flow, and the anchor scroll-padding
   are all restated here.
   ------------------------------------------------------------------------ */
@media (max-width: 1199px) {
  html { scroll-padding-top: calc(7.125rem + 3.1875rem); }
  body { padding-top: calc(7.125rem + 3.1875rem); }   /* header 114 + sub-nav 51 */

  .site-header__inner { min-height: 7.125rem; }       /* 114 (742:15538) */

}
