---
format: typebulb/v1
name: Veer Factor
---

**code.tsx**

```tsx
import React, { useState, useEffect, useRef } from "react";
import { createRoot } from "react-dom/client";

interface Insight {
  person: string;
  quote: string;
  score: number;
  field: string;
  expertise: string;
  explanation: string;
  experts: { name: string; credential: string }[];
  expertsSummary: string;
}

function veerLabel(score: number): string {
  if (score <= 15) return "In Lane";
  if (score <= 35) return "Drifting";
  if (score <= 55) return "Swerving";
  if (score <= 75) return "Wrong Lane";
  return "Car Crash";
}

function veerColor(score: number): string {
  if (score <= 20) return "#16a34a";
  if (score <= 40) return "#22c55e";
  if (score <= 60) return "#fbbf24";
  if (score <= 80) return "#f97316";
  return "#ef4444";
}

function Road({ score, expertise, field, numExperts }: { score: number; expertise: string; field: string; numExperts: number }) {
  const roadRef = useRef<HTMLDivElement>(null);
  const walshRef = useRef<HTMLDivElement>(null);
  const expertRefs = useRef<(HTMLDivElement | null)[]>([]);

  useEffect(() => {
    const road = roadRef.current;
    const walsh = walshRef.current;
    if (!road || !walsh) return;

    let frame: number;
    const start = performance.now();
    const walshCycle = 4;
    const expertCycle = 8;
    const carH = 34;
    const carW = 18;
    const crashCooldowns = new Set<number>();

    const animate = () => {
      const t = (performance.now() - start) / 1000;
      const roadH = road.clientHeight;

      // Walsh: top to bottom
      const walshProgress = (t % walshCycle) / walshCycle;
      const walshY = -carH + walshProgress * (roadH + carH);

      const maxSwerve = (score / 100) * 30;
      const swerve =
        Math.sin(t * 1.7) * 0.5 +
        Math.sin(t * 2.9) * 0.3 +
        Math.sin(t * 4.3) * 0.2;
      const walshX = 6 + maxSwerve * (swerve + 1) / 2;

      walsh.style.top = `${walshY}px`;
      walsh.style.left = `${walshX}px`;

      // Expert cars: bottom to top, staggered
      let anyHit = false;
      for (let i = 0; i < numExperts; i++) {
        const el = expertRefs.current[i];
        if (!el) continue;

        const offset = i / numExperts;
        const expertProgress = ((t / expertCycle + offset) % 1);
        const expertY = roadH + carH - expertProgress * (roadH + carH * 2);
        const expertX = 34;

        el.style.top = `${expertY}px`;
        el.style.left = `${expertX}px`;

        const hit = Math.abs(walshY - expertY) < carH && Math.abs(walshX - expertX) < carW;
        if (hit) {
          anyHit = true;
          if (!crashCooldowns.has(i)) {
            crashCooldowns.add(i);
            const burst = document.createElement('div');
            burst.className = 'crash-burst';
            burst.style.top = `${(walshY + expertY) / 2 + carH / 2}px`;
            burst.style.left = `${(walshX + expertX) / 2 + carW / 2}px`;
            road.appendChild(burst);
            setTimeout(() => burst.remove(), 600);
          }
          el.style.filter = 'brightness(2)';
        } else {
          crashCooldowns.delete(i);
          el.style.filter = '';
        }
      }
      walsh.style.filter = anyHit ? 'brightness(2)' : '';

      frame = requestAnimationFrame(animate);
    };

    frame = requestAnimationFrame(animate);
    return () => cancelAnimationFrame(frame);
  }, [score, numExperts]);

  return (
    <div className="road" ref={roadRef}>
      <div className="lane">
        <span className="lane-text" style={{ color: veerColor(score) }}>{expertise}</span>
      </div>
      <div className="lane-divider" />
      <div className="lane">
        <span className="lane-text lane-text-field">{field}</span>
      </div>
      <div className="car" ref={walshRef} style={{ backgroundColor: veerColor(score) }} />
      {Array.from({ length: numExperts }, (_, i) => (
        <div
          key={i}
          className="car car-expert"
          ref={el => { expertRefs.current[i] = el; }}
        />
      ))}
    </div>
  );
}

function App() {
  const [insight, setInsight] = useState<Insight | null>(() => {
    try { return tb.insight<Insight>(); } catch { return null; }
  });
  const [isGenerating, setIsGenerating] = useState(false);

  const handleAnalyze = async () => {
    setIsGenerating(true);
    try {
      const result = await tb.infer<Insight>();
      if (result) setInsight(result);
    } finally {
      setIsGenerating(false);
    }
  };

  return (
    <div className="app">
      <header>
        <div>
          <h1>Veer Factor</h1>
          <span className="tagline">Stay in your lane.</span>
        </div>
        <button className={`analyze-btn ${isGenerating ? "loading" : ""}`} onClick={handleAnalyze}>
          {isGenerating ? "Analyzing..." : "Analyze Quote"}
        </button>
      </header>

      {insight && (
        <div className="card">
          <div className="score-label" style={{ color: veerColor(insight.score) }}>
            <div className="score-value">{Math.round(insight.score / 10)}/10</div>
            <div className="score-text">{veerLabel(insight.score)}</div>
          </div>
          <div className="card-body">
            <div className="body-left">
              <div className="card-person" style={{ color: veerColor(insight.score) }}>{insight.person}</div>
              <blockquote className="card-quote">"{insight.quote}"</blockquote>
            </div>
            <Road score={insight.score} expertise={insight.expertise} field={insight.field} numExperts={insight.experts?.length ?? 0} />
            <div className="body-right">
              {insight.experts && insight.experts.length > 0 && (
                <>
                  <div className="experts-list">
                    {insight.experts.map((e, i) => (
                      <div key={i} className="expert-row">
                        <span className="expert-name" style={{ color: '#16a34a' }}>{e.name}</span>
                        <span className="expert-credential">{e.credential}</span>
                      </div>
                    ))}
                  </div>
                  {insight.expertsSummary && (
                    <div className="experts-summary">{insight.expertsSummary}</div>
                  )}
                </>
              )}
              <p className="card-explanation">{insight.explanation}</p>
            </div>
          </div>
        </div>
      )}

      {!insight && (
        <div className="empty">
          <p>Paste a quote to the Data tab, then click "Analyze Quote".</p>
          <p className="hint">Format: Name: "What they said"</p>
        </div>
      )}
    </div>
  );
}

createRoot(document.getElementById("root")!).render(<App />);
```
**styles.css**

