/* Renders **bold** markers from the content tree, so a note can emphasise its
   key phrase the way Apple's body copy does without carrying markup. */
function Emphasised({ text }) {
  return text.split(/(\*\*[^*]+\*\*)/g).map((part, i) => (
    part.startsWith('**')
      ? <strong key={i} style={{ fontWeight: 600, color: 'inherit' }}>{part.slice(2, -2)}</strong>
      : <React.Fragment key={i}>{part}</React.Fragment>
  ));
}

function AboutSection({ id, label, title, sub, note, art, children, tone = 'light', tight = false, noteWidth = '42ch', noteSize = 'clamp(17px, 1.8vw, 22px)' }) {
  return (
    <section id={id} style={{ padding: tight ? 'clamp(44px, 7vh, 80px) 0 0' : 'clamp(80px, 14vh, 180px) 0 0', scrollMarginTop: 90 }}>
      <Reveal>
        {label && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 22 }}>
            {art && <span aria-hidden="true" style={{ width: 14, height: 14, borderRadius: 4, background: art, display: 'block', flexShrink: 0 }} />}
            <div className="sm-mono" style={{ fontFamily: 'var(--font-mono)', fontWeight: 500, fontSize: 9.5, letterSpacing: 'var(--tracking-label)', textTransform: 'uppercase', color: tone === 'dark' ? 'var(--gray-on-dark-500)' : 'var(--gray-500)' }}>{label}</div>
          </div>
        )}
        <h2 style={{ margin: 0, whiteSpace: 'pre-line', fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 'clamp(34px, 5.4vw, 76px)', lineHeight: 1.02, letterSpacing: '-0.035em', color: tone === 'dark' ? 'var(--white)' : 'var(--black)' }}>{title}</h2>
        {/* One line between the headline and the intro, in the accent, when a
           section needs to set an expectation before it explains itself. */}
        {sub && <p style={{ margin: '18px 0 0', maxWidth: '46ch', fontFamily: 'var(--font-display)', fontWeight: 500, fontSize: 'clamp(17px, 1.8vw, 22px)', lineHeight: 1.4, letterSpacing: '-0.01em', color: tone === 'dark' ? 'var(--white)' : 'var(--orange-600)', textWrap: 'pretty' }}>{sub}</p>}
        {note && <p style={{ margin: sub ? '14px 0 0' : '24px 0 0', maxWidth: noteWidth, fontFamily: 'var(--font-display)', fontWeight: 400, fontSize: noteSize, lineHeight: 1.45, color: tone === 'dark' ? 'var(--gray-on-dark-200)' : 'var(--gray-600)', textWrap: 'pretty' }}><Emphasised text={note} /></p>}
      </Reveal>
      <div style={{ marginTop: 'clamp(32px, 5vh, 64px)' }}>{children}</div>
    </section>
  );
}

/* Highlights: the page's chapter switcher. Same gradient vocabulary as the
   Services plates but a different form - an accordion strip where the open
   chapter is wide and the rest collapse to a numeral and a vertical label.
   Clicking shows the chapter below; nothing scrolls away. */
function Highlights({ data, active, onSelect }) {
  const monoW = { fontFamily: 'var(--font-mono)', fontWeight: 500, fontSize: 9.5, letterSpacing: 'var(--tracking-label)', textTransform: 'uppercase', color: 'rgba(255,255,255,0.85)' };
  const stripRef = React.useRef(null);
  const drag = React.useRef({ down: false, moved: false, x: 0, left: 0 });
  /* Mouse drag-to-scroll, so the strip swipes in a desktop preview the way it
     does under a thumb. A real drag (>6px) swallows the click. */
  const onPointerDown = (ev) => {
    if (ev.pointerType === 'touch') return;
    const s = stripRef.current; if (!s) return;
    drag.current = { down: true, moved: false, x: ev.clientX, left: s.scrollLeft };
  };
  const onPointerMove = (ev) => {
    const d = drag.current; if (!d.down) return;
    const dx = ev.clientX - d.x;
    if (Math.abs(dx) > 6) d.moved = true;
    if (d.moved) stripRef.current.scrollLeft = d.left - dx;
  };
  const onPointerUp = () => { setTimeout(() => { drag.current.down = false; }, 0); };
  const pick = (i, ev) => {
    if (drag.current.moved) { drag.current.moved = false; return; }
    onSelect(i);
    const strip = stripRef.current;
    /* Centre the opening plate on its FINAL geometry - mid-transition offsets
       lie, so the position is computed, not measured. */
    if (strip) {
      const mobile = window.matchMedia('(max-width: 900px)').matches;
      const closed = mobile ? 96 : 76;
      const open = mobile ? Math.min(300, window.innerWidth * 0.74) : Math.min(380, window.innerWidth * 0.66);
      const target = i * (closed + 10) - (strip.clientWidth - open) / 2;
      setTimeout(() => strip.scrollTo({ left: Math.max(0, target), behavior: 'smooth' }), 60);
    }
  };
  return (
    <section style={{ padding: 'clamp(64px, 11vh, 140px) 0 0' }}>
      <Reveal>
        <h2 style={{ margin: 0, fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 'clamp(28px, 4vw, 56px)', lineHeight: 1.02, letterSpacing: '-0.03em', color: 'var(--black)' }}>{data.title}</h2>
        {data.note && <p style={{ margin: '16px 0 0', maxWidth: '46ch', fontFamily: 'var(--font-display)', fontWeight: 400, fontSize: 'clamp(16px, 1.7vw, 20px)', lineHeight: 1.45, color: 'var(--gray-600)', textWrap: 'pretty' }}>{data.note}</p>}
      </Reveal>
      <Reveal>
        <div ref={stripRef} className="sm-highlights" onPointerDown={onPointerDown} onPointerMove={onPointerMove} onPointerUp={onPointerUp} onPointerLeave={onPointerUp} style={{ display: 'flex', gap: 10, marginTop: 'clamp(24px, 4vh, 40px)', overflowX: 'auto', paddingBottom: 6, WebkitOverflowScrolling: 'touch', cursor: 'grab', touchAction: 'pan-x pan-y' }}>
          {data.items.map((it, i) => {
            const on = active === i;
            return (
              <button key={it.id} onClick={(ev) => pick(i, ev)} aria-current={on} aria-label={it.label} className="sm-chapter-plate"
                style={{
                  position: 'relative', flex: '0 0 auto', width: on ? 'min(380px, 66vw)' : 76, height: 264,
                  border: 'none', cursor: on ? 'default' : 'pointer', textAlign: 'left', padding: 0,
                  borderRadius: 'var(--radius-card)', overflow: 'hidden', background: it.art, boxSizing: 'border-box',
                  transition: 'width .5s cubic-bezier(.22,1,.36,1)',
                }}>
                <span style={{ position: 'absolute', inset: 0, background: 'radial-gradient(120% 90% at 22% 8%, rgba(255,255,255,0.42) 0%, rgba(255,255,255,0) 52%), linear-gradient(to top, rgba(10,10,10,0.5) 0%, rgba(10,10,10,0) 55%)', pointerEvents: 'none' }} />
                <span style={{ position: 'absolute', top: 16, left: 18, fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: on ? 42 : 17, lineHeight: 1, letterSpacing: '-0.04em', color: 'var(--white)', textShadow: '0 1px 14px rgba(0,0,0,0.28)', transition: 'font-size .4s cubic-bezier(.22,1,.36,1)' }}>{String(i + 1).padStart(2, '0')}</span>
                <span className="sm-mono" style={{ ...monoW, position: 'absolute', left: '50%', bottom: 18, transform: 'translateX(-50%)', writingMode: 'vertical-rl', whiteSpace: 'nowrap', opacity: on ? 0 : 1, transition: 'opacity .25s ease', textShadow: '0 1px 12px rgba(0,0,0,0.4)' }}>{it.label}</span>
                <span style={{ position: 'absolute', left: 20, right: 20, bottom: 18, width: 'min(330px, 56vw)', display: 'flex', flexDirection: 'column', gap: 9, opacity: on ? 1 : 0, transition: on ? 'opacity .35s ease .18s' : 'opacity .15s ease' }}>
                  <span className="sm-mono" style={monoW}>{it.label}</span>
                  <span style={{ fontFamily: 'var(--font-display)', fontWeight: 500, fontSize: 21, lineHeight: 1.18, letterSpacing: '-0.02em', color: 'var(--white)', textShadow: '0 1px 16px rgba(0,0,0,0.4)' }}>{it.claim}</span>
                </span>
              </button>
            );
          })}
        </div>
      </Reveal>
    </section>
  );
}

