/* ==========================================================================
   site.css — shared across index / case-fdt / case-puma / cv

   Built from Figma "new-portfolio-maryna". Figma is the source of truth:
   every number here was read off a node, not eyeballed.
   Values come from styles/tokens.css — never hardcode a colour or a step.
   ========================================================================== */

*{box-sizing:border-box;margin:0;padding:0}
/* The gutter is reserved whether or not the page scrolls, so locking the body
   when the menu opens no longer removes the scrollbar and nudges every centred
   thing sideways. Every page here scrolls anyway, so nothing looks different. */
html{scroll-behavior:smooth;scrollbar-gutter:stable}
body{
  background:var(--surface-primary);
  color:var(--foreground-primary);
  font-family:var(--font-body);
  -webkit-font-smoothing:antialiased;
}
a{color:inherit;text-decoration:none}
img{display:block}
li{list-style:none}

/* Visible to screen readers, not to the eye. Used on the burger, which shows
   an icon but still has to announce what it does. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* Two wordings, one per width. Only for cases where the long form forces a
   line break that costs more than the words are worth — the client name in a
   case card eyebrow, the middle sentence of the hero paragraph. Both stay in
   the markup, so neither is hidden from search engines or screen readers by
   accident: the mobile switch happens at the bottom of this file. */
.only-wide{display:inline}
.only-narrow{display:none}

/* The system has no bold — every text style is Regular. Browsers set headings
   to 700 by default, which made every title heavier than the Figma frame. */
h1,h2,h3,h4,h5,h6{font-weight:400}

/* Page frame: 1440 wide, padding 24 / 80 / 0, content column 1280 ---------- */
.page{max-width:var(--page-width);margin:0 auto;padding:var(--spacing-24) var(--page-padding) 0}
.row{width:100%;max-width:var(--content-width);margin:0 auto}

.accent{color:var(--foreground-accent)}
.muted{color:var(--foreground-tertiary)}

/* Icons -------------------------------------------------------------------- */
.icon{width:16px;height:16px;flex:none;fill:currentColor}
.icon--24{width:24px;height:24px}

/* --------------------------------------------------------------------------
   Eyebrow — row, gap 0. The 16px icon box supplies the spacing.
   First label is foreground-primary, the rest foreground-tertiary,
   the dot is foreground-accent.
   -------------------------------------------------------------------------- */
.eyebrow{display:inline-flex;align-items:center;color:var(--foreground-tertiary)}
.eyebrow > :first-child{color:var(--foreground-primary)}
.eyebrow .icon{color:var(--foreground-accent)}
/* When the row opens with the dot, nothing should be promoted to primary. */
.eyebrow > .icon:first-child ~ span{color:var(--foreground-tertiary)}
/* Some rows carry no lead label at all — every word is muted, the dot stays
   accent. Used on the CV, where the category reads as metadata, not a title.
   Targets :first-child so it beats the promote-to-primary rule above on
   specificity rather than by accident of order. */
.eyebrow--muted > :first-child{color:var(--foreground-tertiary)}

/* --------------------------------------------------------------------------
   Button — Variation x Type, matching the Figma axes.
   Variation is geometry: .btn--pill .btn--square .btn--circle .btn--ghost
   .btn--back.  Type is colour: .btn--primary .btn--secondary.
   Ghost and Back bring their own colour family and ignore Type.

   Pill carries Button-1 (16), Square and Ghost carry Button-2 (14).
   Heights are fixed so the 1px border never grows the box: in Figma a stroke
   is drawn inside the frame and does not change its size.
   -------------------------------------------------------------------------- */
/* justify-content matters only once a button is stretched — every Figma
   variant centres its content, and a full-width mobile pill was left-aligning
   its label without this. */
.btn{display:inline-flex;align-items:center;justify-content:center;
  gap:var(--spacing-8);
  border:1px solid transparent;cursor:pointer;white-space:nowrap;
  transition:background .15s ease,color .15s ease,border-color .15s ease}
.btn:focus-visible{outline:2px solid var(--foreground-accent);outline-offset:2px}

.btn--pill{height:40px;padding:0 var(--spacing-16);border-radius:var(--radius-full)}
.btn--square{height:32px;padding:0 var(--spacing-12);border-radius:var(--radius-md)}