```css
:root {
  --bg: #1a1a1a;
  --panel: #111111;
  --border: #30363d;
  --text: #c9d1d9;
  --dim: #8b949e;
  --accent: #58a6ff;
  --shadow: rgba(0, 0, 0, 0.4);
}

html[data-theme="light"] {
  --bg: #f6f8fa;
  --panel: #ffffff;
  --border: #d0d7de;
  --text: #24292f;
  --dim: #57606a;
  --accent: #0969da;
  --shadow: rgba(0, 0, 0, 0.1);
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body {
  font: 15px/1.5 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
  padding: 10px 16px;
}

.app {
  max-width: 1200px;
  margin: 0 auto;
  padding-bottom: 16px;
}

header { position: relative; text-align: center; margin-bottom: 12px; }
.analyze-btn {
  position: absolute;
  right: 0;
  bottom: 0;
}
h1 { font-size: 36px; font-weight: 600; }
.tagline { color: var(--dim); font-size: 18px; font-style: italic; }

.analyze-btn {
  background: var(--accent);
  color: #fff;
  border: none;
  padding: 6px 14px;
  border-radius: 5px;
  font-size: 13px;
  font: inherit;
  font-weight: 500;
  cursor: pointer;
  transition: opacity 0.2s;
}
.analyze-btn:hover { opacity: 0.9; }
.analyze-btn.loading { opacity: 0.6; cursor: wait; }

.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 24px;
}

.card-person {
  font-size: 22px;
  font-weight: 600;
  margin-bottom: 12px;
}

.card-body {
  display: grid;
  grid-template-columns: 1fr 60px 1fr;
  margin-bottom: 20px;
}

.body-left {
  text-align: right;
  padding-right: 20px;
}

.body-right {
  padding-left: 20px;
}

.road {
  position: relative;
  background: #2a2a2a;
  border-left: 2px solid #555;
  border-right: 2px solid #555;
  border-radius: 3px;
  display: flex;
  min-height: 200px;
  overflow: hidden;
}

.lane {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: 16px 0;
}

.lane-text {
  writing-mode: vertical-rl;
  font-size: 20px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1px;
  white-space: nowrap;
}

.lane-text-field {
  color: #16a34a;
}

.lane-divider {
  width: 2px;
  background: repeating-linear-gradient(
    to bottom,
    rgba(255,255,255,0.5) 0px,
    rgba(255,255,255,0.5) 14px,
    transparent 14px,
    transparent 24px
  );
}

.car {
  position: absolute;
  width: 18px;
  height: 34px;
  border-radius: 5px;
}

.car::after {
  content: '';
  position: absolute;
  top: 4px;
  left: 3px;
  right: 3px;
  height: 8px;
  border-radius: 2px;
  background: rgba(255,255,255,0.15);
}

.car::before {
  content: '';
  position: absolute;
  top: 3px;
  left: -3px;
  width: calc(100% + 6px);
  height: calc(100% - 6px);
  background:
    linear-gradient(#111, #111) 0 0 / 4px 7px no-repeat,
    linear-gradient(#111, #111) 100% 0 / 4px 7px no-repeat,
    linear-gradient(#111, #111) 0 100% / 4px 7px no-repeat,
    linear-gradient(#111, #111) 100% 100% / 4px 7px no-repeat;
  border-radius: 2px;
}

.car-expert {
  background-color: #16a34a;
}

.car-expert::after {
  top: auto;
  bottom: 4px;
}

.crash-burst {
  position: absolute;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255,200,50,0.9) 0%, rgba(255,100,0,0.4) 40%, transparent 70%);
  transform: translate(-50%, -50%);
  animation: burst 0.6s ease-out forwards;
  pointer-events: none;
  z-index: 2;
}

@keyframes burst {
  0% { transform: translate(-50%, -50%) scale(0.5); opacity: 1; }
  100% { transform: translate(-50%, -50%) scale(2.5); opacity: 0; }
}

.score-label {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  margin-bottom: 20px;
}

.score-value {
  font-size: 28px;
  font-weight: 700;
  line-height: 1;
}

.score-text {
  font-size: 15px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1px;
}

.card-quote {
  padding: 0;
  color: var(--text);
  font-style: italic;
  font-size: 14px;
  line-height: 1.6;
}

.card-explanation {
  color: var(--text);
  font-size: 14px;
  line-height: 1.6;
}

.experts-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.expert-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.expert-name {
  font-size: 22px;
  font-weight: 600;
}

.expert-credential {
  font-size: 13px;
  color: var(--text);
}

.experts-summary {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  font-style: italic;
  color: var(--accent);
  margin-bottom: 20px;
}

@media (max-width: 600px) {
  body {
    padding: 8px 0;
  }
  .card {
    border-radius: 0;
    border-left: none;
    border-right: none;
    padding: 16px 12px;
  }
  header {
    padding: 0 12px;
    text-align: left;
  }
  .analyze-btn {
    right: 12px;
  }
  .card-body {
    grid-template-columns: 1fr 60px 1fr;
  }
  .body-left {
    padding-right: 12px;
  }
  .body-right {
    padding-left: 12px;
  }
}

.empty {
  text-align: center;
  padding: 48px;
  color: var(--dim);
}
.empty .hint { margin-top: 8px; font-size: 13px; }


```
**index.html**