/* The summit photo animates INTO the house treatment rather than arriving in
   it: the orange band wipes down across both figures, the colour drains to
   greyscale behind it, and the name pins draw themselves in last.

   Two clocks, one value. Desktop is hover: the treatment builds under the
   cursor and unwinds when it leaves. Mobile scroll-locks like the hero puzzle
   - the page stops when the photo reaches the middle of the screen, the next
   gesture spends itself on the treatment, then the page carries on. */
function PhotoBlock({ photo, name }) {
  const mono = { fontFamily: 'var(--font-mono)', fontWeight: 500, fontSize: 9.5, letterSpacing: 'var(--tracking-label)', textTransform: 'uppercase' };
  const tag = { position: 'absolute', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6, color: 'var(--white)', textShadow: '0 1px 12px rgba(0,0,0,0.6)' };
  const ref = React.useRef(null);
  const [p, setP] = React.useState(0);
  const [hover, setHover] = React.useState(false);
  /* Desktop is hover-driven: the treatment builds while the cursor is on the
     photo and unwinds when it leaves. Touch has no hover, so there the scroll
     clock still runs. */
  const pointer = typeof window !== 'undefined' && window.matchMedia ? window.matchMedia('(hover: hover) and (min-width: 1025px)') : null;
  const [desk, setDesk] = React.useState(() => !!(pointer && pointer.matches));
  React.useEffect(() => {
    if (!pointer) return;
    const on = () => setDesk(pointer.matches);
    pointer.addEventListener('change', on);
    return () => pointer.removeEventListener('change', on);
  }, [pointer]);
  /* Hover eases in and out rather than snapping - 0.55s each way. */
  const [h, setH] = React.useState(0);
  React.useEffect(() => {
    if (!desk) return;
    let raf;
    const step = () => {
      setH((v) => {
        const target = hover ? 1 : 0;
        const d = target - v;
        if (Math.abs(d) < 0.004) return target;
        raf = requestAnimationFrame(step);
        return v + d * 0.055;
      });
    };
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, [hover, desk]);
  /* Mobile: the photo scroll-locks like the hero puzzle. When it reaches the
     middle of the screen the page stops and the next scroll gesture spends
     itself on the treatment. It runs both ways - scroll up while the photo is
     centred and the treatment unwinds again. The lock only releases at the
     ends, so the page always has somewhere to go. */
  const prog = React.useRef(0);
  React.useEffect(() => {
    const el = ref.current;
    if (!el || desk) return;
    const layer = el.closest('.sm-screen-layer');
    if (!layer) return;
    let locked = false, touchY = 0, raf = 0, armed = true;
    const onWheel = (ev) => { if (!locked) return; ev.preventDefault(); advance(ev.deltaY); };
    /* preventDefault only survives while the lock holds; advance() releases. */
    const onTouchStart = (ev) => { touchY = ev.touches[0].clientY; };
    const onTouch = (ev) => {
      if (!locked) return;
      ev.preventDefault();
      const y = ev.touches[0].clientY;
      advance(touchY - y);
      touchY = y;
    };
    function release() {
      if (!locked) return;
      locked = false;
      /* Listeners stay attached and simply stop acting - see the note above.
         Re-arms only once the photo has left the middle of the screen, so the
         gesture that released the lock cannot immediately re-take it. */
      armed = false;
    }
    function advance(d) {
      const next = Math.max(0, Math.min(1, prog.current + d / (window.innerHeight * 0.9)));
      /* At either end, let the gesture through instead of eating it: fully
         built and still scrolling down means carry on down the page; back to
         nothing and still scrolling up means carry on up. */
      if ((prog.current >= 1 && d > 0) || (prog.current <= 0 && d < 0)) { release(); return; }
      prog.current = next;
      setP(next);
    }
    const lock = () => {
      if (locked || !armed) return;
      locked = true;
      /* A fast fling can carry the photo well past the middle before the lock
         catches it, which leaves the reader watching a half-played animation
         slide off the top. So on arming, ease the photo back to the centre
         first - the reader's gesture is already being consumed, so this reads
         as the page settling rather than fighting. */
      const box = el.getBoundingClientRect();
      const vh = window.innerHeight || 1;
      const off = (box.top + box.height / 2) - vh / 2;
      if (Math.abs(off) < 8) return;
      const from = layer.scrollTop, to = from + off, t0 = performance.now();
      const settle = (now) => {
        const k = Math.min(1, (now - t0) / 320);
        const eased = 1 - Math.pow(1 - k, 3);
        layer.scrollTop = from + (to - from) * eased;
        if (k < 1 && locked) raf = requestAnimationFrame(settle);
      };
      raf = requestAnimationFrame(settle);
    };
    const onScroll = () => {
      const box = el.getBoundingClientRect();
      const vh = window.innerHeight || 1;
      const centre = box.top + box.height / 2;
      /* Generous band: a fling only emits a handful of scroll events, and the
         lock has to catch one of them. */
      const near = Math.abs(centre - vh / 2) < vh * 0.3;
      if (!near) { armed = true; return; }
      if (!locked) lock();
    };
    onScroll();
    /* Registered for the whole life of the page, not just while locked: a
       browser ignores preventDefault on a gesture that has already started
       scrolling, so a listener attached mid-gesture lets the page move AND the
       animation run at the same time. Both handlers return immediately unless
       the lock is active. */
    layer.addEventListener('scroll', onScroll, { passive: true });
    layer.addEventListener('wheel', onWheel, { passive: false });
    layer.addEventListener('touchstart', onTouchStart, { passive: true });
    layer.addEventListener('touchmove', onTouch, { passive: false });
    window.addEventListener('resize', onScroll);
    return () => {
      release();
      cancelAnimationFrame(raf);
      layer.removeEventListener('scroll', onScroll);
      layer.removeEventListener('wheel', onWheel);
      layer.removeEventListener('touchstart', onTouchStart);
      layer.removeEventListener('touchmove', onTouch);
      window.removeEventListener('resize', onScroll);
    };
  }, [desk]);
  const ease = (x) => 1 - Math.pow(1 - x, 3);
  /* One value drives the whole treatment, whichever clock produced it. */
  const q = desk ? h : p;
  const e = ease(q);
  /* Band wipes down first, colour drains behind it, pins arrive last. */
  const band = Math.max(0, Math.min(1, q));
  const drain = Math.max(0, Math.min(1, (q - 0.12) / 0.6));
  const pins = Math.max(0, Math.min(1, (q - 0.55) / 0.4));
  const pinsMe = Math.max(0, Math.min(1, (q - 0.62) / 0.38));
  return (
    <figure ref={ref}
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      style={{ margin: '8px 0 0', position: 'relative', borderRadius: 'var(--radius-card)', overflow: 'hidden', background: 'var(--black)' }}>
      <img className="sm-about-photo" src={photo.src} alt={`${name} and his fiancée at a summit cross above Kitzbühel`}
        style={{ display: 'block', width: '100%', aspectRatio: '16 / 9', objectFit: 'cover', filter: `grayscale(${(drain * 100).toFixed(1)}%) contrast(${(1 + 0.06 * drain).toFixed(3)})` }} />
      {/* The orange band, wiping down over both figures. Hard-light keeps the
          rock and sky legible underneath instead of flooding them. */}
      <div aria-hidden="true" style={{ position: 'absolute', left: '31%', width: '22%', top: 0, height: `${(band * band * (3 - 2 * band) * 100).toFixed(1)}%`, background: 'var(--orange-500)', mixBlendMode: 'hard-light', opacity: 0.92, pointerEvents: 'none' }} />
      {/* Protection scrims. The bottom one is deeper and reaches further up on
          narrow screens (see .sm-about-scrim), where the caption sits over pale
          rock once the colour has drained. */}
      <div className="sm-about-scrim" style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(10,10,10,0.42) 0%, rgba(10,10,10,0) 26%), linear-gradient(0deg, rgba(10,10,10,0.6) 0%, rgba(10,10,10,0) 22%)', pointerEvents: 'none' }} />
      <div className="sm-about-pin" style={{ ...tag, left: '37.7%', top: '9%', bottom: '81%', transform: 'translateX(-50%)', justifyContent: 'flex-end', opacity: pins }}>
        <span style={{ ...mono }}>{photo.her}</span>
        <span style={{ flex: 1, minHeight: 10, width: 1.5, background: 'rgba(255,255,255,0.85)', transformOrigin: 'bottom', transform: `scaleY(${ease(pins).toFixed(3)})` }} />
      </div>
      <div className="sm-about-pin" style={{ ...tag, left: '47.5%', top: '1%', bottom: '85%', transform: 'translateX(-50%)', justifyContent: 'flex-end', opacity: pinsMe }}>
        <span style={{ ...mono }}>{photo.me}</span>
        <span style={{ flex: 1, minHeight: 10, width: 1.5, background: 'rgba(255,255,255,0.85)', transformOrigin: 'bottom', transform: `scaleY(${ease(pinsMe).toFixed(3)})` }} />
      </div>
      <figcaption style={{ ...mono, position: 'absolute', left: 20, right: 20, bottom: 18, color: 'rgba(255,255,255,0.9)', textShadow: '0 1px 12px rgba(0,0,0,0.6)', textAlign: 'right', opacity: (0.35 + 0.65 * e).toFixed(3) }}>
        <span className="sm-cap-wide">{photo.caption}</span>
        <span className="sm-cap-narrow" style={{ display: 'none' }}>{photo.captionFull}</span>
      </figcaption>
    </figure>
  );
}