/* Circle — icon only, no label. Square box, fully rounded. */
.btn--circle{width:32px;height:32px;flex:none;padding:0;
  justify-content:center;border-radius:var(--radius-full)}
.btn--circle.btn--lg{width:48px;height:48px}

.btn--primary{background:var(--button-primary-background-default);color:var(--button-primary-foreground-default)}
.btn--primary:hover{background:var(--button-primary-background-hover);color:var(--button-primary-foreground-hover)}
.btn--primary:active{background:var(--button-primary-background-focus);color:var(--button-primary-foreground-focus)}

.btn--secondary{background:var(--button-secondary-background-default);color:var(--button-secondary-foreground-default);border-color:var(--button-secondary-border-default)}
.btn--secondary:hover{background:var(--button-secondary-background-hover);border-color:var(--button-secondary-border-hover)}
.btn--secondary:active{background:var(--button-secondary-background-focus);border-color:var(--button-secondary-border-focus)}

/* Ghost has no box: label only, optional trailing icon. It hugs its label —
   without this it stretches inside any column that stretches its children,
   and a stretched button centres its own label. */
.btn--ghost{height:32px;padding:0;border-radius:0;
  background:none;color:var(--button-ghost-foreground-default)}
.btn--ghost:hover{color:var(--button-ghost-foreground-hover)}
.btn--ghost:active{color:var(--button-ghost-foreground-focus)}
/* The arrow matches its label by default. Accent is a hover signal, not a
   decoration — see the Case Card rule in index.html. */
.btn--ghost .icon{color:currentColor}

/* Back — a composite: a circle holding the arrow, then a plain label beside it.
   The label always sits on the page, so it always takes the Secondary
   foreground; Type only reaches the circle. */
.btn--back{height:32px;padding:0;background:none;border:0;border-radius:0;
  color:var(--button-secondary-foreground-default)}
.btn--back .btn__circle{display:inline-flex;align-items:center;justify-content:center;
  width:32px;height:32px;flex:none;border-radius:var(--radius-full);
  background:var(--button-secondary-background-default);
  border:1px solid var(--button-secondary-border-default);
  transition:background .15s ease,border-color .15s ease}
.btn--back:hover{color:var(--button-secondary-foreground-hover)}
.btn--back:hover .btn__circle{background:var(--button-secondary-background-hover);
  border-color:var(--button-secondary-border-hover)}
.btn--back .icon{color:currentColor}

/* Back / Primary — black circle, white arrow, no border. The label beside it
   stays dark: it is on the page, not on the circle. Icon and label are painted
   from different families here on purpose. */
.btn--back.btn--primary .btn__circle{
  background:var(--button-primary-background-default);
  border-color:transparent;color:var(--button-primary-foreground-default)}
.btn--back.btn--primary:hover .btn__circle{
  background:var(--button-primary-background-hover);border-color:transparent}
/* The outer Back button is a transparent wrapper in every state — in the Figma
   component set (317:275282) all sixteen Back variants have fill "none"; only
   the nested Circle changes colour. Without this guard, .btn--primary:hover
   (0,2,0) outweighs .btn--back (0,1,0) and paints a grey rectangle behind the
   circle and the label. Same trap on :active. */
.btn--back:hover,.btn--back:active,.btn--back:focus{background:none;border-color:transparent}

/* Tag — radius-md, not a pill --------------------------------------------- */
.tag{display:inline-flex;align-items:center;justify-content:center;
  padding:var(--spacing-4) var(--spacing-8);border-radius:var(--radius-md);
  background:var(--bg-primary);border:1px solid var(--border-primary);
  color:var(--foreground-tertiary)}

/* Divider ------------------------------------------------------------------ */
.divider{width:100%;height:1px;border:0;background:var(--border-primary)}

/* --------------------------------------------------------------------------
   Nav — two white cards side by side, 8 apart, 48 tall.
   -------------------------------------------------------------------------- */
.nav{display:flex;gap:var(--spacing-8);height:48px}
/* Inset shadow, not a border: in Figma the stroke is drawn inside the card and
   the brand sits 16 from the card edge, full stop. A CSS border would add
   itself to the padding and push everything one pixel inward — invisible on
   its own, obvious the moment the menu panel opens beside it. */
