{"id":1553,"date":"2026-04-06T17:48:29","date_gmt":"2026-04-06T17:48:29","guid":{"rendered":"https:\/\/lorenzoscardicchio.com\/?p=1553"},"modified":"2026-04-06T20:39:59","modified_gmt":"2026-04-06T20:39:59","slug":"applied-nudge-theory","status":"publish","type":"post","link":"https:\/\/lorenzoscardicchio.com\/index.php\/2026\/04\/06\/applied-nudge-theory\/","title":{"rendered":"Applied Nudge Theory"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>The Nudge Pipeline<\/title>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Playfair+Display:wght@400;500;600&#038;family=Source+Serif+Pro:ital,wght@0,400;0,600;1,400&#038;family=Inter:wght@400;500;600;700&#038;display=swap\" rel=\"stylesheet\">\n<style>\n  body {\n    margin: 0;\n    background: #F5F1EA;\n  }\n\n  .embed-page {\n    --site-header-offset: 72px;\n    --font-display: 'Playfair Display', Georgia, serif;\n    --font-body: 'Source Serif Pro', Georgia, serif;\n    --font-ui: 'Inter', system-ui, sans-serif;\n    --color-dark: #291911;\n    --color-light: #F5F1EA;\n    --color-red: #BFA987;\n    --color-muted: #553A28;\n    --color-border: #D2CCC5;\n    --color-bg-alt: #E4DDD2;\n    --color-text: #6A4B35;\n    font-family: var(--font-body);\n    background: var(--color-light);\n    color: var(--color-muted);\n    line-height: 1.75;\n    -webkit-font-smoothing: antialiased;\n  }\n\n  .embed-page * {\n    box-sizing: border-box;\n  }\n\n  .embed-page .page-subnav-wrap {\n    position: sticky;\n    top: var(--site-header-offset);\n    z-index: 40;\n    padding: 10px 18px 0;\n    background: transparent;\n  }\n\n  .embed-page .page-subnav {\n    max-width: 1180px;\n    margin: 0 auto;\n    padding: 12px 24px;\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    gap: 20px;\n    border: 1px solid var(--color-border);\n    border-radius: 16px;\n    background: rgba(245, 241, 234, 0.92);\n    backdrop-filter: blur(12px);\n    -webkit-backdrop-filter: blur(12px);\n    box-shadow: 0 8px 24px rgba(41, 25, 17, 0.05);\n  }\n\n  .embed-page .page-subnav-left {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    font-family: var(--font-ui);\n    font-size: 11px;\n    font-weight: 700;\n    letter-spacing: 0.14em;\n    text-transform: uppercase;\n    color: var(--color-muted);\n    white-space: nowrap;\n  }\n\n  .embed-page .page-subnav-left .dot {\n    color: var(--color-red);\n    font-size: 10px;\n  }\n\n  .embed-page .page-subnav-left .read-time {\n    color: var(--color-dark);\n  }\n\n  .embed-page .page-subnav-links {\n    display: flex;\n    flex-wrap: wrap;\n    justify-content: flex-end;\n    gap: 18px;\n  }\n\n  .embed-page .page-subnav-links a {\n    font-family: var(--font-ui);\n    font-size: 11px;\n    font-weight: 700;\n    letter-spacing: 0.08em;\n    text-transform: uppercase;\n    text-decoration: none;\n    color: var(--color-muted);\n    opacity: 0.86;\n    transition: color 0.15s ease, opacity 0.15s ease;\n  }\n\n  .embed-page .page-subnav-links a:hover {\n    color: var(--color-dark);\n    opacity: 1;\n  }\n\n  .embed-page .hero-cover {\n    min-height: 80vh;\n    position: relative;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    padding: 76px 34px 56px;\n    overflow: hidden;\n    background:\n      radial-gradient(circle at 18% 20%, rgba(191,169,135,0.12), transparent 24%),\n      radial-gradient(circle at 82% 22%, rgba(191,169,135,0.10), transparent 22%),\n      radial-gradient(circle at 72% 78%, rgba(140,118,93,0.08), transparent 20%),\n      linear-gradient(180deg, #f7f3ec 0%, #f5f1ea 100%);\n  }\n\n  .embed-page .hero-shell {\n    position: relative;\n    z-index: 2;\n    max-width: 1120px;\n    width: 100%;\n    text-align: center;\n  }\n\n  .embed-page .hero-kicker {\n    display: inline-flex;\n    align-items: center;\n    gap: 14px;\n    font-family: var(--font-ui);\n    font-size: 12px;\n    font-weight: 700;\n    letter-spacing: 0.16em;\n    text-transform: uppercase;\n    color: var(--color-red);\n    margin-bottom: 34px;\n  }\n\n  .embed-page .hero-kicker .ornament {\n    font-size: 11px;\n    color: var(--color-red);\n  }\n\n  .embed-page .hero-title {\n    font-family: var(--font-display);\n    font-size: clamp(46px, 7vw, 92px);\n    font-weight: 400;\n    line-height: 0.98;\n    letter-spacing: -0.03em;\n    color: var(--color-dark);\n    max-width: 980px;\n    margin: 0 auto;\n    text-wrap: balance;\n  }\n\n  .embed-page .hero-title em {\n    font-style: italic;\n    font-weight: 300;\n    color: #7a6450;\n  }\n\n  .embed-page .hero-standfirst {\n    max-width: 760px;\n    margin: 34px auto 0;\n    font-size: 22px;\n    line-height: 1.72;\n    color: var(--color-text);\n  }\n\n  .embed-page .hero-meta {\n    margin-top: 34px;\n    display: inline-flex;\n    gap: 12px;\n    flex-wrap: wrap;\n    align-items: center;\n    justify-content: center;\n    font-family: var(--font-ui);\n    font-size: 11px;\n    font-weight: 600;\n    letter-spacing: 0.1em;\n    text-transform: uppercase;\n    color: rgba(85,58,40,0.72);\n  }\n\n  .embed-page .hero-meta span {\n    padding: 8px 12px;\n    border: 1px solid rgba(210,204,197,0.9);\n    border-radius: 999px;\n    background: rgba(255,255,255,0.35);\n  }\n\n  .embed-page .hero-paper {\n    position: absolute;\n    z-index: 1;\n    border: 1px solid rgba(210,204,197,0.9);\n    background: rgba(255,255,255,0.38);\n    box-shadow: 0 18px 40px rgba(41,25,17,0.06);\n    backdrop-filter: blur(4px);\n    -webkit-backdrop-filter: blur(4px);\n    pointer-events: none;\n  }\n\n  .embed-page .hero-paper::before {\n    content: '';\n    position: absolute;\n    inset: 12px;\n    border: 1px solid rgba(191,169,135,0.20);\n  }\n\n  .embed-page .hero-paper::after {\n    content: '';\n    position: absolute;\n    left: 18px;\n    right: 18px;\n    top: 24px;\n    height: 1px;\n    background: rgba(191,169,135,0.22);\n    box-shadow:\n      0 16px 0 rgba(191,169,135,0.18),\n      0 32px 0 rgba(191,169,135,0.14),\n      0 48px 0 rgba(191,169,135,0.10);\n  }\n\n  .embed-page .paper-a { width: 150px; height: 190px; left: 7%; top: 16%; transform: rotate(-7deg); }\n  .embed-page .paper-b { width: 132px; height: 164px; right: 8%; top: 14%; transform: rotate(6deg); }\n  .embed-page .paper-c { width: 180px; height: 116px; left: 10%; bottom: 16%; transform: rotate(4deg); }\n  .embed-page .paper-d { width: 165px; height: 110px; right: 10%; bottom: 18%; transform: rotate(-5deg); }\n\n  .embed-page .content {\n    max-width: 800px;\n    margin: 0 auto;\n    padding: 0 40px;\n  }\n\n  .embed-page .prose {\n    max-width: 640px;\n  }\n\n  .embed-page section {\n    padding: 120px 0;\n  }\n\n  .embed-page hr {\n    border: none;\n    height: 1px;\n    background: var(--color-border);\n    max-width: 800px;\n    margin: 0 auto;\n  }\n\n  .embed-page .eyebrow {\n    font-family: var(--font-ui);\n    font-size: 12px;\n    font-weight: 700;\n    letter-spacing: 0.15em;\n    text-transform: uppercase;\n    color: var(--color-red);\n    margin-bottom: 16px;\n  }\n\n  .embed-page h1 {\n    font-family: var(--font-display);\n    font-size: 48px;\n    font-weight: 400;\n    letter-spacing: -0.02em;\n    line-height: 1.15;\n    color: var(--color-dark);\n    margin: 0 0 28px;\n  }\n\n  .embed-page h2 {\n    font-family: var(--font-display);\n    font-size: 32px;\n    font-weight: 400;\n    letter-spacing: -0.02em;\n    line-height: 1.2;\n    color: var(--color-dark);\n    margin: 0 0 20px;\n  }\n\n  .embed-page h3 {\n    font-family: var(--font-display);\n    font-size: 22px;\n    font-weight: 500;\n    color: var(--color-dark);\n    margin: 0 0 12px;\n  }\n\n  .embed-page p {\n    margin: 0 0 20px;\n  }\n\n  .embed-page p:last-child {\n    margin-bottom: 0;\n  }\n\n  .embed-page .lead {\n    font-size: 20px;\n    line-height: 1.7;\n    color: var(--color-muted);\n  }\n\n  .embed-page strong {\n    color: var(--color-dark);\n    font-weight: 600;\n  }\n\n  .embed-page blockquote {\n    border-left: 6px solid var(--color-red);\n    padding: 20px 28px;\n    margin: 36px 0;\n    background: rgba(191,169,135,0.08);\n    border-radius: 0 8px 8px 0;\n  }\n\n  .embed-page blockquote p {\n    font-family: var(--font-body);\n    font-style: italic;\n    font-size: 20px;\n    color: var(--color-dark);\n    line-height: 1.6;\n    margin-bottom: 0;\n  }\n\n  .embed-page blockquote cite {\n    display: block;\n    margin-top: 12px;\n    font-family: var(--font-ui);\n    font-size: 12px;\n    font-style: normal;\n    font-weight: 600;\n    letter-spacing: 0.08em;\n    text-transform: uppercase;\n    color: var(--color-muted);\n  }\n\n  .embed-page .canvas-wrap {\n    margin: 48px 0;\n    position: relative;\n  }\n\n  .embed-page .canvas-wrap canvas {\n    width: 100%;\n    display: block;\n    cursor: crosshair;\n    border-radius: 10px;\n  }\n\n  .embed-page .canvas-hint {\n    font-family: var(--font-ui);\n    font-size: 12px;\n    color: var(--color-muted);\n    text-align: center;\n    margin-top: 10px;\n    opacity: 0.5;\n  }\n\n  .embed-page .stat-row {\n    display: flex;\n    gap: 48px;\n    margin: 48px 0;\n    flex-wrap: wrap;\n  }\n\n  .embed-page .stat {\n    flex: 1;\n    min-width: 160px;\n  }\n\n  .embed-page .stat-number {\n    font-family: var(--font-display);\n    font-size: 48px;\n    color: var(--color-red);\n    line-height: 1;\n    margin-bottom: 8px;\n  }\n\n  .embed-page .stat-label {\n    font-family: var(--font-ui);\n    font-size: 13px;\n    color: var(--color-muted);\n    line-height: 1.5;\n  }\n\n  .embed-page .stage-cards {\n    display: grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 20px;\n    margin: 48px 0;\n  }\n\n  .embed-page .stage-card {\n    border: 1px solid var(--color-border);\n    border-radius: 10px;\n    padding: 28px;\n    transition: border-color 0.2s ease, box-shadow 0.2s ease;\n    cursor: default;\n    background: rgba(255,255,255,0.28);\n  }\n\n  .embed-page .stage-card:hover {\n    border-color: var(--color-red);\n    box-shadow: 0 4px 20px rgba(191,169,135,0.15);\n  }\n\n  .embed-page .stage-num {\n    font-family: var(--font-ui);\n    font-size: 11px;\n    font-weight: 700;\n    letter-spacing: 0.12em;\n    text-transform: uppercase;\n    color: var(--color-red);\n    margin-bottom: 10px;\n  }\n\n  .embed-page .stage-card p {\n    font-size: 15px;\n    line-height: 1.65;\n    margin-bottom: 12px;\n  }\n\n  .embed-page .stage-tag {\n    display: inline-block;\n    font-family: var(--font-ui);\n    font-size: 11px;\n    font-weight: 500;\n    letter-spacing: 0.05em;\n    padding: 4px 10px;\n    border-radius: 4px;\n    background: var(--color-bg-alt);\n    color: var(--color-muted);\n    margin-right: 6px;\n  }\n\n  .embed-page .reveal {\n    opacity: 0;\n    transform: translateY(24px);\n    transition: opacity 0.6s ease, transform 0.6s ease;\n  }\n\n  .embed-page .reveal.visible {\n    opacity: 1;\n    transform: translateY(0);\n  }\n\n  .embed-page footer {\n    padding: 80px 0;\n    text-align: center;\n  }\n\n  .embed-page footer p {\n    font-family: var(--font-ui);\n    font-size: 13px;\n    color: var(--color-muted);\n    opacity: 0.5;\n  }\n\n  @media (max-width: 900px) {\n    .embed-page .paper-a,\n    .embed-page .paper-b,\n    .embed-page .paper-c,\n    .embed-page .paper-d {\n      opacity: 0.45;\n    }\n  }\n\n  @media (max-width: 860px) {\n    .embed-page {\n      --site-header-offset: 64px;\n    }\n\n    .embed-page .page-subnav {\n      padding: 12px 16px;\n      border-radius: 14px;\n      align-items: flex-start;\n      flex-direction: column;\n    }\n\n    .embed-page .page-subnav-links {\n      justify-content: flex-start;\n      gap: 12px 14px;\n    }\n  }\n\n  @media (max-width: 700px) {\n    .embed-page .content {\n      padding: 0 20px;\n    }\n\n    .embed-page section {\n      padding: 80px 0;\n    }\n\n    .embed-page .hero-cover {\n      min-height: auto;\n      padding: 64px 18px 50px;\n    }\n\n    .embed-page .hero-kicker {\n      margin-bottom: 24px;\n      font-size: 11px;\n    }\n\n    .embed-page .hero-title {\n      font-size: 42px;\n    }\n\n    .embed-page .hero-standfirst {\n      font-size: 19px;\n    }\n\n    .embed-page .hero-meta {\n      margin-top: 26px;\n    }\n\n    .embed-page h1 {\n      font-size: 34px;\n    }\n\n    .embed-page h2 {\n      font-size: 26px;\n    }\n\n    .embed-page .stage-cards {\n      grid-template-columns: 1fr;\n    }\n\n    .embed-page .stat-row {\n      gap: 32px;\n    }\n\n    .embed-page .paper-a,\n    .embed-page .paper-b,\n    .embed-page .paper-c,\n    .embed-page .paper-d {\n      width: 110px;\n      height: 120px;\n      opacity: 0.28;\n    }\n\n    .embed-page .paper-c,\n    .embed-page .paper-d {\n      display: none;\n    }\n  }\n<\/style>\n<\/head>\n<body>\n<div class=\"embed-page\">\n  <div class=\"page-subnav-wrap\">\n    <div class=\"page-subnav\">\n      <div class=\"page-subnav-left\">\n        <span>Read<\/span>\n        <span class=\"dot\">\u2022<\/span>\n        <span class=\"read-time\">7 min<\/span>\n      <\/div>\n      <div class=\"page-subnav-links\">\n        <a href=\"#pipeline\">Pipeline<\/a>\n        <a href=\"#stages\">Stages<\/a>\n        <a href=\"#philosophy\">Philosophy<\/a>\n        <a href=\"#signal\">Signal<\/a>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <section class=\"hero-cover\" id=\"top\">\n    <div class=\"hero-paper paper-a\" aria-hidden=\"true\"><\/div>\n    <div class=\"hero-paper paper-b\" aria-hidden=\"true\"><\/div>\n    <div class=\"hero-paper paper-c\" aria-hidden=\"true\"><\/div>\n    <div class=\"hero-paper paper-d\" aria-hidden=\"true\"><\/div>\n\n    <div class=\"hero-shell\">\n      <div class=\"hero-kicker\">\n        <span class=\"ornament\">\u2726<\/span>\n        Applied Nudge Theory\n        <span class=\"ornament\">\u2726<\/span>\n      <\/div>\n\n      <h1 class=\"hero-title\">How a signal <em>earns<\/em> the right to reach a person<\/h1>\n\n      <p class=\"hero-standfirst\">\n        Journey filters context, timing, values, and personality so the final\n        intervention can feel timely, specific, and true to the person receiving it.\n      <\/p>\n\n      <div class=\"hero-meta\">\n        <span>Context-aware<\/span>\n        <span>Values-mirrored<\/span>\n        <span>Personality-matched<\/span>\n        <span>One nudge delivered<\/span>\n      <\/div>\n    <\/div>\n  <\/section>\n\n  <hr>\n\n  <div class=\"content\">\n    <section id=\"pipeline\">\n      <div class=\"reveal\">\n        <p class=\"eyebrow\">The Pipeline<\/p>\n        <h2>Many candidates. One delivery.<\/h2>\n        <p class=\"prose\">The pipeline begins wide. Dozens of candidate nudges are generated from a person&#8217;s capture history, stated values, reflections, and observed patterns. Then the field starts to narrow. Context, timing, emotional fit, values alignment, and personality orientation all shape what survives to the end.<\/p>\n\n        <div class=\"canvas-wrap\">\n          <canvas id=\"funnel-canvas\"><\/canvas>\n          <p class=\"canvas-hint\">Move your cursor across the stages to see the filtering<\/p>\n        <\/div>\n\n        <p class=\"prose\">The particles above represent nudge candidates moving through Journey&#8217;s selection pipeline. Many enter on the left. Very few reach the far end. The strongest candidates keep their relevance through every gate and arrive with much more precision.<\/p>\n      <\/div>\n    <\/section>\n  <\/div>\n\n  <hr>\n\n  <div class=\"content\">\n    <section id=\"stages\">\n      <div class=\"reveal\">\n        <p class=\"eyebrow\">Filter Stages<\/p>\n        <h2>What each gate tests for.<\/h2>\n        <p class=\"prose\">Each stage asks a different question. Together they create a disciplined way to select one intervention that can actually land.<\/p>\n\n        <div class=\"stage-cards\">\n          <div class=\"stage-card\" data-stage=\"0\">\n            <div class=\"stage-num\">Stage 01<\/div>\n            <h3>Generate<\/h3>\n            <p>The raw pool. Candidate nudges drawn from the person&#8217;s captures, quotes library, active goals, and mirror material. The system casts wide at this point so it has enough range to evaluate.<\/p>\n            <span class=\"stage-tag\">30-50 candidates<\/span>\n            <span class=\"stage-tag\">Source material<\/span>\n          <\/div>\n\n          <div class=\"stage-card\" data-stage=\"1\">\n            <div class=\"stage-num\">Stage 02<\/div>\n            <h3>Context<\/h3>\n            <p>Filter by life situation. What happened recently? What are they navigating? The goal is to remove candidates that do not fit the lived reality of the moment.<\/p>\n            <span class=\"stage-tag\">~20 remain<\/span>\n            <span class=\"stage-tag\">Life awareness<\/span>\n          <\/div>\n\n          <div class=\"stage-card\" data-stage=\"2\">\n            <div class=\"stage-num\">Stage 03<\/div>\n            <h3>Timing<\/h3>\n            <p>Right message, wrong moment still misses. This stage filters by time of day, day of week, recency of similar nudges, and the rhythm of the person&#8217;s current life.<\/p>\n            <span class=\"stage-tag\">~12 remain<\/span>\n            <span class=\"stage-tag\">Temporal fit<\/span>\n          <\/div>\n\n          <div class=\"stage-card\" data-stage=\"3\">\n            <div class=\"stage-num\">Stage 04<\/div>\n            <h3>Values<\/h3>\n            <p>This stage checks resonance. The system looks for candidates that align with what the person has already said matters to them and how they naturally describe meaning.<\/p>\n            <span class=\"stage-tag\">~5 remain<\/span>\n            <span class=\"stage-tag\">Values mirroring<\/span>\n          <\/div>\n\n          <div class=\"stage-card\" data-stage=\"4\">\n            <div class=\"stage-num\">Stage 05<\/div>\n            <h3>Personality<\/h3>\n            <p>Orientation shapes reception. Some people respond to challenge. Others respond to permission, warmth, or clarity. This stage tunes the message to the person who will receive it.<\/p>\n            <span class=\"stage-tag\">1-2 remain<\/span>\n            <span class=\"stage-tag\">Orientation match<\/span>\n          <\/div>\n\n          <div class=\"stage-card\" data-stage=\"5\">\n            <div class=\"stage-num\">Stage 06<\/div>\n            <h3>Deliver<\/h3>\n            <p>One nudge survives. It is formatted for the channel, timed for the moment, and written in language that feels grounded in the person&#8217;s own world.<\/p>\n            <span class=\"stage-tag\">1 nudge<\/span>\n            <span class=\"stage-tag\">Final selection<\/span>\n          <\/div>\n        <\/div>\n      <\/div>\n    <\/section>\n  <\/div>\n\n  <hr>\n\n  <div class=\"content\">\n    <section id=\"philosophy\">\n      <div class=\"reveal\">\n        <p class=\"eyebrow\">Philosophy<\/p>\n        <h2>Interventions built from roots.<\/h2>\n        <p class=\"prose\">People are highly individual. The same message can create momentum for one person and fall flat for another. That is why the system starts with source material from the person&#8217;s own life.<\/p>\n        <p class=\"prose\">Captures, reflections, language patterns, and values create a model of the world they actually live in. That gives the intervention a much stronger base than a template alone can provide.<\/p>\n\n        <blockquote>\n          <p>Use what helps. Let go of the rest.<\/p>\n          <cite>Applied Nudge Theory<\/cite>\n        <\/blockquote>\n\n        <p class=\"prose\">When a nudge feels relevant early, the whole process becomes more sustainable. The person stays connected to the reflection, returns to the practice, and builds more trust in the system over time.<\/p>\n      <\/div>\n    <\/section>\n  <\/div>\n\n  <hr>\n\n  <div class=\"content\">\n    <section id=\"signal\">\n      <div class=\"reveal\">\n        <p class=\"eyebrow\">Signal and Noise<\/p>\n        <h2>The constraint is the product.<\/h2>\n        <p class=\"prose\">Move your cursor to feel the constraint tighten. As the filter narrows, candidates slow, compress, and many are eliminated. The point is focus. The system produces fewer interventions with stronger fit.<\/p>\n\n        <div class=\"canvas-wrap\">\n          <canvas id=\"constraint-canvas\"><\/canvas>\n          <p class=\"canvas-hint\">Drag your cursor left and right to move the constraint point<\/p>\n        <\/div>\n\n        <p class=\"prose\">Deep attention is central here. The pipeline is designed to surface what matters most and remove what is vague, noisy, or poorly timed. The remaining message carries much more weight.<\/p>\n      <\/div>\n    <\/section>\n  <\/div>\n\n  <hr>\n\n  <div class=\"content\">\n    <section>\n      <div class=\"reveal\">\n        <div class=\"stat-row\">\n          <div class=\"stat\">\n            <div class=\"stat-number\">50<\/div>\n            <div class=\"stat-label\">candidates generated per cycle<\/div>\n          <\/div>\n          <div class=\"stat\">\n            <div class=\"stat-number\">5<\/div>\n            <div class=\"stat-label\">major filter stages before delivery<\/div>\n          <\/div>\n          <div class=\"stat\">\n            <div class=\"stat-number\">1<\/div>\n            <div class=\"stat-label\">nudge reaches the person<\/div>\n          <\/div>\n        <\/div>\n      <\/div>\n    <\/section>\n  <\/div>\n\n  <hr>\n\n  <footer class=\"content\">\n    <div class=\"reveal\">\n      <p>Journey &middot; Applied Nudge Theory and Values Mirroring<\/p>\n    <\/div>\n  <\/footer>\n<\/div>\n\n<script>\nfunction hexToRgb(hex){\n  const m=\/^#?([a-f\\\\d]{2})([a-f\\\\d]{2})([a-f\\\\d]{2})$\/i.exec(hex);\n  return m?{r:parseInt(m[1],16),g:parseInt(m[2],16),b:parseInt(m[3],16)}:{r:100,g:100,b:100};\n}\nconst _colorCache={};\nfunction rgba(hex,alpha){\n  const key=hex+alpha;\n  if(_colorCache[key])return _colorCache[key];\n  const c=hexToRgb(hex);\n  const val=`rgba(${c.r},${c.g},${c.b},${alpha})`;\n  _colorCache[key]=val;\n  return val;\n}\nfunction ease(t){return t<0.5?2*t*t:-1+(4-2*t)*t;}\nfunction setupCanvas(canvas,width,height){\n  const dpr=window.devicePixelRatio||1;\n  canvas.width=width*dpr;\n  canvas.height=height*dpr;\n  canvas.style.width=width+'px';\n  canvas.style.height=height+'px';\n  const ctx=canvas.getContext('2d');\n  ctx.scale(dpr,dpr);\n  return ctx;\n}\n\nconst C={\n  accent:'#BFA987',\n  dark:'#291911',\n  muted:'#856B58',\n  border:'#D2CCC5',\n  success:'#6B8F5E',\n  light:'#F5F1EA',\n  eliminated:'#D2CCC5'\n};\nconst nudgeColors=['#A67C52','#6B8F5E','#8B6F4E','#B5935F','#7A6B5D'];\n\nfunction initFunnelPipeline(canvasId){\n  const canvas=document.getElementById(canvasId);\n  const W=canvas.parentElement.offsetWidth;\n  const H=340;\n  const ctx=setupCanvas(canvas,W,H);\n\n  const stages=[\n    {name:'GENERATE',survival:1.0},\n    {name:'CONTEXT',survival:0.45},\n    {name:'TIMING',survival:0.28},\n    {name:'VALUES',survival:0.12},\n    {name:'PERSONALITY',survival:0.04},\n    {name:'DELIVER',survival:0.02}\n  ];\n\n  const stageWidth=W\/stages.length;\n  let mouseX=-1,mouseY=-1;\n  let particles=[];\n  let spawnTimer=0;\n  let particleId=0;\n\n  canvas.addEventListener('mousemove',e=>{\n    const rect=canvas.getBoundingClientRect();\n    mouseX=(e.clientX-rect.left)\/rect.width*W;\n    mouseY=(e.clientY-rect.top)\/rect.height*H;\n  });\n  canvas.addEventListener('mouseleave',()=>{mouseX=-1;mouseY=-1;});\n\n  function funnelY(x,top){\n    const progress=x\/W;\n    const funnelHalf=130*(1-progress*0.88);\n    const center=H\/2;\n    return top?center-funnelHalf:center+funnelHalf;\n  }\n\n  function pickDeathStage(){\n    const r=Math.random();\n    if(r<0.35)return 1;\n    if(r<0.58)return 2;\n    if(r<0.78)return 3;\n    if(r<0.94)return 4;\n    return 6;\n  }\n\n  function spawnParticle(){\n    const spread=110;\n    const baseY=H\/2+(Math.random()-0.5)*spread*2;\n    particleId++;\n    return {\n      id:particleId,\n      x:-8-Math.random()*20,\n      y:baseY,\n      baseY:baseY,\n      vx:0.6+Math.random()*0.4,\n      radius:3+Math.random()*2,\n      color:nudgeColors[Math.floor(Math.random()*nudgeColors.length)],\n      opacity:0.75+Math.random()*0.25,\n      alive:true,\n      fadeOut:0,\n      deathStage:pickDeathStage(),\n      trail:[]\n    };\n  }\n\n  function getStageIndex(x){\n    return Math.min(Math.floor(x\/stageWidth),stages.length-1);\n  }\n\n  function frame(){\n    ctx.clearRect(0,0,W,H);\n\n    ctx.beginPath();\n    ctx.moveTo(0,funnelY(0,true));\n    for(let x=0;x<=W;x+=4)ctx.lineTo(x,funnelY(x,true));\n    ctx.lineTo(W,funnelY(W,false));\n    for(let x=W;x>=0;x-=4)ctx.lineTo(x,funnelY(x,false));\n    ctx.closePath();\n    ctx.fillStyle='rgba(191,169,135,0.06)';\n    ctx.fill();\n    ctx.strokeStyle=rgba(C.border,0.35);\n    ctx.lineWidth=1;\n    ctx.stroke();\n\n    stages.forEach((s,i)=>{\n      const x=i*stageWidth;\n      if(i>0){\n        ctx.beginPath();\n        ctx.setLineDash([4,6]);\n        ctx.moveTo(x,funnelY(x,true)-10);\n        ctx.lineTo(x,funnelY(x,false)+10);\n        ctx.strokeStyle=rgba(C.border,0.4);\n        ctx.lineWidth=1;\n        ctx.stroke();\n        ctx.setLineDash([]);\n      }\n\n      const isHovered=mouseX>0&&mouseX>=x&&mouseX<x+stageWidth;\n      ctx.font=`${isHovered?'700':'600'} ${isHovered?'11':'10'}px Inter, system-ui, sans-serif`;\n      ctx.textAlign='center';\n      ctx.fillStyle=isHovered?C.accent:rgba(C.muted,0.4);\n      ctx.fillText(s.name,x+stageWidth\/2,24);\n\n      if(isHovered){\n        ctx.beginPath();\n        ctx.rect(x+2,32,stageWidth-4,H-64);\n        ctx.fillStyle=rgba(C.accent,0.015);\n        ctx.fill();\n      }\n    });\n\n    spawnTimer+=16;\n    if(spawnTimer>120+Math.random()*80){\n      particles.push(spawnParticle());\n      spawnTimer=0;\n    }\n\n    particles.forEach(p=>{\n      if(!p.alive&&p.fadeOut<=0)return;\n\n      const stageIdx=getStageIndex(p.x);\n      p.trail.push({x:p.x,y:p.y});\n      if(p.trail.length>6)p.trail.shift();\n\n      if(p.alive){\n        if(stageIdx>=p.deathStage&&p.deathStage<6){\n          p.alive=false;\n          p.fadeOut=1.0;\n        }\n\n        const funnelTop=funnelY(p.x,true);\n        const funnelBot=funnelY(p.x,false);\n        const funnelCenter=(funnelTop+funnelBot)\/2;\n        const funnelRange=(funnelBot-funnelTop)\/2;\n        const targetY=funnelCenter+((p.baseY-H\/2)\/130)*funnelRange*0.8;\n        p.y+=(targetY-p.y)*0.04;\n        p.x+=p.vx;\n\n        for(let si=1;si<stages.length;si++){\n          const bx=si*stageWidth;\n          const dist=Math.abs(p.x-bx);\n          if(dist<30)p.x-=p.vx*0.3*(1-dist\/30);\n        }\n      } else {\n        p.y+=0.8;\n        p.x+=p.vx*0.2;\n        p.fadeOut-=0.018;\n      }\n\n      if(mouseX>0){\n        const dx=p.x-mouseX;\n        const dy=p.y-mouseY;\n        const dist=Math.sqrt(dx*dx+dy*dy);\n        if(dist<100&#038;&#038;dist>0){\n          const force=(1-dist\/100)*10;\n          p.x+=(dx\/dist)*force*0.06;\n          p.y+=(dy\/dist)*force*0.06;\n        }\n      }\n\n      const drawOpacity=p.alive?p.opacity:Math.max(0,p.fadeOut*0.4);\n      const drawColor=p.alive?p.color:C.eliminated;\n\n      p.trail.forEach((tp,i)=>{\n        const alpha=(i\/p.trail.length)*0.15*(p.alive?1:p.fadeOut);\n        ctx.beginPath();\n        ctx.arc(tp.x,tp.y,p.radius*0.5,0,Math.PI*2);\n        ctx.fillStyle=rgba(drawColor,alpha);\n        ctx.fill();\n      });\n\n      if(drawOpacity>0.1){\n        ctx.beginPath();\n        ctx.arc(p.x,p.y,p.radius*3,0,Math.PI*2);\n        ctx.fillStyle=rgba(drawColor,0.06*drawOpacity);\n        ctx.fill();\n      }\n\n      ctx.beginPath();\n      ctx.arc(p.x,p.y,p.radius*(p.alive?1:0.7),0,Math.PI*2);\n      ctx.fillStyle=rgba(drawColor,drawOpacity);\n      ctx.fill();\n\n      if(p.alive&&p.x>W*0.85&&p.deathStage>=6){\n        ctx.beginPath();\n        ctx.arc(p.x,p.y,p.radius+5,0,Math.PI*2);\n        ctx.strokeStyle=rgba(C.success,0.4);\n        ctx.lineWidth=1.5;\n        ctx.stroke();\n\n        ctx.beginPath();\n        ctx.arc(p.x,p.y,p.radius+12,0,Math.PI*2);\n        ctx.fillStyle=rgba(C.success,0.05);\n        ctx.fill();\n      }\n    });\n\n    particles=particles.filter(p=>{\n      if(p.x>W+30)return false;\n      if(!p.alive&&p.fadeOut<=0)return false;\n      if(p.y>H+30)return false;\n      return true;\n    });\n    if(particles.length>80)particles=particles.slice(-80);\n\n    requestAnimationFrame(frame);\n  }\n  requestAnimationFrame(frame);\n}\n\nfunction initConstraintField(canvasId){\n  const canvas=document.getElementById(canvasId);\n  const W=canvas.parentElement.offsetWidth;\n  const H=260;\n  const ctx=setupCanvas(canvas,W,H);\n\n  const labels=['RAW POOL','CONTEXT','TIMING','VALUES','PERSONALITY'];\n  let particles=[];\n  let mouseX=-1;\n  let lastSpawn=0;\n  let autoCinch={x:W*0.65,phase:'free',timer:0};\n\n  canvas.addEventListener('mousemove',e=>{\n    const rect=canvas.getBoundingClientRect();\n    mouseX=(e.clientX-rect.left)\/rect.width*W;\n  });\n  canvas.addEventListener('mouseleave',()=>{mouseX=-1;});\n\n  function spawnParticle(){\n    return {\n      x:-5,\n      y:H\/2+(Math.random()-0.5)*90,\n      baseY:H\/2+(Math.random()-0.5)*90,\n      speed:1.0+Math.random()*0.4,\n      radius:3+Math.random()*1.5,\n      opacity:0.6+Math.random()*0.3,\n      color:nudgeColors[Math.floor(Math.random()*nudgeColors.length)]\n    };\n  }\n\n  function frame(t){\n    ctx.clearRect(0,0,W,H);\n\n    let cinchX;\n    if(mouseX>0){\n      cinchX=mouseX;\n    } else {\n      autoCinch.timer+=16;\n      if(autoCinch.phase==='free'&&autoCinch.timer>800){autoCinch.phase='cinching';autoCinch.timer=0;}\n      else if(autoCinch.phase==='cinching'&&autoCinch.timer>600){autoCinch.phase='holding';autoCinch.timer=0;}\n      else if(autoCinch.phase==='holding'&&autoCinch.timer>3500){autoCinch.phase='releasing';autoCinch.timer=0;}\n      else if(autoCinch.phase==='releasing'&&autoCinch.timer>450){autoCinch.phase='free';autoCinch.timer=0;autoCinch.x=W*(0.3+Math.random()*0.5);}\n      cinchX=autoCinch.x;\n    }\n\n    const cinchIntensity=(autoCinch.phase==='holding'||mouseX>0)?0.8:\n      autoCinch.phase==='cinching'?ease(autoCinch.timer\/600)*0.8:\n      autoCinch.phase==='releasing'?0.8*(1-ease(autoCinch.timer\/450)):0;\n\n    const narrowing=cinchIntensity*(H\/2-20);\n\n    ctx.beginPath();\n    ctx.moveTo(0,30);\n    ctx.quadraticCurveTo(cinchX,30+narrowing,W,30);\n    ctx.lineTo(W,H-30);\n    ctx.quadraticCurveTo(cinchX,H-30-narrowing,0,H-30);\n    ctx.closePath();\n    ctx.fillStyle='rgba(191,169,135,0.05)';\n    ctx.fill();\n    ctx.strokeStyle=rgba(C.border,0.4);\n    ctx.lineWidth=1;\n    ctx.stroke();\n\n    if(cinchIntensity>0.1){\n      ctx.beginPath();\n      ctx.setLineDash([3,5]);\n      ctx.moveTo(cinchX,30+narrowing*0.3);\n      ctx.lineTo(cinchX,H-30-narrowing*0.3);\n      ctx.strokeStyle=rgba(C.accent,0.25*cinchIntensity);\n      ctx.lineWidth=1;\n      ctx.stroke();\n      ctx.setLineDash([]);\n    }\n\n    const lw=W\/labels.length;\n    ctx.font='600 10px Inter, system-ui, sans-serif';\n    ctx.textAlign='center';\n    labels.forEach((l,i)=>{\n      ctx.fillStyle=rgba(C.muted,0.35);\n      ctx.fillText(l,lw*i+lw\/2,18);\n    });\n\n    if(t-lastSpawn>180+Math.random()*120){\n      particles.push(spawnParticle());\n      lastSpawn=t;\n    }\n\n    particles.forEach(p=>{\n      const distFromCinch=Math.abs(p.x-cinchX);\n      const inCinchZone=distFromCinch<70;\n      const speed=inCinchZone?p.speed*(0.08+0.92*(1-cinchIntensity)):p.speed;\n      p.x+=speed;\n\n      if(inCinchZone&#038;&#038;cinchIntensity>0){\n        const squeeze=1-cinchIntensity*(1-distFromCinch\/70)*0.75;\n        p.y=H\/2+(p.baseY-H\/2)*squeeze;\n      } else {\n        p.y+=(p.baseY-p.y)*0.05;\n      }\n\n      const gapHalf=(H\/2-30)-narrowing*0.95;\n      if(inCinchZone&&cinchIntensity>0.5&&Math.abs(p.y-H\/2)>gapHalf&&distFromCinch<20){\n        p.opacity-=0.04;\n      }\n\n      if(p.opacity<=0)return;\n\n      ctx.beginPath();\n      ctx.arc(p.x,p.y,p.radius,0,Math.PI*2);\n      ctx.fillStyle=rgba(p.color,p.opacity);\n      ctx.fill();\n\n      ctx.beginPath();\n      ctx.arc(p.x,p.y,p.radius*2.5,0,Math.PI*2);\n      ctx.fillStyle=rgba(p.color,0.06*p.opacity);\n      ctx.fill();\n    });\n\n    particles=particles.filter(p=>p.x<W+10&#038;&#038;p.opacity>0);\n    requestAnimationFrame(frame);\n  }\n  requestAnimationFrame(frame);\n}\n\nconst observer=new IntersectionObserver(entries=>{\n  entries.forEach(e=>{if(e.isIntersecting)e.target.classList.add('visible');});\n},{threshold:0.1,rootMargin:'0px 0px -40px 0px'});\ndocument.querySelectorAll('.embed-page .reveal').forEach(el=>observer.observe(el));\n\ndocument.addEventListener('DOMContentLoaded',()=>{\n  initFunnelPipeline('funnel-canvas');\n  initConstraintField('constraint-canvas');\n});\n\nlet resizeTimer;\nwindow.addEventListener('resize',()=>{\n  clearTimeout(resizeTimer);\n  resizeTimer=setTimeout(()=>{\n    initFunnelPipeline('funnel-canvas');\n    initConstraintField('constraint-canvas');\n  },300);\n});\n\ndocument.querySelectorAll('.embed-page .page-subnav-links a[href^=\"#\"]').forEach(link=>{\n  link.addEventListener('click',e=>{\n    e.preventDefault();\n    const target=document.querySelector(link.getAttribute('href'));\n    if(!target)return;\n    const siteOffset=parseInt(getComputedStyle(document.querySelector('.embed-page')).getPropertyValue('--site-header-offset').trim(),10)||72;\n    const y=target.getBoundingClientRect().top+window.pageYOffset-siteOffset-24;\n    window.scrollTo({top:y,behavior:'smooth'});\n  });\n});\n<\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>The Nudge Pipeline Read \u2022 7 min Pipeline Stages Philosophy Signal \u2726 Applied Nudge Theory \u2726 How a signal earns the right to reach a person Journey filters context, timing, values, and personality so the final intervention can feel timely, specific, and true to the person receiving it. Context-aware Values-mirrored Personality-matched One nudge delivered The [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"off","_et_pb_old_content":"<!-- wp:html -->\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>The Nudge Pipeline<\/title>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Playfair+Display:wght@400;500;600&family=Source+Serif+Pro:ital,wght@0,400;0,600;1,400&family=Inter:wght@400;500;600;700&display=swap\" rel=\"stylesheet\">\n<style>\n  :root {\n    --font-display: 'Playfair Display', Georgia, serif;\n    --font-body: 'Source Serif Pro', Georgia, serif;\n    --font-ui: 'Inter', system-ui, sans-serif;\n    --color-dark: #291911;\n    --color-light: #F5F1EA;\n    --color-red: #BFA987;\n    --color-muted: #553A28;\n    --color-border: #D2CCC5;\n    --color-bg-alt: #E4DDD2;\n    --color-text: #6A4B35;\n  }\n\n  *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }\n\n  html { scroll-behavior: smooth; }\n\n  body {\n    font-family: var(--font-body);\n    background: var(--color-light);\n    color: var(--color-muted);\n    line-height: 1.75;\n    -webkit-font-smoothing: antialiased;\n  }\n\n  nav {\n    position: fixed;\n    top: 0;\n    left: 0;\n    right: 0;\n    z-index: 100;\n    padding: 16px 40px;\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    background: rgba(245, 241, 234, 0.84);\n    backdrop-filter: blur(14px);\n    -webkit-backdrop-filter: blur(14px);\n    border-bottom: 1px solid transparent;\n    transition: border-color 0.3s ease, background 0.3s ease;\n  }\n\n  nav.scrolled { border-bottom-color: var(--color-border); }\n\n  .nav-left {\n    display: flex;\n    align-items: center;\n    gap: 12px;\n    font-family: var(--font-ui);\n    font-size: 11px;\n    font-weight: 700;\n    letter-spacing: 0.14em;\n    text-transform: uppercase;\n    color: var(--color-muted);\n    white-space: nowrap;\n  }\n\n  .nav-left .dot {\n    color: var(--color-red);\n    font-size: 10px;\n  }\n\n  .nav-left .read-time {\n    color: var(--color-dark);\n  }\n\n  nav .nav-links { display: flex; gap: 28px; }\n\n  nav .nav-links a {\n    font-family: var(--font-ui);\n    font-size: 12px;\n    font-weight: 600;\n    letter-spacing: 0.08em;\n    text-transform: uppercase;\n    color: var(--color-muted);\n    text-decoration: none;\n    transition: color 0.15s ease;\n  }\n\n  nav .nav-links a:hover { color: var(--color-dark); }\n\n  .hero-cover {\n    min-height: 80vh;\n    position: relative;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    padding: 148px 34px 56px;\n    overflow: hidden;\n    background:\n      radial-gradient(circle at 18% 20%, rgba(191,169,135,0.12), transparent 24%),\n      radial-gradient(circle at 82% 22%, rgba(191,169,135,0.10), transparent 22%),\n      radial-gradient(circle at 72% 78%, rgba(140,118,93,0.08), transparent 20%),\n      linear-gradient(180deg, #f7f3ec 0%, #f5f1ea 100%);\n  }\n\n  .hero-shell {\n    position: relative;\n    z-index: 2;\n    max-width: 1120px;\n    width: 100%;\n    text-align: center;\n  }\n\n  .hero-kicker {\n    display: inline-flex;\n    align-items: center;\n    gap: 14px;\n    font-family: var(--font-ui);\n    font-size: 12px;\n    font-weight: 700;\n    letter-spacing: 0.16em;\n    text-transform: uppercase;\n    color: var(--color-red);\n    margin-bottom: 34px;\n  }\n\n  .hero-kicker .ornament {\n    font-size: 11px;\n    color: var(--color-red);\n  }\n\n  .hero-title {\n    font-family: var(--font-display);\n    font-size: clamp(46px, 7vw, 92px);\n    font-weight: 400;\n    line-height: 0.98;\n    letter-spacing: -0.03em;\n    color: var(--color-dark);\n    max-width: 980px;\n    margin: 0 auto;\n    text-wrap: balance;\n  }\n\n  .hero-title em {\n    font-style: italic;\n    font-weight: 300;\n    color: #7a6450;\n  }\n\n  .hero-standfirst {\n    max-width: 760px;\n    margin: 34px auto 0;\n    font-size: 22px;\n    line-height: 1.72;\n    color: var(--color-text);\n  }\n\n  .hero-meta {\n    margin-top: 34px;\n    display: inline-flex;\n    gap: 12px;\n    flex-wrap: wrap;\n    align-items: center;\n    justify-content: center;\n    font-family: var(--font-ui);\n    font-size: 11px;\n    font-weight: 600;\n    letter-spacing: 0.1em;\n    text-transform: uppercase;\n    color: rgba(85,58,40,0.72);\n  }\n\n  .hero-meta span {\n    padding: 8px 12px;\n    border: 1px solid rgba(210,204,197,0.9);\n    border-radius: 999px;\n    background: rgba(255,255,255,0.35);\n  }\n\n  .hero-paper {\n    position: absolute;\n    z-index: 1;\n    border: 1px solid rgba(210,204,197,0.9);\n    background: rgba(255,255,255,0.38);\n    box-shadow: 0 18px 40px rgba(41,25,17,0.06);\n    backdrop-filter: blur(4px);\n    -webkit-backdrop-filter: blur(4px);\n    pointer-events: none;\n  }\n\n  .hero-paper::before {\n    content: '';\n    position: absolute;\n    inset: 12px;\n    border: 1px solid rgba(191,169,135,0.20);\n  }\n\n  .hero-paper::after {\n    content: '';\n    position: absolute;\n    left: 18px;\n    right: 18px;\n    top: 24px;\n    height: 1px;\n    background: rgba(191,169,135,0.22);\n    box-shadow:\n      0 16px 0 rgba(191,169,135,0.18),\n      0 32px 0 rgba(191,169,135,0.14),\n      0 48px 0 rgba(191,169,135,0.10);\n  }\n\n  .paper-a {\n    width: 150px;\n    height: 190px;\n    left: 7%;\n    top: 16%;\n    transform: rotate(-7deg);\n  }\n\n  .paper-b {\n    width: 132px;\n    height: 164px;\n    right: 8%;\n    top: 14%;\n    transform: rotate(6deg);\n  }\n\n  .paper-c {\n    width: 180px;\n    height: 116px;\n    left: 10%;\n    bottom: 16%;\n    transform: rotate(4deg);\n  }\n\n  .paper-d {\n    width: 165px;\n    height: 110px;\n    right: 10%;\n    bottom: 18%;\n    transform: rotate(-5deg);\n  }\n\n  .content { max-width: 800px; margin: 0 auto; padding: 0 40px; }\n  .prose { max-width: 640px; }\n\n  section { padding: 120px 0; }\n\n  hr {\n    border: none;\n    height: 1px;\n    background: var(--color-border);\n    max-width: 800px;\n    margin: 0 auto;\n  }\n\n  .eyebrow {\n    font-family: var(--font-ui);\n    font-size: 12px;\n    font-weight: 700;\n    letter-spacing: 0.15em;\n    text-transform: uppercase;\n    color: var(--color-red);\n    margin-bottom: 16px;\n  }\n\n  h1 {\n    font-family: var(--font-display);\n    font-size: 48px;\n    font-weight: 400;\n    letter-spacing: -0.02em;\n    line-height: 1.15;\n    color: var(--color-dark);\n    margin-bottom: 28px;\n  }\n\n  h2 {\n    font-family: var(--font-display);\n    font-size: 32px;\n    font-weight: 400;\n    letter-spacing: -0.02em;\n    line-height: 1.2;\n    color: var(--color-dark);\n    margin-bottom: 20px;\n  }\n\n  h3 {\n    font-family: var(--font-display);\n    font-size: 22px;\n    font-weight: 500;\n    color: var(--color-dark);\n    margin-bottom: 12px;\n  }\n\n  p { margin-bottom: 20px; }\n  p:last-child { margin-bottom: 0; }\n\n  .lead {\n    font-size: 20px;\n    line-height: 1.7;\n    color: var(--color-muted);\n  }\n\n  strong { color: var(--color-dark); font-weight: 600; }\n\n  blockquote {\n    border-left: 6px solid var(--color-red);\n    padding: 20px 28px;\n    margin: 36px 0;\n    background: rgba(191, 169, 135, 0.08);\n    border-radius: 0 8px 8px 0;\n  }\n\n  blockquote p {\n    font-family: var(--font-body);\n    font-style: italic;\n    font-size: 20px;\n    color: var(--color-dark);\n    line-height: 1.6;\n  }\n\n  blockquote cite {\n    display: block;\n    margin-top: 12px;\n    font-family: var(--font-ui);\n    font-size: 12px;\n    font-style: normal;\n    font-weight: 600;\n    letter-spacing: 0.08em;\n    text-transform: uppercase;\n    color: var(--color-muted);\n  }\n\n  .canvas-wrap {\n    margin: 48px 0;\n    position: relative;\n  }\n\n  .canvas-wrap canvas {\n    width: 100%;\n    display: block;\n    cursor: crosshair;\n    border-radius: 10px;\n  }\n\n  .canvas-hint {\n    font-family: var(--font-ui);\n    font-size: 12px;\n    color: var(--color-muted);\n    text-align: center;\n    margin-top: 10px;\n    opacity: 0.5;\n  }\n\n  .stat-row {\n    display: flex;\n    gap: 48px;\n    margin: 48px 0;\n    flex-wrap: wrap;\n  }\n\n  .stat { flex: 1; min-width: 160px; }\n\n  .stat-number {\n    font-family: var(--font-display);\n    font-size: 48px;\n    color: var(--color-red);\n    line-height: 1;\n    margin-bottom: 8px;\n  }\n\n  .stat-label {\n    font-family: var(--font-ui);\n    font-size: 13px;\n    color: var(--color-muted);\n    line-height: 1.5;\n  }\n\n  .stage-cards {\n    display: grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 20px;\n    margin: 48px 0;\n  }\n\n  .stage-card {\n    border: 1px solid var(--color-border);\n    border-radius: 10px;\n    padding: 28px;\n    transition: border-color 0.2s ease, box-shadow 0.2s ease;\n    cursor: default;\n    background: rgba(255,255,255,0.28);\n  }\n\n  .stage-card:hover {\n    border-color: var(--color-red);\n    box-shadow: 0 4px 20px rgba(191, 169, 135, 0.15);\n  }\n\n  .stage-card .stage-num {\n    font-family: var(--font-ui);\n    font-size: 11px;\n    font-weight: 700;\n    letter-spacing: 0.12em;\n    text-transform: uppercase;\n    color: var(--color-red);\n    margin-bottom: 10px;\n  }\n\n  .stage-card h3 { margin-bottom: 8px; }\n\n  .stage-card p {\n    font-size: 15px;\n    line-height: 1.65;\n    margin-bottom: 12px;\n  }\n\n  .stage-tag {\n    display: inline-block;\n    font-family: var(--font-ui);\n    font-size: 11px;\n    font-weight: 500;\n    letter-spacing: 0.05em;\n    padding: 4px 10px;\n    border-radius: 4px;\n    background: var(--color-bg-alt);\n    color: var(--color-muted);\n    margin-right: 6px;\n  }\n\n  .reveal {\n    opacity: 0;\n    transform: translateY(24px);\n    transition: opacity 0.6s ease, transform 0.6s ease;\n  }\n\n  .reveal.visible {\n    opacity: 1;\n    transform: translateY(0);\n  }\n\n  footer {\n    padding: 80px 0;\n    text-align: center;\n  }\n\n  footer p {\n    font-family: var(--font-ui);\n    font-size: 13px;\n    color: var(--color-muted);\n    opacity: 0.5;\n  }\n\n  @media (max-width: 900px) {\n    .paper-a, .paper-b, .paper-c, .paper-d { opacity: 0.45; }\n  }\n\n  @media (max-width: 700px) {\n    .content { padding: 0 20px; }\n    section { padding: 80px 0; }\n\n    .hero-cover {\n      min-height: auto;\n      padding: 120px 18px 50px;\n    }\n\n    .hero-kicker {\n      margin-bottom: 24px;\n      font-size: 11px;\n    }\n\n    .hero-title { font-size: 42px; }\n    .hero-standfirst { font-size: 19px; }\n    .hero-meta { margin-top: 26px; }\n\n    h1 { font-size: 34px; }\n    h2 { font-size: 26px; }\n    .stage-cards { grid-template-columns: 1fr; }\n    .stat-row { gap: 32px; }\n\n    nav { padding: 14px 20px; }\n    nav .nav-links { gap: 16px; }\n    .nav-left { font-size: 10px; }\n\n    .paper-a, .paper-b, .paper-c, .paper-d {\n      width: 110px;\n      height: 120px;\n      opacity: 0.28;\n    }\n\n    .paper-c, .paper-d { display: none; }\n  }\n<\/style>\n<\/head>\n<body>\n\n<nav>\n  <div class=\"nav-left\">\n    <span>Read<\/span>\n    <span class=\"dot\">\u2022<\/span>\n    <span class=\"read-time\">7 min<\/span>\n  <\/div>\n  <div class=\"nav-links\">\n    <a href=\"#pipeline\">Pipeline<\/a>\n    <a href=\"#stages\">Stages<\/a>\n    <a href=\"#philosophy\">Philosophy<\/a>\n    <a href=\"#signal\">Signal<\/a>\n  <\/div>\n<\/nav>\n\n<section class=\"hero-cover\" id=\"top\">\n  <div class=\"hero-paper paper-a\" aria-hidden=\"true\"><\/div>\n  <div class=\"hero-paper paper-b\" aria-hidden=\"true\"><\/div>\n  <div class=\"hero-paper paper-c\" aria-hidden=\"true\"><\/div>\n  <div class=\"hero-paper paper-d\" aria-hidden=\"true\"><\/div>\n\n  <div class=\"hero-shell\">\n    <div class=\"hero-kicker\">\n      <span class=\"ornament\">\u2726<\/span>\n      Applied Nudge Theory\n      <span class=\"ornament\">\u2726<\/span>\n    <\/div>\n\n    <h1 class=\"hero-title\">How a signal <em>earns<\/em> the right to reach a person.<\/h1>\n\n    <p class=\"hero-standfirst\">\n      Journey does not deliver the loudest intervention. It filters for context,\n      timing, values, and personality until only the nudge that still feels true\n      remains.\n    <\/p>\n\n    <div class=\"hero-meta\">\n      <span>Context-Aware<\/span>\n      <span>Values-Mirrored<\/span>\n      <span>Personality-Matched<\/span>\n      <span>One Nudge Delivered<\/span>\n    <\/div>\n  <\/div>\n<\/section>\n\n<hr>\n\n<div class=\"content\">\n<section id=\"pipeline\">\n  <div class=\"reveal\">\n    <p class=\"eyebrow\">The Pipeline<\/p>\n    <h2>Many candidates. Only one delivered.<\/h2>\n    <p class=\"prose\">The pipeline begins wide. Dozens of candidate nudges are generated from a person's capture history, their stated values, insights from past reflections, and patterns the system has observed. Then the filtering begins. Each stage narrows the field. Context, timing, emotional resonance, values alignment, personality orientation. What emerges at the far end is singular and precise.<\/p>\n\n    <div class=\"canvas-wrap\">\n      <canvas id=\"funnel-canvas\"><\/canvas>\n      <p class=\"canvas-hint\">Move your cursor across the stages to see the filtering<\/p>\n    <\/div>\n\n    <p class=\"prose\">The particles above represent nudge candidates moving through Journey's selection pipeline. Watch how many enter on the left and how few survive each gate. The funnel narrows. At the personality orientation stage, the most critical filter, the field collapses to one. That's the nudge that reaches the person.<\/p>\n  <\/div>\n<\/section>\n<\/div>\n\n<hr>\n\n<div class=\"content\">\n<section id=\"stages\">\n  <div class=\"reveal\">\n    <p class=\"eyebrow\">Filter Stages<\/p>\n    <h2>What each gate tests for.<\/h2>\n    <p class=\"prose\">Every stage asks a different question. Together they form a sieve that only the right intervention can pass through.<\/p>\n\n    <div class=\"stage-cards\">\n      <div class=\"stage-card\" data-stage=\"0\">\n        <div class=\"stage-num\">Stage 01<\/div>\n        <h3>Generate<\/h3>\n        <p>The raw pool. Candidate nudges drawn from the person's captures, quotes library, active goals, and mirror material. Quantity over quality. Cast the net wide.<\/p>\n        <span class=\"stage-tag\">30-50 candidates<\/span>\n        <span class=\"stage-tag\">Source material<\/span>\n      <\/div>\n\n      <div class=\"stage-card\" data-stage=\"1\">\n        <div class=\"stage-num\">Stage 02<\/div>\n        <h3>Context<\/h3>\n        <p>Filter by life situation. What happened recently? What are they going through? A nudge about career risk doesn't land the week after a layoff scare. Remove anything tone-deaf.<\/p>\n        <span class=\"stage-tag\">~20 remain<\/span>\n        <span class=\"stage-tag\">Life awareness<\/span>\n      <\/div>\n\n      <div class=\"stage-card\" data-stage=\"2\">\n        <div class=\"stage-num\">Stage 03<\/div>\n        <h3>Timing<\/h3>\n        <p>Right message, wrong moment. Filter by time of day, day of week, recency of similar nudges. Morning calls for activation. Evening calls for reflection. Never repeat within the echo window.<\/p>\n        <span class=\"stage-tag\">~12 remain<\/span>\n        <span class=\"stage-tag\">Temporal fit<\/span>\n      <\/div>\n\n      <div class=\"stage-card\" data-stage=\"3\">\n        <div class=\"stage-num\">Stage 04<\/div>\n        <h3>Values<\/h3>\n        <p>Does this nudge resonate with what the person actually cares about? Not what they should care about. What they do. Drawn from their own words, their own declared values, their own patterns of attention.<\/p>\n        <span class=\"stage-tag\">~5 remain<\/span>\n        <span class=\"stage-tag\">Values mirroring<\/span>\n      <\/div>\n\n      <div class=\"stage-card\" data-stage=\"4\">\n        <div class=\"stage-num\">Stage 05<\/div>\n        <h3>Personality<\/h3>\n        <p>The most critical filter. Personality orientation determines how a person receives information. A challenger needs provocation. A nurturer needs permission. Wrong orientation, wrong nudge, regardless of content.<\/p>\n        <span class=\"stage-tag\">1-2 remain<\/span>\n        <span class=\"stage-tag\">Orientation match<\/span>\n      <\/div>\n\n      <div class=\"stage-card\" data-stage=\"5\">\n        <div class=\"stage-num\">Stage 06<\/div>\n        <h3>Deliver<\/h3>\n        <p>One nudge survives. Formatted for the channel, timed for the moment, grounded in the person's own language. It arrives not as advice from a system but as a mirror held up at the right angle.<\/p>\n        <span class=\"stage-tag\">1 nudge<\/span>\n        <span class=\"stage-tag\">The mirror<\/span>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/section>\n<\/div>\n\n<hr>\n\n<div class=\"content\">\n<section id=\"philosophy\">\n  <div class=\"reveal\">\n    <p class=\"eyebrow\">Philosophy<\/p>\n    <h2>Interventions built from roots, not templates.<\/h2>\n    <p class=\"prose\">Individual lives are so unique and different that what works for one person couldn't possibly look or work the same way for another. That's precisely the question Journey set out to answer, and that's where the architecture of large language models enters the picture.<\/p>\n    <p class=\"prose\">With raw source materials, a person's own captures, you can build a model of who they are and the world they see and resonate with. You can build interventions from those roots. Otherwise engagement drops over time, almost regardless of how frequently nudges hit.<\/p>\n\n    <blockquote>\n      <p>\"Nothing in the book is known to be true but a reflection of what we know. Some ideas may resonate, others may not. A few may awaken an inner knowing. Use what's helpful and let go of the rest.\"<\/p>\n      <cite>Applied Nudge Theory<\/cite>\n    <\/blockquote>\n\n    <p class=\"prose\">However, when nudges do hit home and make little error up front, the lifespan of the person feeling connected to that process, watering it, are far more likely to sustain the desired behavior change. The practice of capture becomes the process of becoming a version of yourself you want to be. And it becomes self-reinforcing.<\/p>\n  <\/div>\n<\/section>\n<\/div>\n\n<hr>\n\n<div class=\"content\">\n<section id=\"signal\">\n  <div class=\"reveal\">\n    <p class=\"eyebrow\">Signal vs. Noise<\/p>\n    <h2>The constraint is the product.<\/h2>\n    <p class=\"prose\">Move your cursor to feel the constraint tighten. When the filter narrows, candidates slow, compress, and most are eliminated. This is the core product design: not more nudges, but fewer, better, truer ones.<\/p>\n\n    <div class=\"canvas-wrap\">\n      <canvas id=\"constraint-canvas\"><\/canvas>\n      <p class=\"canvas-hint\">Drag your cursor left and right to move the constraint point<\/p>\n    <\/div>\n\n    <p class=\"prose\">The ability to look deeply is the root of this system. To see past the ordinary and mundane and get to what otherwise ought be invisible. Each nudge that survives the pipeline is an invitation to further inquiry, looking deeper, zooming out or zooming in, looking for possibilities of a new way of being.<\/p>\n  <\/div>\n<\/section>\n<\/div>\n\n<hr>\n\n<div class=\"content\">\n<section>\n  <div class=\"reveal\">\n    <div class=\"stat-row\">\n      <div class=\"stat\">\n        <div class=\"stat-number\">50<\/div>\n        <div class=\"stat-label\">candidates generated<br>per nudge cycle<\/div>\n      <\/div>\n      <div class=\"stat\">\n        <div class=\"stat-number\">5<\/div>\n        <div class=\"stat-label\">progressive filter<br>stages before delivery<\/div>\n      <\/div>\n      <div class=\"stat\">\n        <div class=\"stat-number\">1<\/div>\n        <div class=\"stat-label\">nudge reaches<br>the person<\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/section>\n<\/div>\n\n<hr>\n\n<footer class=\"content\">\n  <div class=\"reveal\">\n    <p>Journey &mdash; Applied Nudge Theory &amp; Values Mirroring<\/p>\n  <\/div>\n<\/footer>\n\n<script>\n\/\/ \u2500\u2500 Shared Utilities \u2500\u2500\nfunction hexToRgb(hex) {\n  const m = \/^#?([a-f\\\\d]{2})([a-f\\\\d]{2})([a-f\\\\d]{2})$\/i.exec(hex);\n  return m ? { r: parseInt(m[1],16), g: parseInt(m[2],16), b: parseInt(m[3],16) } : { r:100, g:100, b:100 };\n}\nconst _colorCache = {};\nfunction rgba(hex, alpha) {\n  const key = hex + alpha;\n  if (_colorCache[key]) return _colorCache[key];\n  const c = hexToRgb(hex);\n  const val = `rgba(${c.r},${c.g},${c.b},${alpha})`;\n  _colorCache[key] = val;\n  return val;\n}\nfunction ease(t) { return t < 0.5 ? 2*t*t : -1 + (4 - 2*t)*t; }\n\nfunction setupCanvas(canvas, width, height) {\n  const dpr = window.devicePixelRatio || 1;\n  canvas.width = width * dpr;\n  canvas.height = height * dpr;\n  canvas.style.width = width + 'px';\n  canvas.style.height = height + 'px';\n  const ctx = canvas.getContext('2d');\n  ctx.scale(dpr, dpr);\n  return ctx;\n}\n\nconst C = {\n  accent: '#BFA987',\n  dark: '#291911',\n  muted: '#856B58',\n  border: '#D2CCC5',\n  success: '#6B8F5E',\n  light: '#F5F1EA',\n  eliminated: '#D2CCC5'\n};\n\n\/\/ \u2500\u2500 Nudge categories\nconst nudgeColors = ['#A67C52', '#6B8F5E', '#8B6F4E', '#B5935F', '#7A6B5D'];\n\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\/\/ FUNNEL PIPELINE VISUALIZATION\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nfunction initFunnelPipeline(canvasId) {\n  const canvas = document.getElementById(canvasId);\n  const W = canvas.parentElement.offsetWidth;\n  const H = 340;\n  const ctx = setupCanvas(canvas, W, H);\n\n  const stages = [\n    { name: 'GENERATE', survival: 1.0 },\n    { name: 'CONTEXT', survival: 0.45 },\n    { name: 'TIMING', survival: 0.28 },\n    { name: 'VALUES', survival: 0.12 },\n    { name: 'PERSONALITY', survival: 0.04 },\n    { name: 'DELIVER', survival: 0.02 }\n  ];\n\n  const stageWidth = W \/ stages.length;\n  let mouseX = -1, mouseY = -1;\n  let particles = [];\n  let spawnTimer = 0;\n  let particleId = 0;\n\n  canvas.addEventListener('mousemove', e => {\n    const rect = canvas.getBoundingClientRect();\n    mouseX = (e.clientX - rect.left) \/ rect.width * W;\n    mouseY = (e.clientY - rect.top) \/ rect.height * H;\n  });\n  canvas.addEventListener('mouseleave', () => { mouseX = -1; mouseY = -1; });\n\n  function funnelY(x, top) {\n    const progress = x \/ W;\n    const funnelHalf = 130 * (1 - progress * 0.88);\n    const center = H \/ 2;\n    return top ? center - funnelHalf : center + funnelHalf;\n  }\n\n  function spawnParticle() {\n    const spread = 110;\n    const baseY = H\/2 + (Math.random() - 0.5) * spread * 2;\n    particleId++;\n    return {\n      id: particleId,\n      x: -8 - Math.random() * 20,\n      y: baseY,\n      baseY: baseY,\n      vx: 0.6 + Math.random() * 0.4,\n      vy: 0,\n      radius: 3 + Math.random() * 2,\n      color: nudgeColors[Math.floor(Math.random() * nudgeColors.length)],\n      opacity: 0.75 + Math.random() * 0.25,\n      alive: true,\n      fadeOut: 0,\n      deathStage: pickDeathStage(),\n      trail: []\n    };\n  }\n\n  function pickDeathStage() {\n    const r = Math.random();\n    if (r < 0.35) return 1;\n    if (r < 0.58) return 2;\n    if (r < 0.78) return 3;\n    if (r < 0.94) return 4;\n    return 6;\n  }\n\n  function getStageIndex(x) {\n    return Math.min(Math.floor(x \/ stageWidth), stages.length - 1);\n  }\n\n  function frame() {\n    ctx.clearRect(0, 0, W, H);\n\n    ctx.beginPath();\n    ctx.moveTo(0, funnelY(0, true));\n    for (let x = 0; x <= W; x += 4) {\n      ctx.lineTo(x, funnelY(x, true));\n    }\n    ctx.lineTo(W, funnelY(W, false));\n    for (let x = W; x >= 0; x -= 4) {\n      ctx.lineTo(x, funnelY(x, false));\n    }\n    ctx.closePath();\n    ctx.fillStyle = 'rgba(191, 169, 135, 0.06)';\n    ctx.fill();\n    ctx.strokeStyle = rgba(C.border, 0.35);\n    ctx.lineWidth = 1;\n    ctx.stroke();\n\n    stages.forEach((s, i) => {\n      const x = i * stageWidth;\n      if (i > 0) {\n        ctx.beginPath();\n        ctx.setLineDash([4, 6]);\n        ctx.moveTo(x, funnelY(x, true) - 10);\n        ctx.lineTo(x, funnelY(x, false) + 10);\n        ctx.strokeStyle = rgba(C.border, 0.4);\n        ctx.lineWidth = 1;\n        ctx.stroke();\n        ctx.setLineDash([]);\n      }\n\n      const isHovered = mouseX > 0 && mouseX >= x && mouseX < x + stageWidth;\n      ctx.font = `${isHovered ? '700' : '600'} ${isHovered ? '11' : '10'}px Inter, system-ui, sans-serif`;\n      ctx.textAlign = 'center';\n      ctx.fillStyle = isHovered ? C.accent : rgba(C.muted, 0.4);\n      ctx.fillText(s.name, x + stageWidth \/ 2, 24);\n\n      if (isHovered) {\n        ctx.beginPath();\n        ctx.rect(x + 2, 32, stageWidth - 4, H - 64);\n        ctx.fillStyle = rgba(C.accent, 0.015);\n        ctx.fill();\n      }\n    });\n\n    spawnTimer += 16;\n    if (spawnTimer > 120 + Math.random() * 80) {\n      particles.push(spawnParticle());\n      spawnTimer = 0;\n    }\n\n    particles.forEach(p => {\n      if (!p.alive && p.fadeOut <= 0) return;\n\n      const stageIdx = getStageIndex(p.x);\n\n      p.trail.push({ x: p.x, y: p.y });\n      if (p.trail.length > 6) p.trail.shift();\n\n      if (p.alive) {\n        if (stageIdx >= p.deathStage && p.deathStage < 6) {\n          p.alive = false;\n          p.fadeOut = 1.0;\n        }\n\n        const progress = Math.max(0, p.x \/ W);\n        const funnelTop = funnelY(p.x, true);\n        const funnelBot = funnelY(p.x, false);\n        const funnelCenter = (funnelTop + funnelBot) \/ 2;\n        const funnelRange = (funnelBot - funnelTop) \/ 2;\n\n        const targetY = funnelCenter + ((p.baseY - H\/2) \/ 130) * funnelRange * 0.8;\n        p.y += (targetY - p.y) * 0.04;\n\n        p.x += p.vx;\n\n        for (let si = 1; si < stages.length; si++) {\n          const bx = si * stageWidth;\n          const dist = Math.abs(p.x - bx);\n          if (dist < 30) {\n            p.x -= p.vx * 0.3 * (1 - dist \/ 30);\n          }\n        }\n      } else {\n        p.y += 0.8;\n        p.x += p.vx * 0.2;\n        p.fadeOut -= 0.018;\n      }\n\n      if (mouseX > 0) {\n        const dx = p.x - mouseX;\n        const dy = p.y - mouseY;\n        const dist = Math.sqrt(dx*dx + dy*dy);\n        if (dist < 100 && dist > 0) {\n          const force = (1 - dist\/100) * 10;\n          p.x += (dx\/dist) * force * 0.06;\n          p.y += (dy\/dist) * force * 0.06;\n        }\n      }\n\n      const drawOpacity = p.alive ? p.opacity : Math.max(0, p.fadeOut * 0.4);\n      const drawColor = p.alive ? p.color : C.eliminated;\n\n      p.trail.forEach((tp, i) => {\n        const alpha = (i \/ p.trail.length) * 0.15 * (p.alive ? 1 : p.fadeOut);\n        ctx.beginPath();\n        ctx.arc(tp.x, tp.y, p.radius * 0.5, 0, Math.PI * 2);\n        ctx.fillStyle = rgba(drawColor, alpha);\n        ctx.fill();\n      });\n\n      if (drawOpacity > 0.1) {\n        ctx.beginPath();\n        ctx.arc(p.x, p.y, p.radius * 3, 0, Math.PI * 2);\n        ctx.fillStyle = rgba(drawColor, 0.06 * drawOpacity);\n        ctx.fill();\n      }\n\n      ctx.beginPath();\n      ctx.arc(p.x, p.y, p.radius * (p.alive ? 1 : 0.7), 0, Math.PI * 2);\n      ctx.fillStyle = rgba(drawColor, drawOpacity);\n      ctx.fill();\n\n      if (p.alive && p.x > W * 0.85 && p.deathStage >= 6) {\n        ctx.beginPath();\n        ctx.arc(p.x, p.y, p.radius + 5, 0, Math.PI * 2);\n        ctx.strokeStyle = rgba(C.success, 0.4);\n        ctx.lineWidth = 1.5;\n        ctx.stroke();\n\n        ctx.beginPath();\n        ctx.arc(p.x, p.y, p.radius + 12, 0, Math.PI * 2);\n        ctx.fillStyle = rgba(C.success, 0.05);\n        ctx.fill();\n      }\n    });\n\n    particles = particles.filter(p => {\n      if (p.x > W + 30) return false;\n      if (!p.alive && p.fadeOut <= 0) return false;\n      if (p.y > H + 30) return false;\n      return true;\n    });\n\n    if (particles.length > 80) particles = particles.slice(-80);\n\n    requestAnimationFrame(frame);\n  }\n  requestAnimationFrame(frame);\n}\n\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\/\/ CONSTRAINT FIELD VISUALIZATION\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nfunction initConstraintField(canvasId) {\n  const canvas = document.getElementById(canvasId);\n  const W = canvas.parentElement.offsetWidth;\n  const H = 260;\n  const ctx = setupCanvas(canvas, W, H);\n\n  const labels = ['RAW POOL', 'CONTEXT', 'TIMING', 'VALUES', 'PERSONALITY'];\n\n  let particles = [];\n  let mouseX = -1;\n  let lastSpawn = 0;\n  let autoCinch = { x: W * 0.65, phase: 'free', timer: 0 };\n\n  canvas.addEventListener('mousemove', e => {\n    const rect = canvas.getBoundingClientRect();\n    mouseX = (e.clientX - rect.left) \/ rect.width * W;\n  });\n  canvas.addEventListener('mouseleave', () => { mouseX = -1; });\n\n  function spawnParticle() {\n    return {\n      x: -5,\n      y: H\/2 + (Math.random()-0.5)*90,\n      baseY: H\/2 + (Math.random()-0.5)*90,\n      speed: 1.0 + Math.random()*0.4,\n      radius: 3 + Math.random()*1.5,\n      opacity: 0.6 + Math.random()*0.3,\n      color: nudgeColors[Math.floor(Math.random() * nudgeColors.length)]\n    };\n  }\n\n  function frame(t) {\n    ctx.clearRect(0, 0, W, H);\n\n    let cinchX;\n    if (mouseX > 0) {\n      cinchX = mouseX;\n    } else {\n      autoCinch.timer += 16;\n      if (autoCinch.phase === 'free' && autoCinch.timer > 800) {\n        autoCinch.phase = 'cinching'; autoCinch.timer = 0;\n      } else if (autoCinch.phase === 'cinching' && autoCinch.timer > 600) {\n        autoCinch.phase = 'holding'; autoCinch.timer = 0;\n      } else if (autoCinch.phase === 'holding' && autoCinch.timer > 3500) {\n        autoCinch.phase = 'releasing'; autoCinch.timer = 0;\n      } else if (autoCinch.phase === 'releasing' && autoCinch.timer > 450) {\n        autoCinch.phase = 'free'; autoCinch.timer = 0;\n        autoCinch.x = W * (0.3 + Math.random() * 0.5);\n      }\n      cinchX = autoCinch.x;\n    }\n\n    const cinchIntensity = (autoCinch.phase === 'holding' || mouseX > 0) ? 0.8 :\n                           autoCinch.phase === 'cinching' ? ease(autoCinch.timer\/600)*0.8 :\n                           autoCinch.phase === 'releasing' ? 0.8*(1-ease(autoCinch.timer\/450)) : 0;\n\n    const narrowing = cinchIntensity * (H\/2 - 20);\n\n    ctx.beginPath();\n    ctx.moveTo(0, 30);\n    ctx.quadraticCurveTo(cinchX, 30 + narrowing, W, 30);\n    ctx.lineTo(W, H - 30);\n    ctx.quadraticCurveTo(cinchX, H - 30 - narrowing, 0, H - 30);\n    ctx.closePath();\n    ctx.fillStyle = 'rgba(191, 169, 135, 0.05)';\n    ctx.fill();\n    ctx.strokeStyle = rgba(C.border, 0.4);\n    ctx.lineWidth = 1;\n    ctx.stroke();\n\n    if (cinchIntensity > 0.1) {\n      ctx.beginPath();\n      ctx.setLineDash([3, 5]);\n      ctx.moveTo(cinchX, 30 + narrowing * 0.3);\n      ctx.lineTo(cinchX, H - 30 - narrowing * 0.3);\n      ctx.strokeStyle = rgba(C.accent, 0.25 * cinchIntensity);\n      ctx.lineWidth = 1;\n      ctx.stroke();\n      ctx.setLineDash([]);\n    }\n\n    const lw = W \/ labels.length;\n    ctx.font = '600 10px Inter, system-ui, sans-serif';\n    ctx.textAlign = 'center';\n    labels.forEach((l, i) => {\n      ctx.fillStyle = rgba(C.muted, 0.35);\n      ctx.fillText(l, lw * i + lw \/ 2, 18);\n    });\n\n    if (t - lastSpawn > 180 + Math.random() * 120) {\n      particles.push(spawnParticle());\n      lastSpawn = t;\n    }\n\n    particles.forEach(p => {\n      const distFromCinch = Math.abs(p.x - cinchX);\n      const inCinchZone = distFromCinch < 70;\n\n      const speed = inCinchZone ? p.speed * (0.08 + 0.92 * (1 - cinchIntensity)) : p.speed;\n      p.x += speed;\n\n      if (inCinchZone && cinchIntensity > 0) {\n        const squeeze = 1 - cinchIntensity * (1 - distFromCinch\/70) * 0.75;\n        p.y = H\/2 + (p.baseY - H\/2) * squeeze;\n      } else {\n        p.y += (p.baseY - p.y) * 0.05;\n      }\n\n      const gapHalf = (H\/2 - 30) - narrowing * 0.95;\n      if (inCinchZone && cinchIntensity > 0.5 && Math.abs(p.y - H\/2) > gapHalf && distFromCinch < 20) {\n        p.opacity -= 0.04;\n      }\n\n      if (p.opacity <= 0) return;\n\n      ctx.beginPath();\n      ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);\n      ctx.fillStyle = rgba(p.color, p.opacity);\n      ctx.fill();\n\n      ctx.beginPath();\n      ctx.arc(p.x, p.y, p.radius * 2.5, 0, Math.PI * 2);\n      ctx.fillStyle = rgba(p.color, 0.06 * p.opacity);\n      ctx.fill();\n    });\n\n    particles = particles.filter(p => p.x < W + 10 && p.opacity > 0);\n\n    requestAnimationFrame(frame);\n  }\n  requestAnimationFrame(frame);\n}\n\n\/\/ \u2500\u2500 Scroll reveal \u2500\u2500\nconst observer = new IntersectionObserver(entries => {\n  entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('visible'); });\n}, { threshold: 0.1, rootMargin: '0px 0px -40px 0px' });\ndocument.querySelectorAll('.reveal').forEach(el => observer.observe(el));\n\n\/\/ \u2500\u2500 Nav scroll border \u2500\u2500\nwindow.addEventListener('scroll', () => {\n  document.querySelector('nav').classList.toggle('scrolled', window.scrollY > 20);\n});\n\n\/\/ \u2500\u2500 Stage card hover \u2500\u2500\ndocument.querySelectorAll('.stage-card').forEach(card => {\n  card.addEventListener('mouseenter', () => {\n    card.style.borderColor = 'var(--color-red)';\n  });\n  card.addEventListener('mouseleave', () => {\n    card.style.borderColor = '';\n  });\n});\n\n\/\/ \u2500\u2500 Init canvases \u2500\u2500\ndocument.addEventListener('DOMContentLoaded', () => {\n  initFunnelPipeline('funnel-canvas');\n  initConstraintField('constraint-canvas');\n});\n\n\/\/ \u2500\u2500 Resize handling \u2500\u2500\nlet resizeTimer;\nwindow.addEventListener('resize', () => {\n  clearTimeout(resizeTimer);\n  resizeTimer = setTimeout(() => {\n    initFunnelPipeline('funnel-canvas');\n    initConstraintField('constraint-canvas');\n  }, 300);\n});\n\n\/\/ \u2500\u2500 Smooth scroll for nav links \u2500\u2500\ndocument.querySelectorAll('nav a[href^=\"#\"]').forEach(link => {\n  link.addEventListener('click', e => {\n    e.preventDefault();\n    const target = document.querySelector(link.getAttribute('href'));\n    if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' });\n  });\n});\n<\/script>\n<\/body>\n<\/html>\n<!-- \/wp:html -->","_et_gb_content_width":"1080","footnotes":""},"categories":[9],"tags":[],"class_list":["post-1553","post","type-post","status-publish","format-standard","hentry","category-research"],"_links":{"self":[{"href":"https:\/\/lorenzoscardicchio.com\/index.php\/wp-json\/wp\/v2\/posts\/1553","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lorenzoscardicchio.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/lorenzoscardicchio.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/lorenzoscardicchio.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lorenzoscardicchio.com\/index.php\/wp-json\/wp\/v2\/comments?post=1553"}],"version-history":[{"count":5,"href":"https:\/\/lorenzoscardicchio.com\/index.php\/wp-json\/wp\/v2\/posts\/1553\/revisions"}],"predecessor-version":[{"id":1584,"href":"https:\/\/lorenzoscardicchio.com\/index.php\/wp-json\/wp\/v2\/posts\/1553\/revisions\/1584"}],"wp:attachment":[{"href":"https:\/\/lorenzoscardicchio.com\/index.php\/wp-json\/wp\/v2\/media?parent=1553"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/lorenzoscardicchio.com\/index.php\/wp-json\/wp\/v2\/categories?post=1553"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lorenzoscardicchio.com\/index.php\/wp-json\/wp\/v2\/tags?post=1553"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}