function WorkProof({ data }) {
  /* Click, not hover. The snippets grew long enough that a mouse crossing the
     list opened and shut three of them on the way past, which is unreadable.
     Nothing is open at rest, so the row of five reads as a list first. */
  const [open, setOpen] = React.useState(-1);
  const [hover, setHover] = React.useState(-1);
  const mono = { fontFamily: 'var(--font-mono)', fontWeight: 500, fontSize: 9.5, letterSpacing: 'var(--tracking-label)', textTransform: 'uppercase' };
  return (
    <div style={{ borderBottom: '1.5px solid var(--gray-200)' }}>
      {data.items.map((it, i) => {
        const on = open === i;
        const lit = on || hover === i;
        return (
          <div key={it.title} onClick={() => setOpen(on ? -1 : i)} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setOpen(on ? -1 : i); } }} onMouseEnter={() => setHover(i)} onMouseLeave={() => setHover(-1)} onFocus={() => setHover(i)} onBlur={() => setHover(-1)} tabIndex={0} role="button" aria-expanded={on}
            style={{ borderTop: '1.5px solid var(--gray-200)', padding: '24px 0', cursor: 'pointer', outline: 'none' }}>
            <div className="sm-about-proof" style={{ display: 'grid', gridTemplateColumns: '150px 1fr 130px', gap: 24, alignItems: 'baseline' }}>
              <span style={{ ...mono, color: lit ? 'var(--orange-500)' : 'var(--gray-500)', transition: 'color .3s ease' }}>{it.kind}</span>
              <h3 style={{ margin: 0, fontFamily: 'var(--font-display)', fontWeight: 500, fontSize: 'clamp(19px, 2vw, 24px)', lineHeight: 1.24, letterSpacing: '-0.015em', color: lit ? 'var(--orange-600)' : 'var(--black)', transition: 'color .3s ease' }}>{it.title}</h3>
              <span style={{ ...mono, display: 'flex', alignItems: 'baseline', justifyContent: 'flex-end', gap: 10, color: 'var(--gray-500)', textAlign: 'right' }}>{it.year}<span aria-hidden="true" style={{ display: 'inline-block', width: 9, color: lit ? 'var(--orange-500)' : 'var(--gray-400)', transform: on ? 'rotate(45deg)' : 'none', transition: 'transform .35s cubic-bezier(.22,1,.36,1), color .3s ease' }}>+</span></span>
            </div>
            <div style={{ display: 'grid', gridTemplateRows: on ? '1fr' : '0fr', transition: 'grid-template-rows .45s cubic-bezier(.22,1,.36,1)' }}>
              <div style={{ overflow: 'hidden' }}>
                <div className="sm-about-proof" style={{ display: 'grid', gridTemplateColumns: '150px 1fr 130px', gap: 24, paddingTop: 16 }}>
                  <span />
                  <div style={{ maxWidth: '54ch' }}>
                    {/* Every snippet has the same three parts, labelled, so the
                        shape is identical down the list and it reads as a few
                        lines rather than a case study. */}
                    {[it.detail, it.did, it.outcome].map((part, k) => part && (
                      <div key={k} style={{ paddingTop: k ? 14 : 0, marginTop: k ? 16 : 0, borderTop: k ? '1.5px solid var(--gray-200)' : 'none' }}>
                        <div style={{ ...mono, color: 'var(--gray-500)' }}>{(data.partLabels || [])[k]}</div>
                        <p style={{ margin: '7px 0 0', fontFamily: k === 2 ? 'var(--font-display)' : 'var(--font-body)', fontWeight: k === 2 ? 500 : 400, fontSize: k === 2 ? 17 : 16.5, lineHeight: k === 2 ? 1.45 : 1.65, letterSpacing: k === 2 ? '-0.01em' : 0, color: k === 2 ? 'var(--black)' : 'var(--gray-600)' }}>{part}</p>
                      </div>
                    ))}
                  </div>
                  <span style={{ ...mono, color: 'var(--gray-500)', textAlign: 'right' }}>{it.org}</span>
                </div>
              </div>
            </div>
          </div>
        );
      })}
    </div>
  );
}