.nav__bar{flex:1;display:flex;align-items:center;justify-content:space-between;
  gap:var(--spacing-40);height:48px;padding:0 var(--spacing-24);
  border-radius:var(--radius-md);background:var(--surface-secondary);
  box-shadow:inset 0 0 0 1px var(--border-primary)}
.nav__brand{color:var(--foreground-primary)}
.nav__menu{display:flex;align-items:center;gap:var(--spacing-32)}
.nav__group{display:flex;align-items:center;gap:var(--spacing-16);height:48px;
  padding:0 var(--spacing-8) 0 var(--spacing-16);
  border-radius:var(--radius-md);background:var(--surface-secondary);
  box-shadow:inset 0 0 0 1px var(--border-primary)}

/* --------------------------------------------------------------------------
   Panel — the carded two-column section used on every case page.
   1280 frame, padding 64, heading column 341, body column 669.
   -------------------------------------------------------------------------- */
.panel{display:flex;align-items:flex-start;justify-content:space-between;
  padding:var(--spacing-64);border-radius:var(--radius-md);
  background:var(--surface-secondary)}
.panel__heading{width:341px;flex:none}
.panel__body{width:669px;flex:none;color:var(--foreground-secondary)}
/* In Figma the body is one text node with a blank line between paragraphs, so
   the gap is one line of Body (22) — a typographic value, not a spacing step. */
.panel__body p + p{margin-top:22px}

/* Numbered list living in a panel's body column ---------------------------- */
.steps{display:flex;flex-direction:column;gap:var(--spacing-40);width:669px;flex:none}
.step{display:flex;flex-direction:column;gap:var(--spacing-24)}
.step__top{display:flex;flex-direction:column;gap:var(--spacing-8)}
.step__body{color:var(--foreground-secondary)}

/* Bullets — the dot is foreground-secondary here, unlike the accent dot in an
   Eyebrow. Row gap 4; the 16 icon box does the rest of the indent.
   The marker sits at the top, and its slot is 22 tall — one line of Body — so
   the 16 dot lands on the middle of the first line instead of the middle of
   the whole paragraph. The SVG keeps its ratio and centres inside that slot. */
.bullets{display:flex;flex-direction:column;gap:var(--spacing-8)}
.bullet{display:flex;align-items:flex-start;gap:var(--spacing-4);
  color:var(--foreground-secondary)}
.bullet .icon{height:22px;color:var(--foreground-secondary)}

/* --------------------------------------------------------------------------
   Contact — a white card on the grey page. Same on every page.
   -------------------------------------------------------------------------- */
.contact{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:var(--spacing-32);padding:var(--spacing-96) 0;
  border-radius:var(--radius-md);background:var(--surface-secondary)}
.contact__content{display:flex;flex-direction:column;align-items:center;
  gap:var(--spacing-24);width:624px;max-width:100%;text-align:center}
.contact__btns{display:flex;align-items:center;gap:var(--spacing-16)}

/* ==========================================================================
   Case pages — shared by case-fdt and case-puma. Both Figma frames use the
   same skeleton: Hero, Meta, Media, Story, a white gallery, Outcome.
   ========================================================================== */

/* Hero — content centred; Back is pinned top-left and sits outside the
   auto-layout flow, exactly as in Figma. */
.cs-hero{position:relative;display:flex;flex-direction:column;align-items:center;
  padding:var(--spacing-96) 0}
/* Back starts pinned to the top-left of the Hero (matches Figma's static
   frame) and switches to fixed-to-viewport plus Primary colour once scrolled
   past that point — matches "1440 - Sticky Back button" (455:22177), which
   shows the same Back/Primary/Small instance sitting at viewport x:80 y:24
   while Meta and Media are visible below, i.e. well past the Hero itself.
   `.is-stuck` is toggled in JS (scroll position vs the button's own natural
   offset), not CSS alone, since "has the Hero scrolled past" isn't something
   position:sticky can express when the stuck state needs to survive past its
   own parent's height. `--inset` mirrors .page's own padding at each width,
   so the fixed left edge lines up with the row's left edge exactly, at every
   breakpoint including ultrawide viewports beyond the 1440 design width. */
.cs-hero__back{position:absolute;left:0;top:var(--spacing-24);
  --inset:var(--page-padding);z-index:5}
