can validate it in the pov of a slp

Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
I now have a thorough picture of the entire manual. Let me give you a full SLP-perspective review.

SLP Validation Review: SpeakEasy Articulation Manual

Overall, this is a well-structured, visually polished tool that covers the right clinical territory. Here's a detailed breakdown of what works, what needs attention, and what's missing.

What's Clinically Sound

Client types and scope The split between pediatric (child) and adult caseloads is appropriate. Targeting /r/, /s/, /z/ for children and dysarthria for adults reflects the most common referral patterns in a typical SLP caseload.
Session data collection (Trial Tracking) The trial buttons (hit/miss per target word) plus a session accuracy percentage are exactly what SLPs use in data-driven practice. The word-initial position labeling on target words (rabbit, rainbow, rocket, river, robot) is clinically accurate - /r/ in initial position is generally easier and is where treatment typically begins before moving to medial/final and clusters like /br/, /tr/, /gr/.
Dysarthria techniques Rate control with pacing boards and contrastive stress drills are genuinely evidence-based for dysarthria (Lee Silverman Voice Treatment framework, PROMPT, and the Yorkston et al. management approach all include these). The step-by-step breakdown is practical and clinically usable.
Apraxia section The DIVA model reference is legitimate - Guenther's work is foundational in motor speech. The MPAT (Motor Programming Articulatory Treatment) steps are accurate: massed practice, CV to CVC to cluster progression, and blocked-to-random practice scheduling align with motor learning principles. The Mauszycki & Wambaugh (2011) citation is real and appropriate.
Assessment tools listed GFTA-3 (Goldman-Fristoe Test of Articulation), KLPA-3 (Khan-Lewis Phonological Analysis), and the Arizona-4 are the standard go-to formal assessments used clinically. Listing both child and adult tools shows awareness of the lifespan scope of SLP practice.
Session notes with quick-insert templates "Good production of /r/ in word-initial position," "Needed phonetic placement cue," and "Spontaneous self-correction observed" are realistic, useful note starters. SLPs will actually use these.

Issues and Gaps to Address

1. The "Safari" game conflates phoneme identification with production

The Sound Safari game asks the child to identify where /r/ appears in a word (beginning, middle, end). That is a phonological awareness task, not an articulation task. For a child working on /r/ production, this is only peripherally relevant. An SLP would flag this because:
  • It does not give the child practice producing /r/
  • A child with /r/ errors may still correctly identify /r/ position - that's a different skill
  • If the goal is stated as articulation, the game should involve the child saying the word
Fix: Label it as a phonological awareness warm-up, or replace with a production game where the child says the /r/ word and the SLP marks it correct/incorrect.

2. The Rocket Repeat game is clinically underdeveloped

The repeat game word list (RABBIT, RAINBOW, ROCKET, ROBOT) is appropriate for initial /r/, but there is no mechanism for the SLP or client to mark accuracy per trial. It appears to be self-paced repetition without data capture. In a clinical tool, every production attempt needs a logged outcome.
Fix: Add trial scoring to the repeat game, or clearly frame it as a home practice/carryover activity rather than a session data tool.

3. "MPAT" is not a recognized widely-used acronym in AOS treatment

The established evidence-based treatments for Acquired Apraxia of Speech (AOS) are:
  • SPT - Sound Production Treatment (Wambaugh et al.)
  • DAPA - Dynamic Approaches to Phonological Assessment
  • SHAIP - Script training
  • MIT - Melodic Intonation Therapy
  • ReST - Rapid Syllable Transition Treatment
"MPAT" does not appear in the ASHA or published AOS literature as a named protocol. The steps described do reflect SPT principles. Renaming it to SPT (Sound Production Treatment) or labeling it as "Motor Programming Principles" (without implying it is a named protocol) would be more accurate and defensible.

4. No error pattern / phonological process analysis

For the pediatric section, the manual focuses entirely on single-sound articulation targets. SLPs working with children also assess and treat phonological processes (patterns of sound errors such as final consonant deletion, stopping, cluster reduction, fronting). A 6-year-old with multiple sound errors likely has a phonological disorder, not just an articulation disorder - and those are treated differently (cycles approach, minimal pairs, etc.). The manual should at minimum acknowledge this distinction.

5. Age of acquisition norms are absent

The manual lists target sounds (/r/, /s/, /z/) but does not anchor them to developmental norms. SLPs need this to justify goals and explain eligibility to families and school teams. Standard norms (e.g., McLeod & Crowe, 2018 - the most current normative study):
  • /s/, /z/ - mastered by age 7-8
  • /r/ - mastered by age 6-8 (highly variable)
Without these norms, an SLP cannot use this tool to write a compliant IEP goal or justify treatment necessity.

6. Cue hierarchy is incomplete

The "Cues Used" tracker at the bottom of the session view is a good idea, but the cueing hierarchy listed should follow a standard least-to-most or most-to-least framework. SLPs document cue level because it tracks independence. A typical hierarchy for articulation is:
  1. Spontaneous (no cue)
  2. Verbal/semantic cue ("Think of a word with /r/")
  3. Phonetic placement cue ("Curl your tongue back")
  4. Tactile/visual cue
  5. Imitation/model
The current "Cues Used" section appears to be a freeform log - it should have structured cue levels so the SLP can track progression toward independence.

7. No discharge criteria or goal-writing guidance

A complete clinical manual should include guidance on:
  • When to advance through levels (word → phrase → sentence → conversation)
  • Mastery criteria (typically 80% accuracy across 2 consecutive sessions in a given context)
  • Goal language that matches IEP/SOAP note standards

Minor Terminology Notes

  • The term "dysarthria" as a badge label on a client is appropriate for a demo, but in practice SLPs would specify the type (spastic, flaccid, ataxic, hypokinetic, hyperkinetic, mixed) since treatment differs per type.
  • "Contrastive Stress Drills" is the correct clinical name - good.
  • "Phonetic placement cue" in the session note template is the correct terminology - good.
  • Progress percentages (72%, 85%, 90%) displayed on the dashboard are clinically meaningful only if they reflect session accuracy, not completion percentage. This should be clarified in the UI label.

Summary Table

AreaStatus
Target sound selectionClinically appropriate
Trial data loggingSolid - needs cue hierarchy
Dysarthria techniquesEvidence-based
Apraxia sectionMostly accurate - rename MPAT to SPT
Safari game (phonological awareness)Mislabeled - not an articulation drill
Rocket repeat gameNeeds data capture
Developmental normsMissing
Phonological process sectionAbsent
Goal-writing / discharge criteriaAbsent
Assessment tool listAccurate
This is a strong foundation. The two highest-priority fixes from a clinical validity standpoint are: (1) correcting the Safari game's framing as articulation practice, and (2) adding age-of-acquisition norms to ground the pediatric section.

can validate it in the pov of a slp

Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
I now have a full picture of the manual. Here is the detailed SLP-perspective validation.

SLP Validation Review: Oromotor & Vegetative Skills Manual


Overview of What the Manual Contains

The manual is organized into two tracks:
  • Oromotor exercises (Levels 1-4): Lip press, Lip stretch, Jaw open-close, Tongue tip tap, Tongue lateralization, and a speech integration/generalization level
  • Vegetative exercises: Gauze chewing/rotary jaw, resistive jaw opening, jaw lateralization, buccal massage, and saliva swallowing with laryngeal elevation palpation
The design is clean and anatomically illustrated with SVGs. Clinically, there is a lot to work with here - but also some significant concerns that need to be addressed before this lands in SLP hands.

What Is Clinically Accurate and Usable

Anatomical specificity The muscle labeling is accurate: masseter, pterygoids, digastric, and buccinator are correctly associated with the exercises that target them. An SLP will appreciate that kind of specificity because it maps directly to their differential reasoning about which structure is impaired.
Rotary jaw chewing pattern The vegetative chewing exercise correctly identifies rotary jaw movement as the mature, functional pattern - as opposed to the vertical up-down pattern seen in younger children or neurologically impaired adults. This distinction matters clinically and is correctly illustrated.
Tongue lateralization Targeting tongue lateralization as a distinct skill (separate from tongue tip elevation) is clinically sound. The lateral tongue movement is essential for bolus management in mastication and for vowel differentiation. Correctly listed.
Laryngeal elevation in swallowing The saliva swallow exercise with finger palpation on the larynx to confirm elevation is a standard clinical technique. The SVG illustration even shows hyoid-up and larynx-up arrows with a bolus path - this is accurate biomechanics. SLPs teaching the Mendelsohn maneuver or effortful swallow will recognize this immediately.
Gauze chewing as a non-food resistive tool The warning tip - "Chewing exercises using food should only be introduced after safe swallowing is confirmed" - is exactly correct and reflects current dysphagia management thinking. Using gauze or chew tubes as initial tools before food introduction is standard practice.
Buccinator training for /b/, /p/, and sibilants Linking cheek inflation exercises to the buccinator's role in /b/, /p/, and sibilant production is accurate. This is the kind of bridge between vegetative function and speech that a knowledgeable SLP makes.

Issues and Concerns - Organized by Severity

MAJOR: The oromotor exercise evidence base is contested and the manual does not address this

This is the most important clinical issue in the document.
Non-speech oral motor exercises (NSOMEs) - which is exactly what Levels 1-3 of this manual contain - have been a contested area in SLP practice for over 20 years. The core problem: strengthening lip press, tongue tapping, and jaw cycling in isolation does not automatically transfer to improved speech production. The neuromuscular demands of speech (fast, highly coordinated, low-force movements) are categorically different from the slow, high-force isolated movements in these exercises.
The ASHA Technical Report and multiple authors (Lof & Watson, 2008; Forrest, 2002; McCauley et al., 2009) have consistently found no robust evidence that NSOMEs improve speech intelligibility. Many SLPs actively avoid them or are required to justify them explicitly in their documentation.
The manual presents these exercises without any of this context. An SLP reading it may:
  • Use it inappropriately as first-line treatment for an articulation disorder
  • Face pushback from a supervising SLP or ASHA auditor who sees a NSOMES-heavy treatment plan without justification
  • Miss the opportunity to use the exercises appropriately (e.g., post-stroke dysarthria where actual muscle weakness is present)
Fix: Add a clinical indication box at the top of the oromotor section that specifies: "These exercises are appropriate for clients with confirmed neuromuscular weakness (e.g., flaccid dysarthria, post-stroke, cerebral palsy with hypotonia) - not for functional articulation disorders or phonological disorders in children." Also add a brief note acknowledging the NSOMES debate.

MAJOR: The generalization level (Level 4) is too thin and disconnected

Level 4 is labeled "Speech Integration" with a "5 min daily" badge, and the steps describe moving from isolated sound to word to sentence. This is clinically correct in concept but the transition from vegetative/oromotor exercises to functional speech is not explained at all. There is no:
  • Criterion for when to move from Level 3 to Level 4
  • Description of what "speech integration" actually means procedurally (drill-based? conversational? reading aloud?)
  • Link to specific speech targets the client has been working on
A Level 4 that says "progress to words and sentences" is not enough. SLPs need to know how - e.g., using immediate imitation, then delayed imitation, then spontaneous production, across at least 3 different communication contexts.

MODERATE: "Resistive jaw opening" instruction is potentially unsafe without precautions

Step B in the vegetative section says: "Place hand under chin and gently resist as jaw opens slowly. Hold 3 sec at maximum. 10 reps." This is a legitimate technique but carries risk for clients with:
  • TMJ disorder or temporomandibular joint pain
  • Trismus (jaw tightness post-radiation)
  • Cervical spine instability (e.g., rheumatoid arthritis, Down syndrome, ALS)
None of these contraindications are listed. For a manual given to clients or caregivers, this is a safety gap. The gauze chewing section does include a swallowing safety precaution - the resistive jaw section needs the same treatment.
Fix: Add a contraindications callout: "Do not perform resistive jaw exercises if the client has TMJ pain, trismus, or cervical spine instability. Consult physician or dentist before introducing resistance."

MODERATE: Tongue tip tap (Level 4 exercise naming is inconsistent)

There is a tongue tip tap exercise with a cross-section illustration showing tongue tip touching the alveolar ridge, which is anatomically accurate. However, the exercise is labeled as part of the same level progression as gross tongue lateralization. In clinical practice, tongue tip elevation to the alveolar ridge is a more refined and speech-specific movement than lateral tongue sweeping, not a lower-level skill. The sequencing should be reviewed - lateral tongue movement typically precedes precise tongue tip placement in a developmental/rehabilitation hierarchy.

MODERATE: Buccal massage is presented without clinical rationale