/* End-of-chapter switch, so nobody scrolls back up to change chapters: a
   hairline row with the previous and next chapter, wrapping at the ends. */
function ChapterNav({ items, active, onPick }) {
  const mono = { fontFamily: 'var(--font-mono)', fontWeight: 500, fontSize: 9.5, letterSpacing: 'var(--tracking-label)', textTransform: 'uppercase' };
  const btn = { display: 'flex', alignItems: 'center', gap: 10, minHeight: 44, border: 'none', background: 'transparent', cursor: 'pointer', padding: 0 };
  const prev = (active + items.length - 1) % items.length;
  const next = (active + 1) % items.length;
  return (
    <div style={{ marginTop: 'clamp(48px, 8vh, 88px)', borderTop: '1.5px solid var(--gray-200)', paddingTop: 8, display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 20 }}>
      <button onClick={() => onPick(prev)} style={btn} aria-label={items[prev].label}>
        <span className="sm-mono" style={{ ...mono, color: 'var(--gray-500)' }}>← {items[prev].label}</span>
      </button>
      <span className="sm-mono" style={{ ...mono, color: 'var(--gray-400)' }}>{String(active + 1).padStart(2, '0')} / {String(items.length).padStart(2, '0')}</span>
      <button onClick={() => onPick(next)} style={btn} aria-label={items[next].label}>
        <span className="sm-mono" style={{ ...mono, color: 'var(--gray-500)' }}>{items[next].label} →</span>
      </button>
    </div>
  );
}

