{"id":"02_wire_globe","name":"Wireframe Globe","width":600,"height":400,"cornerRadius":0,"deviceFrame":"none","fit":"Layout","responsive":{"enabled":true,"mode":"constraint","width":{"type":"fixed"},"height":{"type":"fixed"},"maintainAspectRatio":false,"scaleObjects":false,"breakpoints":[{"id":"bp_mobile","name":"Mobile","minWidth":0,"maxWidth":599},{"id":"bp_tablet","name":"Tablet","minWidth":600,"maxWidth":1023},{"id":"bp_desktop","name":"Desktop","minWidth":1024}]},"backgroundColor":"transparent","opacity":1,"x":-300,"y":-200,"stateMachine":{"inputs":[{"id":"headline","name":"headline","value":{"type":"Text","value":"Ship to 140 countries","defaultValue":"Ship to 140 countries"}},{"id":"sub","name":"sub","value":{"type":"Text","value":"Live routes, updated every minute.","defaultValue":"Live routes, updated every minute."}},{"id":"cta","name":"cta","value":{"type":"Text","value":"See coverage","defaultValue":"See coverage"}},{"id":"bgColor","name":"bgColor","value":{"type":"Color","value":"#070a14","defaultValue":"#070a14"}},{"id":"dotColor","name":"dotColor","value":{"type":"Color","value":"#5b7cff","defaultValue":"#5b7cff"}},{"id":"arcColor","name":"arcColor","value":{"type":"Color","value":"#7cf7d4","defaultValue":"#7cf7d4"}},{"id":"accent","name":"accent","value":{"type":"Color","value":"#ff5a36","defaultValue":"#ff5a36"}},{"id":"textColor","name":"textColor","value":{"type":"Color","value":"#eef1ff","defaultValue":"#eef1ff"}},{"id":"spin","name":"spin","value":{"type":"Number","value":1,"defaultValue":1,"min":0,"max":4}},{"id":"radius","name":"radius","value":{"type":"Number","value":28,"defaultValue":28,"min":0,"max":60}},{"id":"intensity","name":"intensity","value":{"type":"Number","value":1,"defaultValue":1,"min":0.3,"max":2}},{"id":"lift","name":"lift","value":{"type":"Number","value":8,"defaultValue":8,"min":0,"max":24}},{"id":"interactive","name":"interactive","value":{"type":"Boolean","value":true,"defaultValue":true}},{"id":"tilt","name":"tilt","value":{"type":"Number","value":0,"defaultValue":0,"min":0,"max":12}},{"id":"press","name":"press","value":{"type":"Number","value":1,"defaultValue":1,"min":0,"max":2}},{"id":"float","name":"float","value":{"type":"Number","value":0,"defaultValue":0,"min":0,"max":12}},{"id":"speed","name":"speed","value":{"type":"Number","value":1,"defaultValue":1,"min":0,"max":3}},{"id":"intro","name":"intro","value":{"type":"Boolean","value":true,"defaultValue":true}},{"id":"introStyle","name":"introStyle","value":{"type":"Text","value":"none","defaultValue":"none"}},{"id":"introDuration","name":"introDuration","value":{"type":"Number","value":1.2,"defaultValue":1.2,"min":0.2,"max":3}},{"id":"colorTween","name":"colorTween","value":{"type":"Number","value":0.6,"defaultValue":0.6,"min":0,"max":3}},{"id":"pageMode","name":"pageMode","value":{"type":"Text","value":"none","defaultValue":"none"}},{"id":"pageTop","name":"pageTop","value":{"type":"Color","value":"#f4f1ea","defaultValue":"#f4f1ea"}},{"id":"pageBottom","name":"pageBottom","value":{"type":"Color","value":"#ddd5c4","defaultValue":"#ddd5c4"}},{"id":"replay","name":"replay","value":{"type":"Trigger","value":false,"defaultValue":false}},{"id":"tap","name":"tap","value":{"type":"Trigger","value":false,"defaultValue":false}}],"layers":[{"id":"layer_entrance","name":"Entrance","entryStateId":"s_in_a","states":[{"id":"s_in_a","name":"Entrance","animationId":"a_entrance","loop":false,"transitions":[{"id":"tr_in_b","targetStateId":"s_in_b","duration":0,"conditions":[{"inputId":"replay","inputName":"replay","op":"IsTrue","value":true}]}]},{"id":"s_in_b","name":"Entrance (replay)","animationId":"a_entrance","loop":false,"transitions":[{"id":"tr_in_a","targetStateId":"s_in_a","duration":0,"conditions":[{"inputId":"replay","inputName":"replay","op":"IsTrue","value":true}]}]}]},{"id":"layer_ambient","name":"Ambient","entryStateId":"s_ambient","states":[{"id":"s_ambient","name":"Ambient float","animationId":"a_ambient","loop":true,"transitions":[]}]}],"logicNodes":[]},"physics":{"enabled":false,"gravity":{"x":0,"y":9.8},"wind":{"x":0,"y":0},"timeScale":1,"collideWithBounds":true,"joints":[]},"guides":{"horizontal":[],"vertical":[]},"animations":[{"id":"a_entrance","name":"Entrance","duration":1,"loop":false,"tracks":[{"id":"t_in_o","object_id":"02_wire_globe_stage","property":"transform.opacity","keyframes":[{"id":"k_o0","time":0,"value":0,"easing":"easeOutCubic"},{"id":"k_o1","time":0.55,"value":1}]},{"id":"t_in_sx","object_id":"02_wire_globe_stage","property":"transform.scale_x","keyframes":[{"id":"k_sx0","time":0,"value":0.92,"easing":"easeOutBack"},{"id":"k_sx1","time":0.9,"value":1}]},{"id":"t_in_sy","object_id":"02_wire_globe_stage","property":"transform.scale_y","keyframes":[{"id":"k_sy0","time":0,"value":0.92,"easing":"easeOutBack"},{"id":"k_sy1","time":0.9,"value":1}]}]},{"id":"a_ambient","name":"Ambient float","duration":6,"loop":true,"tracks":[{"id":"t_amb_y","object_id":"02_wire_globe_stage","property":"transform.y","keyframes":[{"id":"k_y0","time":0,"value":200,"easing":"easeInOutSine"},{"id":"k_y1","time":3,"value":195,"easing":"easeInOutSine"},{"id":"k_y2","time":6,"value":200}]}]}],"layoutProps":{"mode":"linear","direction":"vertical","gap":0,"padding":{"top":0,"right":0,"bottom":0,"left":0},"alignItems":"stretch","layoutSizingHorizontal":"fixed","layoutSizingVertical":"fixed"},"animationFlow":[],"navigation":[],"stateVariables":[],"lastModified":1793007576038,"logicEditorNodes":[],"logicEditorEdges":[],"stickyNotes":[],"objects":[{"id":"02_wire_globe_stage","name":"Wireframe Globe","type":"Group","transform":{"x":300,"y":200,"rotation":0,"scale_x":1,"scale_y":1,"opacity":1,"skew_x":0,"skew_y":0},"width":600,"height":400,"geometry":{"type":"Rectangle","width":600,"height":400,"cornerRadius":0},"cornerRadius":0,"children":[],"clipContent":true,"style":{"fill":{"type":"Solid","color":"#f4f1ea","opacity":0},"effects":[{"id":"fx","type":"CustomEffect","name":"wire-globe","isEnabled":true,"params":{"headline":"Ship to 140 countries","sub":"Live routes, updated every minute.","cta":"See coverage","bgColor":"#070a14","dotColor":"#5b7cff","arcColor":"#7cf7d4","accent":"#ff5a36","textColor":"#eef1ff","spin":1,"radius":28,"intensity":1,"lift":8,"interactive":true,"tilt":0,"press":1,"float":0,"speed":1,"intro":true,"introStyle":"none","introDuration":1.2,"colorTween":0.6,"pageMode":"none","pageTop":"#f4f1ea","pageBottom":"#ddd5c4"},"code":"export const name = \"Wireframe Globe\";\nexport const params = {\n  headline: { value: \"Ship to 140 countries\", label: \"Headline\" },\n  sub: { value: \"Live routes, updated every minute.\", label: \"Subline\" },\n  cta: { value: \"See coverage\", label: \"Button\" },\n  bgColor: { value: \"#070a14\", label: \"Panel\" },\n  dotColor: { value: \"#5b7cff\", label: \"Globe dots\" },\n  arcColor: { value: \"#7cf7d4\", label: \"Route arcs\" },\n  accent: { value: \"#ff5a36\", label: \"Accent\" },\n  textColor: { value: \"#eef1ff\", label: \"Text\" },\n  spin: { value: 1, min: 0, max: 4, step: 0.1, label: \"Spin speed\" },\n  radius: { value: 28, min: 0, max: 60, step: 1, label: \"Corner radius\" },\n  intensity: { value: 1, min: 0.3, max: 2, step: 0.1, label: \"Dot density\" },\n  lift: { value: 8, min: 0, max: 24, step: 1, label: \"Hover lift\" },\n  interactive: { value: true, label: \"Mouse interaction\" },\n  tilt: { value: 0, min: 0, max: 12, step: 0.5, label: \"Hover tilt (deg, 0 = off)\" },\n  press: { value: 1, min: 0, max: 2, step: 0.1, label: \"Press squish\" },\n  float: { value: 0, min: 0, max: 12, step: 0.5, label: \"Extra idle float (px)\" },\n  speed: { value: 1, min: 0, max: 3, step: 0.1, label: \"Animation speed\" },\n  intro: { value: true, label: \"Entrance animation\" },\n  introStyle: { value: \"none\", options: [\"none\", \"rise\", \"drop\", \"zoom\", \"swing\", \"slide\"], label: \"Extra entrance style\" },\n  introDuration: { value: 1.2, min: 0.2, max: 3, step: 0.1, label: \"Entrance duration (s)\" },\n  colorTween: { value: 0.6, min: 0, max: 3, step: 0.1, label: \"Colour change fade (s)\" },\n  pageMode: { value: \"none\", options: [\"solid\", \"gradient\", \"none\"], label: \"Page background\" },\n  pageTop: { value: \"#f4f1ea\", label: \"Page colour\" },\n  pageBottom: { value: \"#ddd5c4\", label: \"Page gradient end\" }\n};\nlet S = null;\nlet KS = null;\nfunction rr(ctx, x, y, w, h, r) {\n  r = Math.max(0, Math.min(r, w / 2, h / 2));\n  ctx.beginPath();\n  ctx.moveTo(x + r, y); ctx.lineTo(x + w - r, y); ctx.arcTo(x + w, y, x + w, y + r, r);\n  ctx.lineTo(x + w, y + h - r); ctx.arcTo(x + w, y + h, x + w - r, y + h, r);\n  ctx.lineTo(x + r, y + h); ctx.arcTo(x, y + h, x, y + h - r, r);\n  ctx.lineTo(x, y + r); ctx.arcTo(x, y, x + r, y, r); ctx.closePath();\n}\nfunction rnd(i) { const s = Math.sin(i * 127.1 + 311.7) * 43758.5453; return s - Math.floor(s); }\nfunction hx(c) {\n  c = String(c).trim();\n  if (c.charAt(0) === '#') {\n    let h = c.slice(1);\n    if (h.length === 3 || h.length === 4) h = h.split('').map(function (ch) { return ch + ch; }).join('');\n    const n = parseInt(h.slice(0, 6), 16) || 0;\n    return [(n >> 16) & 255, (n >> 8) & 255, n & 255];\n  }\n  const m = c.match(/[\\d.]+/g);\n  return m && m.length >= 3 ? [Number(m[0]), Number(m[1]), Number(m[2])] : [0, 0, 0];\n}\nfunction isCol(v) { return typeof v === 'string' && /^(#[0-9a-f]{3,8}|rgba?\\()/i.test(v.trim()); }\nfunction mix(a, b, k) { const A = hx(a), B = hx(b); const r = A.map(function (v, i) { return Math.round(v + (B[i] - v) * k); }); return 'rgb(' + r[0] + ',' + r[1] + ',' + r[2] + ')'; }\nfunction alpha(c, a) { const v = hx(c); return 'rgba(' + v[0] + ',' + v[1] + ',' + v[2] + ',' + a + ')'; }\nfunction ease(k) { return 1 - Math.pow(1 - k, 3); }\nfunction back(k) { const s = 1.7; k = k - 1; return 1 + k * k * ((s + 1) * k + s); }\nfunction clamp(v, a, b) { return Math.max(a, Math.min(b, v)); }\n/* a state machine input with the same name wins once it differs from the file default;\n   colours cross-fade and text changes replay the sheen */\nfunction cfg(env, defs) {\n  if (!KS) KS = { col: {}, txt: {}, sheen: -9 };\n  const out = {}, now = env.time;\n  for (const k in defs) {\n    const d0 = defs[k] && typeof defs[k] === 'object' && 'value' in defs[k] ? defs[k].value : defs[k];\n    let v = env.params ? env.params[k] : undefined;\n    if (v && typeof v === 'object' && 'value' in v) v = v.value;\n    const iv = env.inputs ? env.inputs[k] : undefined;\n    if (iv !== undefined && iv !== null && iv !== '' && String(iv) !== String(d0)) v = iv;\n    if (v === undefined || v === null || v === '') v = d0;\n    if (typeof d0 === 'number') { const n = Number(v); v = isNaN(n) ? d0 : n; }\n    else if (typeof d0 === 'boolean') v = v === true || v === 'true' || v === 1;\n    else v = String(v);\n    out[k] = v;\n  }\n  const tw = Math.max(0.01, out.colorTween === undefined ? 0.6 : out.colorTween);\n  for (const k in out) {\n    const v = out[k];\n    if (isCol(v)) {\n      let s = KS.col[k];\n      if (!s) s = KS.col[k] = { from: v, to: v, t0: -99 };\n      if (s.to !== v) { s.from = s.cur || s.to; s.to = v; s.t0 = now; }\n      const p = Math.min(1, (now - s.t0) / tw);\n      s.cur = p >= 1 ? v : mix(s.from, s.to, ease(p));\n      out[k] = s.cur;\n    } else if (typeof v === 'string' && k !== 'introStyle' && k !== 'pageMode') {\n      if (KS.txt[k] !== undefined && KS.txt[k] !== v) KS.sheen = now;\n      KS.txt[k] = v;\n    }\n  }\n  return out;\n}\n/* hover, tap (pointer or 'tap' trigger), entrance, numeric tweens; px/py are -1..1 inside the box */\nfunction stage(S, env, P, B) {\n  const now = env.time, inp = env.inputs || {};\n  if (S.t0 === undefined) { S.t0 = now; S.lift = 0; S.tilt = 0; S.tiltY = 0; S.press = 0; S.trg = {}; S.tw = {}; S.mx = 0; S.my = 0; }\n  const trig = function (n) { const v = !!inp[n]; const r = v && !S.trg[n]; S.trg[n] = v; return r; };\n  if (trig('replay')) S.t0 = now;\n  /* interactive off: the piece ignores the pointer (hover, tilt, parallax, press, click);\n     the tap and replay triggers still work from code */\n  const pt = P.interactive === false ? {} : (env.pointer || {});\n  const over = !!pt.isHovered && Math.abs(pt.x || 0) <= B[0] / 2 && Math.abs(pt.y || 0) <= B[1] / 2;\n  S.lift += ((over ? 1 : 0) - S.lift) * 0.12;\n  S.mx += ((over ? clamp((pt.x || 0) / (B[0] / 2), -1, 1) : 0) - S.mx) * 0.1;\n  S.my += ((over ? clamp((pt.y || 0) / (B[1] / 2), -1, 1) : 0) - S.my) * 0.1;\n  S.tilt = S.mx;\n  const down = !!pt.isDown;\n  const tap = (down && !S.wasDown && over) || trig('tap');\n  S.wasDown = down;\n  S.press += ((down && over ? 1 : 0) - S.press) * 0.3;\n  const k = P.intro && P.introStyle !== 'none' ? Math.min(1, Math.max(0, (now - S.t0) / Math.max(0.05, P.introDuration))) : 1;\n  const e = ease(k);\n  const L = S.lift * P.lift;\n  const fl = P.float * Math.sin(now * P.speed * Math.PI / 2);\n  const sk = Math.min(1, Math.max(0, (now - KS.sheen) / 0.9));\n  const tw = function (key) {\n    let s = S.tw[key];\n    if (!s) s = S.tw[key] = { f: P[key], t: P[key], t0: -99, v: P[key] };\n    if (s.t !== P[key]) { s.f = s.v; s.t = P[key]; s.t0 = now; }\n    s.v = s.f + (s.t - s.f) * ease(Math.min(1, (now - s.t0) / 0.9));\n    return s.v * Math.min(1, e * 1.04);\n  };\n  return { over: over, tap: tap, L: L, px: S.mx, py: S.my, rawX: pt.x || 0, rawY: pt.y || 0, k: k, e: e, sk: sk,\n    X: -B[0] / 2, T: -B[1] / 2 - L + fl, W: B[0], H: B[1], R: P.radius === undefined ? B[2] : P.radius, tw: tw, S: S };\n}\n/* page background, then entrance style, hover tilt and press squish */\nfunction begin(ctx, c, P) {\n  const pg = ctx.createLinearGradient(0, -260, 0, 260);\n  pg.addColorStop(0, P.pageTop); pg.addColorStop(1, P.pageMode === 'gradient' ? P.pageBottom : P.pageTop);\n  ctx.globalAlpha = P.pageMode === 'none' ? 0 : 1;\n  ctx.fillStyle = pg; ctx.fillRect(-400, -300, 800, 600);\n  ctx.globalAlpha = 1;\n  ctx.save();\n  const k = c.k, e = c.e, st = P.introStyle;\n  let dx = 0, dy = 0, s = 1, rot = 0;\n  if (st === 'rise') { dy = (1 - e) * 46; s = 0.92 + 0.08 * e; }\n  else if (st === 'drop') { dy = (back(k) - 1) * 90; }\n  else if (st === 'zoom') { s = 0.55 + 0.45 * back(k); }\n  else if (st === 'swing') { rot = (1 - back(k)) * 0.3; dy = (1 - e) * 20; }\n  else if (st === 'slide') { dx = (1 - back(k)) * 160; }\n  rot += c.S.tilt * P.tilt * Math.PI / 180;\n  s *= 1 - 0.03 * c.S.press * P.press;\n  ctx.translate(dx, dy); ctx.rotate(rot); ctx.scale(s, s);\n}\nfunction end(ctx, c, P) {\n  ctx.save(); rr(ctx, c.X, c.T, c.W, c.H, c.R); ctx.clip();\n  const q = c.k < 1 ? c.k : c.sk;\n  const sx = c.X - 120 + q * (c.W + 240);\n  const g = ctx.createLinearGradient(sx - 70, c.T, sx + 70, c.T + 140);\n  g.addColorStop(0, 'rgba(255,255,255,0)'); g.addColorStop(0.5, 'rgba(255,255,255,0.5)'); g.addColorStop(1, 'rgba(255,255,255,0)');\n  ctx.globalAlpha = q < 1 ? 0.7 * Math.sin(q * Math.PI) : 0;\n  ctx.fillStyle = g; ctx.fillRect(c.X, c.T, c.W, c.H);\n  ctx.globalAlpha = 1; ctx.restore();\n  ctx.restore();\n}\nfunction shadowOn(ctx, S, col) { ctx.shadowColor = col || 'rgba(10,12,30,0.35)'; ctx.shadowBlur = 34 + 22 * S.lift; ctx.shadowOffsetY = 18 + 10 * S.lift; }\nfunction shadowOff(ctx) { ctx.shadowColor = 'transparent'; ctx.shadowBlur = 0; ctx.shadowOffsetY = 0; }\nconst BOX = [560, 340, 28];\nconst CITIES = [[40.7, -74], [51.5, -0.1], [35.7, 139.7], [-33.9, 151.2], [1.35, 103.8], [-23.5, -46.6], [19.1, 72.9], [37.8, -122.4]];\nfunction proj(lat, lon, rot, tiltX, R) {\n  const la = lat * Math.PI / 180, lo = lon * Math.PI / 180 + rot;\n  let x = Math.cos(la) * Math.sin(lo), y = Math.sin(la), z = Math.cos(la) * Math.cos(lo);\n  const y2 = y * Math.cos(tiltX) - z * Math.sin(tiltX), z2 = y * Math.sin(tiltX) + z * Math.cos(tiltX);\n  return [x * R, 0 - y2 * R, z2];\n}\nexport function render(ctx, env) {\n  const P = cfg(env, params), t = env.time * P.speed;\n  if (!S) S = { route: 0, r0: 0 };\n  const c = stage(S, env, P, BOX), X = c.X, T = c.T, W = c.W, H = c.H;\n  begin(ctx, c, P);\n  if (c.tap) { S.route = (S.route + 1) % CITIES.length; S.r0 = env.time; }\n  shadowOn(ctx, S, 'rgba(5,10,40,0.5)');\n  rr(ctx, X, T, W, H, c.R); ctx.fillStyle = P.bgColor; ctx.fill(); shadowOff(ctx);\n  ctx.save(); rr(ctx, X, T, W, H, c.R); ctx.clip();\n  const cx = X + W - 160, cy = T + H / 2 + 10, R = 150;\n  const halo = ctx.createRadialGradient(cx, cy, R * 0.6, cx, cy, R * 1.5);\n  halo.addColorStop(0, alpha(P.dotColor, 0.25)); halo.addColorStop(1, alpha(P.dotColor, 0));\n  ctx.fillStyle = halo; ctx.fillRect(X, T, W, H);\n  const rot = t * 0.25 * P.spin + c.px * 0.6, tilt = 0.35 + c.py * 0.2;\n  /* dot sphere, front and back hemispheres batched into two paths */\n  const step = Math.max(4, Math.round(9 / P.intensity));\n  const front = new Path2D(), backp = new Path2D();\n  for (let la = -80; la <= 80; la += step) {\n    const n = Math.max(6, Math.round(360 / step * Math.cos(la * Math.PI / 180)));\n    for (let i = 0; i < n; i++) {\n      const p = proj(la, i * 360 / n, rot, tilt, R);\n      (p[2] > 0 ? front : backp).rect(cx + p[0] - 0.9, cy + p[1] - 0.9, 1.8, 1.8);\n    }\n  }\n  ctx.fillStyle = alpha(P.dotColor, 0.25); ctx.fill(backp);\n  ctx.fillStyle = P.dotColor; ctx.fill(front);\n  /* routes from the hub city, the active one drawing on */\n  const hub = proj(CITIES[0][0], CITIES[0][1], rot, tilt, R);\n  for (let i = 1; i < CITIES.length; i++) {\n    const p = proj(CITIES[i][0], CITIES[i][1], rot, tilt, R);\n    if (p[2] < -0.2 && hub[2] < -0.2) continue;\n    const mx = (hub[0] + p[0]) / 2, my = (hub[1] + p[1]) / 2, lift = 1.5;\n    const active = i === S.route || (S.route === 0 && i === 1);\n    const k = active ? Math.min(1, (env.time - S.r0) / 1.2) : 1;\n    ctx.strokeStyle = active ? P.accent : alpha(P.arcColor, Math.max(0.15, (Math.min(p[2], hub[2]) + 1) / 2));\n    ctx.lineWidth = active ? 2.2 : 1.2;\n    ctx.beginPath(); ctx.moveTo(cx + hub[0], cy + hub[1]);\n    const qx = cx + mx * lift, qy = cy + my * lift;\n    for (let s = 1; s <= 20; s++) {\n      const u = s / 20 * k, iu = 1 - u;\n      ctx.lineTo(iu * iu * (cx + hub[0]) + 2 * iu * u * qx + u * u * (cx + p[0]), iu * iu * (cy + hub[1]) + 2 * iu * u * qy + u * u * (cy + p[1]));\n    }\n    ctx.stroke();\n    if (p[2] > 0) { ctx.fillStyle = active ? P.accent : P.arcColor; ctx.beginPath(); ctx.arc(cx + p[0], cy + p[1], active ? 4 : 2.5, 0, Math.PI * 2); ctx.fill(); }\n  }\n  /* travelling pulse on the routes (2 s loop) */\n  const pu = (t / 2) % 1;\n  ctx.fillStyle = P.accent; ctx.beginPath(); ctx.arc(cx + hub[0], cy + hub[1], 4 + 10 * pu, 0, Math.PI * 2); ctx.globalAlpha = 1 - pu; ctx.fill(); ctx.globalAlpha = 1;\n  ctx.restore();\n  ctx.fillStyle = P.textColor; ctx.textAlign = 'left'; ctx.textBaseline = 'alphabetic';\n  ctx.font = '600 11px Menlo, monospace'; ctx.globalAlpha = 0.55; ctx.fillText('GLOBAL NETWORK', X + 34, T + 56); ctx.globalAlpha = 1;\n  ctx.font = '700 32px Avenir Next, Helvetica Neue, sans-serif';\n  const words = P.headline.split(' '), lines = [''];\n  for (let i = 0; i < words.length; i++) { const tryL = (lines[lines.length - 1] + ' ' + words[i]).trim(); if (ctx.measureText(tryL).width > 230 && lines[lines.length - 1]) lines.push(words[i]); else lines[lines.length - 1] = tryL; }\n  for (let i = 0; i < Math.min(3, lines.length); i++) ctx.fillText(lines[i], X + 34, T + 104 + i * 38);\n  ctx.font = '500 14px Avenir Next, Helvetica Neue, sans-serif'; ctx.globalAlpha = 0.65;\n  ctx.fillText(P.sub, X + 34, T + 120 + Math.min(3, lines.length) * 38); ctx.globalAlpha = 1;\n  rr(ctx, X + 34, T + H - 74, 150, 42, 21); ctx.fillStyle = P.accent; ctx.fill();\n  ctx.fillStyle = '#ffffff'; ctx.font = '700 14px Avenir Next, Helvetica Neue, sans-serif'; ctx.textAlign = 'center';\n  ctx.fillText(P.cta + ' →', X + 109, T + H - 48);\n  end(ctx, c, P);\n}\n"}]},"layoutProps":{"layoutSizingHorizontal":"fill","layoutSizingVertical":"fill"},"zIndex":1,"interactions":[],"isExpanded":true}]}