The step describing "massage cheek mucosa 30 sec each side" is a sensory preparation technique, not a strengthening technique. It is used to normalize oral tactile sensitivity (hypersensitivity or hyposensitivity) - common in children with feeding difficulties or adults with post-stroke sensory changes. However, the manual presents it alongside the inflate-to-strengthen step as if both serve the same purpose. They do not. Massage addresses sensory processing; cheek inflation addresses motor strength. Conflating them is clinically imprecise.
Fix: Separate these into two sub-steps with distinct rationales, or note that massage is a sensory pre-task and inflation is the motor task.

MINOR: Reps and sets are given as fixed numbers without individualization guidance

Exercises list specific dosages (e.g., "8 reps × 3 sets," "10 reps"). In clinical practice, dosage is individualized based on fatigue, severity, and session length. A client with severe flaccid dysarthria will not tolerate 3 sets of 8 from day one. A client with mild weakness may find those numbers too easy and fail to make gains.
Fix: Add a brief note such as: "Starting dosage shown. Adjust based on client fatigue. Reduce sets if quality deteriorates before completion."

MINOR: No population or age range is specified

The manual does not state who it is for - pediatric feeding disorders, adult post-stroke dysarthria, cerebral palsy, or general motor speech. The exercises are appropriate for different populations but with different rationales. A tongue lateralization drill in a 4-year-old with feeding difficulties has a different clinical context than the same drill in a 60-year-old with Parkinson's.

Summary Table

AreaVerdict
Anatomical accuracyAccurate - muscles correctly identified
Rotary chew patternCorrect and well illustrated
Laryngeal elevation / swallow palpationClinically accurate
Gauze chewing safety precautionCorrectly placed
Buccinator-to-speech bridgeAccurate
NSOMES evidence base not addressedMajor gap - needs clinical indication box
Level 4 speech integrationToo thin - needs procedural detail
Resistive jaw contraindicationsMissing - safety risk
Exercise sequencing (tongue tap vs. lateral)Needs review
Buccal massage rationaleConflated with strengthening - needs separation
Fixed dosage without adjustment guidanceMinor - add a dosage note
Population not specifiedMinor - add a scope statement at top

Bottom Line

The manual is anatomically grounded, visually thoughtful, and covers the right vegetative skills for a dysphagia-adjacent oromotor program. The biggest clinical risk is presenting non-speech oromotor exercises without acknowledging the NSOMES debate and clearly stating when they are and are not indicated. Any SLP who has been through a contemporary graduate program or ASHA continuing education will immediately ask: "Who is this for, and what evidence supports it?" Right now the manual cannot answer that. Adding a clinical scope and indication statement at the top of the oromotor section would make this substantially more defensible and usable in practice.

can develop the webbased app on oromotor exercises and vegetative skills stengthining exercises

Asking for Preferences
Running Command

mkdir -p /home/daytona/workspace/oromotor-app

Writing File

~/oromotor-app/index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OroTherapy — Oromotor & Vegetative Skills</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,600;0,9..144,700;1,9..144,400&family=Outfit:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{
  --cream:#F7F3EC; --cream2:#EDE8DE; --cream3:#D8D0BE; --ink:#1C1812; --ink2:#3A3328; --ink3:#5C5446; --muted:#9A9080;
  --l1:#1A6B5A; --l1b:#E4F4F0; --l1c:#A8DDD4; --l1d:#0d4a3e;
  --l2:#8B5E1A; --l2b:#F7EDD8; --l2c:#E8C07A; --l2d:#5e3f10;
  --l3:#9B2C2C; --l3b:#FAE8E8; --l3c:#E89A9A; --l3d:#6b1c1c;
  --l4:#3D2A8A; --l4b:#EDEAF8; --l4c:#B0A4E0; --l4d:#271a5e;
  --veg:#1E5C8A; --vegb:#E4EEF8; --vegc:#90BCD8; --vegd:#123d5e;
  --radius:14px; --radius-sm:8px; --radius-lg:20px;
  --shadow:0 2px 12px rgba(28,24,18,0.08); --shadow-lg:0 8px 32px rgba(28,24,18,0.14);
  --sidebar-w:280px;
}

html{scroll-behavior:smooth}
body{font-family:'Outfit',sans-serif;background:var(--cream);color:var(--ink);min-height:100vh;display:flex;overflow-x:hidden}

/* ─── SIDEBAR ─── */
.sidebar{
  width:var(--sidebar-w);min-width:var(--sidebar-w);background:#fff;
  border-right:1.5px solid var(--cream2);height:100vh;position:sticky;top:0;
  overflow-y:auto;display:flex;flex-direction:column;z-index:100;
  box-shadow:2px 0 12px rgba(28,24,18,0.04);
}
.sidebar-brand{padding:28px 22px 20px;border-bottom:1px solid var(--cream2)}
.brand-logo{display:flex;align-items:center;gap:10px;margin-bottom:6px}
.logo-mark{width:36px;height:36px;border-radius:10px;background:linear-gradient(135deg,var(--l1),var(--veg));display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0}
.brand-name{font-family:'Fraunces',serif;font-size:20px;font-weight:700;color:var(--ink);letter-spacing:-0.3px}
.brand-sub{font-size:11px;color:var(--muted);letter-spacing:0.4px;text-transform:uppercase}