/* Logo wall: eight marks up front, the rest behind one quiet toggle. */
function LogoWall({ note, moreNote, moreBtn, fewerBtn }) {
  const [open, setOpen] = React.useState(false);
  const mono = { fontFamily: 'var(--font-mono)', fontWeight: 500, fontSize: 9.5, letterSpacing: 'var(--tracking-label)', textTransform: 'uppercase', color: 'var(--gray-500)' };
  const shown = open ? CLIENT_LOGOS : CLIENT_LOGOS.slice(0, 10);
  /* FLIP: the collapsed wall centres its half-filled teaser row; expanding
     left-aligns it and appends the rest. Measure before, invert after, then
     let it play - so the row visibly slides into place instead of jumping. */
  const wallRef = React.useRef(null);
  const marks = React.useRef(null);
  const toggle = () => {
    const wall = wallRef.current;
    if (wall) {
      marks.current = new Map();
      wall.querySelectorAll('img').forEach((el) => marks.current.set(el.alt, el.getBoundingClientRect()));
    }
    setOpen((v) => !v);
  };
  React.useEffect(() => {
    const wall = wallRef.current, was = marks.current;
    if (!wall || !was) return;
    marks.current = null;
    wall.querySelectorAll('img').forEach((el) => {
      const from = was.get(el.alt);
      const to = el.getBoundingClientRect();
      if (!from) { el.style.opacity = '0'; requestAnimationFrame(() => { el.style.transition = 'opacity .45s ease .18s'; el.style.opacity = '1'; }); return; }
      const dx = from.left - to.left, dy = from.top - to.top;
      if (!dx && !dy) return;
      el.style.transition = 'none';
      el.style.transform = 'translate(' + dx + 'px,' + dy + 'px)';
      requestAnimationFrame(() => {
        el.style.transition = 'transform .5s cubic-bezier(.22,1,.36,1)';
        el.style.transform = 'translate(0,0)';
      });
    });
  }, [open]);
  const quiet = { fontFamily: 'var(--font-body)', fontSize: 14.5, lineHeight: 1.6, color: 'var(--gray-500)' };
  return (
    <div>
      <div style={{ position: 'relative', maxHeight: open ? 'none' : 300, overflow: 'hidden' }}>
        {/* Flex, not grid: collapsed, the teaser row centres so a half-filled
            row reads as deliberate; expanded, everything aligns left. */}
        {/* Collapsed: a centred row, so the half-filled teaser reads as
            deliberate. Expanded: back to the even column block. The FLIP pass
            animates between the two layouts. */}
        <div ref={wallRef} className="sm-logo-wall" style={open
          ? { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(140px, 1fr))', gap: '30px 16px', alignItems: 'center', justifyItems: 'center' }
          : { display: 'flex', flexWrap: 'wrap', alignItems: 'center', justifyContent: 'center', gap: '30px 46px', paddingBottom: 40 }}>
          {shown.map((l) => (
            <img key={l.name} src={l.src} alt={l.name} style={{ height: (l.h || 28) * 1.15, width: 'auto', maxWidth: '100%', display: 'block' }} />
          ))}
        </div>
        {!open && <div aria-hidden="true" style={{ position: 'absolute', left: 0, right: 0, bottom: 0, height: 110, background: 'linear-gradient(to bottom, rgba(248,248,248,0), var(--paper))', pointerEvents: 'none' }} />}
      </div>
      <div style={{ display: 'flex', justifyContent: 'center', marginTop: open ? 30 : 4 }}>
        <button onClick={toggle} style={{ border: 'none', background: 'transparent', cursor: 'pointer', padding: 0, minHeight: 44, ...quiet, color: 'var(--black)' }}>
          {open ? fewerBtn : moreBtn}
        </button>
      </div>
      {(open || !note) && moreNote && <p style={{ ...quiet, margin: '18px 0 0', textAlign: 'center' }}>{moreNote}</p>}
    </div>
  );
}

const ctaReachLink = { display: 'inline-flex', alignItems: 'center', gap: 9, minHeight: 44, paddingTop: 14, paddingBottom: 14, marginTop: -14, marginBottom: -14, fontFamily: 'var(--font-mono)', fontWeight: 500, fontSize: 9.5, letterSpacing: 'var(--tracking-label)', textTransform: 'uppercase', color: 'var(--black)', textDecoration: 'underline', textUnderlineOffset: 3 };