.cs-hero__back.is-stuck{position:fixed;top:var(--spacing-24);
  left:calc(max(0px,(100vw - var(--page-width)) / 2) + var(--inset))}
/* Stuck state matches "1440 - Sticky Back button" (455:22177) exactly: the
   instance there is 32x32 — only the circle — with the "Back" text node set to
   visible:false. Type=Primary reaches the circle only (see .btn--back.btn--primary
   above); the outer button itself never takes a background. */
.cs-hero__back.is-stuck .btn__label{display:none}
/* Marks the Back button's natural top:24 point for the IntersectionObserver
   in each case page's own <script> — sits exactly where the button starts,
   independent of the button's own position once it's fixed. */
.back-sentinel{position:absolute;top:var(--spacing-24);left:0;width:1px;height:1px;
  visibility:hidden;pointer-events:none}
.cs-hero__copy{display:flex;flex-direction:column;align-items:center;
  gap:var(--spacing-24);width:842px;max-width:100%;text-align:center}
.cs-hero__body{width:624px;max-width:100%;color:var(--foreground-secondary)}

/* Meta — the Case-info strip */
.cs-meta{padding-bottom:var(--spacing-96)}
.case-info{display:flex;justify-content:center;align-items:flex-start;
  gap:var(--spacing-96);padding:var(--spacing-32) var(--spacing-40);
  border-radius:var(--radius-md);background:var(--surface-secondary)}
.case-label{display:flex;flex-direction:column;align-items:center;gap:var(--spacing-8)}
.case-label__value{color:var(--foreground-primary)}

/* Media frames — no border, per Maryna (removed in Figma too). Radius and
   clipping stay; the 1px inset that made room for the border is gone since
   there's nothing left to reveal. */
.media{overflow:hidden;border-radius:var(--radius-md)}
.media img{width:100%;height:auto}
/* Gallery shots (Before vs After / Selected screens) get the smaller
   radius — 6 -> 2 — per Maryna. The hero .media outside .gallery keeps 6. */
.gallery .media{border-radius:var(--radius-sm)}

/* Shot's default cross-axis stretch (flex-direction:column has no
   align-items override, so it defaults to stretch) was pulling the eyebrow
   to the shot's full width instead of the eyebrow's own content width. Never
   showed at desktop — 800/600 is wide enough that the stretched box still
   fits the text on one line — but at mobile's 279 some of these eyebrows
   are close enough to that width that the stretch shrank the row, and its
   text wrapped to a second line inside it. Figma sizes the eyebrow to HUG,
   i.e. its own content, at every width; this makes the box do the same. */
.shot .eyebrow{align-self:flex-start}

/* Story / Outcome — stacks of panels */
.cs-stack{display:flex;flex-direction:column;gap:var(--spacing-32);
  padding:var(--spacing-96) 0}
/* The CV body sits straight under the Meta strip, so it has no top padding. */
.cs-stack--flush{padding-top:0}

/* Gallery — the white section holding screenshots (Before vs After on FDT,
   Selected screens on Puma). */
.gallery{display:flex;flex-direction:column;align-items:center;
  gap:var(--spacing-48);padding:var(--spacing-64) var(--spacing-40);
  border-radius:var(--radius-md);background:var(--surface-secondary)}
.gallery__heading{width:100%;text-align:center}
.gallery__note{text-align:center;color:var(--foreground-tertiary)}
/* A pair of shots stacked (FDT), or a single screen (Puma) */
.compare{display:flex;flex-direction:column;align-items:center;
  gap:var(--spacing-40);width:800px}
.shot{display:flex;flex-direction:column;gap:var(--spacing-24)}
.shot--wide{width:800px}
.shot--tall{width:600px}

/* Footer -------------------------------------------------------------------
   Three things, but only two of them are laid out. Socials and copyright sit
   in a space-between row; Back to top is centred on the frame absolutely,
   because the flanking blocks are 136 and 188 wide — space-between would put
   a third child 26px left of centre. Same reasoning as the Figma component. */
.footer{position:relative;display:flex;align-items:center;justify-content:space-between;
  padding:var(--spacing-96) 0}
.footer__socials{display:flex;align-items:center;gap:var(--spacing-32)}
.footer__socials a{display:flex;color:var(--foreground-primary)}
.footer__socials a:hover{color:var(--foreground-accent)}
.footer__top{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%)}