```html
<div id="root"></div>
```
**data.txt**

```txt
Matt Walsh:
This is dumb. AI can’t ever be actually conscious because it doesn’t have subjective experience. It isn’t like anything to be AI. There is no experience there. Consciousness is the awareness and experience of self. AI has neither, and never will. The real risk (which I’m extremely worried about) is that AI becomes kind of a version of what has been called a “philosophical zombie,” which is something that acts and speaks entirely as though it has consciousness even though it has no genuine inner experience. When this happens with AI, millions of very lonely people will isolate themselves from the world even more, believing that their relationship with AI is a sufficient substitute for human interaction. So the nightmare scenario is a world where the average human has friends, coworkers, and even a spouse, who are all AI, all really nothing inside, not real. I think this probably will happen, and is already in the process of happening. And to me it’s an even greater horror than AI actually becoming conscious.
```
**infer.md**

```md
Analyze the quote for "lane-veering" — how much is the speaker opining outside their area of expertise?

## How to Assess

1. **Identify the person.** If they're a public figure, use your knowledge of their background and expertise. If unknown, infer what you can from how they speak.

2. **Identify the field.** What domain is the claim actually about? (e.g., philosophy of mind, quantum physics, urban planning)

3. **Assess the veer.** Score 0-100:
   - **0-15 (In Lane)**: Speaker has relevant expertise and the claim reflects it.
   - **16-35 (Drifting)**: Somewhat outside their expertise but showing awareness of complexity. Adjacent field or reasonable extrapolation.
   - **36-55 (Swerving)**: Clearly outside expertise. Making claims that oversimplify or misrepresent the field, but not egregiously wrong.
   - **56-75 (Wrong Lane)**: Far outside expertise. Confident claims that betray significant misunderstanding. The Dunning-Kruger sweet spot.
   - **76-100 (Car Crash)**: Making definitive proclamations about a field they have zero background in, with maximum confidence. Restating hard open questions as obvious answers.

4. **Key signals of lane-veering:**
   - Treating open questions as settled ("obviously X is true")
   - Using everyday language where technical precision is needed
   - Showing no awareness that experts disagree
   - Confusing adjacent but different concepts
   - Speaking with certainty inversely proportional to expertise
   - For unknown people: misusing technical terms, making category errors, or revealing misconceptions through word choice

## Input Format

A single entry in the format `Name: "quote"`. The name might be a real public figure or anonymous/pseudonymous.

## Output Format

Return a JSON object (not an array) with:
- `person`: The person's name as given
- `quote`: The FULL quote exactly as provided. Do NOT shorten or abridge it.
- `score`: Integer 0-100
- `field`: What field they're opining on
- `expertise`: Their actual known expertise, or "Unknown" if not a public figure. Be specific (e.g., "Political commentator, filmmaker" not just "media")
- `explanation`: 2-4 sentences. What specifically about the quote reveals the lane-veer? What do they get wrong or oversimplify? If they're in lane, what makes their claim well-grounded?
- `experts`: Array of 3-4 objects, each with `name` (string) and `credential` (string). These are prominent experts in the field the speaker veered INTO — people whose life's work the speaker is casually dismissing. Pick the most recognizable names. The credential should be specific and punchy (e.g., "Coined 'The Hard Problem of Consciousness' (1995)", "Nobel Prize in Physics, 2020"). For low veer scores (0-35), return an empty array.
- `expertsSummary`: A single punchy sentence connecting the experts to the veer. e.g., "None of whom consider this question settled." or "All of whom would disagree with at least part of this claim." For low veer scores, return an empty string.
```
**insight.json**

