/* Self-hosted fonts.
   These used to come from fonts.googleapis.com, which put three sequential
   network hops in front of the first painted character: HTML, then Google's
   stylesheet, then the font files from a second domain. Serving them from
   our own origin removes both third parties and the whole chain — the files
   sit behind the same year-long immutable cache as every other asset.
   latin-ext is a separate face on purpose: Polish copy needs ą/ę/ł/ż/ś, but
   an English reader never downloads that subset. */
@font-face{font-family:'Bricolage Grotesque';font-style:normal;font-weight:200 800;font-display:swap;src:url('../fonts/bricolage-grotesque-latin-standard-normal.woff2') format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Bricolage Grotesque';font-style:normal;font-weight:200 800;font-display:swap;src:url('../fonts/bricolage-grotesque-latin-ext-standard-normal.woff2') format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Hanken Grotesk';font-style:normal;font-weight:100 900;font-display:swap;src:url('../fonts/hanken-grotesk-latin-wght-normal.woff2') format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Hanken Grotesk';font-style:normal;font-weight:100 900;font-display:swap;src:url('../fonts/hanken-grotesk-latin-ext-wght-normal.woff2') format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:500;font-display:swap;src:url('../fonts/jetbrains-mono-latin-500-normal.woff2') format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:500;font-display:swap;src:url('../fonts/jetbrains-mono-latin-ext-500-normal.woff2') format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}


/* =====================================================================
   TAKE A LUKE — V8
   Content first. The page renders and reads with JavaScript disabled;
   motion is enhancement only. Two signature interactions kept:
   the working hero mosaic and the horizontal portfolio. Everything
   else was deliberately calmed down.
   ===================================================================== */
@view-transition{navigation:auto}
::view-transition-old(root),::view-transition-new(root){animation-duration:.36s}
@media(prefers-reduced-motion:reduce){@view-transition{navigation:none}}
html.dark{--bg:#18181F;--fg:#F5F4F2}
:root{
  --paper:#F5F4F2; --ink:#18181F;
  --orange:#FF3C00;        /* surface only */
  --orange-ink:#C03209;    /* text on light — passes AA */
  --blue:#2E2EE6; --mint:#1FA980; --yellow:#B8860B; --pink:#C2185B;
  --blue-ink:#2B2BCC; --mint-ink:#087455; --yellow-ink:#885F00; --pink-ink:#B01F63;
  --bg:var(--paper); --fg:var(--ink);
  /* Type scale — display ratios held near 1.66, easing to 1.53 / 1.35 as it
     meets body text. Every size on the site comes from here. */
  /* TYPE SCALE — one golden-ratio ladder, two anchors.
     Body is 16px at 375px and 18px at 1320px (the container width, so type
     stops growing where the layout stops growing). Every other size is body
     × √φ^k (√φ ≈ 1.272, so two steps = φ ≈ 1.618). On the phone every role is
     one half-step apart; on desktop the display roles sit a whole φ apart
     (sm→md→lg) with the hero one half-step above lg. Between the anchors
     each token interpolates linearly with the viewport. Nothing in this file
     sets a font-size any other way. */
  --t-xl:clamp(41.9px,calc(20.03px + 5.831vw),97px);       --lh-xl:.9;    /* h1 · √φ⁴ → √φ⁷ */
  --t-lg:clamp(32.9px,calc(15.72px + 4.582vw),76.2px);       --lh-lg:.9;    /* h2 · √φ³ → √φ⁶ */
  --t-md:clamp(25.9px,calc(17.49px + 2.243vw),47.1px);       --lh-md:1;     /* h2/h3, big numbers · √φ² → √φ⁴ */
  --t-sm:clamp(20.4px,calc(16.95px + 0.921vw),29.1px);       --lh-sm:1.12;  /* card titles, chapter heads · √φ → √φ² */
  --t-lead:clamp(18px,calc(16.06px + 0.519vw),22.9px);   --lh-lead:1.42;/* intro paragraphs · √φ^½ → √φ */
  --t-body:clamp(16px,calc(15.21px + 0.212vw),18px);   --lh-body:1.55;/* running text, buttons */
  --t-small:clamp(15px,calc(14.60px + 0.106vw),16px); --lh-small:1.5;/* secondary text — floor 15px on phones */
  --t-mono:clamp(12.6px,calc(11.97px + 0.169vw),14.2px);   /* labels, nav, kickers · body ÷ √φ */
  --display:"Bricolage Grotesque",system-ui,sans-serif;
  --body:"Hanken Grotesk",system-ui,sans-serif;
  --mono:"JetBrains Mono",ui-monospace,monospace;
  --gut:clamp(24px,5vw,72px); --max:1320px;   /* was 1560: at 1440 the page ran edge-to-edge and everything scaled to fill it */
  /* the single left/right edge every full-bleed element must respect */
  --edge:max(var(--gut), calc(50vw - (var(--max) / 2) + var(--gut)));
  /* 8pt spacing scale — every structural value below comes from here */
  --s2:16px; --s3:24px; --s4:32px; --s5:40px; --s6:48px; --s7:64px; --s8:80px;
  --s9:96px; --s10:120px; --s11:160px;
  --sec-y:clamp(64px,7vw,104px);      /* 144–256px between sections (ASU floor: 96) */
  --sec-gap:clamp(32px,4vw,64px);     /* header block -> content */
  /* two poles only: sharp containers, pill controls. Nothing mushy in between. */
  --r-box:0; --r-ui:999px;
  /* Two column splits for the whole site. Header bands and content blocks each
     get one, and nothing defines its own — that is how they drifted apart. */
  --split-head:minmax(0,1.05fr) minmax(0,.95fr);   /* heading + lead band     */
  --split-body:minmax(0,.82fr) minmax(0,1.18fr);   /* image/aside + prose     */
  --split-hero:minmax(0,1fr) minmax(0,1.04fr);     /* hero: text + frame      */
  --split-panel:minmax(0,1.1fr) minmax(0,.9fr);    /* inside a package panel  */
  --split-gap:clamp(28px,5vw,80px);
  /* accent defaults: JS and case pages override these, but nothing ever
     renders colourless if a script fails to run */
  --ac:var(--orange); --aci:var(--orange-ink);
  /* Depth only for things that genuinely float above the page. Two layers:
     a tight contact shadow + a wide soft one, both tinted with the ink hue. */
  --lift-1:0 1px 2px rgba(24,24,31,.05), 0 4px 14px rgba(24,24,31,.06);
  --lift-2:0 2px 6px rgba(24,24,31,.08), 0 14px 40px rgba(24,24,31,.12);
  --rule:color-mix(in srgb, currentColor 20%, transparent);
  --out:cubic-bezier(.22,1,.36,1);
}
*{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
body{background:var(--bg);color:var(--fg);font-family:var(--body);
  font-size:var(--t-body);line-height:var(--lh-body);-webkit-font-smoothing:antialiased;overflow-x:hidden;
  transition:background-color .6s var(--out),color .6s var(--out)}
body.locked{overflow:hidden}
a{color:inherit;text-decoration:none}
img{display:block;max-width:100%}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
::selection{background:var(--orange);color:#fff}
:focus-visible{outline:3px solid currentColor;outline-offset:3px;border-radius:2px}
/* Touch layer: no 300ms tap delay, no grey iOS flash (hover/active states
   carry the feedback), and the open drawer must not scroll the page under it. */
a,button{touch-action:manipulation;-webkit-tap-highlight-color:transparent}
#nav .nav-links.open{overscroll-behavior:contain}
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--gut)}
.sec{padding-block:var(--sec-y)}
.skip{position:absolute;left:-9999px;top:0;z-index:500;background:var(--ink);color:var(--paper);
  padding:16px 24px;font-family:var(--mono);font-size:var(--t-mono)}
.skip:focus{left:var(--gut);top:12px}

/* type */
.h-xl{font-family:var(--display);font-weight:800;font-size:var(--t-xl);line-height:var(--lh-xl);letter-spacing:-.04em;text-wrap:balance}
/* Short single-line variant — smaller clamp so it comfortably fits one line at
   typical widths. No forced nowrap: text-wrap:balance (inherited from .h-xl)
   is the overflow safety net if a viewport is ever too narrow for one line. */
.h-xl.kon-h-1l{font-size:var(--t-lg)}
.h-lg{font-family:var(--display);font-weight:800;font-size:var(--t-lg);line-height:var(--lh-lg);letter-spacing:-.035em;text-wrap:balance}
.h-md{font-family:var(--display);font-weight:800;font-size:var(--t-md);line-height:var(--lh-md);letter-spacing:-.03em;text-wrap:balance}
.h-sm{font-family:var(--display);font-weight:800;font-size:var(--t-sm);line-height:var(--lh-sm);letter-spacing:-.02em;text-wrap:balance}
/* mono labels: 12–13px, never below .65 opacity */
.mono{font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.1em;text-transform:uppercase;opacity:.72}
.lead{font-size:var(--t-lead);line-height:var(--lh-lead);max-width:52ch;opacity:.85;text-wrap:pretty}
em{font-style:normal;color:var(--orange-ink)}
/* html.dark guard: .on-dark is static markup (“this section sits in the
   dark range”) but the range only exists once initWorld sets html.dark.
   Unguarded, this painted #FF8A5C on paper before the toggle — 2.1:1. */
html.dark .on-dark em{color:#FF8A5C}
.sec-top{display:grid;grid-template-columns:var(--split-head);gap:var(--split-gap);align-items:end;margin-bottom:var(--sec-gap)}
@media(max-width:860px){.sec-top{grid-template-columns:1fr;gap:16px}}
.kicker{display:block;margin-bottom:16px}

.grain{position:fixed;inset:0;z-index:400;pointer-events:none;opacity:.035;mix-blend-mode:multiply;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");background-size:200px}

/* buttons — always a guaranteed text/background pair */
.btn{display:inline-flex;align-items:center;gap:12px;border-radius:var(--r-ui);
  font-family:var(--display);font-weight:800;font-size:var(--t-body);line-height:1.2;
  padding:12px 24px;min-height:48px;border:2px solid currentColor;
  transition:background-color .28s var(--out),color .28s var(--out),
             border-color .28s var(--out),transform .28s var(--out)}
.ico{width:16px;height:16px;flex:none;transition:transform .3s var(--out)}
.tlink:hover .ico{transform:translateX(4px)}

/* PRIMARY — solid, and on hover it moves to the brand accent rather than
   simply darkening. Paper on #C03209 is 4.6:1, so the state stays legible. */
.btn-p{background:var(--ink);color:var(--paper);border-color:var(--ink)}
.btn-p:hover{background:var(--orange-ink);border-color:var(--orange-ink);color:var(--paper);
  transform:translateY(-2px)}
.btn-p:active{transform:translateY(0)}
.btn-p:hover .ico{transform:translateX(4px)}

/* SECONDARY — stays an outline in every state. It fills with a faint tint of
   its own colour, never with ink, so it can never be mistaken for the primary. */
.btn-s{background:transparent;color:currentColor;border-color:currentColor}
.btn-s:hover{background:color-mix(in srgb,currentColor 10%,transparent);transform:translateY(-2px)}
.btn-s:active{transform:translateY(0)}
.btn-s:hover .ico{transform:translateY(3px)}
.btn-s[href^="mailto"]:hover .ico{transform:translateX(4px)}

/* inverted surfaces keep the same logic: solid vs outline, never converging */
.on-dark .btn-p,.on-orange .btn-p{background:var(--paper);color:var(--ink);border-color:var(--paper)}
.on-dark .btn-p:hover{background:var(--orange-ink);border-color:var(--orange-ink);color:var(--paper)}
.on-orange .btn-p:hover{background:var(--ink);border-color:var(--ink);color:var(--paper)}
.on-dark .btn-s:hover,.on-orange .btn-s:hover{background:color-mix(in srgb,currentColor 14%,transparent)}
@media(prefers-reduced-motion:reduce){.btn,.ico{transition:none}
  .btn-p:hover,.btn-s:hover{transform:none}}
.tlink{display:inline-flex;align-items:center;gap:8px;font-family:var(--display);font-weight:800;
  font-size:var(--t-body);padding:8px 0;min-height:44px;border-bottom:2px solid currentColor}
.tlink:hover{color:var(--orange-ink)}
html.dark .on-dark .tlink:hover{color:#FF8A5C}

/* ===== NAV — solid, predictable contrast ===== */
.nav{position:fixed;top:0;left:0;right:0;z-index:200;display:flex;align-items:center;justify-content:space-between;
  gap:16px;padding-block:16px;padding-inline:max(var(--edge),env(safe-area-inset-left)) max(var(--edge),env(safe-area-inset-right));background:transparent;transition:background .35s,box-shadow .35s,padding .35s}
.nav.stuck{background:color-mix(in srgb,var(--bg) 92%,transparent);backdrop-filter:blur(10px);
  border-bottom:1px solid var(--rule);padding-block:8px}
.nav-logo{display:block;line-height:0;padding:8px}
.nav-logo svg{height:clamp(30px,3.2vw,38px);width:auto;overflow:visible}
/* The mark is two-colour: the 'a' between TAKE and LUKE carries the accent,
   exactly as in the master artwork. */
.nav-logo svg path{fill:currentColor}
.nav-logo svg #a_between,.nav-logo svg [id$="a_between"]{fill:var(--orange)}
html.dark .nav-logo svg #a_between,html.dark .nav-logo svg [id$="a_between"]{fill:#FF7A4D}
.nav-links{display:flex;gap:8px;align-items:center}
.nav-links a{font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.08em;text-transform:uppercase;
  padding:12px 12px;min-height:44px;display:inline-flex;align-items:center;opacity:.8}
.nav-links a:hover{opacity:1;text-decoration:underline;text-underline-offset:5px}
/* #000, not var(--ink): ink on orange measures 4.96:1 in the stylesheet,
   but the grain overlay (multiply, 3.5%) composites on top of the whole
   page and axe measures the button at ~4.2:1 — under AA. Black buys the
   margin back (5.9:1 flat) and is indistinguishable from ink on orange. */
.nav-cta{margin-left:8px;border-radius:var(--r-ui);padding:12px 28px;min-height:44px;background:var(--orange);color:#000;
  font-family:var(--display);font-weight:800;font-size:var(--t-small);display:inline-flex;align-items:center;gap:8px}
.nav-cta .ico{width:14px;height:14px;flex:none}
.on-dark .nav-cta,.on-orange .nav-cta{background:var(--paper);color:var(--ink)}
.nav-burger{display:none;border-radius:var(--r-ui);font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.08em;text-transform:uppercase;
  padding:12px 16px;min-height:44px;border:1px solid var(--rule)}
@media(max-width:920px){
  /* backdrop-filter (like filter/transform) makes an element a containing
     block for its position:fixed descendants. With it on, the drawer below
     resolves inset:0 against the nav BAR instead of the viewport, so it
     collapses into a sliver at the top of the screen: on pages that ship
     .stuck in the markup (case, work index, privacy) the menu button looked
     completely dead, and on the homepage it broke the moment scrolling added
     .stuck. An opaque bar on mobile costs nothing visually and keeps the
     drawer anchored to the viewport where it belongs. */
  .nav.stuck{background:var(--bg);backdrop-filter:none;-webkit-backdrop-filter:none}
  /* Scoped to #nav specifically — only the homepage has the hamburger/JS
     that ever adds .open. The plain .nav-links on case, work-index and
     privacy pages has no toggle at all, so turning it into a fixed,
     off-screen-by-default drawer here made its contents (language switch,
     CTA button) permanently unreachable on mobile for every one of those
     24 pages — it just stays the small inline row from the base rule. */
  #nav .nav-links{position:fixed;inset:0;background:var(--bg);flex-direction:column;justify-content:center;
    gap:4px;padding:var(--gut);transform:translateY(-100%);visibility:hidden;
    transition:transform .45s var(--out),visibility 0s .45s;z-index:210}
  #nav .nav-links.open{transform:none;visibility:visible;transition:transform .45s var(--out)}
  #nav .nav-links a{font-family:var(--display);font-weight:800;font-size:var(--t-md);text-transform:none;
    letter-spacing:-.03em;opacity:1;padding:16px 0}
  #nav .nav-links .nav-cta{margin:16px 0 0;font-size:var(--t-sm);padding:16px 24px}
  .nav-burger{display:inline-flex;align-items:center;position:relative;z-index:220}
}

/* ===== HERO ===== */
.hero{min-height:100svh;display:grid;grid-template-columns:var(--split-hero);
  /* align-content centres the ROW inside the taller section; align-items
     stretch then makes both columns exactly as tall as the taller one.
     The frame therefore takes its height from the text, not from its width. */
  align-content:center;align-items:stretch;
  gap:clamp(40px,6vw,112px);padding-top:clamp(96px,14vh,160px);padding-bottom:clamp(48px,8vh,96px)}
.hero>div:first-child{display:flex;flex-direction:column;justify-content:center}
@media(max-width:960px){.hero{grid-template-columns:1fr;gap:var(--s5);min-height:0;padding-bottom:var(--s7)}}
.hero h1{margin-bottom:clamp(24px,2.6vw,36px);max-width:13ch}
.hero-cta{display:flex;flex-wrap:wrap;gap:16px;margin-top:var(--s4)}
/* One frame with the caption set BELOW it, like a plate under a plate in a
   monograph — not a gradient scrim with a floating pill on top of the work. */
.show{position:relative;display:flex;flex-direction:column;align-self:stretch}
.show-frame{position:relative;flex:1;min-height:clamp(320px,42svh,460px);overflow:hidden;
  background:#22222b;border:1px solid var(--rule)}
.show-track{position:absolute;inset:0}
.show-track img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  visibility:hidden;will-change:clip-path,transform}
.show-track img.on{visibility:visible}
.show-bar{display:flex;gap:4px;margin-top:0}
.show-bar button{flex:1;min-height:44px;padding:20px 0;position:relative;background:none;
  display:flex;align-items:center}
.show-bar button span{display:block;width:100%;height:2px;background:color-mix(in srgb,currentColor 22%,transparent);
  position:relative;overflow:hidden}
.show-bar button i{position:absolute;inset:0;background:currentColor;transform:scaleX(0);transform-origin:left}
.show-bar button i.run{transform:scaleX(1);transition:transform var(--dur,4s) linear}
.show-bar button i.done{transform:scaleX(1)}
.show-meta{display:flex;align-items:flex-end;justify-content:space-between;gap:20px;
  padding-top:12px;border-top:1px solid var(--rule);margin-top:4px}
.show-cap-t{min-width:0}
.show-cap-t .st{display:block;font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.1em;
  text-transform:uppercase;opacity:.7;margin-bottom:8px}
.show-cap-t .sn{display:block;font-family:var(--display);font-weight:800;font-size:var(--t-sm);
  line-height:var(--lh-sm);letter-spacing:-.02em}
.show-cap-t .st,.show-cap-t .sn{transition:transform .4s var(--out),opacity .4s var(--out)}
.show.swap .show-cap-t .st,.show.swap .show-cap-t .sn{transform:translateY(10px);opacity:0;transition-duration:.2s}
.show-side{display:flex;align-items:baseline;gap:20px;flex:none}
.show-side .mono{opacity:.68}
@media(max-width:960px){.show-frame{flex:none;aspect-ratio:4/3;min-height:0}}
@media(max-width:520px){.show-frame{flex:none;aspect-ratio:1/1;min-height:0}
  .show-meta{flex-direction:column;align-items:flex-start;gap:12px}}
/* The actual hide — placed after every .show/.show-* rule above so it wins
   the cascade at equal specificity. This exact bug (a hide rule sitting
   earlier in the file than the unconditional rule it's meant to beat) is
   why the slideshow kept showing on mobile; putting it dead last here is
   deliberate, not incidental — do not move this above the block again. */
@media(max-width:960px){.show{display:none}}

/* landing target flash after a jump from a situation card */
.acc.flash{animation:flash 1.1s var(--out)}
@keyframes flash{0%{background:color-mix(in srgb,var(--ac) 26%,transparent)}
  100%{background:color-mix(in srgb,var(--ac) 7%,transparent)}}

/* ===== CLIENTS ===== */
.clients{border-block:1px solid var(--rule);padding-block:clamp(24px,3vw,32px)}
.clients-head{display:flex;justify-content:space-between;align-items:center;gap:16px;
  margin:0 auto 16px;padding-inline:var(--edge)}
.clients-vp{overflow:hidden;
  /* The strip is a continuous loop, so it should read as one — hard
     cut edges make it look like a clipped list instead. */
  -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 10%,#000 90%,transparent 100%);
  mask-image:linear-gradient(90deg,transparent 0,#000 10%,#000 90%,transparent 100%)}
.clients-in{display:flex;align-items:center;gap:clamp(36px,6vw,80px);width:max-content;
  animation:slide 46s linear infinite}
.clients-vp:hover .clients-in,.clients-vp:focus-within .clients-in,.clients-in.paused{animation-play-state:paused}
@keyframes slide{to{transform:translateX(-50%)}}
.clients-in img{height:clamp(24px,2.4vw,32px);width:auto;opacity:.72;filter:grayscale(1)}
/* Phones: size the strip so roughly three marks sit in view at once —
   enough to read as a row of clients, not so many that each becomes
   an illegible smudge. */
@media(max-width:640px){
  .clients-in{gap:32px}
  .clients-in img{height:22px}}
/* This has to live here, as a normal stylesheet rule, rather than as the
   inline style="display:contents" it used to be — an inline style beats
   any external rule regardless of specificity, so the reduced-motion
   override below could never actually win against it. That's exactly why
   the duplicate stayed visible (wrapped, doubled-up logos) for anyone
   with Reduce Motion on, iOS defaults included. */
.clients-dup{display:contents}
.pause-btn{font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.08em;text-transform:uppercase;
  border:1px solid var(--rule);padding:12px 16px;min-height:44px;border-radius:var(--r-ui)}
@media(max-width:640px){.pause-btn{display:none}}
/* Reduce Motion must stop the auto-scroll, but it should not turn the
   strip into a stacked block of every logo at once — that was the
   three-row pile-up on phones. Keep the single line and hand the
   scrolling to the visitor instead: no motion they didn't ask for,
   same shape as everyone else sees. */
@media(prefers-reduced-motion:reduce){
  .clients-in{animation:none}
  .clients-vp{overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}
  .clients-vp::-webkit-scrollbar{display:none}
  .clients-dup{display:none}}

/* ===== SYTUACJE ===== */
.sit{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(16px,2.4vw,32px)}
@media(max-width:900px){.sit{grid-template-columns:1fr}}
.sit-card{display:flex;flex-direction:column;gap:16px;padding-top:24px;
  border-top:4px solid var(--ac,var(--rule));transition:transform .4s var(--out)}
a.sit-card{text-decoration:none;color:inherit}
.sit-card:hover{transform:translateY(-6px);border-top-color:var(--ac)}
.sit-go{display:inline-flex;align-items:center;gap:12px;margin-top:auto;padding-top:16px;
  font-family:var(--display);font-weight:800;font-size:var(--t-body);color:var(--ac)}
.sit-arrow{display:inline-block;transition:transform .3s var(--out)}
.sit-card:hover .sit-arrow{transform:translateX(5px)}
.sit-card .sit-n{font-family:var(--display);font-weight:800;font-size:var(--t-sm);
  line-height:1;letter-spacing:-.03em;color:var(--ac);opacity:1}
.sit-card .tlink:hover{color:var(--ac)}
.sit-n{font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.1em;opacity:.7}
.sit-card p{opacity:.88;flex:1}
.sit-rec{font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.06em;text-transform:uppercase;
  padding-top:16px;border-top:1px solid var(--rule);opacity:.75}
.sit-card .tlink{align-self:flex-start}

/* ===== PRACE — signature interaction #2 ===== */
#prace{padding:0}
/* Sticky + explicit scroll maths. No pin, no plugin, nothing to desync:
   the wrapper's own height defines the travel, so position is always derived
   from scroll, never accumulated. Native side-scroll is the default state. */
.hz-wrap{position:relative}
.hz-sticky{height:100svh;display:flex;align-items:center;position:relative;
  overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch}
.hz-sticky.pinned{position:sticky;top:0;overflow:hidden;scroll-snap-type:none}
.hz-track{display:flex;align-items:stretch;gap:clamp(16px,2.4vw,40px);
  padding-inline:var(--edge);width:max-content;flex:none;will-change:transform}
.hz-sticky.pinned .pn{scroll-snap-align:none}
/* Every direct child of the scroller needs its own snap-align under
   mandatory snapping, or the browser simply refuses to let scroll rest
   there and pulls back to the nearest child that does — which is exactly
   why the lead copy was unreachable and the CTA rejected too, both
   snapping straight back to the first case card on release. */
.hz-sticky:not(.pinned) .pn,
.hz-sticky:not(.pinned) .hz-lead,
.hz-sticky:not(.pinned) .hz-end{scroll-snap-align:center}
.hz-prog{position:absolute;left:var(--edge);right:var(--edge);bottom:32px;height:2px;
  background:color-mix(in srgb,currentColor 22%,transparent);z-index:3;pointer-events:none}
.hz-prog i{display:block;height:100%;width:0;background:currentColor}
.hz-lead{flex:none;width:clamp(280px,30vw,440px);display:flex;flex-direction:column;justify-content:center;padding-block:80px}
.hz-end{flex:none;width:clamp(240px,24vw,340px);display:flex;flex-direction:column;
  align-items:flex-start;justify-content:center;gap:20px}
.pn{flex:none;width:clamp(320px,54vh,560px)}
.pn>a{display:flex;flex-direction:column;height:100%}
.pn-media{position:relative;width:100%;aspect-ratio:1/1;overflow:hidden;background:#22222b}
.pn-media img{width:100%;height:100%;object-fit:cover}
/* The work is never cut, scaled or distorted — shots simply cross-fade. */
.pn-layers{position:absolute;inset:0}
.pn-layers img{position:absolute;inset:0;opacity:0;transition:opacity .55s var(--out)}
.pn-layers img.on{opacity:1}
.pn-ticks{position:absolute;left:0;right:0;bottom:0;display:flex;height:3px;
  opacity:0;transition:opacity .4s var(--out)}
.pn.live .pn-ticks{opacity:1}
.pn-ticks i{flex:1;background:rgba(255,255,255,.28)}
.pn-ticks i.on{background:var(--ac,var(--orange))}
/* always visible — the judgment info reviewers actually look for */
.pn-role{display:block;margin-top:8px;font-family:var(--mono);font-size:var(--t-mono);
  letter-spacing:.06em;opacity:.72}
@media(prefers-reduced-motion:reduce){.pn-layers,.pn-ticks{display:none}}
.pn-badge{position:absolute;top:0;left:0;right:0;display:flex;justify-content:space-between;
  padding:16px 16px;font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.08em;color:#fff;
  text-shadow:0 1px 5px rgba(0,0,0,.75)}
.pn-meta{padding-top:16px}
.pn-type{font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.08em;text-transform:uppercase;opacity:.72}
.pn-meta h3{font-family:var(--display);font-weight:800;font-size:var(--t-sm);
  letter-spacing:-.03em;line-height:1;margin:8px 0 8px}
.pn-meta p{font-size:var(--t-small);opacity:.82;max-width:40ch}
.pn-more{display:inline-block;margin-top:12px;font-family:var(--mono);font-size:var(--t-mono);
  letter-spacing:.08em;text-transform:uppercase;border-bottom:1px solid currentColor;padding-bottom:3px}
@media(max-width:900px){
  /* the scroller is .hz-sticky since the rebuild — these used to target the
     old .hz class and were silently doing nothing on phones */
  .hz-sticky{height:auto;padding-block:64px;position:static;overflow-x:auto;
    scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch}
  .hz-track{width:max-content}
  .pn{width:min(80vw,360px);scroll-snap-align:center}
  .hz-lead{width:min(84vw,380px);padding-block:0}
  .hz-end{width:min(70vw,300px)}
  .hz-prog{bottom:16px}
}

/* visual break */
.reel{position:relative;min-height:78svh;display:flex;align-items:center;overflow:hidden}
.reel-img{position:absolute;inset:-10% 0;background-size:cover;background-position:center}
.reel::after{content:"";position:absolute;inset:0;background:linear-gradient(90deg,rgba(18,16,24,.88),rgba(18,16,24,.45))}
.reel-in{position:relative;z-index:2;color:#fff;padding-block:clamp(48px,8vh,88px);
  width:100%;padding-inline:var(--edge);max-width:none;margin:0}
.reel-in p{font-family:var(--display);font-weight:800;font-size:var(--t-md);
  line-height:.96;letter-spacing:-.035em;max-width:20ch;margin-top:16px}

/* ===== WSPÓŁPRACA ===== */
.tier{margin-top:clamp(44px,6vw,76px)}
/* the second tier is a different kind of thing, so it gets a real break */
.tier-b{margin-top:clamp(72px,9vw,120px)}
.tier:first-of-type{margin-top:0}
.tier-head{display:flex;align-items:baseline;justify-content:space-between;gap:20px;flex-wrap:wrap;
  padding-bottom:16px;border-bottom:2px solid currentColor}
.tier-head p{font-size:var(--t-body);opacity:.8;max-width:44ch}
.acc{border-bottom:1px solid var(--rule);position:relative}
/* colour returns here — each package now reads as its own thing */
.acc-btn[aria-expanded="true"] .acc-n{color:var(--aci)}
.acc-btn[aria-expanded="true"] .acc-name{color:var(--aci)}
.acc-btn:hover .acc-name{color:var(--aci)}
.acc-swatch{position:absolute;left:0;top:0;bottom:0;width:0;background:var(--ac);
  transition:width .45s var(--out)}
.acc-btn[aria-expanded="true"]~.acc-swatch,
.acc:has([aria-expanded="true"]) .acc-swatch{width:5px}
.acc:has([aria-expanded="true"]){background:color-mix(in srgb,var(--ac) 7%,transparent)}
/* Symmetric inset once open. The left value is 4px larger because the 5px
   colour bar overlays that edge — measured optically, not numerically. */
.acc:has([aria-expanded="true"]) .acc-btn{padding-inline:28px 24px}
.acc:has([aria-expanded="true"]) .acc-in{padding-inline:28px 24px}
.acc-p .acc-box{border-top-color:color-mix(in srgb,var(--ac) 55%,transparent)}
.acc-p .acc-box h4{color:var(--aci);opacity:1}
.acc-p .btn-p{background:var(--aci);border-color:var(--aci)}
.acc-p .btn-p:hover{background:transparent;color:var(--aci)}
@media(max-width:900px){.acc:has([aria-expanded="true"]) .acc-btn,
  .acc:has([aria-expanded="true"]) .acc-in{padding-inline:20px 16px}}
.acc-h{margin:0}
.acc-btn{display:grid;grid-template-columns:auto 1fr auto auto;align-items:center;gap:clamp(12px,2.2vw,32px);
  width:100%;text-align:left;padding:clamp(20px,2.4vw,32px) 0;min-height:56px}
.acc-btn:hover .acc-name{color:var(--orange-ink)}
html.dark .on-dark .acc-btn:hover .acc-name{color:#FF8A5C}
.acc-n{font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.08em;opacity:.7}
/* who it is for, readable without opening the panel */
.acc-who{display:block;margin-top:8px;font-family:var(--mono);font-size:var(--t-mono);
  letter-spacing:.06em;opacity:.65;text-transform:none}
.acc-name{font-family:var(--display);font-weight:800;font-size:var(--t-md);
  line-height:.94;letter-spacing:-.035em;transition:color .25s}
.acc-price{display:flex;align-items:baseline;justify-content:flex-end;gap:8px;
  min-width:clamp(120px,13vw,190px);white-space:nowrap;
  /* a fixed column so every price lands on the same vertical line and the
     list can be scanned top to bottom without reading a word */
  border-left:1px solid var(--rule);padding-left:clamp(16px,2vw,28px)}
.acc-price .pre{font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.1em;
  text-transform:uppercase;opacity:.68}
.acc-price .amt{font-family:var(--display);font-weight:800;font-size:var(--t-sm);
  line-height:1;letter-spacing:-.02em;color:var(--orange-ink);transition:color .3s var(--out)}
.acc-price .unit{font-family:var(--display);font-weight:600;font-size:var(--t-body);opacity:.7}
.acc-price.custom{font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.08em;
  text-transform:uppercase;opacity:.75}
.acc-btn:hover .amt{color:var(--aci)}
.acc-btn[aria-expanded="true"] .amt{color:var(--aci)}
/* on open the numeral lifts into place — enough to draw the eye, not a stunt */
.acc:has([aria-expanded="true"]) .acc-price .amt{animation:priceIn .55s var(--out) both}
.acc:has([aria-expanded="true"]) .acc-price .pre{animation:priceIn .55s var(--out) .05s both}
.acc:has([aria-expanded="true"]) .acc-price .unit{animation:priceIn .55s var(--out) .1s both}
@keyframes priceIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
@media(prefers-reduced-motion:reduce){
  .acc:has([aria-expanded="true"]) .acc-price>*{animation:none}}
.acc-ic{width:24px;height:24px;position:relative;flex:none}
.acc-ic::before,.acc-ic::after{content:"";position:absolute;left:50%;top:50%;background:currentColor;transition:transform .35s var(--out)}
.acc-ic::before{width:16px;height:2px;transform:translate(-50%,-50%)}
.acc-ic::after{width:2px;height:16px;transform:translate(-50%,-50%)}
.acc-btn[aria-expanded="true"] .acc-ic::after{transform:translate(-50%,-50%) scaleY(0)}
.acc-p[hidden]{display:none}
.acc-in{display:grid;grid-template-columns:var(--split-panel);gap:clamp(24px,4vw,64px);
  padding-bottom:clamp(32px,3.6vw,48px);align-items:start}
@media(max-width:900px){.acc-in{grid-template-columns:1fr;gap:24px}
  .acc-btn{grid-template-columns:auto 1fr auto}.acc-price{grid-column:1/-1;grid-row:2;justify-content:flex-end;min-width:0;
    border-left:0;padding-left:0;margin-top:8px}}
.acc-claim{font-family:var(--display);font-weight:800;font-size:var(--t-sm);
  letter-spacing:-.025em;line-height:var(--lh-sm);max-width:22ch}
.acc-sub{margin-top:16px;opacity:.88;max-width:52ch}
.acc-box{border-top:1px solid var(--rule);padding-top:16px;margin-bottom:24px}
.acc-box h4{font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.1em;text-transform:uppercase;
  opacity:.72;font-weight:500;margin-bottom:8px}
.acc-box p{font-size:var(--t-body);opacity:.9}
.price-note{margin-top:clamp(24px,3vw,40px);font-size:var(--t-small);opacity:.8;max-width:60ch}

/* ===== PROCES — calm vertical storytelling ===== */
@media(max-width:860px){.step{grid-template-columns:1fr;gap:12px}
  .step-h{grid-template-columns:36px 1fr}}

/* ===== AI ===== */

/* ===== O MNIE ===== */
.about{display:grid;grid-template-columns:var(--split-body);gap:var(--split-gap);align-items:start}
.about-ph{position:sticky;top:104px;aspect-ratio:3/4;overflow:hidden;background:#22222b;max-width:400px}
.about-ph img{width:100%;height:100%;object-fit:cover}
.about p{font-size:var(--t-body);line-height:var(--lh-body);opacity:.88;max-width:60ch}
.about p+p{margin-top:20px}
.about .lead{max-width:54ch;opacity:1}
/* This must come AFTER the unconditional .about-ph rule above — same
   specificity (one class each), so with equal specificity the LATER rule
   wins regardless of the media query matching. Had this stayed above,
   position:sticky would silently win back on every mobile viewport,
   which is exactly what was happening: the photo stuck in place while
   the text scrolled up underneath/over it, "unlocking" only once the
   whole section had scrolled past. */
@media(max-width:900px){.about{grid-template-columns:1fr;gap:28px}
  .about-ph{position:static;max-width:280px;margin-inline:auto;aspect-ratio:4/5}
  .about-ph img{object-position:center top}}

/* ===== TESTIMONIALS — editorial columns, no card chrome =====
   Borders and shadows around a quote are what make a wall of them look
   templated. A hairline, an index and generous type carry more authority. */

/* ===== KONTAKT ===== */
.k-proc{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(20px,3vw,44px);
  margin-top:clamp(48px,6vw,80px);padding-top:var(--s4);border-top:1px solid var(--rule)}
@media(max-width:860px){.k-proc{grid-template-columns:1fr;gap:24px}}
/* Right above the footer, this reads as the tail end of the page on a
   phone — dropped there to keep the close of the page light. */
@media(max-width:640px){.k-proc{display:none}}
.k-proc h3{font-family:var(--display);font-weight:800;font-size:var(--t-body);
  line-height:1.2;letter-spacing:-.01em;margin:8px 0 8px}
.k-proc p{font-size:var(--t-small);opacity:.8;max-width:34ch}
.foot{display:flex;justify-content:space-between;flex-wrap:wrap;gap:16px;padding-block:32px;padding-inline:var(--edge);border-top:1px solid var(--rule)}
.foot a,.foot span{font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.08em;opacity:.8}
.foot a:hover{text-decoration:underline;text-underline-offset:4px}

/* sticky CTA after first screen */
.sticky-cta{position:fixed;border-radius:var(--r-ui);box-shadow:var(--lift-2);right:var(--gut);bottom:22px;z-index:180;background:var(--ink);color:var(--paper);
  font-family:var(--display);font-weight:800;font-size:var(--t-body);padding:16px 24px;min-height:48px;
  display:inline-flex;align-items:center;opacity:0;transform:translateY(16px);pointer-events:none;
  transition:opacity .4s,transform .4s var(--out)}
.sticky-cta.on{opacity:1;transform:none;pointer-events:auto}
@media(max-width:640px){.sticky-cta{right:12px;left:12px;justify-content:center;bottom:12px}}

/* ===== CASE STUDY PAGE ===== */
.case-hero{position:relative;height:min(78svh,760px);overflow:hidden;background:#22222b;
  margin-top:0}
.case-hero img{width:100%;height:100%;object-fit:cover}
.case-head{padding-top:clamp(96px,13vh,152px)}
.case-head h1{margin-top:12px}

.case-facts{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(16px,2.4vw,32px);
  padding-block:clamp(28px,3.4vw,44px);border-block:1px solid var(--rule);margin-top:clamp(32px,4vw,52px)}
@media(max-width:860px){.case-facts{grid-template-columns:1fr 1fr;gap:24px}}
@media(max-width:520px){.case-facts{grid-template-columns:1fr}}
.case-facts dt{font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.1em;text-transform:uppercase;
  opacity:.72;margin-bottom:8px}
.case-facts dd{font-size:var(--t-body);line-height:var(--lh-body)}
.case-block{display:grid;grid-template-columns:var(--split-body);gap:clamp(24px,5vw,80px);
  padding-block:clamp(44px,6vw,88px);align-items:start}
@media(max-width:860px){.case-block{grid-template-columns:1fr;gap:20px}}
.case-block h2{font-family:var(--display);font-weight:800;font-size:var(--t-md);
  letter-spacing:-.03em;line-height:1}
.case-block h2::before{content:"";display:block;width:56px;height:5px;background:var(--ac);margin-bottom:16px}
.case-block .lead{max-width:60ch}
.case-block p+p{margin-top:16px}
.case-pull{font-family:var(--display);font-weight:800;font-size:var(--t-md);
  line-height:1.02;letter-spacing:-.03em;max-width:22ch;color:var(--ac)}


/* ===== CASE STUDY BLOCKS — optional, per-case deep-dive content ===== */
.case-cs{padding-bottom:clamp(48px,7vw,100px);display:grid;gap:clamp(56px,7vw,96px)}
.cs-block h3{font-family:var(--display);font-weight:800;font-size:var(--t-sm);
  letter-spacing:-.02em;line-height:1.15;margin-bottom:28px;max-width:32ch;
  display:flex;align-items:baseline;gap:16px}
.cs-block h3::before{content:"";flex:none;width:32px;height:5px;background:var(--ac,var(--orange));
  transform:translateY(-6px)}

/* Objectives — a numbered grid, not a stacked list: two short columns read
   in one glance instead of one long column that reads as a wall of text. */
.cs-list{width:100%}
.cs-list-grid{list-style:none;display:grid;grid-template-columns:1fr 1fr;
  gap:24px clamp(24px,3vw,48px)}
@media(max-width:640px){.cs-list-grid{grid-template-columns:1fr}}
.cs-list-grid li{display:flex;gap:16px;align-items:baseline}
.cs-num{flex:none;font-family:var(--mono);font-size:var(--t-mono);font-weight:600;
  color:var(--ac,var(--orange-ink));letter-spacing:.04em}
.cs-list-grid li>span:last-child{font-size:var(--t-body);opacity:.92}

/* Challenges / research — bordered cards instead of a bulleted paragraph
   list, so each point is scannable on its own rather than blurring into
   the next in one long run of text. */
.cs-cards{width:100%}
.cs-card-row{display:grid;grid-template-columns:repeat(var(--n,3),1fr);gap:1px;
  background:var(--rule);border:1px solid var(--rule)}
@media(max-width:820px){.cs-card-row{grid-template-columns:1fr}}
.cs-card{background:var(--bg);padding:clamp(20px,2.4vw,28px)}
.cs-card .cs-num{display:block;margin-bottom:16px}
.cs-card h4{font-family:var(--display);font-weight:800;font-size:var(--t-small);
  letter-spacing:-.01em;line-height:1.25;margin-bottom:8px}
.cs-card p{font-size:var(--t-small);opacity:.8;line-height:1.5}

/* Process — numbered circles on a connecting line, the standard timeline
   read, instead of a floating arrow that had nowhere good to sit. */
.cs-steps{width:100%}
.cs-step-row{list-style:none;display:flex;justify-content:space-between;
  position:relative;padding-top:4px}
.cs-step-row::before{content:"";position:absolute;top:22px;left:18px;right:18px;
  height:1px;background:var(--rule);z-index:0}
.cs-step-row li{display:flex;flex-direction:column;align-items:center;gap:16px;
  position:relative;z-index:1;flex:1;text-align:center}
.cs-step-c{width:36px;height:36px;border-radius:50%;display:grid;place-items:center;
  background:var(--bg);border:1.5px solid var(--ac,var(--orange));
  font-family:var(--mono);font-size:var(--t-mono);font-weight:600;color:var(--ac,var(--orange-ink))}
.cs-step-l{font-family:var(--display);font-weight:700;font-size:var(--t-small);
  letter-spacing:-.01em;max-width:14ch}
@media(max-width:640px){.cs-step-row{flex-wrap:wrap;gap:28px 8px;justify-content:flex-start}
  .cs-step-row::before{display:none}
  .cs-step-row li{flex:0 0 calc(50% - 4px)}}

/* Strategy pillars — a hairline card so three columns of bullets read as
   three distinct ideas rather than one continuous block of small print. */
.cs-pillars{width:100%}
.cs-pillar-row{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(20px,2.5vw,32px)}
@media(max-width:860px){.cs-pillar-row{grid-template-columns:1fr}}
.cs-pillar{border-top:3px solid var(--ac,var(--orange));padding-top:20px}
.cs-pillar-n{display:block;font-family:var(--mono);font-size:var(--t-mono);font-weight:600;
  color:var(--ac,var(--orange-ink));margin-bottom:12px;letter-spacing:.04em}
.cs-pillar h4{font-family:var(--display);font-weight:800;font-size:var(--t-small);
  letter-spacing:-.02em;margin-bottom:16px;line-height:1.25}
.cs-pillar ul{list-style:none;display:grid;gap:12px}
.cs-pillar li{font-size:var(--t-small);opacity:.82;line-height:1.45;padding-left:16px;position:relative}
.cs-pillar li::before{content:"";position:absolute;left:0;top:8px;width:5px;height:5px;background:var(--rule)}

.cs-notes-row{display:grid;grid-template-columns:1fr 1fr;gap:clamp(24px,4vw,56px)}
@media(max-width:760px){.cs-notes-row{grid-template-columns:1fr;gap:28px}}
.cs-notes-row>div{border-top:1px solid var(--rule);padding-top:20px}
.cs-notes h4{font-family:var(--display);font-weight:800;font-size:var(--t-small);margin-bottom:12px}
.cs-notes p{font-size:var(--t-body);opacity:.85;max-width:44ch;line-height:1.55}

/* Stats — a few big numbers pulled straight from user research, the kind
   of concrete evidence that a bullet list can't carry on its own. */
.cs-stats-intro{font-size:var(--t-body);opacity:.85;max-width:70ch;margin-bottom:28px}
.cs-stats{width:100%}
.cs-stat-row{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(20px,3vw,32px)}
@media(max-width:760px){.cs-stat-row{grid-template-columns:1fr;gap:24px}}
.cs-stat-n{display:block;font-family:var(--display);font-weight:800;
  font-size:var(--t-md);line-height:1;color:var(--ac,var(--orange-ink));margin-bottom:12px}
.cs-stat-row p{font-size:var(--t-small);opacity:.8;max-width:26ch}

/* Competitor analysis — a name and its strengths, side by side. */
.cs-comps{width:100%}
.cs-comp-row{display:grid;grid-template-columns:1fr 1fr;gap:clamp(20px,3vw,32px)}
@media(max-width:640px){.cs-comp-row{grid-template-columns:1fr}}
.cs-comp{border:1px solid var(--rule);padding:clamp(20px,2.4vw,28px)}
.cs-comp-name{display:block;font-family:var(--display);font-weight:800;font-size:var(--t-small);
  color:var(--ac,var(--orange-ink));margin-bottom:16px}
.cs-comp ul{list-style:none;display:grid;gap:8px}
.cs-comp li{font-size:var(--t-small);opacity:.85;padding-left:16px;position:relative;line-height:1.45}
.cs-comp li::before{content:"";position:absolute;left:0;top:8px;width:5px;height:5px;background:var(--rule)}

/* Five Whys — a chain of causes narrowing down to the one that mattered,
   closing on the fix rather than just the diagnosis. */
.cs-whys{width:100%}
.cs-why-chain{list-style:none;display:grid;gap:12px;max-width:70ch;margin-bottom:24px}
.cs-why-chain li{display:flex;gap:16px;align-items:baseline;font-size:var(--t-body);opacity:.9}
.cs-why-n{flex:none;font-family:var(--mono);font-size:var(--t-mono);font-weight:600;
  color:var(--ac,var(--orange-ink))}
.cs-why-solution{border-top:3px solid var(--ac,var(--orange));padding-top:20px;max-width:70ch}
.cs-why-solution .cs-num{color:var(--ac,var(--orange-ink));font-family:var(--mono);
  font-size:var(--t-mono);font-weight:600;display:block;margin-bottom:8px}
.cs-why-solution p{font-size:var(--t-body);font-weight:600}

/* Persona — identity facts on one side, the narrative on the other. */
.cs-persona{width:100%}
.cs-persona-card{display:grid;grid-template-columns:var(--split-body);
  gap:clamp(24px,4vw,56px);border:1px solid var(--rule);padding:clamp(24px,3vw,40px)}
@media(max-width:760px){.cs-persona-card{grid-template-columns:1fr}}
.cs-persona-av{display:grid;place-items:center;width:56px;height:56px;border-radius:50%;
  background:var(--ac,var(--orange));color:#fff;font-family:var(--display);font-weight:800;
  font-size:var(--t-lead);margin-bottom:16px}
.cs-persona-id h4{font-family:var(--display);font-weight:800;font-size:var(--t-sm);margin-bottom:4px}
.cs-persona-role{display:block;font-family:var(--mono);font-size:var(--t-mono);opacity:.68;
  text-transform:uppercase;letter-spacing:.06em;margin-bottom:20px}
.cs-persona-facts{list-style:none;display:grid;gap:8px}
.cs-persona-facts li{font-size:var(--t-small);opacity:.8;padding-left:16px;position:relative}
.cs-persona-facts li::before{content:"";position:absolute;left:0;top:8px;width:5px;height:5px;background:var(--rule)}
.cs-persona-body p{font-size:var(--t-body);opacity:.92;max-width:60ch;margin-bottom:20px}
.cs-persona-body h5{font-family:var(--display);font-weight:800;font-size:var(--t-small);margin-bottom:12px}
.cs-persona-body>ul{list-style:none;display:grid;gap:8px;margin-bottom:24px}
.cs-persona-body>ul li{font-size:var(--t-small);opacity:.85;padding-left:16px;position:relative;max-width:56ch}
.cs-persona-body>ul li::before{content:"";position:absolute;left:0;top:8px;width:5px;height:5px;background:var(--rule)}
.cs-persona-body blockquote{margin:0;padding-left:20px;border-left:2px solid var(--ac,var(--orange));
  font-family:var(--display);font-weight:600;font-size:var(--t-small);font-style:italic;
  opacity:.85;max-width:52ch}

/* Prioritisation matrix — a real 2x2, not a list pretending to be one. */
.cs-matrix{width:100%}
.cs-mx-grid{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:var(--rule);border:1px solid var(--rule)}
@media(max-width:640px){.cs-mx-grid{grid-template-columns:1fr}}
.cs-mx-q{background:var(--bg);padding:clamp(20px,2.2vw,24px)}
.cs-mx-label{display:block;font-family:var(--mono);font-size:var(--t-mono);font-weight:600;
  text-transform:uppercase;letter-spacing:.05em;color:var(--ac,var(--orange-ink));margin-bottom:16px}
.cs-mx-q ul{list-style:none;display:grid;gap:8px}
.cs-mx-q li{font-size:var(--t-small);opacity:.85;padding-left:16px;position:relative;line-height:1.4}
.cs-mx-q li::before{content:"";position:absolute;left:0;top:7px;width:5px;height:5px;background:var(--rule)}
.cs-mx-empty{opacity:.5!important;padding-left:0!important;padding-right:0!important}
.cs-mx-empty::before{display:none}

/* Task flow — reuses the process timeline, with a scenario line above and
   a takeaway below so the diagram reads as a story, not just a shape. */
.cs-flow{width:100%}
.cs-flow-scenario{font-size:var(--t-body);opacity:.9;max-width:70ch;margin-bottom:28px}
.cs-flow-summary{font-size:var(--t-body);opacity:.85;max-width:70ch;margin-top:28px;line-height:1.55}

/* Before / after — the single most persuasive image in a redesign case,
   so it gets its own full-width pairing instead of hiding in the mosaic. */
.cs-compare{width:100%}
.cs-compare-row{display:grid;grid-template-columns:1fr 1fr;gap:clamp(12px,1.6vw,20px)}
@media(max-width:760px){.cs-compare-row{grid-template-columns:1fr}}
.cs-compare-row figure{position:relative;margin:0;overflow:hidden;background:#22222b}
.cs-compare-row img{width:100%;height:100%;object-fit:cover;display:block}
.cs-compare-row figcaption{position:absolute;top:16px;left:16px}
.cs-compare-tag{display:inline-block;padding:8px 16px;border-radius:var(--r-ui);
  background:rgba(24,24,31,.72);color:#fff;font-family:var(--mono);font-size:var(--t-mono);
  font-weight:600;text-transform:uppercase;letter-spacing:.08em;backdrop-filter:blur(4px)}
.cs-compare-tag-after{background:var(--ac,var(--orange))}

/* Extra screens shown at a safe, fixed cell size — never full-bleed, so a
   mockup crop that isn't huge to begin with never has to stretch past
   its own pixels to fill the row. */
.cs-shots{width:100%}
.cs-shots-grid{display:grid;grid-template-columns:repeat(var(--cols,3),1fr);gap:clamp(12px,1.6vw,20px)}
@media(max-width:640px){.cs-shots-grid{grid-template-columns:repeat(min(2,var(--cols,3)),1fr)}}
.cs-shots-grid figure{margin:0;position:relative;overflow:hidden;background:#22222b}
.cs-shots-grid img{width:100%;height:100%;object-fit:cover;display:block}

.cs-anim{width:100%}

/* Chapter accordion — for cases whose story is long enough that printing it
   all at once would read as a wall. Collapsed, the number/label/headline of
   every chapter stays visible, so the arc of the argument is scannable
   without opening anything; only the prose hides. */
.cs-chapters{width:100%}
.cs-ch-hint{font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.08em;
  text-transform:uppercase;opacity:.68;margin-bottom:24px}
/* --ch-num / --ch-gap are shared by the summary grid and the body indent so
   the prose starts on exactly the same vertical as the headline. */
.cs-ch{border-top:1px solid var(--rule);--ch-num:32px;--ch-gap:clamp(16px,2.4vw,28px)}
.cs-ch:last-of-type{border-bottom:1px solid var(--rule)}
.cs-ch summary{display:grid;grid-template-columns:var(--ch-num) minmax(0,1fr) auto;
  gap:var(--ch-gap);align-items:start;padding:clamp(20px,2.4vw,28px) 0;
  cursor:pointer;list-style:none}
.cs-ch summary::-webkit-details-marker{display:none}
.cs-ch summary:focus-visible{outline:2px solid var(--ac,var(--orange));outline-offset:4px}
.cs-ch-n{font-family:var(--mono);font-size:var(--t-mono);font-weight:600;
  color:var(--ac,var(--orange-ink));padding-top:4px}
/* min-width:0 so a long headline wraps inside the grid column instead of
   forcing the row wider than its container. */
.cs-ch-t{min-width:0}
.cs-ch-k{display:block;font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.08em;
  text-transform:uppercase;opacity:.68;margin-bottom:8px}
.cs-ch-h{display:block;font-family:var(--display);font-weight:800;
  font-size:var(--t-sm);line-height:1.2;letter-spacing:-.02em;text-wrap:balance}
/* Chevron drawn in CSS so it inherits colour and needs no icon payload. */
.cs-ch-i{width:12px;height:12px;border-right:2px solid currentColor;
  border-bottom:2px solid currentColor;transform:rotate(45deg);opacity:.5;
  margin-top:8px;transition:transform .25s var(--out),opacity .25s}
.cs-ch[open] .cs-ch-i{transform:rotate(225deg);opacity:1}
.cs-ch summary:hover .cs-ch-i{opacity:1}
/* Same two tokens as the summary grid, so the body starts on exactly the
   same vertical as the headline above it — no eyeballed offset to drift. */
.cs-ch-b{padding:0 0 clamp(24px,3vw,36px);
  margin-left:calc(var(--ch-num) + var(--ch-gap))}
@media(max-width:640px){.cs-ch-b{margin-left:0}}
/* The prose runs in two columns once there is room for it. Letting a single
   column stretch the full module would mean ~150 characters per line, where
   the eye loses its way back to the start of the next one; capping it at a
   readable measure instead left an obvious hole on the right. Two columns
   fill the width and keep each line around 70 characters. The pull quote
   and the copy specimens stay outside this wrapper, so they still span the
   full module and break up the rhythm. */
.cs-ch-p p{font-size:var(--t-body);line-height:1.7;margin-bottom:20px}
.cs-ch-p p:last-child{margin-bottom:0}
/* The !important flags were guarding against a .cs-ch-b p rule that no
   longer exists — the paragraphs moved into .cs-ch-p. Plain declarations
   now, and a wider measure because at display weight the quote is meant to
   read as a single confident line, not a narrow stack. */
.cs-ch-pull{font-family:var(--display);font-weight:800;font-size:var(--t-body);
  line-height:1.5;letter-spacing:-.01em;border-left:3px solid var(--ac,var(--orange));
  padding-left:20px;margin-top:28px;text-wrap:balance}
/* The copy system is the payoff of this particular case, so its examples get
   a real specimen layout instead of another paragraph. */
.cs-ch-ex{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
  gap:1px;background:var(--rule);border:1px solid var(--rule);margin:24px 0}
.cs-ch-ex>div{background:var(--bg);padding:20px}
.cs-ch-ex-t{display:block;font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.08em;
  text-transform:uppercase;color:var(--ac,var(--orange-ink));margin-bottom:8px}
.cs-ch-ex-l{display:block;font-family:var(--display);font-weight:800;font-size:var(--t-small);
  font-style:italic;line-height:1.3}

.case-gal{display:grid;grid-template-columns:repeat(6,1fr);gap:clamp(8px,1.4vw,20px);
  padding-bottom:clamp(48px,7vw,100px)}
.case-gal figure{position:relative;margin:0;overflow:hidden;background:#22222b}
.case-gal img{width:100%;height:100%;object-fit:cover;display:block}
/* Column width only — the actual shape is set inline per figure from the
   photo's real dimensions (build.py), so cover-fit never has a mismatched
   box to crop into. Guessing a fixed ratio here (16:9 heroes, 1:1 squares)
   is exactly what used to slice off baked-in captions near frame edges. */
.case-gal .w6{grid-column:span 6}
.case-gal .w4{grid-column:span 4}
.case-gal .w3{grid-column:span 3}
.case-gal .w2{grid-column:span 2}
@media(max-width:860px){.case-gal{grid-template-columns:repeat(2,1fr)}
  .case-gal .w6,.case-gal .w4{grid-column:span 2}.case-gal .w3,.case-gal .w2{grid-column:span 1}
  /* !important: beats the desktop-row-fill inline style (build.py) some
     figures carry, which is only meant for the 6-column desktop grid. */
  .case-gal figure:last-child{grid-column:1/-1!important}}
.case-discover{border-top:1px solid var(--rule);padding-block:clamp(36px,5vw,64px)}
.discover-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(20px,3vw,40px);margin-top:clamp(28px,3.5vw,44px)}
@media(max-width:820px){.discover-grid{grid-template-columns:1fr;gap:32px}}
.discover-grid .wi-meta{padding-top:16px}
.discover-grid .wi-meta h2{font-size:var(--t-body)}
.case-cta{padding-block:clamp(56px,8vw,112px)}

/* motion is enhancement: only applied when JS marks the document */
.js .rv{opacity:0;transform:translateY(24px)}
.js .rv.in{opacity:1;transform:none;transition:opacity .7s var(--out),transform .7s var(--out)}
#intro{position:fixed;inset:0;z-index:350;background:var(--paper);display:grid;
  place-items:center;overflow:hidden;pointer-events:none;opacity:0}
/* display must never win over the hidden attribute, or an invisible overlay
   sits on top of the page and swallows every click. */
#intro[hidden]{display:none}

/* intro: pure CSS, ~1.5s, removed from the DOM when it ends */
#intro.run{opacity:1;animation:introOut .42s var(--out) 1.05s forwards}
#intro.run #introLogo{animation:introIn .5s var(--out) both}
/* The mark has to blink here too — it is the whole point of the name.
   The clone prefixes every id with intro_, hence the suffix match. */
#intro.run #introLogo [id$="i_eyeL"],
#intro.run #introLogo [id$="i_eyeR"]{transform-box:fill-box;transform-origin:50% 50%;
  animation:eyeblink .3s var(--out) .6s both}
#intro.run #introLogo [id$="i_eyeR"]{animation-delay:.66s}
#introLogo{width:min(380px,62vw);height:auto;overflow:visible}
#introLogo path{fill:var(--ink)}
#introLogo [id$="a_between"]{fill:var(--orange)}
@keyframes introIn{from{opacity:0;transform:scale(.94)}to{opacity:1;transform:none}}
@keyframes introOut{to{opacity:0;visibility:hidden}}

@media(prefers-reduced-motion:reduce){
  .js .rv{opacity:1!important;transform:none!important}
  #intro{display:none!important}
  *,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important}
}

.nav-logo img{height:clamp(30px,3.2vw,38px);width:auto}

/* ===== BRAND DETAIL: the eyes from the U in LUKE, reused as a marker =====
   Two dots that blink once when a section arrives. Same gesture as the
   logotype, so it reads as signature rather than decoration. */
.eyes{display:inline-flex;gap:4px;margin-right:12px;vertical-align:baseline;position:relative;top:-1px}
.eyes b{display:block;width:6px;height:6px;border-radius:50%;background:currentColor;
  transform-origin:50% 50%}
.eyes.blink b{animation:eyeblink .52s var(--out) both}
.eyes.blink b:last-child{animation-delay:.07s}
@keyframes eyeblink{0%,100%{transform:scaleY(1)}42%{transform:scaleY(.1)}}
@media(prefers-reduced-motion:reduce){.eyes.blink b{animation:none}}

/* language switch */
.nav-links .lang{font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.1em;
  border:1px solid var(--rule);min-height:44px;width:44px;padding:0;display:inline-flex;
  align-items:center;justify-content:center;border-radius:50%;margin-left:8px;opacity:.85}
.nav-links .lang:hover{opacity:1;border-color:currentColor;text-decoration:none}
@media(max-width:920px){#nav .nav-links .lang{font-size:var(--t-body);margin:8px 0 0;padding:12px 20px;width:auto;border-radius:var(--r-ui)}}

/* the reel headline must never orphan a short Polish word at a line end */
.reel-in p{text-wrap:balance;max-width:24ch;hyphens:none}
@media(max-width:640px){.reel-in p{max-width:18ch;font-size:var(--t-md);line-height:36px}}

/* ===== WORK INDEX ===== */
.crumbs{font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.08em;
  text-transform:uppercase;opacity:.75;display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.crumbs a{border-bottom:1px solid transparent;padding-block:8px}
.crumbs a:hover{border-bottom-color:currentColor}
.crumbs [aria-current]{opacity:.68}
.work-grid{display:grid;grid-template-columns:1fr 1fr;gap:clamp(24px,4vw,64px);
  padding-top:clamp(40px,5vw,72px)}
@media(max-width:820px){.work-grid{grid-template-columns:1fr;gap:32px}}
.wi-media{position:relative;aspect-ratio:4/3;overflow:hidden;background:#22222b}
.wi-media img{width:100%;height:100%;object-fit:cover;transition:transform .8s var(--out)}
.wi:hover .wi-media img{transform:scale(1.03)}
.wi-meta{padding-top:20px}
.wi-meta h2{font-family:var(--display);font-weight:800;font-size:var(--t-sm);
  line-height:var(--lh-sm);letter-spacing:-.02em;margin:8px 0 12px}
.wi:hover .wi-meta h2{color:var(--orange-ink)}
.wi-meta p{font-size:var(--t-body);opacity:.85;max-width:46ch}

/* ===== LOTTIE ===== */
.anim-wrap{display:grid;gap:clamp(16px,2.4vw,32px)}
.case-anim{width:100%}
.anim-p{width:100%;aspect-ratio:16/10;display:block;background:color-mix(in srgb,var(--ac) 6%,transparent)}
/* Reduce Motion used to hide these sections outright, which meant the work
   itself — the animation IS the deliverable being shown — simply vanished
   for anyone with the setting on. The preference is about unrequested
   motion, not about hiding content, so the player stays and JS strips the
   autoplay instead (see the loader), leaving the visitor to press play. */

/* ===== FAQ — native <details>, so it works with zero JavaScript ===== */
.faq{border-top:1px solid var(--rule)}
.faq-i{border-bottom:1px solid var(--rule)}
.faq-i summary{display:flex;align-items:center;justify-content:space-between;gap:24px;
  padding:clamp(20px,2.4vw,28px) 0;min-height:56px;cursor:pointer;list-style:none;
  font-family:var(--display);font-weight:800;font-size:var(--t-sm);line-height:var(--lh-sm);
  letter-spacing:-.02em;transition:color .25s var(--out)}
.faq-i summary::-webkit-details-marker{display:none}
.faq-i .faq-ic{position:relative;width:18px;height:18px;flex:none;color:var(--orange-ink)}
.faq-i .faq-ic::before,.faq-i .faq-ic::after{content:"";position:absolute;left:50%;top:50%;
  background:currentColor;transition:transform .3s var(--out)}
.faq-i .faq-ic::before{width:16px;height:2px;transform:translate(-50%,-50%)}
.faq-i .faq-ic::after{width:2px;height:16px;transform:translate(-50%,-50%)}
.faq-i[open] .faq-ic::after{transform:translate(-50%,-50%) scaleY(0)}
.faq-i summary:hover{color:var(--orange-ink)}
.faq-i p{padding-bottom:clamp(20px,2.4vw,28px);max-width:none;padding-right:clamp(40px,8vw,160px);font-size:var(--t-body);
  line-height:var(--lh-body);opacity:.88}
@media(prefers-reduced-motion:no-preference){
  .faq-i[open] p{animation:faqIn .3s var(--out) both}
  @keyframes faqIn{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
}

/* ===== CONTACT MICROCOPY + CLOSING LINE ===== */
/* Contact opens with a single column of copy; the calendar below is the
   full-width element, so a two-column intro just orphaned the microcopy.
   The lead's own default cap (52ch, tuned for the narrower sec-top grid
   column used elsewhere) reads as boxed-in here where it sits under a
   full-width h-xl headline with nothing beside it — so this, the site's
   closing/conversion moment, gets a wider, slightly larger-set column
   of its own instead of inheriting the standard .lead measure.
   The row now spans the full section width — same as the calendar
   embed below, which has no max-width of its own — with the mail
   button parked on the right so the two ends line up on one grid. */
.k-row{display:flex;justify-content:space-between;align-items:flex-end;gap:clamp(24px,4vw,48px)}
.k-row .lead{max-width:86ch;font-size:var(--t-lead)}
.k-mail{flex:none}
@media(max-width:760px){.k-row{flex-direction:column;align-items:flex-start;gap:24px}}

.k-micro{margin-top:16px;font-family:var(--mono);font-size:var(--t-mono);letter-spacing:.06em;opacity:.8}
.k-micro a{color:inherit;text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--rule)}
.k-micro a:hover{color:var(--orange-ink);text-decoration-color:currentColor}

/* ===== LEGAL PAGES ===== */
.lg-head{padding-top:clamp(96px,13vh,152px)}
.lg-upd{margin-top:20px;opacity:.65}
.lg-body{max-width:820px;padding-block:clamp(48px,6vw,80px) clamp(64px,9vw,120px)}
.lg-sec{padding-block:clamp(24px,3vw,36px);border-top:1px solid var(--rule)}
.lg-sec:last-of-type{border-bottom:1px solid var(--rule)}
.lg-sec h2{font-family:var(--display);font-weight:800;font-size:var(--t-sm);
  line-height:var(--lh-sm);letter-spacing:-.02em;margin-bottom:16px}
.lg-sec p{font-size:var(--t-body);line-height:var(--lh-body);opacity:.88;max-width:70ch}
.lg-sec a{text-decoration:underline;text-underline-offset:3px}
.lg-foot{margin-top:clamp(32px,4vw,48px);font-size:var(--t-body);opacity:.85}
/* 404 only. The page is short by nature — two short paragraphs — so without
   this the footer stops wherever the text ends and leaves a band of empty
   paper under it, which reads as a page that failed to finish loading
   rather than one telling you the URL was wrong. .grain is position:fixed,
   so making the body a flex column does not disturb it. */
.nf{display:flex;flex-direction:column;min-height:100vh}
.nf main{flex:1 0 auto}

/* ===== CAL.COM INLINE EMBED =====
   The widget is heavy, so it only loads once the section is in view. Until
   then the placeholder holds the exact height and nothing shifts (CLS = 0). */
.k-cal{margin-top:clamp(40px,5vw,64px)}
/* No plate behind the widget: Cal renders its own white card, so a second
   surface underneath just reads as a grey gap. */
.cal-embed{position:relative;min-height:600px}
@media(max-width:820px){.cal-embed{min-height:720px}}
.cal-ph{position:absolute;inset:0;display:grid;place-items:center;pointer-events:none;
  border:1px solid var(--rule);transition:opacity .4s var(--out)}
.cal-embed.ready .cal-ph{opacity:0}
.cal-ph .mono{opacity:.68}
.cal-embed iframe{border:0;width:100%!important}