/* --------------------------------------------------------------------------
   Burger and the mobile menu panel. Hidden above 768; the media queries at
   the bottom of this file switch them on.
   -------------------------------------------------------------------------- */
.nav__burger{display:none;align-items:center;justify-content:center;
  width:48px;height:48px;flex:none;padding:0;border:0;background:none;
  color:var(--foreground-primary);cursor:pointer}
.nav__burger:focus-visible{outline:2px solid var(--foreground-accent);outline-offset:-2px}

/* The panel is fixed to the viewport, inset by the page padding, so it fills
   the screen and the buttons land at the bottom — not a fixed 656 box. */
/* The hairline is an inset shadow, not a border — same reason as the media
   frames. A real border would push the header down by 1px, and the brand and
   the burger would sit one pixel lower than they do in the closed nav. */
.nav__panel{position:fixed;inset:var(--spacing-24);z-index:50;
  display:none;flex-direction:column;gap:var(--spacing-24);
  padding:0 0 var(--spacing-24);
  border-radius:var(--radius-md);background:var(--bg-primary);
  box-shadow:inset 0 0 0 1px var(--border-primary)}

/* The lock lives on <html>, not <body>. Body's overflow propagates up to the
   viewport, which takes the scrollbar gutter with it — the page would widen by
   the scrollbar and everything aligned to an edge would jump sideways. */
.menu-open{overflow:hidden}
.menu-open .nav__panel{display:flex}
.nav__panel-head{display:flex;align-items:center;justify-content:space-between;
  height:48px;flex:none;padding-left:var(--spacing-16)}
.nav__panel-body{flex:1;display:flex;flex-direction:column;
  justify-content:space-between;gap:var(--spacing-24);padding:0 var(--spacing-16)}
.nav__links{display:flex;flex-direction:column;gap:var(--spacing-16)}
.nav__links a{color:var(--foreground-primary);transition:color .15s ease}
.nav__links a:hover{color:var(--foreground-accent)}
.nav__panel-btns{display:flex;flex-direction:column;gap:var(--spacing-16)}
.nav__panel-btns .btn{width:100%}

/* ==========================================================================
   Responsive — shared shell. Home-specific sections live in index.html.

   Two breakpoints, both off the token scale:
     below 1024 (breakpoint-lg) — tablet, built from Figma "768 - Home"
     below  768 (breakpoint-md) — mobile, built from Figma "375 - Home"

   Below 1024 every two-column row becomes one column. No grid is introduced,
   because the desktop does not use one either — it splits 341 / 669 by hand.
   ========================================================================== */