.nav-section{padding:14px 12px 8px}
.nav-label{font-size:10px;letter-spacing:1px;text-transform:uppercase;color:var(--muted);padding:0 10px 8px;font-weight:600}
.nav-item{
  display:flex;align-items:center;gap:10px;padding:9px 12px;
  border-radius:var(--radius-sm);cursor:pointer;font-size:13.5px;font-weight:500;
  color:var(--ink3);transition:all 0.15s;margin-bottom:2px;
}
.nav-item:hover{background:var(--cream);color:var(--ink)}
.nav-item.active{color:#fff;font-weight:600}
.nav-item .nav-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
.nav-cat-oromotor .nav-item.active{background:var(--l1)}
.nav-cat-veg .nav-item.active{background:var(--veg)}
.nav-badge{margin-left:auto;font-size:10px;padding:2px 7px;border-radius:20px;font-family:'JetBrains Mono',monospace;font-weight:500}

.sidebar-footer{margin-top:auto;padding:16px 22px;border-top:1px solid var(--cream2);font-size:11.5px;color:var(--muted);line-height:1.5}

/* ─── MAIN ─── */
.main{flex:1;overflow-y:auto;min-height:100vh}

/* ─── TOP BAR ─── */
.topbar{
  padding:20px 32px 16px;border-bottom:1px solid var(--cream2);
  background:rgba(247,243,236,0.92);backdrop-filter:blur(8px);
  position:sticky;top:0;z-index:50;display:flex;align-items:center;gap:16px;
}
.topbar-title{font-family:'Fraunces',serif;font-size:26px;font-weight:700;color:var(--ink);flex:1}
.topbar-sub{font-size:13px;color:var(--muted);margin-top:1px}
.filter-row{display:flex;gap:8px;flex-wrap:wrap}
.filter-btn{
  font-size:12px;font-weight:500;padding:6px 14px;border-radius:20px;
  border:1.5px solid var(--cream3);background:#fff;color:var(--ink3);cursor:pointer;
  transition:all 0.15s;font-family:'Outfit',sans-serif;
}
.filter-btn:hover{border-color:var(--ink3);color:var(--ink)}
.filter-btn.active{background:var(--ink);border-color:var(--ink);color:#fff}

/* ─── CONTENT AREA ─── */
.content{padding:32px}

/* ─── SECTION HEADER ─── */
.section-heading{
  display:flex;align-items:center;gap:14px;
  padding:20px 28px;border-radius:var(--radius-lg);margin-bottom:28px;
}
.section-heading.oromotor{background:linear-gradient(135deg,var(--l1b),#f0faf8)}
.section-heading.vegetative{background:linear-gradient(135deg,var(--vegb),#eaf4fd)}
.sh-icon{width:48px;height:48px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:22px;flex-shrink:0}
.sh-icon.oromotor{background:var(--l1);color:#fff}
.sh-icon.vegetative{background:var(--veg);color:#fff}
.sh-title{font-family:'Fraunces',serif;font-size:20px;font-weight:700;color:var(--ink)}
.sh-desc{font-size:13px;color:var(--ink3);margin-top:3px;line-height:1.5}
.sh-warning{
  margin-top:10px;padding:10px 14px;border-radius:var(--radius-sm);
  font-size:12px;line-height:1.6;color:var(--l3d);background:var(--l3b);border-left:3px solid var(--l3);
}
.sh-info{
  margin-top:10px;padding:10px 14px;border-radius:var(--radius-sm);
  font-size:12px;line-height:1.6;color:var(--vegd);background:var(--vegb);border-left:3px solid var(--veg);
}

/* ─── EXERCISE GRID ─── */
.exercise-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:20px;margin-bottom:40px}

/* ─── EXERCISE CARD ─── */
.ex-card{
  background:#fff;border-radius:var(--radius-lg);border:1.5px solid var(--cream2);
  box-shadow:var(--shadow);overflow:hidden;transition:box-shadow 0.2s,transform 0.15s;cursor:pointer;
}
.ex-card:hover{box-shadow:var(--shadow-lg);transform:translateY(-2px)}
.ex-card.expanded{box-shadow:var(--shadow-lg)}

.ex-card-top{padding:18px 20px 14px;display:flex;align-items:flex-start;gap:14px}
.ex-num-badge{
  width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;
  font-family:'Fraunces',serif;font-size:17px;font-weight:700;flex-shrink:0;
}
.ex-info{flex:1}
.ex-title{font-size:15px;font-weight:600;color:var(--ink);margin-bottom:3px}
.ex-muscle{font-size:11.5px;color:var(--muted);line-height:1.4}
.ex-meta-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:6px}
.meta-chip{
  font-size:11px;font-family:'JetBrains Mono',monospace;padding:3px 9px;border-radius:6px;
  font-weight:500;border:1px solid;
}
.expand-arrow{width:28px;height:28px;border-radius:50%;background:var(--cream);display:flex;align-items:center;justify-content:center;font-size:14px;transition:transform 0.25s;flex-shrink:0;margin-top:4px}
.ex-card.expanded .expand-arrow{transform:rotate(180deg)}

/* ─── ILLUSTRATION ─── */
.ex-illus{
  background:var(--cream);border-top:1px solid var(--cream2);border-bottom:1px solid var(--cream2);
  display:flex;align-items:center;justify-content:center;padding:20px;min-height:160px;
}
.ex-illus svg{max-width:180px;height:auto}

/* ─── STEPS ─── */
.ex-steps-wrap{padding:16px 20px;display:none}
.ex-card.expanded .ex-steps-wrap{display:block}
.step-row{display:flex;gap:12px;margin-bottom:10px;align-items:flex-start}
.step-n{
  width:24px;height:24px;border-radius:6px;display:flex;align-items:center;justify-content:center;
  font-size:11px;font-weight:700;flex-shrink:0;margin-top:2px;font-family:'JetBrains Mono',monospace;
}
.step-text{font-size:13.5px;color:var(--ink2);line-height:1.65}
.step-text strong{color:var(--ink);font-weight:500}

/* ─── TIP BOX ─── */
.ex-tip{
  padding:10px 16px;font-size:12.5px;line-height:1.6;
  display:flex;gap:8px;align-items:flex-start;margin-top:4px;
  border-top:1px solid var(--cream2);border-radius:0 0 var(--radius-sm) var(--radius-sm);
}
.ex-tip.warn{background:#fff8e8;color:#7a5310}
.ex-tip.info{background:#eaf4fd;color:var(--vegd)}
.ex-tip.safe{background:#faf0f0;color:var(--l3d)}

/* ─── TIMER PANEL ─── */
.timer-panel{
  background:#fff;border-radius:var(--radius-lg);border:1.5px solid var(--cream2);
  padding:20px;margin-top:16px;display:none;
}
.ex-card.expanded .timer-panel{display:block}
.timer-title{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:0.8px;color:var(--muted);margin-bottom:12px}
.timer-controls{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.rep-counter{display:flex;align-items:center;gap:10px}
.rep-display{
  font-family:'JetBrains Mono',monospace;font-size:28px;font-weight:500;
  color:var(--ink);min-width:60px;text-align:center;
}
.rep-label{font-size:11px;color:var(--muted)}
.btn{
  padding:8px 18px;border-radius:var(--radius-sm);border:none;font-family:'Outfit',sans-serif;
  font-size:13px;font-weight:600;cursor:pointer;transition:all 0.15s;
}
.btn-primary{background:var(--l1);color:#fff}
.btn-primary:hover{background:var(--l1d)}
.btn-veg{background:var(--veg);color:#fff}
.btn-veg:hover{background:var(--vegd)}
.btn-outline{background:#fff;color:var(--ink);border:1.5px solid var(--cream3)}
.btn-outline:hover{border-color:var(--ink3)}
.btn-sm{padding:5px 12px;font-size:12px}
.btn-icon{width:32px;height:32px;padding:0;display:flex;align-items:center;justify-content:center;border-radius:50%;background:var(--cream);border:1.5px solid var(--cream3);cursor:pointer;font-size:16px;transition:all 0.12s}
.btn-icon:hover{background:var(--cream2)}

.hold-timer{
  font-family:'JetBrains Mono',monospace;font-size:38px;font-weight:500;
  color:var(--ink);text-align:center;margin:8px 0;
}
.hold-ring-wrap{position:relative;width:80px;height:80px;flex-shrink:0}
.hold-ring-wrap svg{position:absolute;top:0;left:0;transform:rotate(-90deg)}
.hold-ring-center{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-family:'JetBrains Mono',monospace;font-size:20px;font-weight:600;color:var(--ink)}
.hold-label{font-size:11px;color:var(--muted);text-align:center;margin-top:4px}
.progress-bar{height:6px;background:var(--cream2);border-radius:3px;overflow:hidden;margin-top:10px}
.progress-fill{height:100%;border-radius:3px;background:var(--l1);transition:width 0.3s}
.progress-fill.veg{background:var(--veg)}
.sets-row{display:flex;gap:6px;margin-top:8px}
.set-dot{width:10px;height:10px;border-radius:50%;background:var(--cream2);transition:background 0.2s}
.set-dot.done{background:var(--l1)}
.set-dot.done-veg{background:var(--veg)}
.status-chip{font-size:11px;font-weight:600;padding:4px 10px;border-radius:20px;font-family:'JetBrains Mono',monospace}
.status-idle{background:var(--cream);color:var(--muted);border:1px solid var(--cream3)}
.status-active{background:var(--l1b);color:var(--l1);border:1px solid var(--l1c)}
.status-active-veg{background:var(--vegb);color:var(--veg);border:1px solid var(--vegc)}
.status-done{background:#e8f8ec;color:#1a7a4a;border:1px solid #8fd4a8}
.status-rest{background:var(--l2b);color:var(--l2);border:1px solid var(--l2c)}

/* ─── INDICATION BOX ─── */
.indication-box{
  background:#fff;border-radius:var(--radius-lg);border:1.5px solid var(--cream2);
  padding:22px 26px;margin-bottom:32px;box-shadow:var(--shadow);
}
.ib-title{font-family:'Fraunces',serif;font-size:17px;font-weight:700;color:var(--ink);margin-bottom:14px;display:flex;align-items:center;gap:8px}
.ib-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.ib-block{border-radius:var(--radius-sm);padding:14px 16px;font-size:13px;line-height:1.6}
.ib-block h4{font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:0.6px;margin-bottom:6px}
.ib-block.green{background:var(--l1b);color:var(--l1d)}
.ib-block.green h4{color:var(--l1)}
.ib-block.red{background:var(--l3b);color:var(--l3d)}
.ib-block.red h4{color:var(--l3)}
.ib-block ul{padding-left:14px}
.ib-block li{margin-bottom:3px}

/* ─── PAGE SECTIONS ─── */
.page{display:none}
.page.active{display:block}

/* ─── OVERVIEW DASHBOARD ─── */
.dash-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px;margin-bottom:32px}
.dash-card{
  background:#fff;border-radius:var(--radius);border:1.5px solid var(--cream2);
  padding:18px 20px;box-shadow:var(--shadow);
}
.dc-num{font-family:'Fraunces',serif;font-size:32px;font-weight:700;margin-bottom:2px}
.dc-label{font-size:12.5px;color:var(--muted)}
.dc-sub{font-size:11px;color:var(--muted);margin-top:4px}
.quick-start-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:32px}
.qs-card{
  background:#fff;border-radius:var(--radius-lg);border:1.5px solid var(--cream2);
  padding:22px;cursor:pointer;box-shadow:var(--shadow);transition:all 0.15s;
  display:flex;align-items:center;gap:16px;
}
.qs-card:hover{box-shadow:var(--shadow-lg);transform:translateY(-2px)}
.qs-icon{width:52px;height:52px;border-radius:14px;display:flex;align-items:center;justify-content:center;font-size:24px;flex-shrink:0}
.qs-icon.oro{background:linear-gradient(135deg,var(--l1b),var(--l1c))}
.qs-icon.veg{background:linear-gradient(135deg,var(--vegb),var(--vegc))}
.qs-title{font-size:15px;font-weight:600;color:var(--ink);margin-bottom:4px}
.qs-desc{font-size:12.5px;color:var(--ink3);line-height:1.5}
.qs-count{font-size:11px;font-family:'JetBrains Mono',monospace;margin-top:6px;padding:3px 8px;border-radius:6px;display:inline-block}
.qs-count.oro{background:var(--l1b);color:var(--l1)}
.qs-count.veg{background:var(--vegb);color:var(--veg)}
.welcome-banner{
  background:linear-gradient(135deg,#0d4a3e,#1A6B5A 60%,#1E5C8A);
  border-radius:var(--radius-lg);padding:28px 32px;margin-bottom:28px;color:#fff;
  display:flex;align-items:center;gap:24px;
}
.wb-text h2{font-family:'Fraunces',serif;font-size:22px;font-weight:700;margin-bottom:6px}
.wb-text p{font-size:13.5px;opacity:0.8;line-height:1.6;max-width:480px}
.wb-icon{font-size:52px;flex-shrink:0;opacity:0.9}

/* ─── LEVEL COLORS ─── */
.c-l1{background:var(--l1b);color:var(--l1);border-color:var(--l1c)}
.c-l2{background:var(--l2b);color:var(--l2);border-color:var(--l2c)}
.c-l3{background:var(--l3b);color:var(--l3);border-color:var(--l3c)}
.c-l4{background:var(--l4b);color:var(--l4);border-color:var(--l4c)}
.c-veg{background:var(--vegb);color:var(--veg);border-color:var(--vegc)}
.n-l1{background:var(--l1b);color:var(--l1)}
.n-l2{background:var(--l2b);color:var(--l2)}
.n-l3{background:var(--l3b);color:var(--l3)}
.n-l4{background:var(--l4b);color:var(--l4)}
.n-veg{background:var(--vegb);color:var(--veg)}
.sn-l1{background:var(--l1b);color:var(--l1)}
.sn-l2{background:var(--l2b);color:var(--l2)}
.sn-l3{background:var(--l3b);color:var(--l3)}
.sn-l4{background:var(--l4b);color:var(--l4)}
.sn-veg{background:var(--vegb);color:var(--veg)}

/* ─── RESPONSIVE ─── */
@media(max-width:860px){
  .sidebar{position:fixed;left:-300px;transition:left 0.3s}
  .sidebar.open{left:0}
  .main{margin-left:0;width:100%}
  .ib-grid{grid-template-columns:1fr}
  .quick-start-grid{grid-template-columns:1fr}
  .exercise-grid{grid-template-columns:1fr}
  .content{padding:16px}
  .topbar{padding:14px 16px}
  .topbar-title{font-size:20px}
}
.menu-btn{display:none;background:none;border:none;font-size:22px;cursor:pointer;color:var(--ink)}
@media(max-width:860px){.menu-btn{display:flex;align-items:center}}
.overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.35);z-index:90}
.overlay.open{display:block}
</style>
</head>
<body>

<div class="overlay" id="overlay" onclick="closeSidebar()"></div>

<!-- SIDEBAR -->
<nav class="sidebar" id="sidebar">
  <div class="sidebar-brand">
    <div class="brand-logo">
      <div class="logo-mark">🫦</div>
      <div>
        <div class="brand-name">OroTherapy</div>
        <div class="brand-sub">SLP Exercise Library</div>
      </div>
    </div>
  </div>

  <div class="nav-section">
    <div class="nav-label">Overview</div>
    <div class="nav-item active" onclick="showPage('dashboard')" id="nav-dashboard">
      <div class="nav-dot" style="background:#1A6B5A"></div> Dashboard
    </div>
  </div>

  <div class="nav-section nav-cat-oromotor">
    <div class="nav-label">Oromotor Exercises</div>
    <div class="nav-item" onclick="showPage('oro-l1')" id="nav-oro-l1">
      <div class="nav-dot" style="background:var(--l1)"></div> Level 1 — Lips
      <span class="nav-badge c-l1">3 ex</span>
    </div>
    <div class="nav-item" onclick="showPage('oro-l2')" id="nav-oro-l2">
      <div class="nav-dot" style="background:var(--l2)"></div> Level 2 — Jaw
      <span class="nav-badge c-l2">2 ex</span>
    </div>
    <div class="nav-item" onclick="showPage('oro-l3')" id="nav-oro-l3">
      <div class="nav-dot" style="background:var(--l3)"></div> Level 3 — Tongue
      <span class="nav-badge c-l3">3 ex</span>
    </div>
    <div class="nav-item" onclick="showPage('oro-l4')" id="nav-oro-l4">
      <div class="nav-dot" style="background:var(--l4)"></div> Level 4 — Integration
      <span class="nav-badge c-l4">2 ex</span>
    </div>
  </div>

  <div class="nav-section nav-cat-veg">
    <div class="nav-label">Vegetative Skills</div>
    <div class="nav-item" onclick="showPage('veg')" id="nav-veg">
      <div class="nav-dot" style="background:var(--veg)"></div> All Vegetative Exercises
      <span class="nav-badge c-veg">5 ex</span>
    </div>
  </div>

  <div class="sidebar-footer">
    <strong>Clinical Note:</strong> Oromotor exercises are indicated for clients with confirmed neuromuscular weakness. Not for functional articulation or phonological disorders in isolation.
  </div>
</nav>

<!-- MAIN -->
<div class="main">

  <!-- TOP BAR -->
  <div class="topbar" id="topbar">
    <button class="menu-btn" onclick="openSidebar()">☰</button>
    <div>
      <div class="topbar-title" id="page-title">Dashboard</div>
      <div class="topbar-sub" id="page-sub">OroTherapy — Oromotor & Vegetative Skills Library</div>
    </div>
  </div>

  <!-- CONTENT -->
  <div class="content">

    <!-- ═══════════════════════════════ DASHBOARD ═══════════════════════════════ -->
    <div class="page active" id="page-dashboard">
      <div class="welcome-banner">
        <div class="wb-icon">🫦</div>
        <div class="wb-text">
          <h2>Oromotor & Vegetative Skills Library</h2>
          <p>A structured SLP exercise reference covering lip, jaw, and tongue strengthening through 4 progressive levels, plus vegetative skills for swallowing readiness.</p>
        </div>
      </div>

      <div class="dash-grid">
        <div class="dash-card">
          <div class="dc-num" style="color:var(--l1)">10</div>
          <div class="dc-label">Oromotor Exercises</div>
          <div class="dc-sub">Levels 1–4 · Lips, Jaw, Tongue</div>
        </div>
        <div class="dash-card">
          <div class="dc-num" style="color:var(--veg)">5</div>
          <div class="dc-label">Vegetative Exercises</div>
          <div class="dc-sub">Chewing · Swallowing readiness</div>
        </div>
        <div class="dash-card">
          <div class="dc-num" style="color:var(--l2)">4</div>
          <div class="dc-label">Progressive Levels</div>
          <div class="dc-sub">Isolated → Integrated speech</div>
        </div>
        <div class="dash-card">
          <div class="dc-num" style="color:var(--l4)">⏱</div>
          <div class="dc-label">Built-in Timers</div>
          <div class="dc-sub">Rep & hold tracking per exercise</div>
        </div>
      </div>

      <div class="quick-start-grid">
        <div class="qs-card" onclick="showPage('oro-l1')">
          <div class="qs-icon oro">💪</div>
          <div>
            <div class="qs-title">Oromotor Exercises</div>
            <div class="qs-desc">Progressive lip, jaw, and tongue strengthening exercises with illustrated instructions and hold timers.</div>
            <span class="qs-count oro">10 exercises · 4 levels</span>
          </div>
        </div>
        <div class="qs-card" onclick="showPage('veg')">
          <div class="qs-icon veg">🌊</div>
          <div>
            <div class="qs-title">Vegetative Skills</div>
            <div class="qs-desc">Rotary chewing, tongue lateralization, buccal tone, saliva swallowing, and laryngeal elevation exercises.</div>
            <span class="qs-count veg">5 exercises</span>
          </div>
        </div>
      </div>

      <div class="indication-box">
        <div class="ib-title">🩺 Clinical Indication Guide</div>
        <div class="ib-grid">
          <div class="ib-block green">
            <h4>✓ Appropriate for</h4>
            <ul>
              <li>Flaccid dysarthria (post-stroke, TBI)</li>
              <li>Cerebral palsy with hypotonia</li>
              <li>Pediatric feeding difficulties</li>
              <li>Post-surgical oral weakness</li>
              <li>Neurodegenerative conditions (ALS, MS)</li>
            </ul>
          </div>
          <div class="ib-block red">
            <h4>✗ Not a substitute for</h4>
            <ul>
              <li>Functional articulation disorders</li>
              <li>Phonological process therapy</li>
              <li>Motor speech programming (AOS)</li>
              <li>Clients without confirmed weakness</li>
              <li>Phonological awareness training</li>
            </ul>
          </div>
        </div>
      </div>
    </div>

    <!-- ═══════════════════════════════ LEVEL 1 — LIPS ═══════════════════════════════ -->
    <div class="page" id="page-oro-l1">
      <div class="section-heading oromotor">
        <div class="sh-icon oromotor">💋</div>
        <div>
          <div class="sh-title">Level 1 — Lip Strengthening</div>
          <div class="sh-desc">Orbicularis oris, levator labii, depressor labii · Foundation for bilabial consonants /p/, /b/, /m/ and vowel rounding</div>
          <div class="sh-warning">⚠️ Progress to Level 2 only after client achieves 80% accuracy with sustained hold on all Level 1 exercises across 2 consecutive sessions.</div>
        </div>
      </div>
      <div class="exercise-grid" id="grid-l1"></div>
    </div>

    <!-- ═══════════════════════════════ LEVEL 2 — JAW ═══════════════════════════════ -->
    <div class="page" id="page-oro-l2">
      <div class="section-heading oromotor" style="background:linear-gradient(135deg,var(--l2b),#fdf4e3)">
        <div class="sh-icon" style="background:var(--l2);color:#fff">🦷</div>
        <div>
          <div class="sh-title">Level 2 — Jaw Grading & Range</div>
          <div class="sh-desc">Masseter, medial pterygoid, lateral pterygoid, digastric · Jaw excursion and graded control for vowel articulation</div>
          <div class="sh-warning">⚠️ Do not perform resistive jaw exercises if the client has TMJ pain, trismus, cervical spine instability, or rheumatoid arthritis. Consult physician or dentist before introducing resistance.</div>
        </div>
      </div>
      <div class="exercise-grid" id="grid-l2"></div>
    </div>

    <!-- ═══════════════════════════════ LEVEL 3 — TONGUE ═══════════════════════════════ -->
    <div class="page" id="page-oro-l3">
      <div class="section-heading oromotor" style="background:linear-gradient(135deg,var(--l3b),#fdf0f0)">
        <div class="sh-icon" style="background:var(--l3);color:#fff">👅</div>
        <div>
          <div class="sh-title">Level 3 — Tongue Mobility & Strength</div>
          <div class="sh-desc">Genioglossus, hyoglossus, styloglossus, intrinsic tongue muscles · Precision and range for alveolar, palatal, and velar consonants</div>
          <div class="sh-warning">⚠️ Tongue exercises should be introduced before tongue tip elevation as tongue lateralization is generally an earlier developmental skill.</div>
        </div>
      </div>
      <div class="exercise-grid" id="grid-l3"></div>
    </div>

    <!-- ═══════════════════════════════ LEVEL 4 — INTEGRATION ═══════════════════════════════ -->
    <div class="page" id="page-oro-l4">
      <div class="section-heading oromotor" style="background:linear-gradient(135deg,var(--l4b),#f0eeff)">
        <div class="sh-icon" style="background:var(--l4);color:#fff">🗣️</div>
        <div>
          <div class="sh-title">Level 4 — Speech Integration</div>
          <div class="sh-desc">Applying motor gains to real speech · Transition: isolated movement → phoneme → word → phrase → connected speech</div>
          <div class="sh-info">ℹ️ Bridge exercises are only effective after adequate muscle activation is established at Levels 1–3. Dosage: 5–10 minutes daily, structured as imitation → delayed production → spontaneous production.</div>
        </div>
      </div>
      <div class="exercise-grid" id="grid-l4"></div>
    </div>

    <!-- ═══════════════════════════════ VEGETATIVE ═══════════════════════════════ -->
    <div class="page" id="page-veg">
      <div class="section-heading vegetative">
        <div class="sh-icon vegetative">🌊</div>
        <div>
          <div class="sh-title">Vegetative Skills Strengthening</div>
          <div class="sh-desc">Functional feeding and swallowing preparation · Rotary chewing, tongue lateralization, buccal tone, laryngeal elevation</div>
          <div class="sh-warning">⚠️ All food-based chewing exercises must only be introduced after safe swallowing is confirmed by clinical or instrumental assessment (MBS/FEES). Use non-food items (gauze, chew tubes) as initial tools.</div>
        </div>
      </div>
      <div class="exercise-grid" id="grid-veg"></div>
    </div>

  </div><!-- /content -->
</div><!-- /main -->

<script>
// ─── DATA ───────────────────────────────────────────────────────────────────────

const exercises = {
  'l1': [
    {
      id:'l1-1', level:'l1', title:'Lip Press & Hold',
      muscles:'Orbicularis oris · Levator labii · Depressor labii',
      reps:10, sets:3, holdSec:5,
      chips:['10 reps','3 sets','5 sec hold'],
      steps:[
        {n:'1', text:'Bring lips together into a <strong>firm, flat seal</strong>. No puffing of cheeks — maintain even, symmetrical pressure.'},
        {n:'2', text:'Press upper and lower lips hard against each other. <strong>Feel the orbicularis oris working.</strong> Hold for 5 seconds.'},
        {n:'3', text:'Release completely. Rest 2 seconds. Repeat.'},
        {n:'4', text:'Progress to holding <strong>10 seconds</strong> as lip strength increases over sessions.'},
      ],
      tip:{type:'info', text:'Resistance upgrade: Place a clean tongue depressor between the lips and try to hold it in place while the clinician gently attempts to pull it out.'},
      illus: `<svg viewBox="0 0 180 160" xmlns="http://www.w3.org/2000/svg" aria-label="Lips pressed together in firm seal">
        <ellipse cx="90" cy="80" rx="70" ry="50" fill="#FDDBB4" opacity="0.6"/>
        <path d="M40 78 Q90 68 140 78" stroke="#8B3A35" stroke-width="4" fill="none" stroke-linecap="round"/>
        <path d="M40 78 Q90 68 140 78 Q120 73 90 72 Q60 73 40 78 Z" fill="#C0605A"/>
        <path d="M40 82 Q90 92 140 82" stroke="#8B3A35" stroke-width="4" fill="none" stroke-linecap="round"/>
        <path d="M40 82 Q90 92 140 82 Q120 87 90 88 Q60 87 40 82 Z" fill="#C0605A"/>
        <path d="M36 80 L20 80" stroke="#1A6B5A" stroke-width="2.5" stroke-linecap="round" marker-end="url(#arR1)"/>
        <path d="M144 80 L160 80" stroke="#1A6B5A" stroke-width="2.5" stroke-linecap="round" marker-end="url(#arL1)"/>
        <defs>
          <marker id="arR1" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><polygon points="0,0 6,3 0,6" fill="#1A6B5A"/></marker>
          <marker id="arL1" markerWidth="6" markerHeight="6" refX="1" refY="3" orient="auto"><polygon points="6,0 0,3 6,6" fill="#1A6B5A"/></marker>
        </defs>
        <rect x="20" y="130" width="140" height="14" rx="4" fill="#E4F4F0"/>
        <text x="90" y="141" text-anchor="middle" font-size="10" font-weight="600" fill="#1A6B5A" font-family="Outfit,sans-serif">PRESS firmly · even seal</text>
      </svg>`
    },
    {
      id:'l1-2', level:'l1', title:'Lip Stretch (Smile–Round)',
      muscles:'Zygomaticus major · Orbicularis oris · Risorius',
      reps:10, sets:2, holdSec:3,
      chips:['10 reps','2 sets','3 sec each'],
      steps:[
        {n:'1', text:'<strong>Smile position:</strong> Retract lips horizontally as wide as possible — "eeeee" shape. Hold 3 seconds.'},
        {n:'2', text:'<strong>Round position:</strong> Push lips forward into a tight "O" shape — "oooo". Hold 3 seconds.'},
        {n:'3', text:'Alternate smoothly between smile and round without pausing in between. 1 rep = both positions.'},
        {n:'4', text:'Focus on full range of motion. Reduce range before increasing speed.'},
      ],
      tip:{type:'info', text:'Use a mirror so the client can monitor symmetry. Asymmetry (one side lagging) indicates unilateral weakness and should be documented.'},
      illus: `<svg viewBox="0 0 180 160" xmlns="http://www.w3.org/2000/svg" aria-label="Lips alternating between smile and round positions">
        <ellipse cx="52" cy="80" rx="38" ry="30" fill="#FDDBB4" opacity="0.55"/>
        <path d="M22 80 Q52 72 82 80 Q66 88 52 89 Q38 88 22 80 Z" fill="#C0605A"/>
        <path d="M22 80 Q52 72 82 80" stroke="#8B3A35" stroke-width="2" fill="none"/>
        <path d="M20 78 L16 74 M20 82 L16 86" stroke="#8B3A35" stroke-width="1.5"/>
        <path d="M82 78 L86 74 M82 82 L86 86" stroke="#8B3A35" stroke-width="1.5"/>
        <text x="52" y="122" text-anchor="middle" font-size="10" fill="#8B5E1A" font-family="Outfit">SMILE</text>
        <text x="128" y="122" text-anchor="middle" font-size="10" fill="#1A6B5A" font-family="Outfit">ROUND</text>
        <text x="90" y="80" text-anchor="middle" font-size="16" fill="#9A9080">⟺</text>
        <ellipse cx="128" cy="80" rx="20" ry="28" fill="#FDDBB4" opacity="0.55"/>
        <ellipse cx="128" cy="84" rx="14" ry="18" fill="#C0605A" opacity="0.8"/>
        <circle cx="128" cy="84" r="8" fill="#8B3A35" opacity="0.6"/>
      </svg>`
    },
    {
      id:'l1-3', level:'l1', title:'Lip Pucker & Release',
      muscles:'Orbicularis oris · Mentalis',
      reps:12, sets:2, holdSec:4,
      chips:['12 reps','2 sets','4 sec hold'],
      steps:[
        {n:'1', text:'<strong>Pucker:</strong> Push lips forward in a tight pucker — like blowing a kiss or whistling. Hold 4 seconds.'},
        {n:'2', text:'<strong>Release:</strong> Relax completely. Do not open the mouth — simply release the pucker.'},
        {n:'3', text:'Ensure cheeks do not puff. Tension should be centered in the lips only.'},
        {n:'4', text:'As strength improves, attempt to pucker and retract lips quickly 5 times in a row (speed drill).'},
      ],
      tip:{type:'info', text:'Pucker strength contributes to /w/ production and liquid management during feeding. Combine with lip press for full orbicularis circuit.'},
      illus: `<svg viewBox="0 0 180 160" xmlns="http://www.w3.org/2000/svg" aria-label="Tight lip pucker forward">
        <ellipse cx="90" cy="78" rx="60" ry="42" fill="#FDDBB4" opacity="0.6"/>
        <ellipse cx="90" cy="82" rx="22" ry="28" fill="#C0605A" opacity="0.85"/>
        <circle cx="90" cy="82" r="11" fill="#8B3A35" opacity="0.7"/>
        <circle cx="90" cy="82" r="5" fill="#2C1810" opacity="0.4"/>
        <path d="M44 80 L64 80" stroke="#1A6B5A" stroke-width="2" stroke-linecap="round" marker-end="url(#arPuck)"/>
        <path d="M136 80 L116 80" stroke="#1A6B5A" stroke-width="2" stroke-linecap="round" marker-end="url(#arPuck2)"/>
        <defs>
          <marker id="arPuck" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><polygon points="0,0 6,3 0,6" fill="#1A6B5A"/></marker>
          <marker id="arPuck2" markerWidth="6" markerHeight="6" refX="1" refY="3" orient="auto"><polygon points="6,0 0,3 6,6" fill="#1A6B5A"/></marker>
        </defs>
        <rect x="20" y="130" width="140" height="14" rx="4" fill="#E4F4F0"/>
        <text x="90" y="141" text-anchor="middle" font-size="10" font-weight="600" fill="#1A6B5A" font-family="Outfit,sans-serif">PUCKER forward · hold 4 sec</text>
      </svg>`
    }
  ],
  'l2': [
    {
      id:'l2-1', level:'l2', title:'Jaw Open–Close with Grading',
      muscles:'Masseter · Medial pterygoid · Lateral pterygoid · Temporalis',
      reps:8, sets:3, holdSec:0,
      chips:['8 reps','3 sets','graded range'],
      steps:[
        {n:'1', text:'Begin with mouth <strong>closed and relaxed</strong>. Jaw should be in neutral, not clenched.'},
        {n:'2', text:'Open jaw <strong>slowly to ¼ range</strong>. Hold 1 second. Continue to ½ range. Hold 1 second. Continue to maximum comfortable opening. Hold 2 seconds.'},
        {n:'3', text:'Close jaw slowly, reversing through each position. Avoid snapping shut.'},
        {n:'4', text:'Goal: controlled, smooth excursion — no jaw deviation to either side. Use mirror for feedback.'},
      ],
      tip:{type:'warn', text:'Stop immediately if client reports clicking, pain, or locking. Document and refer for TMJ evaluation before continuing jaw exercises.'},
      illus: `<svg viewBox="0 0 180 180" xmlns="http://www.w3.org/2000/svg" aria-label="Jaw opening with graded range markers">
        <ellipse cx="90" cy="84" rx="60" ry="52" fill="#FDDBB4" opacity="0.7"/>
        <path d="M50 78 Q90 68 130 78 Q115 74 90 73 Q65 74 50 78Z" fill="#C0605A"/>
        <path d="M60 98 Q90 108 120 98 Q106 104 90 105 Q74 104 60 98Z" fill="#C0605A"/>
        <path d="M60 95 Q90 120 120 95" stroke="#8B3A35" stroke-width="1.5" fill="none" stroke-dasharray="4 3"/>
        <path d="M76 130 L76 110" stroke="#8B5E1A" stroke-width="1.8" stroke-dasharray="3 3"/>
        <path d="M90 136 L90 110" stroke="#8B5E1A" stroke-width="1.8" stroke-dasharray="3 3"/>
        <path d="M104 130 L104 110" stroke="#8B5E1A" stroke-width="1.8" stroke-dasharray="3 3"/>
        <text x="68" y="148" text-anchor="middle" font-size="9" fill="#8B5E1A" font-family="Outfit">¼</text>
        <text x="90" y="152" text-anchor="middle" font-size="9" fill="#8B5E1A" font-family="Outfit">½</text>
        <text x="112" y="148" text-anchor="middle" font-size="9" fill="#8B5E1A" font-family="Outfit">max</text>
        <defs><marker id="arJaw" markerWidth="6" markerHeight="6" refX="3" refY="3" orient="auto"><polygon points="0,0 6,3 0,6" fill="#8B5E1A"/></marker></defs>
        <path d="M90 108 L90 122" stroke="#8B5E1A" stroke-width="2" marker-end="url(#arJaw)"/>
        <rect x="18" y="162" width="144" height="14" rx="4" fill="#F7EDD8"/>
        <text x="90" y="173" text-anchor="middle" font-size="10" font-weight="600" fill="#8B5E1A" font-family="Outfit,sans-serif">Graded open · smooth close</text>
      </svg>`
    },
    {
      id:'l2-2', level:'l2', title:'Resistive Jaw Opening',
      muscles:'Lateral pterygoid · Digastric · Mylohyoid',
      reps:10, sets:2, holdSec:3,
      chips:['10 reps','2 sets','3 sec resist'],
      steps:[
        {n:'1', text:'<strong>Clinician places two fingers under the chin</strong> (or client uses own hand). Apply firm upward resistance.'},
        {n:'2', text:'Client opens jaw slowly against the resistance. Hold at maximum comfortable opening for 3 seconds.'},
        {n:'3', text:'Clinician maintains steady pressure throughout. Do not allow the jaw to snap open.'},
        {n:'4', text:'Close jaw slowly. Rest 2 seconds. Repeat.'},
      ],
      tip:{type:'safe', text:'⚠️ Contraindications: TMJ disorder, trismus, cervical spine instability (rheumatoid arthritis, Down syndrome, ALS), post-surgical jaw fixation. Confirm with physician before introducing resistance.'},
      illus: `<svg viewBox="0 0 180 180" xmlns="http://www.w3.org/2000/svg" aria-label="Hand applying resistance under chin during jaw opening">
        <ellipse cx="90" cy="72" rx="58" ry="50" fill="#FDDBB4" opacity="0.8"/>
        <path d="M44 68 Q90 58 136 68 Q118 64 90 63 Q62 64 44 68Z" fill="#C0605A"/>
        <path d="M56 96 Q90 106 124 96" stroke="#8B3A35" stroke-width="2.5" fill="none"/>
        <path d="M56 96 Q90 106 124 96 Q108 112 90 113 Q72 112 56 96Z" fill="#C0605A"/>
        <rect x="64" y="120" width="52" height="18" rx="9" fill="#FDDBB4" stroke="#D4956A" stroke-width="1.5"/>
        <circle cx="72" cy="129" r="4" fill="#D4956A" opacity="0.7"/>
        <circle cx="82" cy="129" r="4" fill="#D4956A" opacity="0.7"/>
        <circle cx="92" cy="129" r="4" fill="#D4956A" opacity="0.7"/>
        <circle cx="102" cy="129" r="4" fill="#D4956A" opacity="0.7"/>
        <defs><marker id="arResist" markerWidth="5" markerHeight="5" refX="2.5" refY="1" orient="auto"><polygon points="0,5 2.5,0 5,5" fill="#9B2C2C"/></marker></defs>
        <path d="M90 138 L90 128" stroke="#9B2C2C" stroke-width="2.5" stroke-linecap="round" marker-end="url(#arResist)"/>
        <text x="90" y="157" text-anchor="middle" font-size="9" fill="#9B2C2C" font-weight="600" font-family="Outfit">RESIST ↑ while opening ↓</text>
        <rect x="18" y="162" width="144" height="14" rx="4" fill="#FAE8E8"/>
        <text x="90" y="173" text-anchor="middle" font-size="10" font-weight="600" fill="#9B2C2C" font-family="Outfit,sans-serif">Resistive · 3 sec hold</text>
      </svg>`
    }
  ],
  'l3': [
    {
      id:'l3-1', level:'l3', title:'Tongue Lateralization',
      muscles:'Styloglossus · Transverse intrinsic · Palatoglossus',
      reps:10, sets:3, holdSec:3,
      chips:['10 reps','3 sets','3 sec hold'],
      steps:[
        {n:'1', text:'With mouth slightly open, move tongue tip slowly to the <strong>right corner</strong> of the mouth. Hold 3 seconds.'},
        {n:'2', text:'Return tongue to center. Hold 1 second.'},
        {n:'3', text:'Move tongue tip to the <strong>left corner</strong>. Hold 3 seconds. Return to center.'},
        {n:'4', text:'1 rep = right + left. Maintain tongue tip position — avoid tongue body moving instead.'},
      ],
      tip:{type:'info', text:'Tongue lateralization is a prerequisite skill for rotary chewing and bolus management. Master lateral movement before advancing to tongue tip elevation.'},
      illus: `<svg viewBox="0 0 180 160" xmlns="http://www.w3.org/2000/svg" aria-label="Tongue lateralizing to right corner of mouth">
        <ellipse cx="90" cy="78" rx="62" ry="44" fill="#FDDBB4" opacity="0.7"/>
        <path d="M36 76 Q90 64 144 76 Q126 70 90 69 Q54 70 36 76Z" fill="#C0605A"/>
        <path d="M36 82 Q90 94 144 82" stroke="#8B3A35" stroke-width="2" fill="none"/>
        <path d="M68 82 Q100 78 134 80" stroke="#E8A0A0" stroke-width="8" stroke-linecap="round" fill="none"/>
        <circle cx="134" cy="80" r="8" fill="#D08080" stroke="#9B2C2C" stroke-width="2"/>
        <defs><marker id="arLat" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><polygon points="0,0 6,3 0,6" fill="#9B2C2C"/></marker></defs>
        <path d="M108 78 L128 78" stroke="#9B2C2C" stroke-width="2" stroke-linecap="round" marker-end="url(#arLat)"/>
        <rect x="20" y="130" width="140" height="14" rx="4" fill="#FAE8E8"/>
        <text x="90" y="141" text-anchor="middle" font-size="10" font-weight="600" fill="#9B2C2C" font-family="Outfit,sans-serif">Lateral sweep · hold at corner</text>
      </svg>`
    },
    {
      id:'l3-2', level:'l3', title:'Tongue Tip Elevation (Alveolar)',
      muscles:'Genioglossus (anterior) · Superior longitudinal intrinsic',
      reps:10, sets:3, holdSec:3,
      chips:['10 reps','3 sets','3 sec hold'],
      steps:[
        {n:'1', text:'With mouth slightly open, <strong>elevate tongue tip</strong> to touch the alveolar ridge — the bony bump just behind the upper front teeth.'},
        {n:'2', text:'Maintain firm, precise contact. Hold 3 seconds. No jaw movement.'},
        {n:'3', text:'Lower tongue tip to resting position. Rest 1 second. Repeat.'},
        {n:'4', text:'Progress: tap quickly 10× (for /t/, /d/ phoneme precision), then add voiced airflow for sustained /n/ sound.'},
      ],
      tip:{type:'info', text:'Tongue tip alveolar contact is the placement for /t/, /d/, /n/, /l/. Precise placement here translates directly to consonant clarity.'},
      illus: `<svg viewBox="0 0 180 160" xmlns="http://www.w3.org/2000/svg" aria-label="Side profile showing tongue tip touching alveolar ridge">
        <path d="M148 14 Q162 34 164 60 Q168 90 161 118 Q150 148 132 164 Q112 178 92 180 Q68 180 52 166 Q36 152 34 130 Q30 106 36 86 Q30 66 34 48 Q42 26 58 16 Q76 6 100 6 Q126 6 148 14 Z" fill="#FDDBB4" opacity="0.6" transform="scale(0.78) translate(18,0)"/>
        <path d="M102 108 Q124 103 148 110 Q154 116 150 124" stroke="#C87D50" stroke-width="3" fill="none" stroke-linecap="round" transform="scale(0.78) translate(18,0)"/>
        <ellipse cx="128" cy="88" rx="8" ry="6" fill="#9B2C2C" opacity="0.85" transform="scale(0.78) translate(18,0)"/>
        <text x="122" y="72" text-anchor="middle" font-size="9" fill="#9B2C2C" font-family="Outfit" transform="scale(0.78) translate(18,0)">alveolar ridge</text>
        <path d="M58 144 Q80 136 108 128 Q122 122 136 115 Q142 111 146 109" fill="#E8A0A0" stroke="#C07070" stroke-width="1.5" transform="scale(0.78) translate(18,0)"/>
        <circle cx="128" cy="88" r="7" fill="#D08080" stroke="#9B2C2C" stroke-width="2" transform="scale(0.78) translate(18,0)"/>
        <rect x="18" y="132" width="144" height="14" rx="4" fill="#FAE8E8"/>
        <text x="90" y="143" text-anchor="middle" font-size="10" font-weight="600" fill="#9B2C2C" font-family="Outfit,sans-serif">Tongue tip → alveolar ridge</text>
      </svg>`
    },
    {
      id:'l3-3', level:'l3', title:'Tongue Protrusion & Retraction',
      muscles:'Genioglossus · Hyoglossus · Styloglossus',
      reps:10, sets:2, holdSec:3,
      chips:['10 reps','2 sets','3 sec each'],
      steps:[
        {n:'1', text:'<strong>Protrude:</strong> Extend tongue as far forward as possible beyond the lips. Hold 3 seconds.'},
        {n:'2', text:'<strong>Retract:</strong> Pull tongue as far back as comfortable into the mouth. Hold 3 seconds.'},
        {n:'3', text:'1 rep = protrude + retract. Maintain midline position — avoid lateral deviation.'},
        {n:'4', text:'Use a flat tongue blade lightly placed on the tongue tip as a tactile target to touch during protrusion.'},
      ],
      tip:{type:'info', text:'Full range protrusion-retraction builds the range of motion required for posterior vowels and velar consonants /k/ and /g/.'},
      illus: `<svg viewBox="0 0 180 160" xmlns="http://www.w3.org/2000/svg" aria-label="Tongue protruding forward beyond lips">
        <ellipse cx="90" cy="78" rx="60" ry="42" fill="#FDDBB4" opacity="0.6"/>
        <path d="M40 76 Q90 66 140 76 Q122 70 90 69 Q58 70 40 76Z" fill="#C0605A"/>
        <path d="M40 84 Q90 94 140 84" stroke="#8B3A35" stroke-width="2" fill="none"/>
        <path d="M68 80 Q90 84 112 80 Q122 78 148 78" fill="#E8A0A0" stroke="#C07070" stroke-width="7" stroke-linecap="round"/>
        <defs><marker id="arProt" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><polygon points="0,0 6,3 0,6" fill="#9B2C2C"/></marker></defs>
        <path d="M132 78 L152 78" stroke="#9B2C2C" stroke-width="2" marker-end="url(#arProt)"/>
        <rect x="18" y="130" width="144" height="14" rx="4" fill="#FAE8E8"/>
        <text x="90" y="141" text-anchor="middle" font-size="10" font-weight="600" fill="#9B2C2C" font-family="Outfit,sans-serif">Protrude → hold → retract</text>
      </svg>`
    }
  ],
  'l4': [
    {
      id:'l4-1', level:'l4', title:'Phoneme Integration Drill',
      muscles:'Speech integration · Motor planning · Articulatory precision',
      reps:5, sets:3, holdSec:0,
      chips:['5 min daily','3 complexity tiers'],
      steps:[
        {n:'1', text:'<strong>Tier 1 — Phoneme:</strong> Produce the target phoneme in isolation 5× (e.g., /t/, /l/, /m/). Clinician models, client imitates immediately.'},
        {n:'2', text:'<strong>Tier 2 — Word:</strong> Produce 5 words containing the phoneme in initial, medial, and final positions. Use immediate imitation, then delayed.'},
        {n:'3', text:'<strong>Tier 3 — Phrase:</strong> Produce 3–5 carrier phrases embedding the target word. Move to spontaneous generation when 80% accuracy is stable.'},
        {n:'4', text:'Criterion for level advancement: 80% accuracy across 2 consecutive sessions at each tier.'},
      ],
      tip:{type:'info', text:'Phoneme integration is the bridge between oromotor exercise and functional speech. Do not advance to this level if Level 1–3 exercises are not yet mastered.'},
      illus: `<svg viewBox="0 0 180 160" xmlns="http://www.w3.org/2000/svg" aria-label="Person speaking with sound waves showing levels of production">
        <ellipse cx="78" cy="48" rx="46" ry="42" fill="#4A3728"/>
        <ellipse cx="78" cy="88" rx="44" ry="52" fill="#F0C090"/>
        <path d="M56 110 Q78 106 100 110 Q90 120 78 121 Q66 120 56 110 Z" fill="#C0605A"/>
        <path d="M108 112 Q118 104 118 114 Q118 124 108 118" stroke="#3D2A8A" stroke-width="2" fill="none"/>
        <path d="M122 106 Q136 96 136 114 Q136 132 122 124" stroke="#3D2A8A" stroke-width="1.5" fill="none" opacity="0.55"/>
        <path d="M139 100 Q157 88 157 114 Q157 140 139 130" stroke="#3D2A8A" stroke-width="1" fill="none" opacity="0.3"/>
        <text x="38" y="148" text-anchor="middle" font-size="9" fill="#3D2A8A" font-weight="600" font-family="Outfit">/t/</text>
        <text x="78" y="148" text-anchor="middle" font-size="9" fill="#3D2A8A" font-weight="600" font-family="Outfit">word</text>
        <text x="118" y="148" text-anchor="middle" font-size="9" fill="#3D2A8A" font-weight="600" font-family="Outfit">phrase</text>
        <rect x="18" y="152" width="144" height="6" rx="3" fill="#EDEAF8"/>
        <rect x="18" y="152" width="48" height="6" rx="3" fill="#3D2A8A" opacity="0.5"/>
      </svg>`
    },
    {
      id:'l4-2', level:'l4', title:'Connected Speech Carryover',
      muscles:'Generalisation · Real-world transfer · Prosody',
      reps:0, sets:0, holdSec:0,
      chips:['5 min daily','3 contexts'],
      steps:[
        {n:'1', text:'<strong>Context 1 — Structured reading:</strong> Read a paragraph aloud. Client self-monitors target phoneme accuracy. Clinician marks errors.'},
        {n:'2', text:'<strong>Context 2 — Structured conversation:</strong> Clinician asks open questions on a familiar topic. Client responds in 2–3 sentence turns.'},
        {n:'3', text:'<strong>Context 3 — Naturalistic:</strong> Client names what they see in a picture scene or describes a route. No overt monitoring.'},
        {n:'4', text:'Compare accuracy across the three contexts. Generalization is achieved when accuracy in Context 3 matches Contexts 1 and 2.'},
      ],
      tip:{type:'info', text:'Home practice: Assign 2–3 target words for the week. Family members are briefed to prompt gently when errors occur in natural conversation.'},
      illus: `<svg viewBox="0 0 180 160" xmlns="http://www.w3.org/2000/svg" aria-label="Two people conversing with speech bubble and generalization arrows">
        <ellipse cx="52" cy="46" rx="30" ry="28" fill="#4A3728"/>
        <ellipse cx="52" cy="78" rx="28" ry="36" fill="#F0C090"/>
        <ellipse cx="130" cy="46" rx="30" ry="28" fill="#2C1810"/>
        <ellipse cx="130" cy="78" rx="28" ry="36" fill="#FDDBB4"/>
        <rect x="65" y="30" width="52" height="26" rx="8" fill="#EDEAF8" stroke="#B0A4E0" stroke-width="1.5"/>
        <text x="91" y="44" text-anchor="middle" font-size="9" fill="#3D2A8A" font-family="Outfit" font-weight="500">sentence ...</text>
        <path d="M66 43 L63 50 L72 44" fill="#EDEAF8" stroke="#B0A4E0" stroke-width="1"/>
        <path d="M72 108 L90 108" stroke="#1A6B5A" stroke-width="1.5" stroke-dasharray="4 3" stroke-linecap="round"/>
        <defs><marker id="arGen" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><polygon points="0,0 6,3 0,6" fill="#1A6B5A"/></marker></defs>
        <path d="M96 108 L108 108" stroke="#1A6B5A" stroke-width="1.5" marker-end="url(#arGen)"/>
        <rect x="18" y="132" width="144" height="14" rx="4" fill="#EDEAF8"/>
        <text x="90" y="143" text-anchor="middle" font-size="10" font-weight="600" fill="#3D2A8A" font-family="Outfit,sans-serif">Structured → Spontaneous</text>
      </svg>`
    }
  ],
  'veg': [
    {
      id:'veg-1', level:'veg', title:'Rotary Chewing & Tongue Lateralization',
      muscles:'Masseter · Pterygoids · Tongue intrinsics · Buccinator',
      reps:20, sets:1, holdSec:0,
      chips:['20 cycles','gauze/chew tube','rotary pattern'],
      steps:[
        {n:'A', text:'<strong>Gauze chewing:</strong> Wet a folded gauze pad and place on the molars. Perform 20 rhythmic chewing cycles with tongue pushing food to each side alternately. Builds rotary jaw pattern safely without food.'},
        {n:'B', text:'<strong>Tongue lateralization to teeth:</strong> After each chew cycle, tongue sweeps across the lower gum line to clear residue. 5 sweeps per side.'},
        {n:'C', text:'Progress to graded chew tube resistance when gauze is well-tolerated for 3 consecutive sessions.'},
        {n:'D', text:'Only introduce real food after instrumental swallowing assessment (MBS/FEES) confirms safety.'},
      ],
      tip:{type:'safe', text:'⚠️ Food-based chewing must NOT be introduced until safe swallowing is confirmed. Use gauze or chew tubes exclusively as initial tools.'},
      illus: `<svg viewBox="0 0 180 160" xmlns="http://www.w3.org/2000/svg" aria-label="Rotary chewing motion with tongue lateralizing food">
        <ellipse cx="90" cy="78" rx="62" ry="48" fill="#FDDBB4" opacity="0.7"/>
        <path d="M36 76 Q90 64 144 76 Q126 70 90 69 Q54 70 36 76Z" fill="#C0605A"/>
        <path d="M40 86 Q90 100 140 86" stroke="#8B3A35" stroke-width="2" fill="none"/>
        <path d="M58 86 Q80 82 108 84" stroke="#E8A0A0" stroke-width="7" stroke-linecap="round" fill="none"/>
        <circle cx="108" cy="84" r="7" fill="#D08080" stroke="#C07070"/>
        <path d="M140 100 Q154 88 150 74 Q144 62 134 60 Q122 58 116 64" stroke="#8B5E1A" stroke-width="2" fill="none" stroke-dasharray="5 3"/>
        <defs><marker id="arRot2" markerWidth="5" markerHeight="5" refX="2.5" refY="2.5" orient="auto"><polygon points="0,0 5,2.5 0,5" fill="#8B5E1A"/></marker></defs>
        <path d="M116 65 L110 70" stroke="#8B5E1A" stroke-width="2" marker-end="url(#arRot2)"/>
        <text x="148" y="84" font-size="8" fill="#8B5E1A" font-family="Outfit">rotary</text>
        <rect x="18" y="132" width="144" height="14" rx="4" fill="#E4EEF8"/>
        <text x="90" y="143" text-anchor="middle" font-size="10" font-weight="600" fill="#1E5C8A" font-family="Outfit,sans-serif">Rotary chew + tongue lateral</text>
      </svg>`
    },
    {
      id:'veg-2', level:'veg', title:'Resistive Jaw Opening (Feeding)',
      muscles:'Lateral pterygoid · Digastric · Mylohyoid',
      reps:10, sets:2, holdSec:3,
      chips:['10 reps','2 sets','3 sec resist'],
      steps:[
        {n:'1', text:'<strong>Clinician position:</strong> Place index and middle fingers firmly under the client\'s chin. Apply steady upward resistance.'},
        {n:'2', text:'Client opens jaw slowly against resistance. Hold at maximum comfortable opening for 3 seconds.'},
        {n:'3', text:'Release resistance. Client closes jaw slowly. Rest 2 seconds.'},
        {n:'4', text:'Document maximum comfortable opening in cm. Track across sessions as a progress measure.'},
      ],
      tip:{type:'safe', text:'⚠️ Contraindications: TMJ disorder, trismus, post-radiation fibrosis, cervical spine instability. Do not apply forceful resistance. If pain is reported, stop immediately.'},
      illus: `<svg viewBox="0 0 180 160" xmlns="http://www.w3.org/2000/svg" aria-label="Chin resistance technique for jaw opening">
        <ellipse cx="90" cy="66" rx="56" ry="46" fill="#FDDBB4" opacity="0.8"/>
        <path d="M46 64 Q90 54 134 64" stroke="#8B3A35" stroke-width="2" fill="none"/>
        <path d="M58 88 Q90 98 122 88 Q108 106 90 107 Q72 106 58 88Z" fill="#C0605A"/>
        <rect x="68" y="110" width="44" height="16" rx="8" fill="#FDDBB4" stroke="#D4956A" stroke-width="1.5"/>
        <circle cx="78" cy="118" r="3.5" fill="#D4956A" opacity="0.7"/>
        <circle cx="88" cy="118" r="3.5" fill="#D4956A" opacity="0.7"/>
        <circle cx="98" cy="118" r="3.5" fill="#D4956A" opacity="0.7"/>
        <circle cx="108" cy="118" r="3.5" fill="#D4956A" opacity="0.7"/>
        <defs><marker id="arChin" markerWidth="5" markerHeight="5" refX="2.5" refY="1" orient="auto"><polygon points="0,5 2.5,0 5,5" fill="#1E5C8A"/></marker></defs>
        <path d="M90 126 L90 116" stroke="#1E5C8A" stroke-width="2.5" stroke-linecap="round" marker-end="url(#arChin)"/>
        <rect x="18" y="140" width="144" height="14" rx="4" fill="#E4EEF8"/>
        <text x="90" y="151" text-anchor="middle" font-size="10" font-weight="600" fill="#1E5C8A" font-family="Outfit,sans-serif">Resist ↑ · open ↓ · hold 3s</text>
      </svg>`
    },
    {
      id:'veg-3', level:'veg', title:'Jaw Lateralization Drills',
      muscles:'Lateral pterygoid · Internal pterygoid · Masseter',
      reps:8, sets:2, holdSec:3,
      chips:['8 reps','2 sets','3 sec hold'],
      steps:[
        {n:'1', text:'Move jaw <strong>slowly to the right</strong> as far as comfortably possible. Hold 3 seconds.'},
        {n:'2', text:'Return to center. Hold 1 second.'},
        {n:'3', text:'Move jaw <strong>slowly to the left</strong>. Hold 3 seconds. Return to center.'},
        {n:'4', text:'Graded jaw lateralization trains the medial pterygoid range of motion needed for mature rotary chewing and vowel articulation precision.'},
      ],
      tip:{type:'warn', text:'Jaw lateralization should be pain-free. If client reports jaw joint pain, clicking, or locking, discontinue and assess for TMJ dysfunction.'},
      illus: `<svg viewBox="0 0 180 160" xmlns="http://www.w3.org/2000/svg" aria-label="Jaw lateralizing to the right with arrow indicators">
        <ellipse cx="90" cy="76" rx="60" ry="46" fill="#FDDBB4" opacity="0.7"/>
        <path d="M42 74 Q95 64 140 72 Q125 68 96 67 Q68 68 42 74Z" fill="#C0605A"/>
        <path d="M44 84 Q98 94 140 84" stroke="#8B3A35" stroke-width="2" fill="none"/>
        <defs><marker id="arLJ" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><polygon points="0,0 6,3 0,6" fill="#1E5C8A"/></marker></defs>
        <path d="M60 100 L100 100" stroke="#1E5C8A" stroke-width="2.5" stroke-linecap="round" marker-end="url(#arLJ)"/>
        <rect x="18" y="130" width="144" height="14" rx="4" fill="#E4EEF8"/>
        <text x="90" y="141" text-anchor="middle" font-size="10" font-weight="600" fill="#1E5C8A" font-family="Outfit,sans-serif">Lateral sweep · 3 sec hold</text>
      </svg>`
    },
    {
      id:'veg-4', level:'veg', title:'Buccal Tone & Cheek Inflation',
      muscles:'Buccinator · Orbicularis oris · Zygomaticus',
      reps:10, sets:2, holdSec:5,
      chips:['10 reps','2 sets','5 sec hold'],
      steps:[
        {n:'A', text:'<strong>Sensory prep (massage):</strong> Massage cheek mucosa (inside cheek) with fingertip in circular motion — 30 seconds per side. This normalizes oral tactile sensitivity before the motor task.'},
        {n:'B', text:'<strong>Cheek inflation:</strong> Inflate both cheeks with air. Hold lips firmly sealed. Hold 5 seconds. Release slowly.'},
        {n:'C', text:'<strong>Unilateral inflation:</strong> Inflate right cheek only, transferring air back and forth 5 times. Then left. Builds differential buccinator control.'},
        {n:'D', text:'Buccinator tone directly supports /b/, /p/, and sibilant production by maintaining lateral oral seal during speech.'},
      ],
      tip:{type:'info', text:'Massage (Step A) targets sensory processing; inflation (Steps B–C) targets motor strength. These serve different clinical purposes — document them separately.'},
      illus: `<svg viewBox="0 0 180 160" xmlns="http://www.w3.org/2000/svg" aria-label="Both cheeks inflated with air, sealed lips">
        <ellipse cx="90" cy="78" rx="72" ry="52" fill="#FDDBB4" opacity="0.75"/>
        <ellipse cx="44" cy="82" rx="22" ry="16" fill="#FDDBB4" stroke="#D4956A" stroke-width="2" opacity="0.9"/>
        <ellipse cx="136" cy="82" rx="22" ry="16" fill="#FDDBB4" stroke="#D4956A" stroke-width="2" opacity="0.9"/>
        <path d="M66 80 Q90 72 114 80 Q100 76 90 75 Q80 76 66 80Z" fill="#C0605A"/>
        <path d="M66 84 Q90 92 114 84 Q100 88 90 89 Q80 88 66 84Z" fill="#C0605A"/>
        <text x="44" y="86" text-anchor="middle" font-size="18" fill="rgba(212,149,106,0.5)">💨</text>
        <text x="136" y="86" text-anchor="middle" font-size="18" fill="rgba(212,149,106,0.5)">💨</text>
        <rect x="18" y="130" width="144" height="14" rx="4" fill="#E4EEF8"/>
        <text x="90" y="141" text-anchor="middle" font-size="10" font-weight="600" fill="#1E5C8A" font-family="Outfit,sans-serif">Inflate · sealed · 5 sec hold</text>
      </svg>`
    },
    {
      id:'veg-5', level:'veg', title:'Saliva Swallow with Laryngeal Palpation',
      muscles:'Suprahyoids · Thyrohyoid · Constrictors · Soft palate',
      reps:10, sets:2, holdSec:0,
      chips:['10 reps','2 sets','palpate larynx'],
      steps:[
        {n:'1', text:'<strong>Clinician palpation:</strong> Place two fingers lightly on the client\'s throat — index on the hyoid bone, middle finger on the thyroid notch.'},
        {n:'2', text:'Instruct client: "Gather saliva, close lips, and swallow." Feel for hyoid and larynx elevating under your fingers.'},
        {n:'3', text:'<strong>Effortful swallow variant:</strong> "Swallow as hard as you can, squeezing all your throat muscles." Increased hyoid elevation should be palpable.'},
        {n:'4', text:'Document: (a) laryngeal elevation present/absent, (b) timing of elevation (immediate vs. delayed), (c) any wet/gurgly voice quality post-swallow.'},
      ],
      tip:{type:'safe', text:'⚠️ If the client coughs, chokes, or voice quality changes (wet/gurgly), this may indicate aspiration risk. Refer for instrumental assessment (MBS or FEES) before continuing oral feeding.'},
      illus: `<svg viewBox="0 0 180 180" xmlns="http://www.w3.org/2000/svg" aria-label="Side profile showing laryngeal elevation during swallow with palpation">
        <path d="M130 16 Q145 36 148 64 Q152 94 144 122 Q132 152 112 166 Q90 178 68 172 Q44 164 34 144 Q22 120 26 94 Q20 70 26 50 Q36 28 54 18 Q72 8 98 8 Q116 8 130 16 Z" fill="#FDDBB4" opacity="0.6" transform="scale(0.75) translate(22, 0)"/>
        <path d="M78 108 Q106 102 128 106" stroke="#8B5E1A" stroke-width="3" fill="none" stroke-linecap="round" transform="scale(0.75) translate(22,0)"/>
        <defs>
          <marker id="arHy" markerWidth="5" markerHeight="5" refX="2.5" refY="1" orient="auto"><polygon points="0,5 2.5,0 5,5" fill="#8B5E1A"/></marker>
          <marker id="arLar" markerWidth="5" markerHeight="5" refX="2.5" refY="1" orient="auto"><polygon points="0,5 2.5,0 5,5" fill="#1A7A4A"/></marker>
        </defs>
        <path d="M82 84 L82 74" stroke="#8B5E1A" stroke-width="2" stroke-linecap="round" marker-end="url(#arHy)" transform="scale(0.75) translate(22,0)"/>
        <text x="72" y="72" text-anchor="middle" font-size="8" fill="#8B5E1A" font-family="Outfit">hyoid↑</text>
        <path d="M80 122 Q98 114 120 120 Q128 128 120 138 Q106 145 94 144 Q80 141 76 130 Z" fill="#B07040" stroke="#D4956A" stroke-width="1.2" transform="scale(0.75) translate(22,0)"/>
        <path d="M98 114 L98 104" stroke="#1A7A4A" stroke-width="2" stroke-linecap="round" marker-end="url(#arLar)" transform="scale(0.75) translate(22,0)"/>
        <text x="56" y="100" text-anchor="middle" font-size="8" fill="#1A7A4A" font-family="Outfit">larynx↑</text>
        <ellipse cx="54" cy="120" rx="10" ry="12" fill="#FDDBB4" opacity="0.85" stroke="#D4956A" stroke-width="1.2"/>
        <text x="36" y="138" text-anchor="middle" font-size="8" fill="var(--muted)" font-family="Outfit">palpate</text>
        <rect x="18" y="152" width="144" height="14" rx="4" fill="#E4EEF8"/>
        <text x="90" y="163" text-anchor="middle" font-size="10" font-weight="600" fill="#1E5C8A" font-family="Outfit,sans-serif">Palpate hyoid + larynx elevation</text>
      </svg>`
    }
  ]
};

// ─── RENDER ──────────────────────────────────────────────────────────────────
function colorClass(level) {
  return {l1:'l1',l2:'l2',l3:'l3',l4:'l4',veg:'veg'}[level];
}

function renderExercise(ex) {
  const cc = colorClass(ex.level);
  const isVeg = ex.level === 'veg';
  const btnColor = isVeg ? 'btn-veg' : 'btn-primary';

  const chips = ex.chips.map(c => `<span class="meta-chip c-${cc}">${c}</span>`).join('');
  const steps = ex.steps.map(s =>
    `<div class="step-row">
      <div class="step-n sn-${cc}">${s.n}</div>
      <div class="step-text">${s.text}</div>
    </div>`
  ).join('');

  const tipHtml = ex.tip ? `
    <div class="ex-tip ${ex.tip.type === 'safe' ? 'safe' : ex.tip.type === 'warn' ? 'warn' : 'info'}">
      <span>${ex.tip.type === 'safe' ? '🚨' : ex.tip.type === 'warn' ? '⚠️' : '💡'}</span>
      <span>${ex.tip.text}</span>
    </div>` : '';

  // Timer panel
  const hasHold = ex.holdSec > 0;
  const hasReps = ex.reps > 0;
  let timerHtml = '';
  if (hasReps || hasHold) {
    const setDots = ex.sets > 0 ? Array.from({length:ex.sets}, (_,i) =>
      `<div class="set-dot" id="dot-${ex.id}-${i}" data-set="${i}" data-ex="${ex.id}"></div>`
    ).join('') : '';

    timerHtml = `
    <div class="timer-panel" id="timer-${ex.id}">
      <div class="timer-title">Exercise Tracker</div>
      <div class="timer-controls">
        ${hasHold ? `
        <div class="hold-ring-wrap" id="ring-${ex.id}">
          <svg width="80" height="80" viewBox="0 0 80 80">
            <circle cx="40" cy="40" r="34" fill="none" stroke="#EDE8DE" stroke-width="7"/>
            <circle cx="40" cy="40" r="34" fill="none" stroke="var(--l${isVeg ? '1' : cc.slice(-1) || '1'})" stroke-width="7"
              stroke-dasharray="213.6" stroke-dashoffset="213.6"
              id="ring-circle-${ex.id}" style="transition:stroke-dashoffset 1s linear;stroke:var(--${isVeg?'veg':'l'+cc.slice(-1)})"/>
          </svg>
          <div class="hold-ring-center" id="ring-num-${ex.id}">${ex.holdSec}</div>
        </div>
        ` : ''}
        <div>
          ${hasReps ? `<div class="rep-counter">
            <button class="btn-icon" onclick="adjustRep('${ex.id}',-1)">−</button>
            <div>
              <div class="rep-display"><span id="rep-cur-${ex.id}">0</span><span style="font-size:16px;color:var(--muted)">/${ex.reps}</span></div>
              <div class="rep-label" style="text-align:center">reps</div>
            </div>
            <button class="btn-icon" onclick="adjustRep('${ex.id}',1)">+</button>
          </div>` : ''}
          <div style="margin-top:10px;display:flex;gap:8px;flex-wrap:wrap">
            ${hasHold ? `<button class="btn ${btnColor} btn-sm" onclick="startHold('${ex.id}',${ex.holdSec})">▶ Start Hold</button>` : ''}
            <button class="btn btn-outline btn-sm" onclick="nextSet('${ex.id}')">✓ Complete Set</button>
            <button class="btn btn-outline btn-sm" onclick="resetExercise('${ex.id}',${ex.sets},${ex.holdSec})">↺ Reset</button>
          </div>
          <span class="status-chip status-idle" id="status-${ex.id}" style="margin-top:8px;display:inline-block">Idle</span>
        </div>
      </div>
      ${ex.sets > 0 ? `<div class="sets-row">${setDots}</div>` : ''}
      ${hasReps ? `<div class="progress-bar" style="margin-top:8px"><div class="progress-fill ${isVeg?'veg':''}" id="prog-${ex.id}" style="width:0%"></div></div>` : ''}
    </div>`;
  }

  return `
  <div class="ex-card" id="card-${ex.id}" onclick="toggleCard('${ex.id}', event)">
    <div class="ex-card-top">
      <div class="ex-num-badge n-${cc}">${ex.id.toUpperCase().replace(/[A-Z]+-/,'').replace('-',' ')}</div>
      <div class="ex-info">
        <div class="ex-title">${ex.title}</div>
        <div class="ex-muscle">${ex.muscles}</div>
        <div class="ex-meta-row">${chips}</div>
      </div>
      <div class="expand-arrow">▾</div>
    </div>
    <div class="ex-illus">${ex.illus}</div>
    <div class="ex-steps-wrap">
      ${steps}
      ${tipHtml}
      ${timerHtml}
    </div>
  </div>`;
}

function renderAll() {
  document.getElementById('grid-l1').innerHTML = exercises.l1.map(renderExercise).join('');
  document.getElementById('grid-l2').innerHTML = exercises.l2.map(renderExercise).join('');
  document.getElementById('grid-l3').innerHTML = exercises.l3.map(renderExercise).join('');
  document.getElementById('grid-l4').innerHTML = exercises.l4.map(renderExercise).join('');
  document.getElementById('grid-veg').innerHTML = exercises.veg.map(renderExercise).join('');
}

// ─── INTERACTIONS ─────────────────────────────────────────────────────────────
function toggleCard(id, e) {
  if (e && e.target.closest('.timer-panel, .btn, .btn-icon')) return;
  const card = document.getElementById('card-' + id);
  card.classList.toggle('expanded');
}

const pageMap = {
  'dashboard': {title:'Dashboard', sub:'OroTherapy — Oromotor & Vegetative Skills Library'},
  'oro-l1':    {title:'Level 1 — Lip Strengthening', sub:'Orbicularis oris · Levator labii · Foundation for /p/, /b/, /m/'},
  'oro-l2':    {title:'Level 2 — Jaw Grading', sub:'Masseter · Pterygoids · Jaw range of motion'},
  'oro-l3':    {title:'Level 3 — Tongue Mobility', sub:'Genioglossus · Styloglossus · Intrinsic tongue muscles'},
  'oro-l4':    {title:'Level 4 — Speech Integration', sub:'Motor gains → functional speech production'},
  'veg':       {title:'Vegetative Skills', sub:'Chewing · Swallowing readiness · Laryngeal elevation'},
};

function showPage(id) {
  document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
  document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
  document.getElementById('page-' + id).classList.add('active');
  const nav = document.getElementById('nav-' + id);
  if (nav) nav.classList.add('active');
  const meta = pageMap[id] || {title: id, sub:''};
  document.getElementById('page-title').textContent = meta.title;
  document.getElementById('page-sub').textContent = meta.sub;
  closeSidebar();
  window.scrollTo(0, 0);
}

function openSidebar() {
  document.getElementById('sidebar').classList.add('open');
  document.getElementById('overlay').classList.add('open');
}
function closeSidebar() {
  document.getElementById('sidebar').classList.remove('open');
  document.getElementById('overlay').classList.remove('open');
}

// ─── TIMER / REP LOGIC ────────────────────────────────────────────────────────
const timers = {};
const repState = {};
const setStates = {};

function initState(id, maxSets) {
  if (!repState[id]) repState[id] = 0;
  if (!setStates[id]) setStates[id] = {current:0, max:maxSets, done:false};
}

function adjustRep(id, delta) {
  const ex = findEx(id);
  if (!ex) return;
  initState(id, ex.sets);
  repState[id] = Math.max(0, Math.min(ex.reps, repState[id] + delta));
  const cur = document.getElementById('rep-cur-' + id);
  if (cur) cur.textContent = repState[id];
  const prog = document.getElementById('prog-' + id);
  if (prog) prog.style.width = (repState[id] / ex.reps * 100) + '%';
  setStatus(id, repState[id] >= ex.reps ? 'done' : 'active', ex.level === 'veg');
}

function startHold(id, secs) {
  if (timers[id]) {clearInterval(timers[id]); timers[id] = null;}
  const ex = findEx(id);
  const isVeg = ex && ex.level === 'veg';
  let remaining = secs;
  const circle = document.getElementById('ring-circle-' + id);
  const num = document.getElementById('ring-num-' + id);
  const circumference = 213.6;
  setStatus(id, 'active', isVeg);
  if (circle) {
    circle.style.strokeDashoffset = '0';
    circle.style.transition = 'none';
  }
  if (num) num.textContent = remaining;

  setTimeout(() => {
    if (circle) {
      circle.style.transition = `stroke-dashoffset ${secs}s linear`;
      circle.style.strokeDashoffset = circumference + '';
    }
  }, 50);

  timers[id] = setInterval(() => {
    remaining--;
    if (num) num.textContent = Math.max(0, remaining);
    if (remaining <= 0) {
      clearInterval(timers[id]);
      timers[id] = null;
      setStatus(id, 'done', isVeg);
      if (num) num.textContent = '✓';
    }
  }, 1000);
}

function nextSet(id) {
  const ex = findEx(id);
  if (!ex) return;
  initState(id, ex.sets);
  const st = setStates[id];
  if (st.current < st.max) {
    const dot = document.getElementById(`dot-${id}-${st.current}`);
    if (dot) dot.classList.add(ex.level === 'veg' ? 'done-veg' : 'done');
    st.current++;
    repState[id] = 0;
    const cur = document.getElementById('rep-cur-' + id);
    if (cur) cur.textContent = '0';
    const prog = document.getElementById('prog-' + id);
    if (prog) prog.style.width = '0%';
    if (st.current >= st.max) {
      setStatus(id, 'done', ex.level === 'veg');
    } else {
      setStatus(id, 'rest', false);
      setTimeout(() => setStatus(id, 'idle', false), 3000);
    }
  }
}

function resetExercise(id, maxSets, holdSec) {
  const ex = findEx(id);
  if (timers[id]) {clearInterval(timers[id]); timers[id] = null;}
  repState[id] = 0;
  setStates[id] = {current:0, max:maxSets, done:false};
  const cur = document.getElementById('rep-cur-' + id);
  if (cur) cur.textContent = '0';
  const prog = document.getElementById('prog-' + id);
  if (prog) prog.style.width = '0%';
  const num = document.getElementById('ring-num-' + id);
  if (num) num.textContent = holdSec;
  const circle = document.getElementById('ring-circle-' + id);
  if (circle) {circle.style.transition='none'; circle.style.strokeDashoffset = '213.6';}
  for (let i = 0; i < maxSets; i++) {
    const dot = document.getElementById(`dot-${id}-${i}`);
    if (dot) {dot.classList.remove('done','done-veg');}
  }
  setStatus(id, 'idle', false);
}

function setStatus(id, state, isVeg) {
  const el = document.getElementById('status-' + id);
  if (!el) return;
  el.className = 'status-chip';
  if (state === 'idle')   { el.classList.add('status-idle'); el.textContent = 'Idle'; }
  else if (state === 'active') { el.classList.add(isVeg ? 'status-active-veg' : 'status-active'); el.textContent = 'Active'; }
  else if (state === 'done')  { el.classList.add('status-done'); el.textContent = '✓ Done'; }
  else if (state === 'rest')  { el.classList.add('status-rest'); el.textContent = '⏸ Rest'; }
}

function findEx(id) {
  const all = [...exercises.l1, ...exercises.l2, ...exercises.l3, ...exercises.l4, ...exercises.veg];
  return all.find(e => e.id === id) || null;
}

// ─── INIT ─────────────────────────────────────────────────────────────────────
renderAll();
</script>
</body>
</html>
Running Command

wc -l /home/daytona/workspace/oromotor-app/index.html && grep -c "ex-card" /home/daytona/workspace/oromotor-app/index.html && echo "File OK"

Running Command

grep -n "grid-l1\|grid-l2\|grid-l3\|grid-l4\|grid-veg\|renderAll\|showPage\|startHold\|nextSet\|adjustRep" /home/daytona/workspace/oromotor-app/index.html | head -30

The app is built and ready. Here's what was delivered:

index

HTML File · HTML


What's Inside

Structure

A single-file, self-contained web app (no server needed - open directly in any browser).

Navigation (left sidebar)

  • Dashboard - overview, stats, quick-start cards, and the clinical indication guide
  • Level 1 - Lips (3 exercises)
  • Level 2 - Jaw (2 exercises)
  • Level 3 - Tongue (3 exercises)
  • Level 4 - Speech Integration (2 exercises)
  • Vegetative Skills - all 5 exercises

Each Exercise Card Contains

FeatureDetail
SVG illustrationCustom-drawn anatomical diagrams for every exercise
Muscle labelsExact muscles targeted (e.g., genioglossus, buccinator)
Step-by-step instructionsClinically accurate procedures
Dosage chipsReps, sets, hold duration
Tip/warning boxColor-coded: 💡 info, ⚠️ caution, 🚨 safety/contraindication
Rep counter+ / − buttons with live progress bar
Hold timerCircular countdown ring with animated stroke
Set trackerDot indicators per set, marks complete on each set
Status chipIdle → Active → Rest → Done

SLP-specific Safeguards Built In

  • Clinical Indication Guide on the dashboard (when to use vs. not)
  • Contraindication warnings on all resistive jaw and food-based exercises
  • 80% mastery criterion documented in Level 4
  • Sensory vs. motor rationale separated in the buccal exercise
  • Level progression guidance between sections

can develop the webbased app on oromotor exercises and vegetative skills stengthining exercises

The app was just built in our previous exchange. You can download it right here:

index

HTML File · HTML

Just open the file in any browser — no server or internet connection required.

Here's a quick recap of everything included:
15 total exercises across 6 sections:
  • Level 1 — Lips (3 ex): Lip Press & Hold, Lip Stretch Smile–Round, Lip Pucker & Release
  • Level 2 — Jaw (2 ex): Jaw Open–Close with Grading, Resistive Jaw Opening
  • Level 3 — Tongue (3 ex): Tongue Lateralization, Tongue Tip Elevation, Tongue Protrusion & Retraction
  • Level 4 — Speech Integration (2 ex): Phoneme Integration Drill, Connected Speech Carryover
  • Vegetative Skills (5 ex): Rotary Chewing, Resistive Jaw Opening (Feeding), Jaw Lateralization, Buccal Tone & Cheek Inflation, Saliva Swallow with Laryngeal Palpation
Every card has: custom SVG anatomy illustration, muscle targets, step-by-step instructions, rep counter + hold timer with animated ring, set tracker dots, and color-coded safety/tip boxes.
If you'd like any changes - additional exercises, a patient-facing version, print layout, or session logging - just let me know.

cant open

Running Command

ls -lh /home/daytona/workspace/oromotor-app/index.html && head -5 /home/daytona/workspace/oromotor-app/index.html

Running Command

cd /home/daytona/workspace/oromotor-app && python3 -m http.server 7860 &> /tmp/server.log & sleep 2 && echo "Server started" && curl -s -o /dev/null -w "%{http_code}" http://localhost:7860/index.html

The server is running and returning 200. Try opening it here:
If that still doesn't work in your environment, here are two other options:
Option 1 - Download and open locally:
  1. Click this link to download:

    index

    HTML File · HTML

  2. Save it to your computer
  3. Double-click the downloaded file to open it in Chrome, Firefox, or Edge
Option 2 - Copy the raw HTML: The entire app is one self-contained HTML file with no external dependencies except Google Fonts. You can:
  1. Create a new file on your computer called orotherapy.html
  2. Paste the content from the downloaded file
  3. Open it in any browser
Would you like me to try a different format - such as packaging it differently or hosting it on a different port? Also, what environment are you using (Windows, Mac, Linux)?

Help me download and save the HTML file to my computer

Here's exactly how to save it:

Step-by-Step: Save the HTML File

Step 1 — Open the raw file

Click this link:

index

HTML File · HTML


Step 2 — Save it to your computer

Once the file opens (you'll see raw HTML code), use your keyboard:
  • Windows/Linux: Press Ctrl + S
  • Mac: Press Cmd + S
A "Save As" dialog will appear. Make sure:
  • File name: orotherapy.html (or any name you like)
  • File type / Save as type: select "All Files (.)" or "Webpage, HTML Only"
  • Choose a folder (e.g., Desktop)
Click Save.

Step 3 — Open it in your browser

Go to where you saved the file, then double-click it. It will open in Chrome, Edge, Firefox, or Safari.

Alternative — Right-click method

If the link above opens as raw code in your browser tab:
  1. Right-click anywhere on the page
  2. Select "Save As" or "Save Page As"
  3. Save as orotherapy.html
  4. Double-click the saved file to open the app

If the link doesn't work at all in your browser, let me know and I'll paste the full HTML code directly in the chat so you can copy-paste it into a text editor and save it yourself.
This is a shared conversation. Sign in to Orris to start your own chat.