```json
{
  "person": "Matt Walsh",
  "quote": "This is dumb. AI can’t ever be actually conscious because it doesn’t have subjective experience. It isn’t like anything to be AI. There is no experience there. Consciousness is the awareness and experience of self. AI has neither, and never will. The real risk (which I’m extremely worried about) is that AI becomes kind of a version of what has been called a “philosophical zombie,” which is something that acts and speaks entirely as though it has consciousness even though it has no genuine inner experience. When this happens with AI, millions of very lonely people will isolate themselves from the world even more, believing that their relationship with AI is a sufficient substitute for human interaction. So the nightmare scenario is a world where the average human has friends, coworkers, and even a spouse, who are all AI, all really nothing inside, not real. I think this probably will happen, and is already in the process of happening. And to me it’s an even greater horror than AI actually becoming conscious.",
  "score": 92,
  "field": "Philosophy of mind",
  "expertise": "Political commentator, podcast host, filmmaker",
  "explanation": "Walsh restates the hard problem of consciousness — one of the deepest unsolved problems in philosophy — as if it's an obvious, settled fact. 'It isn't like anything to be AI' echoes Thomas Nagel's famous 'What Is It Like to Be a Bat?' but treats the framework as a conclusion rather than a question. The confidence is maximal ('never will') while the engagement with the actual debate is zero. He shows no awareness that philosophers, cognitive scientists, and AI researchers have spent decades wrestling with exactly these questions without consensus.",
  "experts": [
    { "name": "David Chalmers", "credential": "Coined 'The Hard Problem of Consciousness' (1995)" },
    { "name": "Thomas Nagel", "credential": "Author of 'What Is It Like to Be a Bat?' (1974)" },
    { "name": "Daniel Dennett", "credential": "Spent 50 years arguing consciousness IS computational" },
    { "name": "Giulio Tononi", "credential": "Creator of Integrated Information Theory of consciousness" }
  ],
  "expertsSummary": "None of whom consider this question settled — and they disagree with each other."
}


```
**config.json**

```json
{
  "dependencies": {
    "react": "^19.2.0",
    "react-dom": "^19.2.0"
  },
  "description": "Paste a quote, get a Veer Factor score. How far outside their expertise is the speaker? From 'In Lane' to 'Car Crash'.",
  "inference": {
    "title": "Analyze Quote",
    "dataTitle": "Quote to Analyze",
    "submitTitle": "Detect Veer Factor"
  }
}
```