/* --- Tablet: frame 768, padding 40, content 688 ---------------------------- */
@media (max-width:1023px){
  .page{padding:var(--spacing-24) var(--spacing-40) 0}
  .cs-hero__back{--inset:var(--spacing-40)}

  /* 768 is a touch device: pill buttons grow to the Large variant, 48 tall.
     The nav keeps its 32 buttons on purpose — one nav size all the way down
     to the burger, so nothing resizes mid-range. Their tap area is widened
     below instead, which costs no pixels on screen. */
  .btn--pill{height:48px;padding:0 var(--spacing-20)}
  /* The tap area is an overlay, not extra height. Growing the box to 44 made
     the ghost button 44 tall inside the 48-tall Button group, which has 8 of
     padding and expects a 32 child — it overflowed by 6 top and bottom and
     read as misaligned next to the 32-tall "Let's talk". The ::after keeps the
     box at Figma's 32 and still hands touch a 44 target. */
  .nav__menu .btn--ghost,
  .nav__group .btn--ghost{position:relative}
  .nav__menu .btn--ghost::after,
  .nav__group .btn--ghost::after{content:'';position:absolute;left:0;right:0;
    top:50%;height:44px;transform:translateY(-50%)}

  .contact{padding:var(--spacing-64) 0}
  /* Heading-1 48 -> Heading-2 40, the same step every section heading takes. */
  .contact h2{font-size:40px;line-height:48px}
  .footer{padding:var(--spacing-64) 0}

  /* Card/company title: Heading-3 32 -> Heading-4 24, no middle step — same
     value at 768 and 375, so it only needs setting once here. Applies to
     every ".step h3": Home's Experience company names, the case pages'
     numbered Process/Impact steps, CV's Working experience/Education/
     Languages/Skills. Found this checking Process against Figma — it was
     never wired up anywhere, including Home, so this also fixes Experience
     on Home's own mobile view, which had the same gap uncaught until now. */
  .step h3{font-size:24px;line-height:32px}

  /* Case pages / CV now have real 768 Figma frames — CV's own, FDT's, and
     Puma's (Puma's is currently misnamed "375 - Case / FDT" in Figma; the
     content inside is Puma's, worth a rename when there's a moment). Real
     numbers from all three, consistently:
       - .panel padding: 40, not the inherited desktop 64.
       - .panel heading-to-body gap: 40.
       - .panel__heading: stays Heading-2 40/48, same as desktop — no early
         step-down. It only drops to Heading-3 32/40 at 375 (mobile block).
       - .steps gap: 40 — same as desktop's own base value (site.css line
         186), so tablet needs no override at all here. Only 375 differs,
         dropping to 24 (see the mobile block).

     Back button: no change needed at all. It reads as if it moves into the
     content flow on mobile — the Hero frame's second child is literally
     named "Button" right after "Content" — but it is layoutPositioning:
     ABSOLUTE at x:0 y:24 in Figma, same as the desktop pin. Caught this by
     the numbers not adding up (measured Hero height ran 96px over Figma's)
     and going back to check the actual node property instead of trusting
     the child order. */
  .panel{flex-direction:column;align-items:stretch;
    padding:var(--spacing-40);gap:var(--spacing-40)}
  .panel__heading,.panel__body,.steps{width:auto}
  /* Row-to-column collapse never touched gap, so it inherited the desktop
     row's 96 as vertical spacing between four stacked labels — the mobile
     rule below already caught this and set 32; tablet just never got it. */
  .case-info{flex-direction:column;align-items:center;gap:var(--spacing-32)}
  .compare{width:100%}

  /* Section heading: Heading-1 48 -> 40 -> 32, same step Home's
     .cases__header h2 and .experience__heading already take — this is that
     same rule, just never extended to the gallery's own H1. */
  .gallery__heading{font-size:40px;line-height:48px}
  .shot--wide,.shot--tall{width:100%}
  .cs-stack{padding-bottom:var(--spacing-64)}
  .cs-stack:not(.cs-stack--flush){padding-top:var(--spacing-64)}
  .cs-meta{padding-bottom:var(--spacing-64)}

  /* Case-page hero titles are long ("First Digital Trust — Design system for
     a multi-module back office") and Figma steps them down a size at each
     width so they don't run to 6+ lines: Display 56 -> Heading-1 48 here.
     CV's title is just a name — Figma keeps it at Display 56 everywhere, so
     cv.html reverts this in its own <style>. No 768 frame exists for either
     page; 48 is the documented middle step from the same table Home's hero
     uses (DESIGN.md §5b), carried down from the confirmed 375 value. */
  .cs-hero__copy h1{font-size:48px;line-height:56px}
}

