{
 "site": "Floema",
 "slug": "floema",
 "urls": [
  "https://floema.com/en",
  "https://floema.com/en/about"
 ],
 "stack": {
  "framework": "Nuxt 4.5.2 on Vue 3.5.42 (SSR then hydration), Pinia, @nuxtjs/i18n 10.0.1; made by Büro (burocratik.com) with its in-house BuroCore utilities",
  "cms": "Sanity (project 535lnz3g, dataset production, API 2025-08-08, GROQ, portable text); images via Vercel image optimisation from cdn.sanity.io; hosted on Vercel",
  "animation": [
   "CSS transitions (the nav separation)",
   "GSAP 3.13.0 with ScrollTrigger, SplitText and Observer (bundled, no globals)",
   "Rive (@rive-app/canvas 2.31.6), bundled for pictograms; no Rive canvas found on the home"
  ],
  "scroll": "Lenis 1.3.8 in a Scroller component; ScrollTrigger pins and section triggers",
  "three_d": "three.js r181 in module Workers via OffscreenCanvas, wrapped by Büro's BüroGL layer (home image tunnel, product ModelView, ShadowsPortal)",
  "fonts": [
   "Zimula variable (self-hosted TTF, custom cnct axis plus wght; nav labels 12 px, cnct 1000, wght 600, uppercase)"
  ],
  "evidence": [
   "response headers: server Vercel, x-powered-by Nuxt, x-vercel-cache HIT",
   "/_nuxt/cgz0BpdW.js: versions { get nuxt() { return '4.5.2' } }",
   "/_nuxt/xJNWDYji.js: Vue version string 3.5.42",
   "/_nuxt/B5Sj-0NH.js: nuxtI18n version getter 10.0.1; Menu above sm 1023, MenuMobile at or below",
   "inline window.__NUXT__.config.public.sanity: projectId 535lnz3g, apiVersion 2025-08-08",
   "/_nuxt/DKtYPyz8.js: GreenSock, version 3.13.0 (core, ScrollTrigger, Observer); /_nuxt/CHE2-zLL.js: SplitText",
   "/_nuxt/Mqeh8a45.js: window.lenisVersion = '1.3.8'; @rive-app/canvas 2.31.6; '[BuroCore--utils] NaN in arguments.'; MetaRow, Navigation, Menu, MenuMobile components",
   "/_nuxt/DVps963u.js and /_nuxt/Images.worker-BIRhFE1x.js: __THREE__ = '181'; '🍀 BüroGL' errors; OffscreenCanvas",
   "/_nuxt/entry.D9Lu-YTe.css: @font-face Zimula-Variable.Cb2n2uX-.ttf",
   "probe: no svg filters, no CSS filter (the one backdrop-filter is blur(2px) on the wishlist counter's .bg), no view-transition CSS, three canvases without a context, fonts ['Zimula normal normal']",
   "probe scripts: Cookiebot (GTM implementation), GTM GTM-TTGLTQDL, GA4 G-CSPJEQXC07, Plausible, Umami, Meta Pixel, Meta CAPI param builder",
   "footer: 'Made by Büro' link to https://burocratik.com",
   "head: <link rel=alternate type=text/markdown href=https://floema.com/en/home.md>"
  ]
 },
 "components": [
  {
   "name": "Navigation pills with tissue (desktop nav)",
   "owner_words": "I really like the navigation that when you hover the way the little pills separate but have a little connecting, quote-unquote, tissue between them so they kind of look flexible and are responsive on hover, perhaps tap too. I really like that mechanism.",
   "selectors": [
    "[data-component=\"menu\"] .navigation",
    "[data-component=\"navigation\"]",
    ".meta-row-container",
    "svg[data-meta-row-shape]",
    "[data-meta-row-shape-bridges]",
    "[data-meta-row-rect=\"0..3\"]",
    "#glow",
    "a.button.link[data-meta-row-node]"
   ],
   "mechanism": "Drawn SVG, not a filter. Büro's MetaRow puts one absolutely positioned SVG behind the links: a rounded-rect path per link (r = 0.34 x height) and one path of metaball bridges between neighbours (Paper.js-style connector on the facing end-cap circles, spread 0.3 to 0.4, handle 1, detach distance 30). A ResizeObserver on the width:auto row re-measures the links and rewrites the paths every frame while CSS animates the hovered link's margin (0 20px; the last link only on its inner side; the first link takes both sides, because its :first-child rule never matches with the SVG as the row's first child); each link's ::before halo extends the hit area by 20 px so the hover fires before contact. Optional glow: a radial-gradient ellipse clipped to the skin, mix-blend-mode difference, tweened with GSAP and lerped at 0.12 to the pointer.",
   "libraries": [
    "none for the effect (CSS transitions plus about 60 lines of SVG path maths)",
    "GSAP 3.13.0 for the glow only",
    "Vue 3 / Nuxt 4 as the host"
   ],
   "states": [
    "rest: four links touching, 419.5 x 36.8 px strip, seam bridges 20.2 px tall leaving 8.3 px notches",
    "pointer 60 px or more away: unchanged",
    "pointer within 20 px of a link (halo): that link takes 20 px margins, strip grows 40 px to the left, necks 13.0 px tall at the middle of each 20 px gap",
    "hover first or last link: one gap (the first link's box still grows 40 px, its left 20 px empty)",
    "pointer over a link's right-hand 20 px: the next link opens (its halo lies on top)",
    "glide along the strip: the opening travels from link to link",
    "current page: permanent margins plus a 4 px dot",
    "theme dark, light or fluor from the section under the bar (fluor fills the skin #e9e778, 1 s fill transition)",
    "keyboard focus: no separation and no visible outline",
    "navigating: pointer events off, progress cursor",
    "1023 px and below: not rendered (MenuMobile instead)"
   ],
   "timings": "hover in: margin 0.5s cubic-bezier(.175,.885,.32,1.275), measured about 500 ms with a 3.4 px overshoot (8.5 %) near 330 ms; release: 0.3s cubic-bezier(.19,1,.22,1), measured about 250 ms; glow: GSAP 1 s expo.out to rx/ry 30 on enter, 60 x 30 while moving, back to 30 after 500 ms still, 0 on leave, centre lerp 0.12 per frame; skin fill 1s cubic-bezier(.19,1,.22,1)",
   "shots": [
    "shots/nav-states-sheet.png",
    "shots/nav-01-rest.png",
    "shots/nav-02-pointer-120px.png",
    "shots/nav-02-pointer-060px.png",
    "shots/nav-02-pointer-020px.png",
    "shots/nav-02-pointer-010px.png",
    "shots/nav-03-hover-1-products.png",
    "shots/nav-03-hover-2-about.png",
    "shots/nav-03-hover-3-sustainability.png",
    "shots/nav-03-hover-4-journal.png",
    "shots/nav-crop-rest-2x.png",
    "shots/nav-crop-pointer-020px-2x.png",
    "shots/nav-crop-hover-about-2x.png",
    "shots/nav-approach-sheet.png",
    "shots/nav-leave-sheet.png",
    "shots/nav-04-glide-end.png",
    "shots/page-06-about-landed.png",
    "shots/verify/verify-01-halo-overlap.png",
    "shots/verify/verify-02-keyboard-focus.png"
   ],
   "rebuild": "Row container (flex, gap 0, width auto, relative) with transparent items and one SVG behind them (absolute, full size, pointer-events none, overflow visible, filled with the skin colour) holding a path per item and a bridges path. ResizeObserver on the row: measure items relative to the row, set the viewBox, write rounded rects (r 0.34 h) and metaball bridges (v 0.3 to 0.4, handle 1, detach 30); redraw on fonts ready. CSS only for motion: ::before halo inset -20px; :hover and :focus-visible margin 0 20px (ends: inner side only, picked with :first-of-type/:last-of-type, since :first-child misses when the SVG comes first); in 0.5s cubic-bezier(.175,.885,.32,1.275), out 0.3s cubic-bezier(.19,1,.22,1). Add pointerdown pre-separation for touch, drop overshoot and glow under prefers-reduced-motion. For the ETS27 bar: nodes are the mark, the menu word, the contact word and the ask field; awake means the gaps are open.",
   "confidence": "high"
  },
  {
   "name": "The same skin on CTA buttons and the phone search field",
   "owner_words": null,
   "selectors": [
    "[data-component=\"special-button\"] .meta-row-container",
    ".icon-side",
    ".label-side",
    "[data-component=\"search-button\"].is-mobile-menu .meta-row-container"
   ],
   "mechanism": "Two-node MetaRow: a 44 x 44 icon square and a label pill touching at rest; on hover (inside @media (hover:hover)) the square takes a 16 px right margin and a neck 15.0 px tall spans the gap; on the phone the search input and its icon sit 8 px apart, joined by a still neck. On phones the CTAs keep the touching skin and never separate (their hover rules sit inside @media (hover:hover)).",
   "libraries": [
    "none beyond the MetaRow drawing"
   ],
   "states": [
    "rest: touching",
    "hover: 16 px gap with neck, icon scale 1.07",
    "phone search: fixed 8 px gap with neck",
    "phone CTA: touching, seam drawn, no separation"
   ],
   "timings": "margin 0.5s cubic-bezier(.175,.885,.32,1.275) in, 0.3s cubic-bezier(.19,1,.22,1) out; icon scale 0.5s cubic-bezier(.12,1.2,.16,2.35)",
   "shots": [
    "shots/cta-13-rest.png",
    "shots/cta-14-hover.png",
    "shots/cta-tissue-sheet.png",
    "shots/phone-search-pill-crop.png",
    "shots/phone-11-menu-open.png",
    "shots/verify/phone/verify-04-phone-cta.png"
   ],
   "rebuild": "Same recipe as the nav with two nodes; the ETS27 ask field plus its send button.",
   "confidence": "high"
  },
  {
   "name": "Home scattered images (image tunnel)",
   "owner_words": null,
   "selectors": [
    "header[data-component=\"intro\"] .floating-images .three-container canvas"
   ],
   "mechanism": "three.js r181 scene in a module Worker (OffscreenCanvas via BüroGL). About 46 textured planes; plane i at i x golden angle (137.5 deg) on a ring of radius 10 around the view axis, 2 units behind the previous; short side 1.5 to 2.0 units keeping aspect; fog 0 to 78 in the page colour 0xF3EFEA; camera fov 45 at z 5. Planes fly toward the viewer at 300 units/s slowing to 10 over 1.1 s (expo out), plus up to 40 units/s with intro scroll progress; recycled to the back after passing the camera. Perspective produces the scatter.",
   "libraries": [
    "three.js r181",
    "GSAP ScrollTrigger (progress into the worker)",
    "BüroGL (Büro in-house worker layer)"
   ],
   "states": [
    "arrival rush",
    "idle drift",
    "faster drift while scrolling the intro"
   ],
   "timings": "speed 300 to 10 units/s over 1.1 s, expo out (1 - 2^(-6t)); scroll adds up to 40 units/s",
   "shots": [
    "shots/nav-01-rest.png",
    "shots/home-07-settled.png",
    "shots/home-opening-sheet.png"
   ],
   "rebuild": "Any WebGL layer (three.js or OGL), ideally off the main thread with OffscreenCanvas; place images with the golden-angle ring rule and fog to the page colour; for ETS27 the same rule could scatter speakers or session cards around the stage.",
   "confidence": "high"
  }
 ],
 "transitions": [
  {
   "name": "Nav pill hover in",
   "from": "rest",
   "to": "pill open",
   "layers": [
    "hovered link margin 0 20px (CSS)",
    "SVG rects and bridges redrawn per frame (ResizeObserver)",
    "glow ellipse grows (GSAP)"
   ],
   "duration_ms": 500,
   "shots": [
    "shots/nav-approach-sheet.png",
    "shots/nav-burst-approach-00.png"
   ]
  },
  {
   "name": "Nav pill release",
   "from": "pill open",
   "to": "rest",
   "layers": [
    "margin back to 0 (CSS, expo out)",
    "SVG redrawn per frame",
    "glow shrinks to 0 over 1 s"
   ],
   "duration_ms": 250,
   "shots": [
    "shots/nav-leave-sheet.png"
   ]
  },
  {
   "name": "Page change through a nav pill (home to about)",
   "from": "https://floema.com/en",
   "to": "https://floema.com/en/about",
   "layers": [
    "clicked pill opens on the click's hover and gets the 4 px dot once the route is active (by 0.78 s)",
    "7 px flour-yellow progress bar along the top edge (Loading's bar for both passes; it restarts while the first pass fades; the Curtain stays at 0)",
    "old page slides down by the viewport height, accelerating (about 1.6 to 2.0 s in these frames), then is squashed to zero height below the fold and removed",
    "About hero rises from the bottom 2.5 to 3.4 s",
    "wordmark grey to black by 3.4 s",
    "side copy and card by 4.3 s"
   ],
   "duration_ms": 4300,
   "shots": [
    "shots/page-to-about-sheet.png",
    "shots/page-loading-bar-sheet.png",
    "shots/page-06-about-landed.png",
    "shots/verify/verify-03-about-landed.png"
   ]
  },
  {
   "name": "Home arrival",
   "from": "blank",
   "to": "home with tunnel",
   "layers": [
    "PreLoader ivory cover",
    "nav and title on an empty stage by 0.6 s",
    "image tunnel by 1.6 s",
    "title lines drop in (SplitText, 0.8 s circ.out, 0.1 s stagger, from source)"
   ],
   "duration_ms": 1600,
   "shots": [
    "shots/home-opening-sheet.png"
   ]
  },
  {
   "name": "Phone menu open",
   "from": "home",
   "to": "full-screen menu sheet",
   "layers": [
    "menu button out, dark close button in (0.2 s)",
    "sheet opacity 0.6 s",
    "search field up 12 px over 0.45 s",
    "links up 18 px over 0.5 s, 0.06 s stagger"
   ],
   "duration_ms": 1250,
   "shots": [
    "shots/phone-menu-sheet.png",
    "shots/phone-11-menu-open.png"
   ]
  }
 ],
 "not_confirmed": [
  "What emits the Loading bar's second run during one page change (both passes are Loading's bar and the Curtain stays at 0, per the verifier's per-frame record)",
  "Tap behaviour on touch screens wider than 1023 px (sticky separation predicted from CSS, not captured)",
  "Exact moment the hover starts relative to the pointer crossing the halo",
  "Whether the About hero (drawn over a .three-container WebGL canvas) is a ModelView model or a textured plane; what ShadowsPortal draws",
  "Content of the Markdown alternates (/en/home.md), not fetched",
  "Screen reader behaviour of the halo and the moving targets (the active pill does carry aria-current=page)",
  "The title's SplitText entrance (between burst frames; source only)",
  "Which element painted the arrival's blank ivory frame (most likely the PreLoader)"
 ]
}
