1031 lines
41 KiB
TypeScript
1031 lines
41 KiB
TypeScript
import React, { useRef, useState, useEffect } from "react";
|
|
import {
|
|
Check,
|
|
BookOpen,
|
|
AlertTriangle,
|
|
Zap,
|
|
FileText,
|
|
GitBranch,
|
|
} from "lucide-react";
|
|
import { PracticeFromDataset } from "../../../components/lessons/LessonShell";
|
|
import {
|
|
BOUNDARIES_EASY,
|
|
BOUNDARIES_MEDIUM,
|
|
} from "../../../data/rw/boundaries";
|
|
import ClauseBreakdownWidget, {
|
|
type ClauseExample,
|
|
} from "../../../components/lessons/ClauseBreakdownWidget";
|
|
import DecisionTreeWidget, {
|
|
type TreeScenario,
|
|
type TreeNode,
|
|
} from "../../../components/lessons/DecisionTreeWidget";
|
|
import RevealCardGrid, {
|
|
type RevealCard,
|
|
} from "../../../components/lessons/RevealCardGrid";
|
|
import useScrollReveal from "../../../components/lessons/useScrollReveal";
|
|
|
|
interface LessonProps {
|
|
onFinish?: () => void;
|
|
}
|
|
|
|
/* ── Data for RevealCardGrid widgets ── */
|
|
const FRAGMENT_TYPES: RevealCard[] = [
|
|
{
|
|
label: "Missing Subject",
|
|
sublabel: '"Collected data for three years."',
|
|
content: 'Add a subject: "She collected data…"',
|
|
},
|
|
{
|
|
label: "Missing Verb",
|
|
sublabel: '"The team of researchers in the lab."',
|
|
content: 'Add a verb: "The team…worked in the lab."',
|
|
},
|
|
{
|
|
label: "-ING Fragment",
|
|
sublabel: '"Running the experiment."',
|
|
content:
|
|
'-ING words are never the main verb alone. Add "was" or a full subject+verb.',
|
|
},
|
|
{
|
|
label: "Subordinate Clause Alone",
|
|
sublabel: '"Although the results were promising."',
|
|
content: 'Remove "although," or attach to a main clause.',
|
|
},
|
|
{
|
|
label: "Relative Clause Alone",
|
|
sublabel: '"Which confirmed the hypothesis."',
|
|
content: "Attach to the noun it modifies.",
|
|
},
|
|
];
|
|
|
|
const CLAUSE_EXAMPLES: ClauseExample[] = [
|
|
{
|
|
title: "Legal — Period / Semicolon",
|
|
segments: [
|
|
{
|
|
text: "The experiment failed",
|
|
type: "ic",
|
|
label: "Independent Clause",
|
|
},
|
|
{ text: ";", type: "punct" },
|
|
{
|
|
text: " the researchers revised their hypothesis",
|
|
type: "ic",
|
|
label: "Independent Clause",
|
|
},
|
|
{ text: ".", type: "punct" },
|
|
],
|
|
},
|
|
{
|
|
title: "Legal — Comma + FANBOYS",
|
|
segments: [
|
|
{
|
|
text: "The evidence was strong",
|
|
type: "ic",
|
|
label: "Independent Clause",
|
|
},
|
|
{ text: ",", type: "punct" },
|
|
{ text: " but", type: "conjunction", label: "FANBOYS Conjunction" },
|
|
{
|
|
text: " the committee was skeptical",
|
|
type: "ic",
|
|
label: "Independent Clause",
|
|
},
|
|
{ text: ".", type: "punct" },
|
|
],
|
|
},
|
|
{
|
|
title: "Legal — Colon / Single Dash",
|
|
segments: [
|
|
{
|
|
text: "There was only one solution",
|
|
type: "ic",
|
|
label: "Independent Clause (required before colon)",
|
|
},
|
|
{ text: ":", type: "punct" },
|
|
{
|
|
text: " abandon the project entirely",
|
|
type: "modifier",
|
|
label: "Explanation / List",
|
|
},
|
|
{ text: ".", type: "punct" },
|
|
],
|
|
},
|
|
{
|
|
title: "Legal — Subordinating Conjunction",
|
|
segments: [
|
|
{
|
|
text: "Although the evidence was strong",
|
|
type: "modifier",
|
|
label: "Dependent Clause",
|
|
},
|
|
{ text: ",", type: "punct" },
|
|
{
|
|
text: " the committee remained skeptical",
|
|
type: "ic",
|
|
label: "Main Clause",
|
|
},
|
|
{ text: ".", type: "punct" },
|
|
],
|
|
},
|
|
{
|
|
title: "No Comma — FANBOYS joins a phrase (not two ICs)",
|
|
segments: [
|
|
{
|
|
text: "The researcher collected data",
|
|
type: "ic",
|
|
label: "Independent Clause",
|
|
},
|
|
{
|
|
text: " and",
|
|
type: "conjunction",
|
|
label: "FANBOYS — no comma (phrase follows)",
|
|
},
|
|
{
|
|
text: " analyzed results",
|
|
type: "modifier",
|
|
label: "Verb Phrase (no subject = not IC)",
|
|
},
|
|
{ text: ".", type: "punct" },
|
|
],
|
|
},
|
|
];
|
|
|
|
const TRANSITION_SUBTREE: TreeNode = {
|
|
id: "transition-check",
|
|
question:
|
|
"Is the connector a TRANSITION word (however, therefore, moreover, furthermore, consequently)?",
|
|
hint: "Transitions are NOT conjunctions. They cannot join two sentences on their own — they need a semicolon before them.",
|
|
yesLabel: "Yes — there is a transition word",
|
|
noLabel: "No — no transition word",
|
|
yes: {
|
|
id: "transition-fix",
|
|
question: "",
|
|
result:
|
|
"✓ Use a SEMICOLON before the transition: [IC]; however, [IC]. A comma before a transition creates a comma splice.",
|
|
resultType: "correct",
|
|
ruleRef: "[IC]; however, / therefore, / moreover, [IC]",
|
|
},
|
|
no: {
|
|
id: "no-transition",
|
|
question: "",
|
|
result:
|
|
"✓ Use a PERIOD or SEMICOLON to separate the two complete sentences.",
|
|
resultType: "correct",
|
|
ruleRef: "[IC]. [IC] or [IC]; [IC]",
|
|
},
|
|
};
|
|
|
|
const BOUNDARY_TREE: TreeNode = {
|
|
id: "root",
|
|
question:
|
|
"Is there a FANBOYS conjunction (for, and, nor, but, or, yet, so) between the two clauses?",
|
|
hint: "FANBOYS = For · And · Nor · But · Or · Yet · So. These 7 words can join independent clauses when preceded by a comma.",
|
|
yesLabel: "Yes — there is a FANBOYS conjunction",
|
|
noLabel: "No FANBOYS conjunction",
|
|
yes: {
|
|
id: "fanboys-check",
|
|
question:
|
|
"Can BOTH sides of the conjunction stand alone as complete sentences?",
|
|
hint: "Cover each side. Does each have its own subject and verb and express a complete thought?",
|
|
yesLabel: "Yes — both sides are complete sentences",
|
|
noLabel: "No — one side is a phrase or fragment",
|
|
yes: {
|
|
id: "fanboys-both-ic",
|
|
question: "",
|
|
result:
|
|
"✓ Use a COMMA before the FANBOYS conjunction: [IC], [FANBOYS] [IC].",
|
|
resultType: "correct",
|
|
ruleRef: "[Independent Clause], [FANBOYS] [Independent Clause]",
|
|
},
|
|
no: {
|
|
id: "fanboys-one-fragment",
|
|
question: "",
|
|
result:
|
|
"✗ No comma needed. The conjunction joins a clause to a phrase, not two full sentences.",
|
|
resultType: "info",
|
|
ruleRef: "[IC] [FANBOYS] [phrase] — no comma before conjunction",
|
|
},
|
|
},
|
|
no: {
|
|
id: "no-fanboys",
|
|
question:
|
|
"Is there a SUBORDINATING CONJUNCTION (because, although, since, when, while, if, unless, after, before)?",
|
|
hint: "Subordinating conjunctions make one clause dependent — this prevents a run-on without needing FANBOYS.",
|
|
yesLabel: "Yes — there is a subordinating conjunction",
|
|
noLabel: "No — no subordinating conjunction",
|
|
yes: {
|
|
id: "sub-conj",
|
|
question: "Does the dependent clause come BEFORE the main clause?",
|
|
hint: "If the dependent clause opens the sentence, a comma separates it from the main clause.",
|
|
yesLabel: "Yes — dependent clause comes first",
|
|
noLabel: "No — main clause comes first",
|
|
yes: {
|
|
id: "sub-first",
|
|
question: "",
|
|
result:
|
|
"✓ Use a COMMA after the dependent clause: [Subordinate clause], [Main clause].",
|
|
resultType: "correct",
|
|
ruleRef: "[Although/Because/When...], [Main clause]",
|
|
},
|
|
no: {
|
|
id: "sub-last",
|
|
question: "",
|
|
result:
|
|
"✓ No comma needed when the main clause comes first: [Main clause] [subordinate clause].",
|
|
resultType: "info",
|
|
ruleRef: "[Main clause] [because/although/when...]",
|
|
},
|
|
},
|
|
no: TRANSITION_SUBTREE,
|
|
},
|
|
};
|
|
|
|
const TREE_SCENARIOS: TreeScenario[] = [
|
|
{
|
|
label: "Sentence 1",
|
|
sentence:
|
|
"The data was collected over three years, the researchers published their findings.",
|
|
tree: BOUNDARY_TREE,
|
|
},
|
|
{
|
|
label: "Sentence 2",
|
|
sentence:
|
|
"The findings were unexpected however the team stood by their methodology.",
|
|
tree: BOUNDARY_TREE,
|
|
},
|
|
{
|
|
label: "Sentence 3",
|
|
sentence:
|
|
"Although the sample size was small the results were statistically significant.",
|
|
tree: BOUNDARY_TREE,
|
|
},
|
|
];
|
|
|
|
const EBRWBoundariesLesson: React.FC<LessonProps> = ({ onFinish }) => {
|
|
const [activeSection, setActiveSection] = useState(0);
|
|
const sectionsRef = useRef<(HTMLElement | null)[]>([]);
|
|
|
|
useEffect(() => {
|
|
const observers: IntersectionObserver[] = [];
|
|
sectionsRef.current.forEach((el, idx) => {
|
|
if (!el) return;
|
|
const obs = new IntersectionObserver(
|
|
([entry]) => {
|
|
if (entry.isIntersecting) setActiveSection(idx);
|
|
},
|
|
{ threshold: 0.3 },
|
|
);
|
|
obs.observe(el);
|
|
observers.push(obs);
|
|
});
|
|
return () => observers.forEach((o) => o.disconnect());
|
|
}, []);
|
|
|
|
useScrollReveal();
|
|
|
|
const scrollToSection = (index: number) => {
|
|
setActiveSection(index);
|
|
sectionsRef.current[index]?.scrollIntoView({ behavior: "smooth" });
|
|
};
|
|
|
|
const SectionMarker = ({
|
|
index,
|
|
title,
|
|
icon: Icon,
|
|
}: {
|
|
index: number;
|
|
title: string;
|
|
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
|
|
}) => {
|
|
const isActive = activeSection === index;
|
|
const isPast = activeSection > index;
|
|
return (
|
|
<button
|
|
onClick={() => scrollToSection(index)}
|
|
className={`flex items-center gap-3 p-3 w-full rounded-lg text-left transition-all ${isActive ? "bg-purple-50" : "hover:bg-slate-50"}`}
|
|
>
|
|
<div
|
|
className={`w-8 h-8 rounded-full flex items-center justify-center shrink-0
|
|
${isActive ? "bg-purple-600 text-white" : isPast ? "bg-purple-400 text-white" : "bg-slate-200 text-slate-500"}`}
|
|
>
|
|
{isPast ? (
|
|
<Check className="w-4 h-4" />
|
|
) : (
|
|
<Icon className="w-4 h-4" />
|
|
)}
|
|
</div>
|
|
<p
|
|
className={`text-sm font-bold ${isActive ? "text-purple-900" : "text-slate-600"}`}
|
|
>
|
|
{title}
|
|
</p>
|
|
</button>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div className="flex flex-col lg:flex-row min-h-screen">
|
|
<aside className="w-full lg:w-64 lg:fixed lg:top-14 lg:bottom-0 lg:overflow-y-auto p-4 border-r border-slate-200 bg-slate-50 z-0 hidden lg:block">
|
|
<nav className="space-y-2 pt-6">
|
|
<SectionMarker
|
|
index={0}
|
|
title="What Is a Sentence?"
|
|
icon={BookOpen}
|
|
/>
|
|
<SectionMarker
|
|
index={1}
|
|
title="Three Legal Separators"
|
|
icon={FileText}
|
|
/>
|
|
<SectionMarker
|
|
index={2}
|
|
title="Comma Splices & Traps"
|
|
icon={AlertTriangle}
|
|
/>
|
|
<SectionMarker index={3} title="Decision Tree Lab" icon={GitBranch} />
|
|
<SectionMarker index={4} title="Practice Questions" icon={Zap} />
|
|
</nav>
|
|
</aside>
|
|
|
|
<div className="flex-1 lg:ml-64 md:p-12 max-w-full mx-auto">
|
|
{/* ── Section 0: What Is a Sentence? ── */}
|
|
<section
|
|
ref={(el) => {
|
|
sectionsRef.current[0] = el;
|
|
}}
|
|
className="min-h-screen flex flex-col justify-center mb-24 pt-20 lg:pt-0"
|
|
>
|
|
<div className="inline-flex items-center gap-2 bg-purple-100 text-purple-700 px-3 py-1 rounded-full text-xs font-bold uppercase tracking-wider mb-4 w-fit">
|
|
Standard English Conventions — Domain 3
|
|
</div>
|
|
<h2 className="text-4xl font-extrabold text-slate-900 mb-2">
|
|
Boundaries
|
|
</h2>
|
|
<p className="text-lg text-slate-500 mb-8">
|
|
Every boundary question is really a sentence question: can you tell
|
|
where one sentence ends and another begins?
|
|
</p>
|
|
|
|
{/* §7.1 What Makes a Sentence */}
|
|
<div className="scroll-reveal stagger-1 rounded-2xl p-6 mb-8 bg-purple-50 border border-purple-200 space-y-4">
|
|
<h3 className="text-lg font-bold text-purple-900">
|
|
What Makes a Complete Sentence?
|
|
</h3>
|
|
<p className="text-sm text-slate-700">
|
|
A sentence (independent clause) requires all three elements. If
|
|
any is missing, the group of words is a{" "}
|
|
<span className="font-bold text-red-700">fragment</span>.
|
|
</p>
|
|
<div className="grid grid-cols-3 gap-3">
|
|
{[
|
|
{ req: "A Subject", ex: "The researcher" },
|
|
{ req: "A Verb", ex: "collected" },
|
|
{ req: "A Complete Thought", ex: "(no dangling subordinator)" },
|
|
].map((r) => (
|
|
<div
|
|
key={r.req}
|
|
className="card-tilt bg-white border border-purple-200 rounded-xl p-3 text-center"
|
|
>
|
|
<p className="font-bold text-purple-900 text-sm">{r.req}</p>
|
|
<p className="text-xs text-slate-500 mt-1 italic">{r.ex}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<p className="text-sm font-semibold text-slate-800">
|
|
Fragment Types — tap to reveal how to fix each:
|
|
</p>
|
|
<RevealCardGrid
|
|
cards={FRAGMENT_TYPES}
|
|
columns={3}
|
|
accentColor="purple"
|
|
/>
|
|
<div className="bg-red-50 border border-red-200 rounded-xl p-4">
|
|
<p className="font-bold text-red-800 text-sm mb-2">
|
|
Critical Warning: -ING Words Are Never the Main Verb
|
|
</p>
|
|
<p className="text-xs text-slate-600 mb-2">
|
|
A word ending in <span className="font-bold">-ing</span> can
|
|
never be the main verb of a sentence by itself. It always needs
|
|
a helping verb (is, was, are, were) or it functions as a
|
|
modifier.
|
|
</p>
|
|
<ul className="space-y-1 text-xs">
|
|
<li className="text-red-600">
|
|
✗ "The scientist <strong>discovering</strong> a new compound."
|
|
— fragment, no main verb
|
|
</li>
|
|
<li className="text-green-700">
|
|
✓ "The scientist <strong>was discovering</strong> a new
|
|
compound." — complete, "was" is the main verb
|
|
</li>
|
|
<li className="text-green-700">
|
|
✓ "<strong>Discovering</strong> a new compound, the scientist
|
|
published immediately." — modifier, "published" is the main
|
|
verb
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div className="bg-amber-50 border border-amber-200 rounded-xl p-4">
|
|
<p className="font-bold text-amber-800 text-sm mb-1">
|
|
Meaning ≠ Sentence Status
|
|
</p>
|
|
<p className="text-xs text-slate-700">
|
|
A group of words can sound complete and meaningful but still be
|
|
a fragment. Test it grammatically, not by how it sounds.{" "}
|
|
<em>"The data that confirmed the hypothesis"</em> sounds fine
|
|
but is a fragment — no verb applies to "the data."
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Group Pronouns */}
|
|
<div className="scroll-reveal stagger-2 rounded-2xl p-6 mb-8 bg-white border border-slate-200 space-y-4">
|
|
<h3 className="text-lg font-bold text-slate-900">
|
|
Group Pronouns: Who vs. That
|
|
</h3>
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div className="card-tilt bg-purple-50 border border-purple-200 rounded-xl p-4">
|
|
<p className="font-bold text-purple-800 text-sm mb-2">
|
|
WHO — for people
|
|
</p>
|
|
<p className="text-xs text-slate-600 mb-1">
|
|
Introduces a clause modifying a person or group of people.
|
|
</p>
|
|
<p className="text-xs italic text-green-700">
|
|
✓ "The researchers <strong>who</strong> studied the data…"
|
|
</p>
|
|
</div>
|
|
<div className="card-tilt bg-purple-50 border border-purple-200 rounded-xl p-4">
|
|
<p className="font-bold text-purple-800 text-sm mb-2">
|
|
THAT — for things
|
|
</p>
|
|
<p className="text-xs text-slate-600 mb-1">
|
|
Introduces a clause modifying a thing, idea, or place.
|
|
</p>
|
|
<p className="text-xs italic text-green-700">
|
|
✓ "The study <strong>that</strong> confirmed the data…"
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* The Four Sentence Types */}
|
|
<div className="scroll-reveal stagger-3 bg-purple-50 border border-purple-200 rounded-2xl p-6 mb-8 space-y-4">
|
|
<h3 className="text-lg font-bold text-purple-900">
|
|
The Four Sentence Types
|
|
</h3>
|
|
<p className="text-sm text-slate-700">
|
|
Understanding these types helps you identify where boundaries
|
|
belong.
|
|
</p>
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
{[
|
|
{
|
|
name: "Simple",
|
|
desc: "One independent clause.",
|
|
ex: '"The researcher collected data."',
|
|
},
|
|
{
|
|
name: "Compound",
|
|
desc: "Two independent clauses joined by a comma + FANBOYS or semicolon.",
|
|
ex: '"The data was clear, but the committee disagreed."',
|
|
},
|
|
{
|
|
name: "Complex",
|
|
desc: "One independent + one dependent clause.",
|
|
ex: '"Although the data was clear, the committee disagreed."',
|
|
},
|
|
{
|
|
name: "Compound-Complex",
|
|
desc: "Two independent clauses + at least one dependent.",
|
|
ex: '"Although the data was clear, the lead researcher presented it, and the committee reconsidered."',
|
|
},
|
|
].map((s) => (
|
|
<div
|
|
key={s.name}
|
|
className="card-tilt bg-white border border-purple-200 rounded-xl p-4"
|
|
>
|
|
<p className="font-bold text-purple-800 text-sm mb-1">
|
|
{s.name}
|
|
</p>
|
|
<p className="text-xs text-slate-600 mb-1">{s.desc}</p>
|
|
<p className="text-xs italic text-green-700">{s.ex}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* ── Section 1: Three Legal Separators ── */}
|
|
<section
|
|
ref={(el) => {
|
|
sectionsRef.current[1] = el;
|
|
}}
|
|
className="min-h-screen flex flex-col justify-center mb-24"
|
|
>
|
|
<h2 className="text-4xl font-extrabold text-slate-900 mb-2">
|
|
The Three Legal Separators
|
|
</h2>
|
|
<p className="text-lg text-slate-500 mb-8">
|
|
There are exactly three legal ways to separate two independent
|
|
clauses. Anything else is an error.
|
|
</p>
|
|
|
|
{/* Rule 1 */}
|
|
<div className="scroll-reveal stagger-1 rounded-2xl p-6 mb-8 bg-purple-50 border border-purple-200 space-y-4">
|
|
<div className="flex items-center gap-3">
|
|
<span className="w-8 h-8 rounded-full bg-purple-600 text-white flex items-center justify-center font-bold shrink-0">
|
|
1
|
|
</span>
|
|
<h3 className="text-lg font-bold text-purple-900">
|
|
Period = Semicolon
|
|
</h3>
|
|
</div>
|
|
<p className="text-sm text-slate-700">
|
|
A semicolon works{" "}
|
|
<span className="font-bold">exactly like a period</span> — it
|
|
separates two complete independent clauses. They are
|
|
interchangeable for boundary purposes.
|
|
</p>
|
|
<div className="bg-white border border-purple-200 rounded-xl p-4 space-y-1">
|
|
<p className="text-sm">
|
|
<span className="text-green-600 font-bold">✓</span>{" "}
|
|
<span className="text-slate-700">
|
|
The experiment failed. The researchers revised their
|
|
hypothesis.
|
|
</span>
|
|
</p>
|
|
<p className="text-sm">
|
|
<span className="text-green-600 font-bold">✓</span>{" "}
|
|
<span className="text-slate-700">
|
|
The experiment failed<span className="font-bold">;</span> the
|
|
researchers revised their hypothesis.
|
|
</span>
|
|
</p>
|
|
<p className="text-xs text-slate-500 mt-2">
|
|
Critical: never use a semicolon after a comma (,;) and never
|
|
before a FANBOYS conjunction.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Rule 2 */}
|
|
<div className="scroll-reveal stagger-2 rounded-2xl p-6 mb-8 bg-purple-50 border border-purple-200 space-y-4">
|
|
<div className="flex items-center gap-3">
|
|
<span className="w-8 h-8 rounded-full bg-purple-600 text-white flex items-center justify-center font-bold shrink-0">
|
|
2
|
|
</span>
|
|
<h3 className="text-lg font-bold text-purple-900">
|
|
Comma + FANBOYS
|
|
</h3>
|
|
</div>
|
|
<p className="text-sm text-slate-700">
|
|
<span className="font-bold">FANBOYS</span> = For · And · Nor · But
|
|
· Or · Yet · So. The comma comes{" "}
|
|
<span className="font-bold">before</span> the conjunction — never
|
|
after.
|
|
</p>
|
|
<div className="bg-amber-50 border border-amber-200 rounded-xl p-4">
|
|
<p className="text-xs font-bold text-amber-800 mb-2">
|
|
Critical Rule: Only use a comma when BOTH sides are independent
|
|
clauses
|
|
</p>
|
|
<p className="text-xs text-slate-600 mb-2">
|
|
If FANBOYS joins a clause to a{" "}
|
|
<span className="font-bold">phrase</span> (no second subject),
|
|
do NOT use a comma.
|
|
</p>
|
|
<ul className="space-y-1 text-xs">
|
|
<li className="text-red-600">
|
|
✗ "She ran to the lab, and collected the samples." —
|
|
"collected" is a phrase, not an IC
|
|
</li>
|
|
<li className="text-green-700">
|
|
✓ "She ran to the lab and collected the samples." — no comma,
|
|
FANBOYS joins two verbs
|
|
</li>
|
|
<li className="text-green-700">
|
|
✓ "She ran to the lab, and she collected the samples." —
|
|
comma, both sides are full ICs
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Rule 3 */}
|
|
<div className="scroll-reveal stagger-3 rounded-2xl p-6 mb-8 bg-purple-50 border border-purple-200 space-y-4">
|
|
<div className="flex items-center gap-3">
|
|
<span className="w-8 h-8 rounded-full bg-purple-600 text-white flex items-center justify-center font-bold shrink-0">
|
|
3
|
|
</span>
|
|
<h3 className="text-lg font-bold text-purple-900">
|
|
Colon = Single Em Dash
|
|
</h3>
|
|
</div>
|
|
<p className="text-sm text-slate-700">
|
|
A colon and a single em dash function identically: the material{" "}
|
|
<span className="font-bold">before</span> must be a complete
|
|
independent clause. What follows is an explanation, list, or
|
|
restatement.
|
|
</p>
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
<div className="card-tilt bg-white border border-green-200 rounded-xl p-4">
|
|
<p className="text-xs font-bold text-green-700 mb-2">
|
|
Legal — IC before colon
|
|
</p>
|
|
<p className="text-xs italic text-slate-600">
|
|
"There was only one option: abandon the project."
|
|
</p>
|
|
<p className="text-xs italic text-slate-600 mt-1">
|
|
"She brought three things: a notebook, a pen, and data."
|
|
</p>
|
|
</div>
|
|
<div className="card-tilt bg-white border border-red-200 rounded-xl p-4">
|
|
<p className="text-xs font-bold text-red-700 mb-2">
|
|
Illegal — fragment before colon
|
|
</p>
|
|
<p className="text-xs italic text-slate-600">
|
|
"The three items she needed: a notebook, a pen, and data."{" "}
|
|
<span className="text-red-500">(no verb before colon)</span>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div className="bg-purple-900 text-white rounded-xl p-4">
|
|
<p className="font-bold text-sm mb-1">Period/Colon Shortcut</p>
|
|
<p className="text-xs text-purple-100">
|
|
If an answer choice ends a clause with a period or colon, the
|
|
material before it must be a complete sentence. Use this to
|
|
immediately eliminate fragment answers.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* §7.5 Subordinating Conjunctions */}
|
|
<div className="scroll-reveal stagger-4 rounded-2xl p-6 mb-8 bg-white border border-slate-200 space-y-4">
|
|
<h3 className="text-lg font-bold text-slate-900">
|
|
Subordinating Conjunctions
|
|
</h3>
|
|
<p className="text-sm text-slate-600">
|
|
Subordinating conjunctions (because, although, since, when, while,
|
|
if, unless, after, before, as, once, until) create a dependent
|
|
clause — solving the run-on without needing FANBOYS.
|
|
</p>
|
|
<div className="space-y-2">
|
|
{[
|
|
{
|
|
rule: "Rule 1",
|
|
label: "Comma after dependent clause (when it comes first)",
|
|
ex: '"Although the results were surprising, the team continued." → Comma after the subordinate clause.',
|
|
},
|
|
{
|
|
rule: "Rule 2",
|
|
label:
|
|
"No comma before subordinate clause (when main clause comes first)",
|
|
ex: '"The team continued because the results were surprising." → No comma before "because."',
|
|
},
|
|
{
|
|
rule: "Rule 3",
|
|
label: "Do NOT use two conjunctions together",
|
|
ex: '"Although X, but Y" is wrong — use only one. Drop either "although" or "but."',
|
|
},
|
|
].map((r) => (
|
|
<div
|
|
key={r.rule}
|
|
className="flex gap-3 bg-purple-50 border border-purple-100 rounded-lg px-4 py-3"
|
|
>
|
|
<span className="w-6 h-6 rounded-full bg-purple-600 text-white flex items-center justify-center text-xs font-bold shrink-0">
|
|
{r.rule}
|
|
</span>
|
|
<div>
|
|
<p className="font-bold text-slate-800 text-xs">
|
|
{r.label}
|
|
</p>
|
|
<p className="text-xs text-slate-500 mt-0.5">{r.ex}</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="bg-red-50 border border-red-200 rounded-xl p-4">
|
|
<p className="font-bold text-red-800 text-sm mb-2">
|
|
Double Conjunction Error (Very Common on SAT)
|
|
</p>
|
|
<ul className="space-y-1 text-xs">
|
|
<li className="text-red-600">
|
|
✗ "<strong>Although</strong> the data was convincing,{" "}
|
|
<strong>but</strong> the committee rejected it."
|
|
</li>
|
|
<li className="text-red-600">
|
|
✗ "<strong>Because</strong> the sample was large,{" "}
|
|
<strong>so</strong> the results were significant."
|
|
</li>
|
|
<li className="text-green-700">
|
|
✓ "Although the data was convincing, the committee rejected
|
|
it." (drop "but")
|
|
</li>
|
|
<li className="text-green-700">
|
|
✓ "The data was convincing, but the committee rejected it."
|
|
(drop "although")
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Clause Anatomy Widget */}
|
|
<div className="scroll-reveal stagger-5 rounded-2xl p-6 mb-8 bg-white border border-slate-200 space-y-4">
|
|
<h3 className="text-lg font-bold text-slate-900">Clause Anatomy</h3>
|
|
<p className="text-sm text-slate-500">
|
|
Hover over each segment to see its grammatical role and why the
|
|
boundary is legal.
|
|
</p>
|
|
<ClauseBreakdownWidget
|
|
examples={CLAUSE_EXAMPLES}
|
|
accentColor="purple"
|
|
/>
|
|
</div>
|
|
</section>
|
|
|
|
{/* ── Section 2: Comma Splices & Hidden Traps ── */}
|
|
<section
|
|
ref={(el) => {
|
|
sectionsRef.current[2] = el;
|
|
}}
|
|
className="min-h-screen flex flex-col justify-center mb-24"
|
|
>
|
|
<h2 className="text-4xl font-extrabold text-slate-900 mb-2">
|
|
Comma Splices & Hidden Traps
|
|
</h2>
|
|
<p className="text-lg text-slate-500 mb-8">
|
|
The SAT hides boundary errors behind familiar-looking patterns.
|
|
Learn to spot all of them.
|
|
</p>
|
|
|
|
{/* §7.3 Comma Splice Patterns */}
|
|
<div className="scroll-reveal stagger-1 rounded-2xl p-6 mb-8 bg-purple-50 border border-purple-200 space-y-4">
|
|
<h3 className="text-lg font-bold text-purple-900">
|
|
Comma Splice Trigger Patterns
|
|
</h3>
|
|
<p className="text-sm text-slate-700">
|
|
A comma splice is two independent clauses joined by a comma alone.
|
|
The SAT uses predictable structures to hide them.
|
|
</p>
|
|
|
|
<div className="bg-white border border-red-200 rounded-xl p-4 space-y-3">
|
|
<p className="font-bold text-red-800 text-sm">
|
|
Pattern 1: Comma + Pronoun
|
|
</p>
|
|
<p className="text-xs text-slate-600">
|
|
The most common comma splice on the SAT. A pronoun (he, she, it,
|
|
they, this, that) after a comma usually signals a new
|
|
independent clause.
|
|
</p>
|
|
<ul className="space-y-1 text-xs">
|
|
<li className="text-red-600">
|
|
✗ "The study examined 1,000 participants,{" "}
|
|
<strong>they</strong> were all volunteers."
|
|
</li>
|
|
<li className="text-green-700">
|
|
✓ "The study examined 1,000 participants
|
|
<strong>; they</strong> were all volunteers."
|
|
</li>
|
|
<li className="text-green-700">
|
|
✓ "The study examined 1,000 participants,{" "}
|
|
<strong>all of whom</strong> were volunteers." (relative
|
|
clause, not IC)
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div className="bg-white border border-red-200 rounded-xl p-4 space-y-3">
|
|
<p className="font-bold text-red-800 text-sm">
|
|
Pattern 2: Comma + Conjunctive Adverb (The Transition Trap)
|
|
</p>
|
|
<p className="text-xs text-slate-600">
|
|
Words like{" "}
|
|
<span className="font-bold">
|
|
however, therefore, moreover, furthermore, consequently,
|
|
nevertheless
|
|
</span>{" "}
|
|
are transition adverbs — not conjunctions. They cannot join two
|
|
clauses with only a comma.
|
|
</p>
|
|
<ul className="space-y-1 text-xs">
|
|
<li className="text-red-600">
|
|
✗ "The data was inconclusive, <strong>however</strong> the
|
|
team continued." — comma splice
|
|
</li>
|
|
<li className="text-green-700">
|
|
✓ "The data was inconclusive<strong>; however,</strong> the
|
|
team continued."
|
|
</li>
|
|
<li className="text-green-700">
|
|
✓ "The data was inconclusive. The team,{" "}
|
|
<strong>however,</strong> continued." — mid-sentence position
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div className="bg-purple-900 text-white rounded-xl p-4">
|
|
<p className="font-bold text-sm mb-1">
|
|
Three Legal Positions for Transition Words
|
|
</p>
|
|
<ul className="space-y-1 text-xs text-purple-100">
|
|
<li>
|
|
<span className="font-bold">Start:</span> "[IC]. However,
|
|
[IC]." — period, then transition at start of new sentence.
|
|
</li>
|
|
<li>
|
|
<span className="font-bold">Middle:</span> "[IC]; however,
|
|
[IC]." — semicolon before, comma after.
|
|
</li>
|
|
<li>
|
|
<span className="font-bold">Interior:</span> "[IC]. The team,
|
|
however, continued." — inserted mid-sentence between two
|
|
commas.
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div className="bg-amber-50 border border-amber-200 rounded-xl p-4">
|
|
<p className="font-bold text-amber-800 text-sm mb-1">
|
|
Edge Case: Colon or Dash After a Non-Essential Transition
|
|
</p>
|
|
<p className="text-xs text-slate-600 mb-2">
|
|
When a transition word is set off as non-essential (between
|
|
commas), you may see a colon or dash immediately after the
|
|
closing comma. This is legal because the colon/dash follows the
|
|
full independent clause, not the transition itself.
|
|
</p>
|
|
<p className="text-xs text-green-700 mb-2">
|
|
✓ "The data, however, pointed to one conclusion: the hypothesis
|
|
was wrong."
|
|
</p>
|
|
<p className="text-xs text-slate-500 italic">
|
|
The colon follows the IC "The data...pointed to one conclusion"
|
|
— the transition "however" is just a parenthetical insert.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* §7.4 Hidden Boundary Phrases */}
|
|
<div className="scroll-reveal stagger-2 rounded-2xl p-6 mb-8 bg-white border border-slate-200 space-y-4">
|
|
<h3 className="text-lg font-bold text-slate-900">
|
|
Hidden Boundary Phrases
|
|
</h3>
|
|
<p className="text-sm text-slate-600">
|
|
These three constructions appear innocent but create boundary
|
|
problems if not punctuated correctly.
|
|
</p>
|
|
<div className="space-y-3">
|
|
{[
|
|
{
|
|
type: "Participial Phrase (-ing modifier)",
|
|
rule: "An -ing phrase at the start of a sentence modifies the subject of the main clause. Separate it with a comma.",
|
|
bad: '"Running the experiment the researcher observed unexpected results."',
|
|
good: '"Running the experiment, the researcher observed unexpected results."',
|
|
note: 'The -ing phrase is NOT a main verb — "observed" is the main verb.',
|
|
},
|
|
{
|
|
type: "With...-ing Construction",
|
|
rule: '"With + noun + -ing" is a modifying phrase attached to the main clause with a comma.',
|
|
bad: '"With data showing a 40% increase scientists declared success."',
|
|
good: '"With data showing a 40% increase, scientists declared success."',
|
|
note: 'The entire "with…" phrase is a modifier, not a main clause.',
|
|
},
|
|
{
|
|
type: "Which Clause",
|
|
rule: 'A "which" clause is non-essential — it needs a comma before it. It modifies the noun immediately preceding it.',
|
|
bad: '"The result which surprised everyone was published."',
|
|
good: '"The result, which surprised everyone, was published."',
|
|
note: '"Which" clauses always get commas. "That" clauses never do.',
|
|
},
|
|
].map((p) => (
|
|
<div
|
|
key={p.type}
|
|
className="card-tilt bg-slate-50 border border-slate-200 rounded-xl p-4"
|
|
>
|
|
<p className="font-bold text-purple-800 text-sm mb-1">
|
|
{p.type}
|
|
</p>
|
|
<p className="text-xs text-slate-500 mb-2">{p.rule}</p>
|
|
<ul className="space-y-1 text-xs">
|
|
<li className="text-red-600">
|
|
✗ <em>{p.bad}</em>
|
|
</li>
|
|
<li className="text-green-700">
|
|
✓ <em>{p.good}</em>
|
|
</li>
|
|
<li className="text-slate-400 italic mt-1">{p.note}</li>
|
|
</ul>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="bg-amber-50 border border-amber-200 rounded-xl p-4">
|
|
<p className="font-bold text-amber-800 text-sm mb-1">
|
|
The "Which-Clause-Does-It-Belong-To" Problem
|
|
</p>
|
|
<p className="text-xs text-slate-600 mb-2">
|
|
A "which" clause must immediately follow the noun it modifies.
|
|
Otherwise it is a misplaced modifier.
|
|
</p>
|
|
<ul className="space-y-1 text-xs">
|
|
<li className="text-red-600">
|
|
✗ "The scientist published the paper in the journal,{" "}
|
|
<strong>which</strong> was groundbreaking." — was the journal
|
|
or paper groundbreaking?
|
|
</li>
|
|
<li className="text-green-700">
|
|
✓ "The scientist published the paper, <strong>which</strong>{" "}
|
|
was groundbreaking, in the journal."
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="scroll-reveal-scale golden-rule-glow bg-purple-900 text-white rounded-2xl p-5 mb-8">
|
|
<p className="font-bold mb-1">Golden Rule</p>
|
|
<p className="text-sm text-purple-100">
|
|
A comma alone can NEVER join two independent clauses. Semicolons =
|
|
periods. Transition words (however, therefore) need a semicolon or
|
|
period before them — never just a comma. -ING words are never the
|
|
main verb.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
{/* ── Section 3: Decision Tree Lab ── */}
|
|
<section
|
|
ref={(el) => {
|
|
sectionsRef.current[3] = el;
|
|
}}
|
|
className="min-h-screen flex flex-col justify-center mb-24"
|
|
>
|
|
<h2 className="text-4xl font-extrabold text-slate-900 mb-2">
|
|
Decision Tree Lab
|
|
</h2>
|
|
<p className="text-lg text-slate-500 mb-8">
|
|
Work through the boundary logic step by step to identify errors and
|
|
apply the correct fix.
|
|
</p>
|
|
<div className="space-y-3 mb-8">
|
|
{[
|
|
{
|
|
label: "Comma Splice",
|
|
desc: "Two complete sentences joined by a comma alone — the most tested boundary error on the SAT.",
|
|
},
|
|
{
|
|
label: "Run-On Sentence",
|
|
desc: "Two complete sentences with no punctuation between them at all.",
|
|
},
|
|
{
|
|
label: "Transition Trap",
|
|
desc: 'Using "however" or "therefore" after only a comma. These words need a semicolon before them.',
|
|
},
|
|
].map((t) => (
|
|
<div
|
|
key={t.label}
|
|
className="flex gap-3 bg-red-50 border border-red-200 rounded-xl px-4 py-3"
|
|
>
|
|
<AlertTriangle className="w-4 h-4 text-red-500 shrink-0 mt-0.5" />
|
|
<div>
|
|
<p className="text-sm font-bold text-red-800">{t.label}</p>
|
|
<p className="text-xs text-slate-600 mt-0.5">{t.desc}</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<DecisionTreeWidget scenarios={TREE_SCENARIOS} accentColor="purple" />
|
|
</section>
|
|
|
|
{/* ── Section 4: Practice ── */}
|
|
<section
|
|
ref={(el) => {
|
|
sectionsRef.current[4] = el;
|
|
}}
|
|
className="min-h-screen flex flex-col justify-center mb-24"
|
|
>
|
|
<h2 className="text-4xl font-extrabold text-slate-900 mb-6">
|
|
Practice Questions
|
|
</h2>
|
|
{BOUNDARIES_EASY.slice(0, 2).map((q) => (
|
|
<PracticeFromDataset key={q.id} question={q} color="purple" />
|
|
))}
|
|
{BOUNDARIES_MEDIUM.slice(0, 1).map((q) => (
|
|
<PracticeFromDataset key={q.id} question={q} color="purple" />
|
|
))}
|
|
<div className="mt-8 text-center">
|
|
<button
|
|
onClick={onFinish}
|
|
className="px-6 py-3 bg-purple-900 text-white font-bold rounded-full hover:bg-purple-700 transition-colors"
|
|
>
|
|
Finish Lesson ✓
|
|
</button>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default EBRWBoundariesLesson;
|