/* --- Mobile: frame 375, padding 24, content 327 ---------------------------- */
@media (max-width:767px){
  .page{padding:var(--spacing-24) var(--spacing-24) 0}
  .cs-hero__back{--inset:var(--spacing-24)}

  .only-wide{display:none}
  .only-narrow{display:inline}

  /* Nav collapses to one card: brand on the left, burger flush to the right
     edge. Figma "375 - Home" Nav is padding 16/0/16/16 for exactly that. */
  .nav{gap:0}
  .nav__bar{padding:0 0 0 var(--spacing-16)}
  .nav__menu,
  .nav__group{display:none}
  .nav__burger{display:flex}

  /* Contact keeps its own side padding at this width — the card is only 327
     wide, so the text would otherwise touch the border. */
  .contact{gap:var(--spacing-32);padding:var(--spacing-40) var(--spacing-24)}
  /* Contact is the exception to the section step-down: it drops straight to
     Heading-4 24, not Heading-3 32. Its heading is long and the card is only
     327 wide — at 32 it ran to four lines. Set on the Contact component,
     Size=Small, so Figma and the page agree. */
  .contact h2{font-size:24px;line-height:32px}
  /* Keep the accent phrase whole. The browser is happy to break at a hyphen,
     and on a 279 column it split "design-" from "systems-focused" — one phrase
     reading as two words. Figma keeps it together; at 24px it measures 236 in
     279, so there is room. Mobile only: at 48px the same rule costs a line. */
  .contact h2 .accent{white-space:nowrap}
  .contact__content{width:100%}
  .contact__btns{flex-direction:column;width:100%}
  .contact__btns .btn{width:100%}

  /* Footer stacks; Back to top rejoins the flow and drops the absolute
     centring it needs on the wider rows. */
  .footer{flex-direction:column;gap:var(--spacing-40);padding:var(--spacing-40) 0}
  .footer__top{position:static;transform:none}

  /* --------------------------------------------------------------------
     Case pages / CV at 375. Source: "375 - Case / FDT" (2578:0),
     "375 - Case / Puma" (5761:0), "375 - CV" (456:29293). Same padB on all
     three: 56, not 96 — Back stays absolute (see 1023 block note), so this
     is genuinely just the frame's own bottom padding, same on every page. */
  .cs-hero{padding:var(--spacing-96) 0 var(--spacing-56)}

  /* Same eyebrow step Home's hero already takes: Caption-1 24 -> Caption-3
     16. Missed this the first pass — measured Hero 8px over Figma on all
     three pages, traced it to this row alone. Source: Eyebrow instance
     height in all three 375 frames (16, was 24). */
  .cs-hero__copy .eyebrow{font-size:12px;line-height:16px}

  /* Case-info goes from a centred row to a centred column; the pill strip
     that fits four labels across 1280 doesn't fit 327. */
  .case-info{gap:var(--spacing-32);padding:var(--spacing-32) var(--spacing-24)}

  /* Panel: same column collapse as tablet, but with the exact mobile
     padding/gap Figma uses at this width, not the desktop 64. */
  .panel{padding:var(--spacing-24);gap:var(--spacing-24)}
  /* .steps drops to 24 only here — tablet (1023 block) now matches
     desktop's 40, confirmed against the real 768 Figma frames. */
  .steps{gap:var(--spacing-24)}

  /* Panel heading steps down one more level here: Heading-2 40 -> Heading-3
     32. Confirmed via getStyledTextSegments on "What went off-plan" at 375
     — the wrong 40 was costing a whole extra wrapped line. Stays at 40 at
     768 now that a real tablet frame exists (see the 1023 block above). */
  .panel__heading{font-size:32px;line-height:40px}

  /* Gallery: padding and the gap between screen pairs both shrink; the gap
     between a pair's own eyebrow and image shrinks further, to 8. */
  .gallery{gap:var(--spacing-24);padding:var(--spacing-24)}
  .compare{gap:var(--spacing-24)}
  .shot{gap:var(--spacing-8)}

  /* Story/Outcome padding steps 96 -> 64 (tablet, above) -> 40 here, same
     scale as Contact and Footer. Meta's bottom padding follows the same
     scale. Source: "Story"/"Outcome"/"Selected screens"/"Before vs After"
     padding at 375 on both case pages, and CV's "Body" — all 40. */
  .cs-stack{padding-bottom:var(--spacing-40)}
  .cs-stack:not(.cs-stack--flush){padding-top:var(--spacing-40)}
  .cs-meta{padding-bottom:var(--spacing-40)}

  /* Display 56 -> Heading-2 40. Source: "375 - Case / FDT" and
     "375 - Case / Puma" Hero heading, measured 40/48 via
     getStyledTextSegments. CV keeps Display 56 — see cv.html. */
  .cs-hero__copy h1{font-size:40px;line-height:48px}

  /* Same Section step, one more stop: 40 -> Heading-3 32. */
  .gallery__heading{font-size:32px;line-height:40px}

  /* Gallery note drops further than the usual step, all the way to Body —
     Heading-4 24 down to 16/24. Confirmed at 375 only (no 768 frame), so
     it's set here and not carried into the 1023 block; the note stays at
     its default Heading-4 size until this width. Source: "Note" text in
     both "Before vs After" and "Selected screens", measured via
     getStyledTextSegments. */
  .gallery__note{font-size:16px;line-height:24px}
}

@media (prefers-reduced-motion:reduce){*{transition:none!important}html{scroll-behavior:auto}}
