Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 171 additions & 5 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -506,16 +506,64 @@
color: var(--accent);
}

.modal-media {
.modal-visuals {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
padding: 14px 18px 0;
}

.modal-media {
padding: 0;
}

.modal-gif {
width: 100%;
max-height: 320px;
max-height: 280px;
object-fit: contain;
border-radius: var(--radius-md);
background: var(--bg-elevated);
border: 1px solid var(--border);
}

.modal-muscle-map {
background: var(--bg-elevated);
border-radius: var(--radius-md);
border: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: center;
padding: 12px;
max-height: 280px;
}

.modal-muscle-map svg {
width: 100%;
height: 100%;
max-height: 250px;
}

.muscle-path {
fill: var(--bg-surface);
stroke: var(--border);
stroke-width: 0.8;
transition: fill 0.2s ease;
}

.muscle-path.static-bg {
fill: var(--bg-elevated);
stroke: var(--border);
stroke-width: 0.5;
}

.muscle-path.primary {
fill: #ff4757;
stroke: #ff4757;
}

.muscle-path.secondary {
fill: #ffa502;
stroke: #ffa502;
}

.modal-meta {
Expand Down Expand Up @@ -780,7 +828,19 @@
.modal-panel { max-height: 95dvh; }

.modal-header { padding: 16px 16px 0; }
.modal-media { padding: 12px 16px 0; }
.modal-media { padding: 0; }
.modal-visuals {
grid-template-columns: 1fr;
gap: 12px;
padding: 12px 16px 0;
}
.modal-muscle-map {
max-height: 180px;
padding: 6px;
}
.modal-muscle-map svg {
max-height: 160px;
}
.modal-meta { padding: 12px 16px 0; }
.modal-muscles { padding: 12px 16px 14px; }
.modal-instructions { padding: 0 16px 20px; }
Expand Down Expand Up @@ -1159,8 +1219,11 @@ <h3 class="db-step-title">Media Files</h3>
<h2 class="modal-title" id="modal-title"></h2>
<button class="modal-close" id="modal-close" aria-label="Close">✕</button>
</div>
<div class="modal-media">
<img class="modal-gif" id="modal-gif" alt="" />
<div class="modal-visuals">
<div class="modal-media">
<img class="modal-gif" id="modal-gif" alt="" />
</div>
<div class="modal-muscle-map" id="modal-muscle-map"></div>
</div>
<div class="modal-meta" id="modal-meta"></div>
<div class="modal-muscles" id="modal-muscles"></div>
Expand Down Expand Up @@ -1200,6 +1263,7 @@ <h2 class="modal-title" id="modal-title"></h2>
const modalTitle = document.getElementById('modal-title');
const modalGif = document.getElementById('modal-gif');
const modalMeta = document.getElementById('modal-meta');
const modalMuscleMap = document.getElementById('modal-muscle-map');
const modalMuscles = document.getElementById('modal-muscles');
const modalInstr = document.getElementById('modal-instructions');
const modalClose = document.getElementById('modal-close');
Expand Down Expand Up @@ -1464,13 +1528,115 @@ <h2 class="modal-title" id="modal-title"></h2>
}

// ── Modal ──────────────────────────────────────────
function getMuscleKeys(name) {
if (!name) return [];
const n = name.toLowerCase().trim();
if (n.includes('biceps')) return ['biceps'];
if (n.includes('triceps')) return ['triceps'];
if (n.includes('chest') || n.includes('pectoral')) return ['chest'];
if (n.includes('abs') || n.includes('abdominal') || n.includes('serratus')) return ['abs'];
if (n.includes('back') || n.includes('rhomboid')) return ['upper-back'];
if (n.includes('lats') || n.includes('latissimus')) return ['lats'];
if (n.includes('lower back') || n.includes('spine')) return ['lower-back'];
if (n.includes('traps') || n.includes('trapezius') || n.includes('levator')) return ['traps'];
if (n.includes('delts') || n.includes('deltoid') || n.includes('shoulder')) return ['shoulders'];
if (n.includes('forearm') || n.includes('wrist')) return ['forearms'];
if (n.includes('glute')) return ['glutes'];
if (n.includes('hamstring')) return ['hamstrings'];
if (n.includes('quad')) return ['quads'];
if (n.includes('calf') || n.includes('calves') || n.includes('soleus')) return ['calves'];
return [];
}

function renderMuscleMap(ex) {
const svgHtml = `
<svg viewBox="0 0 200 180" xmlns="http://www.w3.org/2000/svg">
<g id="anterior">
<circle cx="50" cy="18" r="8" class="muscle-path static-bg" />
<polygon points="47,26 53,26 54,32 46,32" class="muscle-path static-bg" />
<polygon points="46,32 38,34 35,42 43,40" class="muscle-path" data-muscle="shoulders" />
<polygon points="54,32 62,34 65,42 57,40" class="muscle-path" data-muscle="shoulders" />
<polygon points="43,40 43,50 50,50 50,32" class="muscle-path" data-muscle="chest" />
<polygon points="50,32 50,50 57,50 57,40" class="muscle-path" data-muscle="chest" />
<polygon points="43,50 57,50 55,75 45,75" class="muscle-path" data-muscle="abs" />
<polygon points="38,34 35,42 32,58 36,56" class="muscle-path" data-muscle="biceps" />
<polygon points="62,34 65,42 68,58 64,56" class="muscle-path" data-muscle="biceps" />
<polygon points="32,58 36,56 39,78 36,78" class="muscle-path" data-muscle="forearms" />
<polygon points="68,58 64,56 61,78 64,78" class="muscle-path" data-muscle="forearms" />
<polygon points="36,78 39,78 38,84 35,84" class="muscle-path static-bg" />
<polygon points="64,78 61,78 62,84 65,84" class="muscle-path static-bg" />
<polygon points="45,75 55,75 50,82" class="muscle-path static-bg" />
<polygon points="45,75 50,82 50,118 42,118" class="muscle-path" data-muscle="quads" />
<polygon points="55,75 50,82 50,118 58,118" class="muscle-path" data-muscle="quads" />
<polygon points="42,118 50,118 49,124 43,124" class="muscle-path static-bg" />
<polygon points="58,118 50,118 51,124 57,124" class="muscle-path static-bg" />
<polygon points="43,124 49,124 47,162 44,162" class="muscle-path" data-muscle="calves" />
<polygon points="57,124 51,124 53,162 56,162" class="muscle-path" data-muscle="calves" />
<polygon points="44,162 47,162 45,170 41,170" class="muscle-path static-bg" />
<polygon points="56,162 53,162 55,170 59,170" class="muscle-path static-bg" />
</g>
<g id="posterior">
<circle cx="150" cy="18" r="8" class="muscle-path static-bg" />
<polygon points="147,26 153,26 154,32 146,32" class="muscle-path static-bg" />
<polygon points="146,32 154,32 156,42 144,42" class="muscle-path" data-muscle="traps" />
<polygon points="143,40 135,42 132,48 141,48" class="muscle-path" data-muscle="shoulders" />
<polygon points="157,40 165,42 168,48 159,48" class="muscle-path" data-muscle="shoulders" />
<polygon points="144,42 156,42 155,54 145,54" class="muscle-path" data-muscle="upper-back" />
<polygon points="138,34 135,42 132,58 136,56" class="muscle-path" data-muscle="triceps" />
<polygon points="162,34 165,42 168,58 164,56" class="muscle-path" data-muscle="triceps" />
<polygon points="132,58 136,56 139,78 136,78" class="muscle-path" data-muscle="forearms" />
<polygon points="168,58 164,56 161,78 164,78" class="muscle-path" data-muscle="forearms" />
<polygon points="136,78 139,78 138,84 135,84" class="muscle-path static-bg" />
<polygon points="164,78 161,78 162,84 165,84" class="muscle-path static-bg" />
<polygon points="145,54 150,54 150,75 142,75" class="muscle-path" data-muscle="lats" />
<polygon points="155,54 150,54 150,75 158,75" class="muscle-path" data-muscle="lats" />
<polygon points="145,75 155,75 154,82 146,82" class="muscle-path" data-muscle="lower-back" />
<polygon points="142,82 150,82 150,96 142,96" class="muscle-path" data-muscle="glutes" />
<polygon points="158,82 150,82 150,96 158,96" class="muscle-path" data-muscle="glutes" />
<polygon points="142,96 150,96 150,132 143,132" class="muscle-path" data-muscle="hamstrings" />
<polygon points="158,96 150,96 150,132 157,132" class="muscle-path" data-muscle="hamstrings" />
<polygon points="143,132 149,132 147,162 144,162" class="muscle-path" data-muscle="calves" />
<polygon points="157,132 151,132 153,162 156,162" class="muscle-path" data-muscle="calves" />
<polygon points="144,162 147,162 146,170 142,170" class="muscle-path static-bg" />
<polygon points="156,162 153,162 154,170 158,170" class="muscle-path static-bg" />
</g>
</svg>
`;
modalMuscleMap.innerHTML = svgHtml;

const primaryKeys = new Set([
...getMuscleKeys(ex.target),
...getMuscleKeys(ex.muscle_group)
]);
const secondaryKeys = new Set();
if (ex.secondary_muscles) {
ex.secondary_muscles.forEach(m => {
getMuscleKeys(m).forEach(k => {
if (!primaryKeys.has(k)) {
secondaryKeys.add(k);
}
});
});
}

primaryKeys.forEach(key => {
const paths = modalMuscleMap.querySelectorAll(`[data-muscle="${key}"]`);
paths.forEach(p => p.classList.add('primary'));
});
secondaryKeys.forEach(key => {
const paths = modalMuscleMap.querySelectorAll(`[data-muscle="${key}"]`);
paths.forEach(p => p.classList.add('secondary'));
});
}

function openModal(id) {
const ex = state.exercises.find(e => e.id === id);
if (!ex) return;

modalTitle.textContent = ex.name;
modalGif.src = ex.gif_url;
modalGif.alt = ex.name;
renderMuscleMap(ex);

// Meta chips
modalMeta.innerHTML = '';
Expand Down