function About({ s, t, onBack }) {
  const { Button, Icon } = window.SevcikMarketingDesignSystem_3203fa;
  const a = s.htmlLang === 'cs' ? ABOUT_CS : ABOUT_EN;
  const [active, setActive] = React.useState(0);
  const chaptersRef = React.useRef(null);
  const chapterContentRef = React.useRef(null);
  const pickFromBottom = (i) => {
    setActive(i);
    /* Scroll to the incoming chapter's headline, not the strip above it. The
       new content mounts on the next frame, so measure after the re-render. */
    requestAnimationFrame(() => {
      const el = chapterContentRef.current;
      const layer = el && el.closest('.sm-screen-layer');
      if (el && layer) layer.scrollTo({ top: Math.max(0, el.getBoundingClientRect().top + layer.scrollTop - 76), behavior: 'smooth' });
    });
  };
  const mono = { fontFamily: 'var(--font-mono)', fontWeight: 500, fontSize: 9.5, letterSpacing: 'var(--tracking-label)', textTransform: 'uppercase', color: 'var(--gray-500)' };
  const wrap = { maxWidth: 1080, margin: '0 auto', padding: '0 32px' };
  /* Swipe, mouse-drag and trackpad between chapters, the same gesture as the
     services carousel. The chapters themselves stay one-at-a-time - the map,
     the timeline and the ticker each set up their own observers and drawing, so
     mounting all six side by side to slide them costs more than it buys. The
     content follows the finger and hands over to the existing fade. */
  const chapterCount = a.highlights ? a.highlights.items.length : 1;
  const activeRef = React.useRef(active);
  activeRef.current = active;
  const gesture = React.useRef({ id: null, x: 0, y: 0, lock: null });
  /* The offset goes on a wrapper, never on the chapter node itself: that node
     runs the sm-symptom-in keyframe with fill-mode both, and a CSS animation
     outranks an inline style, so a transform written there is simply ignored. */
  const chapterWrapRef = React.useRef(null);
  /* A horizontal drag that springs back must not also count as a click. The
     rows inside a chapter are div[role="button"] with their own onClick, so a
     failed swipe would expand whatever the finger came to rest on. */
  const swallowClick = React.useRef(false);
  const paintChapter = (px, animate) => {
    const el = chapterWrapRef.current;
    if (!el) return;
    el.style.transition = animate ? 'transform .3s cubic-bezier(.22,1,.36,1)' : 'none';
    el.style.transform = px ? `translateX(${px}px)` : 'none';
  };
  /* Every chapter change releases the offset, whatever caused it. The wheel
     path calls setActive directly, so resetting only inside the pointer handler
     left the content parked where the swipe ended. */
  React.useEffect(() => { paintChapter(0, false); }, [active]);
  const stepChapter = (dir) => {
    paintChapter(0, false);
    setActive((i) => (i + dir + chapterCount) % chapterCount);
  };
  const onChapterDown = (ev) => {
    /* Cleared at the START of every gesture, not after one. A touch swipe
       synthesises no click at all, so a flag cleared only by a click survives
       the gesture and eats the next genuine tap. The same-gesture click always
       lands before the next pointerdown, so it is still swallowed. */
    swallowClick.current = false;
    if (ev.pointerType === 'mouse' && ev.button !== 0) return;
    if (ev.target.closest('button, a, input, textarea, select, svg')) return;
    gesture.current = { id: ev.pointerId, x: ev.clientX, y: ev.clientY, lock: null };
  };
  const onChapterMove = (ev) => {
    const g = gesture.current;
    if (g.id !== ev.pointerId) return;
    const dx = ev.clientX - g.x, dy = ev.clientY - g.y;
    if (!g.lock) {
      if (Math.abs(dx) < 12 && Math.abs(dy) < 12) return;
      g.lock = Math.abs(dx) > Math.abs(dy) * 1.4 ? 'x' : 'y';
    }
    if (g.lock !== 'x') return;
    if (ev.cancelable) ev.preventDefault();
    const sel = window.getSelection && window.getSelection();
    if (sel && !sel.isCollapsed) sel.removeAllRanges();
    const el = chapterWrapRef.current;
    if (el) { el.style.userSelect = 'none'; el.style.cursor = 'grabbing'; }
    paintChapter(Math.max(-120, Math.min(120, dx * 0.5)), false);
  };
  const onChapterUp = (ev) => {
    const g = gesture.current;
    if (g.id !== ev.pointerId) return;
    const dx = ev.clientX - g.x;
    gesture.current = { id: null, x: 0, y: 0, lock: null };
    const el = chapterWrapRef.current;
    if (el) { el.style.userSelect = 'auto'; el.style.cursor = 'auto'; }
    const w = el ? el.clientWidth : 600;
    if (g.lock === 'x') swallowClick.current = true;
    if (g.lock === 'x' && Math.abs(dx) > Math.min(140, w * 0.22)) stepChapter(dx < 0 ? 1 : -1);
    else paintChapter(0, true);
  };
  React.useEffect(() => {
    const el = chapterWrapRef.current;
    if (!el) return;
    const onClickCapture = (ev) => {
      if (!swallowClick.current) return;
      swallowClick.current = false;
      ev.stopPropagation();
      ev.preventDefault();
    };
    el.addEventListener('click', onClickCapture, true);
    return () => el.removeEventListener('click', onClickCapture, true);
  }, []);
  React.useEffect(() => {
    /* The whole screen listens. A wheel event goes to whatever is under the
       cursor, so anything narrower than the page means the gesture works in some
       places and not others - which is why this needed the mouse moved after a
       flip. The plate strip is the one exclusion: it scrolls horizontally
       itself. */
    const host = chaptersRef.current;
    const el = (host && host.closest('.sm-screen-layer')) || host;
    if (!el || chapterCount < 2) return;
    /* One flip per stream, decided by the stream's SHAPE, with no clocks
       except the 150ms gap detector. Timing gates kept failing because macOS
       momentum can emit for seconds and preview/iframe clocks lie (see
       notes/handover-swipe-gesture.md for the full failure table). The rules:
       - events under 4px never accumulate: that is tail noise, and a dying tail
         emitting 1-2px for a second could otherwise creep past the threshold;
       - after a step, the stream must prove the hand lifted before it may step
         again: four consecutive sub-4px events (an exhausted tail) or a 150ms
         gap. A push-ease-push sweep never goes that quiet, so it stays one
         flip; a real second flick lands on an exhausted tail and re-arms. */
    let acc = 0, idle = null, last = 0, stepped = false, smallRun = 0, lastDir = 0;
    const rec = [];
    let peak = 0, inTail = false;
    const LOG = (window.__wheelLog = window.__wheelLog || []);
    const STEP = 90;
    const onWheel = (ev) => {
      const mag = Math.abs(ev.deltaX);
      if (mag < Math.abs(ev.deltaY) * 1.2 || mag < 2) return;
      /* Swipes only mean "change chapter" over the chapters themselves. The
         listener sits on the whole screen layer (so the shrinking chapter body
         cannot dodge the cursor), so everything that is not the carousel opts
         out here: the clients/logo section and the plate strip. */
      if (ev.target.closest && ev.target.closest('.sm-highlights, #clients')) return;
      ev.preventDefault();
      const now = performance.now();
      if (now - last > 150) { acc = 0; stepped = false; smallRun = 0; rec.length = 0; peak = 0; inTail = false; }
      /* A sign flip is the one unambiguous signal a wheel stream carries:
         momentum never reverses direction, only a hand does. A reversal of 8px
         or more re-arms instantly - without this, starting a right swipe while
         a left fling's tail was still emitting left the stream locked until the
         next 150ms gap, which is exactly "works a few times, then dead". */
      const dirNow = ev.deltaX > 0 ? 1 : -1;
      if (mag >= 8 && lastDir && dirNow !== lastDir) { acc = 0; stepped = false; smallRun = 0; peak = 0; inTail = false; }
      if (mag >= 8) lastDir = dirNow;
      last = now;
      LOG.push([Math.round(now), Math.round(ev.deltaX), stepped ? 1 : 0]);
      if (LOG.length > 600) LOG.splice(0, 200);
      clearTimeout(idle);
      idle = setTimeout(() => { acc = 0; stepped = false; smallRun = 0; rec.length = 0; peak = 0; inTail = false; paintChapter(0, true); }, 150);
      smallRun = mag < 4 ? smallRun + 1 : 0;
      /* Re-arm rules, all shape-based. A step often lands while the fling's
         own deltas are still growing, so "deltas rising" alone re-armed against
         the same gesture and double-flipped. The stream must first prove it
         entered its tail - decayed below 40% of the peak seen since the step -
         and only a rise out of THAT is a new flick. Momentum never rises out of
         its own tail; jitter (13,15,14) fails the growth test. The other ways
         back: the tail dies (four sub-4px events), the direction flips
         (above), or a 150ms gap. */
      if (stepped) {
        peak = Math.max(peak, mag);
        if (mag < peak * 0.4) inTail = true;
      }
      const m1 = rec.length > 1 ? rec[rec.length - 2] : 0;
      const m2 = rec.length ? rec[rec.length - 1] : 0;
      const rising = mag > m2 && m2 > m1 && (mag - m1) >= 15 && mag >= 25;
      if (stepped && (smallRun >= 4 || (inTail && rising))) { stepped = false; acc = 0; }
      rec.push(mag);
      if (rec.length > 4) rec.shift();
      if (stepped || mag < 4) return;
      acc += ev.deltaX;
      paintChapter(Math.max(-120, Math.min(120, -acc * 0.7)), false);
      if (Math.abs(acc) > STEP) {
        const dir = acc > 0 ? 1 : -1;
        acc = 0;
        stepped = true;
        peak = mag; inTail = false;
        setActive((i) => (i + dir + chapterCount) % chapterCount);
      }
    };
    el.addEventListener('wheel', onWheel, { passive: false });
    return () => { clearTimeout(idle); el.removeEventListener('wheel', onWheel); };
  }, [chapterCount]);

  if (a.stub) {
    return (
      <div style={{ flex: 1, background: 'var(--paper)', minHeight: '100vh', boxSizing: 'border-box', padding: '40px 72px' }}>
        <button onClick={onBack} aria-label={t.backHome} style={{ background: 'var(--gray-150)', border: 'none', borderRadius: 'var(--radius-card)', width: 44, height: 44, cursor: 'pointer' }}>↖</button>
        <h1 style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 44, marginTop: 40 }}>{a.kicker}</h1>
        <p style={{ fontFamily: 'var(--font-body)', fontSize: 17, color: 'var(--gray-600)', maxWidth: '50ch' }}>{a.standfirst}</p>
      </div>
    );
  }

  return (
    <div className="sm-screen-layer" style={{ position: 'fixed', inset: 0, zIndex: 60, background: 'var(--paper)', overflowY: 'auto', overflowX: 'hidden' }}>
      <GradientField height="auto">
        <div style={{ ...wrap, paddingTop: 28, paddingBottom: 20 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 20, marginBottom: 'clamp(28px, 7vh, 140px)' }}>
            <button onClick={onBack} aria-label={t.backHome} style={{ background: 'rgba(255,255,255,0.7)', backdropFilter: 'blur(8px)', border: 'none', borderRadius: 'var(--radius-card)', width: 44, height: 44, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <Icon name="arrow-up-left" size={16} color="var(--black)" />
            </button>
            <span style={mono}>{a.kicker}</span>
          </div>
          <h1 style={{ margin: 0, fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 'clamp(30px, 5.6vw, 82px)', lineHeight: 1.06, letterSpacing: '-0.03em', color: 'var(--black)' }}>
            {a.headline.map((line, i) => (
              <Reveal key={i} as="span" delay={i * 130} style={{ display: 'block' }}>{line}</Reveal>
            ))}
          </h1>
          {a.headlineSub ? (
            <Reveal delay={a.headline.length * 130}>
              <p style={{ margin: '14px 0 0', maxWidth: '30ch', fontFamily: 'var(--font-display)', fontWeight: 500, fontSize: 'clamp(21px, 2.9vw, 40px)', lineHeight: 1.12, letterSpacing: '-0.022em', color: 'var(--gray-600)', textWrap: 'balance' }}>{a.headlineSub}</p>
            </Reveal>
          ) : null}
          <Reveal delay={(a.headline.length + (a.headlineSub ? 1 : 0)) * 130}>
            <p style={{ margin: '36px 0 0', maxWidth: '68ch', fontFamily: 'var(--font-display)', fontWeight: 400, fontSize: 'clamp(18px, 2vw, 23px)', lineHeight: 1.5, color: 'var(--gray-700)' }}>{String(a.standfirst).split('\n').map((line, i) => (<React.Fragment key={i}>{i > 0 && <span style={{ display: 'block', height: '0.9em' }} />}{line}</React.Fragment>))}</p>
          </Reveal>
          <div style={{ marginTop: 56 }}><PhotoBlock photo={a.photo} name={s.siteName} /></div>
        </div>
      </GradientField>

      <div style={{ ...wrap, paddingBottom: 120 }}>
        <div ref={chaptersRef}>
        {a.highlights && <Highlights data={a.highlights} active={active} onSelect={setActive} />}

        {a.highlights && (() => {
          const chapter = a.highlights.items[active].id;
          return (
            <div ref={chapterWrapRef} onPointerDown={onChapterDown} onPointerMove={onChapterMove} onPointerUp={onChapterUp} onPointerCancel={onChapterUp} style={{ touchAction: 'pan-y' }}>
            <div key={chapter} ref={chapterContentRef} style={{ animation: 'sm-symptom-in .45s cubic-bezier(.22,1,.36,1) both' }}>
              {chapter === 'calls' && a.symptoms && (
                <AboutSection tight id={a.symptoms.id} label={a.symptoms.label} title={a.symptoms.heading} art={a.symptoms.art}>
                  <div style={{ maxWidth: 780 }}><SymptomTicker data={a.symptoms} /></div>
                </AboutSection>
              )}
              {chapter === 'approach' && a.bring && (
                <section id={a.bring.id} style={{ padding: 'clamp(44px, 7vh, 80px) 0 0', scrollMarginTop: 90 }}>
            <Reveal>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 22 }}>
                {a.bring.art && <span aria-hidden="true" style={{ width: 14, height: 14, borderRadius: 4, background: a.bring.art, display: 'block', flexShrink: 0 }} />}
                <div className="sm-mono" style={mono}>{a.bring.label}</div>
              </div>
              <h2 style={{ margin: 0, whiteSpace: 'pre-line', fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 'clamp(34px, 5.4vw, 76px)', lineHeight: 1.02, letterSpacing: '-0.035em', color: 'var(--black)' }}>{a.bring.title}</h2>
              <p style={{ margin: '24px 0 0', maxWidth: '42ch', fontFamily: 'var(--font-display)', fontWeight: 400, fontSize: 'clamp(17px, 1.8vw, 22px)', lineHeight: 1.45, color: 'var(--gray-600)' }}><Emphasised text={a.bring.note} /></p>
            </Reveal>
            {a.bring.pairs.map((p, i) => (
              <Reveal key={p.neu}>
                <div className="sm-bring-pair" style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 300px) minmax(0, 1fr)', gap: 'clamp(24px, 4vw, 64px)', alignItems: 'start', borderTop: '1.5px solid var(--gray-200)', margin: 'clamp(40px, 7vh, 72px) 0 0', padding: 'clamp(28px, 4vh, 40px) 0 0' }}>
                  <div>
                    <span className="sm-mono" style={mono}>{String(i + 1).padStart(2, '0')}</span>
                    <p style={{ margin: '14px 0 0', fontFamily: 'var(--font-body)', fontSize: 'clamp(15px, 1.5vw, 17px)', lineHeight: 1.55, color: 'var(--gray-500)', textDecoration: 'line-through', textDecorationColor: 'var(--gray-300)' }}>{p.old}</p>
                  </div>
                  <div>
                    <span className="sm-mono" style={{ ...mono, display: 'block', marginBottom: 14 }}>{a.bring.expLabel}</span>
                    <p style={{ margin: 0, fontFamily: 'var(--font-display)', fontWeight: 500, fontSize: 'clamp(24px, 3.4vw, 44px)', lineHeight: 1.08, letterSpacing: '-0.025em', color: 'var(--black)', maxWidth: '28ch' }}>{p.neu}</p>
                    <p style={{ margin: '18px 0 0', fontFamily: 'var(--font-body)', fontSize: 'clamp(15px, 1.5vw, 17px)', lineHeight: 1.6, color: 'var(--gray-600)', maxWidth: '52ch' }}>{p.aside}</p>
                  </div>
                </div>
              </Reveal>
            ))}
                </section>
              )}
              {chapter === 'work' && (
                <AboutSection tight id={a.work_proof.id} label={a.work_proof.label} title={a.work_proof.title} sub={a.work_proof.sub} note={a.work_proof.note} art={a.work_proof.art} noteWidth="62ch" noteSize="clamp(16px, 1.7vw, 20px)">
                  <WorkProof data={a.work_proof} />
                </AboutSection>
              )}
              {chapter === 'markets' && (
                <AboutSection tight id={a.markets.id} label={a.markets.label} title={a.markets.title} note={a.markets.note} art={a.markets.art}>
                  <MarketMap data={a.markets} />
                </AboutSection>
              )}
              {chapter === 'path' && (
                <AboutSection tight id={a.timeline.id} label={a.timeline.label} title={a.timeline.title} note={a.timeline.note} art={a.timeline.art}>
                  <CareerTimeline data={a.timeline} />
                </AboutSection>
              )}
              <ChapterNav items={a.highlights.items} active={active} onPick={pickFromBottom} />
            </div>
            </div>
          );
        })()}
        </div>

        <AboutSection id={a.work.id} label={a.work.label} title={a.work.title} note={a.work.lead} art={a.work.art} noteWidth="52ch">
          <Reveal>
            <LogoWall note={a.work.logosNote} moreNote={a.work.moreNote} moreBtn={a.work.moreBtn} fewerBtn={a.work.fewerBtn} />
          </Reveal>
        </AboutSection>

        <section style={{ padding: '96px 0 0' }}>
          <Reveal>
            <div className="sm-about-cta" style={{ marginRight: 35, background: 'var(--surface-cta)', borderRadius: 'var(--radius-card)', padding: 'clamp(36px, 5vw, 64px)', display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) 150px', gap: 'clamp(24px, 4vw, 56px)', alignItems: 'center' }}>
              <div>
                <h2 style={{ margin: 0, fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 'clamp(30px, 4vw, 52px)', lineHeight: 1.04, letterSpacing: '-0.025em', color: 'var(--black)' }}>{a.cta.title}</h2>
                <p style={{ margin: '20px 0 32px', maxWidth: '46ch', fontFamily: 'var(--font-body)', fontSize: 17.5, lineHeight: 1.65, color: 'var(--gray-700)' }}>{a.cta.text}</p>
                <Button variant="pill-dark" size="md" href={`mailto:${CONTACT.email}`}>{a.cta.button}</Button>
                {a.cta.qrAlt && (
                  <div className="sm-cta-reach" style={{ marginTop: 32, paddingTop: 22, borderTop: '1.5px solid rgba(10,10,10,0.16)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 24, flexWrap: 'wrap' }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 24, flexWrap: 'wrap' }}>
                    <a href={`mailto:${CONTACT.email}`} style={ctaReachLink}>
                      <Icon name="mail" size={15} color="var(--black)" />{CONTACT.email}
                    </a>
                    <a href={`tel:${CONTACT.whatsapp}`} style={ctaReachLink}>
                      <Icon name="phone" size={15} color="var(--black)" />{CONTACT.phone}
                    </a>
                    </div>
                  </div>
                )}
              </div>
              {a.photoSmall && (
                <figure className="sm-about-cta-figure" style={{ margin: '0 -225px 0 0', justifySelf: 'end', display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 18 }}>
                  <span className="sm-about-cta-plate" style={{ position: 'relative', display: 'block', width: 300, height: 300, flexShrink: 0 }}>
                    <span aria-hidden="true" style={{ position: 'absolute', left: '-22%', top: '-30%', width: '58%', height: '58%', borderRadius: '50%', background: 'var(--gradient-hero)', opacity: 0.95 }} />
                    <span style={{ position: 'absolute', inset: 0, borderRadius: '58% 42% 47% 53% / 52% 48% 52% 48%', overflow: 'hidden', background: 'var(--black)', display: 'block' }}>
                      <img className="sm-about-cta-photo" src={a.photoSmall.src} alt={s.siteName} style={{ width: '100%', height: '100%', display: 'block', objectFit: 'cover', filter: 'grayscale(1) contrast(1.06)' }} />
                      <span aria-hidden="true" style={{ position: 'absolute', right: 'var(--cta-stripe-right, calc(225px - clamp(36px, 5vw, 64px)))', top: 0, bottom: 0, width: 'var(--cta-stripe-w, 51px)', background: 'var(--orange-500)', mixBlendMode: 'hard-light', opacity: 0.95 }} />
                      <span aria-hidden="true" style={{ position: 'absolute', left: '-22%', top: '-30%', width: '58%', height: '58%', borderRadius: '50%', background: 'var(--gradient-hero)', opacity: 0.95 }} />
                    </span>
                  </span>
                  {a.photoSmall.note && (
                    <figcaption style={{ order: -1, width: 'max-content', maxWidth: '14ch', marginRight: 26, position: 'relative', paddingLeft: 26, fontFamily: 'var(--font-display)', fontWeight: 500, fontSize: 18, lineHeight: 1.35, letterSpacing: '-0.01em', color: 'var(--gray-700)', textAlign: 'right', fontStyle: 'italic' }}>
                      <span aria-hidden="true" style={{ position: 'absolute', left: 0, top: -12, fontFamily: 'var(--font-display)', fontStyle: 'normal', fontWeight: 600, fontSize: 46, lineHeight: 1, color: 'rgba(10,10,10,0.22)' }}>“</span>
                      {a.photoSmall.note}
                      {a.photoSmall.sign && (
                        <span style={{ display: 'block', marginTop: 14, fontFamily: 'var(--font-display)', fontStyle: 'italic', fontWeight: 600, fontSize: 26, lineHeight: 1, letterSpacing: '-0.02em', color: 'var(--black)' }}>{a.photoSmall.sign}</span>
                      )}
                    </figcaption>
                  )}
                </figure>
              )}
            </div>
          </Reveal>
        </section>
      </div>
    </div>
  );
}

Object.assign(window, { About });
