942 lines
38 KiB
TypeScript
942 lines
38 KiB
TypeScript
import React, { useRef, useState, useEffect } from "react";
|
|
import {
|
|
Check,
|
|
BookOpen,
|
|
AlertTriangle,
|
|
Zap,
|
|
Clock,
|
|
Users,
|
|
GitBranch,
|
|
} from "lucide-react";
|
|
import { PracticeFromDataset } from "../../../components/lessons/LessonShell";
|
|
import {
|
|
FORM_STRUCTURE_EASY,
|
|
FORM_STRUCTURE_MEDIUM,
|
|
} from "../../../data/rw/form-structure-sense";
|
|
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 SVA_DISGUISES: RevealCard[] = [
|
|
{
|
|
label: "Prepositional Phrase Decoy",
|
|
sublabel: "The list [of requirements] __ long.",
|
|
content: 'Cross out the phrase. "List" is singular → IS long.',
|
|
},
|
|
{
|
|
label: "Inverted Order (There is/are)",
|
|
sublabel: "There __ many reasons for the decision.",
|
|
content:
|
|
'"Many reasons" is the subject (plural) → THERE ARE. Flip: "Many reasons there are."',
|
|
},
|
|
{
|
|
label: "Gerund Subject",
|
|
sublabel: "Studying the data __ essential.",
|
|
content:
|
|
"A gerund (-ing phrase) as subject is always singular → IS essential.",
|
|
},
|
|
{
|
|
label: "Collective Nouns",
|
|
sublabel: "The committee __ voted unanimously.",
|
|
content:
|
|
"Committee = one group = singular → HAS voted. Same for team, group, family, audience, class.",
|
|
},
|
|
{
|
|
label: "Indefinite Pronouns",
|
|
sublabel: "Each of the participants __ required.",
|
|
content: "Each is always singular → IS required.",
|
|
},
|
|
{
|
|
label: "Neither/Nor Proximity",
|
|
sublabel: "Neither the principal nor the teachers __",
|
|
content:
|
|
"Or/nor: agree with the NEAREST noun → teachers (plural) → ARE responsible.",
|
|
},
|
|
];
|
|
|
|
const PRONOUN_RULES: RevealCard[] = [
|
|
{
|
|
label: "Number Agreement",
|
|
content:
|
|
'Pronoun must match its antecedent in number. "Each student should bring their own materials" is accepted on SAT for singular "they."',
|
|
},
|
|
{
|
|
label: "This/That Must Have a Clear Referent",
|
|
content:
|
|
'Always make sure "this" or "that" refers to a specific noun, not a vague idea. If unclear, replace with the noun.',
|
|
},
|
|
{
|
|
label: "Emphatic Pronouns",
|
|
content:
|
|
'These (himself/herself/themselves) can NEVER be the subject. ✗ "Himself was the only one who knew." ✓ "He himself was the only one who knew."',
|
|
},
|
|
{
|
|
label: "Missing Antecedent",
|
|
content:
|
|
'Every pronoun must refer to a specific named noun. If "they" could refer to two different nouns, replace it with the noun it means.',
|
|
},
|
|
{
|
|
label: "Person Consistency",
|
|
content:
|
|
'Do not switch between "one," "you," and "we" within the same passage. Match the pronoun person already established.',
|
|
},
|
|
];
|
|
|
|
const SVA_TREE: TreeNode = {
|
|
id: "sva-root",
|
|
question:
|
|
"Is there a prepositional phrase or interrupting clause BETWEEN the subject and verb?",
|
|
hint: 'Cross out everything introduced by "of," "in," "with," "for," "by," "including," etc., up to the next punctuation.',
|
|
yesLabel: "Yes — there is interrupting material",
|
|
noLabel: "No — subject and verb are adjacent",
|
|
yes: {
|
|
id: "cross-out",
|
|
question:
|
|
"After crossing out the interrupting material, is the true subject singular or plural?",
|
|
hint: "The true subject is the noun BEFORE the interrupting phrase. Ignore the noun inside the phrase.",
|
|
yesLabel: "Singular subject",
|
|
noLabel: "Plural subject",
|
|
yes: {
|
|
id: "singular-verb",
|
|
question: "",
|
|
result:
|
|
"✓ Use a SINGULAR verb (is, was, has, does). The interrupting phrase cannot change the subject.",
|
|
resultType: "correct",
|
|
ruleRef: 'The list [of requirements] IS long — not "are"',
|
|
},
|
|
no: {
|
|
id: "plural-verb",
|
|
question: "",
|
|
result:
|
|
"✓ Use a PLURAL verb (are, were, have, do). The interrupting phrase cannot change the subject.",
|
|
resultType: "correct",
|
|
ruleRef: 'The scientists [in the lab] ARE conducting — not "is"',
|
|
},
|
|
},
|
|
no: {
|
|
id: "no-interrupt",
|
|
question:
|
|
"Is the subject an indefinite pronoun (each, every, either, neither, anyone, everyone, someone, nobody)?",
|
|
hint: "These pronouns are always grammatically singular even when they refer to groups.",
|
|
yesLabel: "Yes — indefinite pronoun subject",
|
|
noLabel: "No — regular noun or pronoun",
|
|
yes: {
|
|
id: "indefinite",
|
|
question: "",
|
|
result:
|
|
"✓ Use a SINGULAR verb. Each, every, either, neither, anyone, everyone, someone, nobody are all grammatically singular.",
|
|
resultType: "correct",
|
|
ruleRef: 'Each of the students IS responsible — not "are"',
|
|
},
|
|
no: {
|
|
id: "regular-agree",
|
|
question: "",
|
|
result:
|
|
"✓ Match the verb to the subject normally: singular noun = singular verb; plural noun = plural verb.",
|
|
resultType: "info",
|
|
ruleRef: "Standard agreement: The dog barks / The dogs bark",
|
|
},
|
|
},
|
|
};
|
|
|
|
const TREE_SCENARIOS: TreeScenario[] = [
|
|
{
|
|
label: "SVA 1",
|
|
sentence:
|
|
"The collection of ancient manuscripts were donated to the university library.",
|
|
tree: SVA_TREE,
|
|
},
|
|
{
|
|
label: "SVA 2",
|
|
sentence:
|
|
"Neither of the proposed solutions address the root cause of the problem.",
|
|
tree: SVA_TREE,
|
|
},
|
|
{
|
|
label: "SVA 3",
|
|
sentence:
|
|
"The team of researchers have published their findings in three separate journals.",
|
|
tree: SVA_TREE,
|
|
},
|
|
];
|
|
|
|
const EBRWFormStructureSenseLesson: 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.ElementType;
|
|
}) => {
|
|
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" />
|
|
) : (
|
|
// @ts-ignore
|
|
<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="Subject-Verb Agreement"
|
|
icon={BookOpen}
|
|
/>
|
|
<SectionMarker
|
|
index={1}
|
|
title="Verb Forms & Tense"
|
|
icon={Clock}
|
|
/>
|
|
<SectionMarker
|
|
index={2}
|
|
title="Pronouns & Apostrophes"
|
|
icon={Users}
|
|
/>
|
|
<SectionMarker
|
|
index={3}
|
|
title="Modifiers & Parallel"
|
|
icon={AlertTriangle}
|
|
/>
|
|
<SectionMarker index={4} title="SVA Decision Tree" icon={GitBranch} />
|
|
<SectionMarker index={5} title="Practice Questions" icon={Zap} />
|
|
</nav>
|
|
</aside>
|
|
|
|
<div className="flex-1 lg:ml-64 md:p-12 max-w-full mx-auto">
|
|
{/* ── Section 0: Subject-Verb Agreement ── */}
|
|
<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">
|
|
Form, Structure & Sense
|
|
</h2>
|
|
<p className="text-lg text-slate-500 mb-8">
|
|
Seven grammar skills: subject-verb agreement, verb forms, pronouns,
|
|
apostrophes, modification, parallel structure, and comma usage.
|
|
</p>
|
|
|
|
{/* §8.1 SVA */}
|
|
<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">
|
|
Subject-Verb Agreement: All 6 Disguises
|
|
</h3>
|
|
<p className="text-sm text-slate-700">
|
|
The SAT hides the subject behind phrases and structural tricks.
|
|
Tap each disguise to see how to solve it:
|
|
</p>
|
|
<RevealCardGrid
|
|
cards={SVA_DISGUISES}
|
|
columns={3}
|
|
accentColor="purple"
|
|
/>
|
|
<div className="bg-white border border-purple-200 rounded-xl p-4">
|
|
<p className="font-bold text-purple-800 text-sm mb-2">
|
|
Indefinite Pronoun Quick Reference
|
|
</p>
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full text-xs border-collapse rounded-xl overflow-hidden">
|
|
<thead>
|
|
<tr className="bg-purple-100">
|
|
<th className="px-3 py-1 text-left text-purple-800">
|
|
Always Singular
|
|
</th>
|
|
<th className="px-3 py-1 text-left text-purple-800">
|
|
Always Plural
|
|
</th>
|
|
<th className="px-3 py-1 text-left text-purple-800">
|
|
Context-Dependent
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr className="bg-white">
|
|
<td className="px-3 py-2 text-slate-600">
|
|
each, every, either, neither, one, anyone, everyone,
|
|
someone, no one, nobody, somebody, everybody, anything,
|
|
everything, something, nothing
|
|
</td>
|
|
<td className="px-3 py-2 text-slate-600">
|
|
both, few, many, several, others
|
|
</td>
|
|
<td className="px-3 py-2 text-slate-600">
|
|
all, any, more, most, none, some — match to the noun in
|
|
the "of" phrase: "most of the water IS" / "most of the
|
|
results ARE"
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* ── Section 1: Verb Forms & Tense ── */}
|
|
<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">
|
|
Verb Forms & Tense
|
|
</h2>
|
|
<p className="text-lg text-slate-500 mb-8">
|
|
The SAT tests specific verb form errors, not just tense consistency.
|
|
</p>
|
|
|
|
{/* Tense Table */}
|
|
<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">
|
|
Tense Consistency
|
|
</h3>
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full text-sm border-collapse rounded-xl overflow-hidden">
|
|
<thead>
|
|
<tr className="bg-purple-600 text-white">
|
|
<th className="px-3 py-2 text-left text-xs">Tense</th>
|
|
<th className="px-3 py-2 text-left text-xs">
|
|
Signal Words
|
|
</th>
|
|
<th className="px-3 py-2 text-left text-xs">Example</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{[
|
|
[
|
|
"Simple Present",
|
|
"now, every day, always, usually, generally",
|
|
"The study examines 500 participants.",
|
|
],
|
|
[
|
|
"Simple Past",
|
|
"yesterday, last year, in 1990, ago",
|
|
"The study examined 500 participants.",
|
|
],
|
|
[
|
|
"Present Perfect",
|
|
"since, for, recently, already, yet",
|
|
"Researchers have found new evidence.",
|
|
],
|
|
[
|
|
"Past Perfect",
|
|
"before, by the time, had already",
|
|
"By 2020, scientists had confirmed the theory.",
|
|
],
|
|
[
|
|
"Future",
|
|
"tomorrow, next year, will",
|
|
"The team will publish next month.",
|
|
],
|
|
[
|
|
"Progressive",
|
|
"currently, at the moment, was/is + -ing",
|
|
"She was collecting data when the power failed.",
|
|
],
|
|
].map(([tense, signals, ex], i) => (
|
|
<tr
|
|
key={tense}
|
|
className={i % 2 === 0 ? "bg-white" : "bg-purple-50"}
|
|
>
|
|
<td className="px-3 py-2 font-bold text-purple-800 text-xs">
|
|
{tense}
|
|
</td>
|
|
<td className="px-3 py-2 text-slate-500 text-xs">
|
|
{signals}
|
|
</td>
|
|
<td className="px-3 py-2 text-slate-700 text-xs italic">
|
|
{ex}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Past Conditional */}
|
|
<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">
|
|
Past Conditional & Irregular Past Participles
|
|
</h3>
|
|
<div className="bg-purple-50 border border-purple-200 rounded-xl p-4">
|
|
<p className="font-bold text-purple-800 text-sm mb-2">
|
|
Past Conditional (Counterfactual)
|
|
</p>
|
|
<p className="text-xs text-slate-600 mb-2">
|
|
Use <span className="font-bold">had + past participle</span> in
|
|
the "if" clause and{" "}
|
|
<span className="font-bold">would have + past participle</span>{" "}
|
|
in the result clause.
|
|
</p>
|
|
<ul className="space-y-1 text-xs">
|
|
<li className="text-red-600">
|
|
✗ "If she would have studied harder, she would have passed."
|
|
</li>
|
|
<li className="text-green-700">
|
|
✓ "If she <strong>had studied</strong> harder, she{" "}
|
|
<strong>would have passed</strong>."
|
|
</li>
|
|
<li className="text-slate-400 italic mt-1">
|
|
The SAT frequently places "would have" in the if-clause —
|
|
always wrong.
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full text-xs border-collapse rounded-xl overflow-hidden">
|
|
<thead>
|
|
<tr className="bg-slate-700 text-white">
|
|
<th className="px-3 py-2 text-left">Base Form</th>
|
|
<th className="px-3 py-2 text-left">Simple Past</th>
|
|
<th className="px-3 py-2 text-left">
|
|
Past Participle (with have/had)
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{[
|
|
["go", "went", "gone (had gone ✓ / had went ✗)"],
|
|
["rise", "rose", "risen (had risen ✓ / had rose ✗)"],
|
|
["lie (recline)", "lay", "lain (had lain ✓)"],
|
|
["lay (place)", "laid", "laid (had laid ✓)"],
|
|
["choose", "chose", "chosen (had chosen ✓)"],
|
|
["swim", "swam", "swum (had swum ✓ / had swam ✗)"],
|
|
["begin", "began", "begun (had begun ✓ / had began ✗)"],
|
|
["see", "saw", "seen (had seen ✓ / had saw ✗)"],
|
|
].map(([base, past, participle], i) => (
|
|
<tr
|
|
key={base}
|
|
className={i % 2 === 0 ? "bg-white" : "bg-slate-50"}
|
|
>
|
|
<td className="px-3 py-2 font-bold text-purple-800">
|
|
{base}
|
|
</td>
|
|
<td className="px-3 py-2 text-slate-600">{past}</td>
|
|
<td className="px-3 py-2 text-slate-700">{participle}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div className="bg-amber-50 border border-amber-200 rounded-xl p-4">
|
|
<p className="font-bold text-amber-800 text-sm mb-2">
|
|
To vs. -ing After Certain Verbs
|
|
</p>
|
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 text-xs">
|
|
{[
|
|
{
|
|
label: "Verbs that take TO",
|
|
words:
|
|
"want, hope, plan, decide, need, agree, refuse, offer, expect, seem",
|
|
},
|
|
{
|
|
label: "Verbs that take -ING",
|
|
words:
|
|
"enjoy, avoid, finish, consider, deny, practice, suggest, admit, keep, quit",
|
|
},
|
|
{
|
|
label: "Meaning changes",
|
|
words:
|
|
"stop to do (pause) / stop doing (cease); remember to do / remember doing",
|
|
},
|
|
].map((v) => (
|
|
<div key={v.label}>
|
|
<p className="font-bold text-amber-700 mb-1">{v.label}</p>
|
|
<p className="text-slate-600">{v.words}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<div className="bg-purple-50 border border-purple-200 rounded-xl p-4">
|
|
<p className="font-bold text-purple-800 text-sm mb-2">
|
|
Passive Voice
|
|
</p>
|
|
<p className="text-xs text-slate-600 mb-3">
|
|
Passive voice reverses the usual subject-verb-object order: the
|
|
receiver of the action becomes the grammatical subject. The SAT
|
|
does not treat passive voice as inherently wrong — but it tests
|
|
whether you can identify the correct form.
|
|
</p>
|
|
<ul className="space-y-1 text-xs mb-3">
|
|
<li className="text-slate-600">
|
|
<span className="font-bold">Active:</span> "The researcher
|
|
collected the data."
|
|
</li>
|
|
<li className="text-slate-600">
|
|
<span className="font-bold">Passive:</span> "The data was
|
|
collected by the researcher."
|
|
</li>
|
|
</ul>
|
|
<p className="text-xs text-slate-500 italic">
|
|
Passive is formed with a form of 'be' + past participle. On the
|
|
SAT, check whether the passive form matches the correct tense
|
|
and whether the subject-verb agreement is correct in the passive
|
|
construction.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* ── Section 2: Pronouns & Apostrophes ── */}
|
|
<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">
|
|
Pronouns & Apostrophes
|
|
</h2>
|
|
<p className="text-lg text-slate-500 mb-8">
|
|
Pronoun errors are among the most commonly tested items in Form,
|
|
Structure & Sense.
|
|
</p>
|
|
|
|
{/* Relative Pronouns */}
|
|
<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">Pronoun Rules</h3>
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full text-sm border-collapse rounded-xl overflow-hidden">
|
|
<thead>
|
|
<tr className="bg-purple-600 text-white">
|
|
<th className="px-3 py-2 text-left text-xs">Pronoun</th>
|
|
<th className="px-3 py-2 text-left text-xs">Used For</th>
|
|
<th className="px-3 py-2 text-left text-xs">Example</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{[
|
|
[
|
|
"who / whom",
|
|
"People only",
|
|
'"the researcher who discovered…" / "the scientist whom they selected…"',
|
|
],
|
|
[
|
|
"which",
|
|
"Things / non-people (non-essential)",
|
|
'"the study, which examined 500 people…" — always gets a comma',
|
|
],
|
|
[
|
|
"that",
|
|
"Things / non-people (essential)",
|
|
'"the study that confirmed the theory" — no comma',
|
|
],
|
|
[
|
|
"where",
|
|
"Places",
|
|
'"the laboratory where the experiment occurred…"',
|
|
],
|
|
["when", "Times", '"the decade when the research began…"'],
|
|
].map(([pron, use, ex], i) => (
|
|
<tr
|
|
key={pron}
|
|
className={i % 2 === 0 ? "bg-white" : "bg-purple-50"}
|
|
>
|
|
<td className="px-3 py-2 font-bold text-purple-800 text-xs whitespace-nowrap">
|
|
{pron}
|
|
</td>
|
|
<td className="px-3 py-2 text-slate-600 text-xs">
|
|
{use}
|
|
</td>
|
|
<td className="px-3 py-2 text-slate-600 text-xs italic">
|
|
{ex}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div className="bg-purple-900 text-white rounded-xl p-4">
|
|
<p className="font-bold text-sm mb-1">
|
|
Who vs. Whom — Substitution Test
|
|
</p>
|
|
<ul className="text-xs text-purple-100 space-y-1">
|
|
<li>
|
|
<span className="font-bold">Who</span> = subject (substitutes
|
|
he/she): "Who wrote the report?" → "He wrote it." ✓
|
|
</li>
|
|
<li>
|
|
<span className="font-bold">Whom</span> = object (substitutes
|
|
him/her): "To whom did you give it?" → "You gave it to him." ✓
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<p className="text-sm font-semibold text-slate-800">
|
|
Additional Pronoun Rules — tap to reveal:
|
|
</p>
|
|
<RevealCardGrid
|
|
cards={PRONOUN_RULES}
|
|
columns={3}
|
|
accentColor="purple"
|
|
/>
|
|
</div>
|
|
|
|
{/* Apostrophes */}
|
|
<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">Apostrophes</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">
|
|
Noun Apostrophes (Possession)
|
|
</p>
|
|
<ul className="space-y-1 text-xs text-slate-700">
|
|
<li>
|
|
<span className="font-bold">Singular noun:</span> add 's →
|
|
the scientist's data
|
|
</li>
|
|
<li>
|
|
<span className="font-bold">Plural noun ending in s:</span>{" "}
|
|
add ' → the scientists' lab
|
|
</li>
|
|
<li>
|
|
<span className="font-bold">
|
|
Plural noun NOT ending in s:
|
|
</span>{" "}
|
|
add 's → the children's results
|
|
</li>
|
|
</ul>
|
|
</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">
|
|
Pronoun Apostrophes (Contractions Only)
|
|
</p>
|
|
<div className="grid grid-cols-2 gap-x-2 gap-y-1 text-xs">
|
|
{[
|
|
["its", "possessive"],
|
|
["it's", "it is / it has"],
|
|
["their", "possessive"],
|
|
["they're", "they are"],
|
|
["whose", "possessive"],
|
|
["who's", "who is / who has"],
|
|
["your", "possessive"],
|
|
["you're", "you are"],
|
|
].map(([form, meaning]) => (
|
|
<div key={form} className="flex gap-1">
|
|
<span className="font-bold text-purple-700">{form}</span>
|
|
<span className="text-slate-500">= {meaning}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</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">
|
|
Apostrophe Shortcut
|
|
</p>
|
|
<p className="text-xs text-slate-700">
|
|
If you can read the word as two words (it is → it's), it's a
|
|
contraction — use the apostrophe. Pronouns{" "}
|
|
<span className="font-bold">never</span> use apostrophes for
|
|
possession (opposite of nouns).
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* ── Section 3: Modifiers, Parallel Structure & Commas ── */}
|
|
<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">
|
|
Modifiers, Parallel Structure & Commas
|
|
</h2>
|
|
<p className="text-lg text-slate-500 mb-8">
|
|
Three interrelated skills that test whether elements are correctly
|
|
placed and balanced.
|
|
</p>
|
|
|
|
{/* §8.5 Modification */}
|
|
<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">
|
|
Modification Errors
|
|
</h3>
|
|
<div className="space-y-3">
|
|
<div className="card-tilt bg-white border border-red-200 rounded-xl p-4">
|
|
<p className="font-bold text-red-800 text-sm mb-2">
|
|
Dangling Modifier
|
|
</p>
|
|
<p className="text-xs text-slate-500 mb-2">
|
|
The modifier has no logical subject in the sentence, or the
|
|
subject it should modify does not immediately follow it.
|
|
</p>
|
|
<ul className="space-y-1 text-xs">
|
|
<li className="text-red-600">
|
|
✗ "Running quickly, the bus was missed." — The bus isn't
|
|
running.
|
|
</li>
|
|
<li className="text-green-700">
|
|
✓ "Running quickly, she missed the bus." — She was running;
|
|
subject appears right after comma.
|
|
</li>
|
|
<li className="text-amber-700 italic mt-1">
|
|
Possessive Trap: "Wishing to pass,{" "}
|
|
<strong>the student's</strong> exam…" — the possessive makes
|
|
"exam" the subject. Use: "Wishing to pass,{" "}
|
|
<strong>the student</strong> reviewed…"
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div className="card-tilt bg-white border border-amber-200 rounded-xl p-4">
|
|
<p className="font-bold text-amber-800 text-sm mb-2">
|
|
Misplaced Modifier
|
|
</p>
|
|
<p className="text-xs text-slate-500 mb-2">
|
|
The modifier is positioned too far from the noun it modifies.
|
|
</p>
|
|
<ul className="space-y-1 text-xs">
|
|
<li className="text-red-600">
|
|
✗ "She only eats salad." — only implies she does nothing
|
|
else?
|
|
</li>
|
|
<li className="text-green-700">
|
|
✓ "She eats only salad." — only modifies "salad."
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div className="card-tilt bg-white border border-slate-200 rounded-xl p-4">
|
|
<p className="font-bold text-purple-800 text-sm mb-2">
|
|
Parenthetical Modifiers — Matching Punctuation
|
|
</p>
|
|
<p className="text-xs text-slate-500 mb-2">
|
|
A non-essential phrase must be enclosed in matching
|
|
punctuation: two commas, two dashes, or two parentheses. Never
|
|
mix them.
|
|
</p>
|
|
<ul className="space-y-1 text-xs">
|
|
<li className="text-red-600">
|
|
✗ "The researcher, who had published 40 papers— presented
|
|
her findings."
|
|
</li>
|
|
<li className="text-green-700">
|
|
✓ "The researcher, who had published 40 papers, presented
|
|
her findings."
|
|
</li>
|
|
<li className="text-green-700">
|
|
✓ "The researcher — who had published 40 papers — presented
|
|
her findings."
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* §8.6 Parallel Structure */}
|
|
<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">
|
|
Parallel Structure
|
|
</h3>
|
|
<div className="space-y-2">
|
|
{[
|
|
{
|
|
type: "Lists",
|
|
bad: "She enjoys hiking, to swim, and reading.",
|
|
good: "She enjoys hiking, swimming, and reading.",
|
|
note: "All items must be the same grammatical form.",
|
|
},
|
|
{
|
|
type: "Correlative Pairs (not only…but also, both…and, either…or)",
|
|
bad: "He not only speaks French but also is fluent in German.",
|
|
good: "He not only speaks French but also speaks German.",
|
|
note: 'What follows "not only" must be parallel to what follows "but also."',
|
|
},
|
|
{
|
|
type: "Comparisons",
|
|
bad: "Running is healthier than to sit all day.",
|
|
good: "Running is healthier than sitting all day.",
|
|
note: "Both sides of a comparison must be the same form.",
|
|
},
|
|
{
|
|
type: "Semicolon Lists",
|
|
bad: "The study examined diet; the exercise habits; and their stress levels.",
|
|
good: "The study examined diet; exercise habits; and stress levels.",
|
|
note: "Items separated by semicolons must be parallel.",
|
|
},
|
|
].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-xs mb-1">
|
|
{p.type}
|
|
</p>
|
|
<p className="text-xs text-red-600">✗ {p.bad}</p>
|
|
<p className="text-xs text-green-700">✓ {p.good}</p>
|
|
<p className="text-xs text-slate-400 italic mt-1">{p.note}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* §8.7 Commas */}
|
|
<div className="scroll-reveal stagger-3 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 Rules</h3>
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div className="card-tilt bg-white border border-green-200 rounded-xl p-4">
|
|
<p className="font-bold text-green-800 text-sm mb-2">
|
|
When a Comma IS Required
|
|
</p>
|
|
<ul className="space-y-1 text-xs text-slate-700">
|
|
<li>
|
|
✓ After an introductory element at the start of a sentence.
|
|
</li>
|
|
<li>
|
|
✓ Before and after a non-essential (parenthetical) clause.
|
|
</li>
|
|
<li>✓ Before a FANBOYS joining two independent clauses.</li>
|
|
<li>✓ Between items in a list of three or more.</li>
|
|
<li>
|
|
✓ Between coordinate adjectives that independently modify
|
|
the noun.
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div className="card-tilt bg-white border border-red-200 rounded-xl p-4">
|
|
<p className="font-bold text-red-800 text-sm mb-2">
|
|
When a Comma Is NOT Used
|
|
</p>
|
|
<ul className="space-y-1 text-xs text-slate-700">
|
|
<li>✗ Between a subject and its verb.</li>
|
|
<li>
|
|
✗ Before a subordinate clause that follows the main clause
|
|
("left because she was tired" — no comma before "because").
|
|
</li>
|
|
<li>
|
|
✗ Before a FANBOYS when one side is a phrase, not an IC.
|
|
</li>
|
|
<li>✗ After a FANBOYS conjunction.</li>
|
|
<li>✗ Between a verb and its object or complement.</li>
|
|
</ul>
|
|
</div>
|
|
</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">
|
|
For SVA: always cross out the material between subject and verb.
|
|
For modifiers: the noun being modified must immediately follow the
|
|
modifying phrase. For apostrophes: pronouns never use apostrophes
|
|
for possession — only for contractions.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
{/* ── Section 4: SVA Decision Tree ── */}
|
|
<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-2">
|
|
SVA Decision Tree
|
|
</h2>
|
|
<p className="text-lg text-slate-500 mb-8">
|
|
Work through subject-verb agreement step by step. Find the true
|
|
subject, then match the verb.
|
|
</p>
|
|
<DecisionTreeWidget scenarios={TREE_SCENARIOS} accentColor="purple" />
|
|
</section>
|
|
|
|
{/* ── Section 5: Practice ── */}
|
|
<section
|
|
ref={(el) => {
|
|
sectionsRef.current[5] = 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>
|
|
{FORM_STRUCTURE_EASY.slice(0, 2).map((q) => (
|
|
<PracticeFromDataset key={q.id} question={q} color="purple" />
|
|
))}
|
|
{FORM_STRUCTURE_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 EBRWFormStructureSenseLesson;
|