+ {/* Sticky title bar — sits at top of article, becomes sticky on scroll */}
+
+
+
{displayTitle}
+
{displayCategory && (
{displayCategory}
)}
+ {technique.creator_info && (
+
+
+ {technique.creator_info.name}
+
+ )}
- {technique.creator_info && (
-
-
-
- {technique.creator_info.name}
-
- {technique.creator_info.genres && technique.creator_info.genres.length > 0 && (
-
- {technique.creator_info.genres.map((g) => (
- {g}
- ))}
-
+
+
+
+ {/* Stats + version switcher */}
+
+ {(() => {
+ const sourceCount = new Set(
+ technique.key_moments
+ .map((km) => km.video_filename)
+ .filter(Boolean),
+ ).size;
+ const momentCount = technique.key_moments.length;
+ const updated = new Date(technique.updated_at).toLocaleDateString(
+ "en-US",
+ { year: "numeric", month: "short", day: "numeric" },
+ );
+ const parts = [
+ `Compiled from ${sourceCount} source${sourceCount !== 1 ? "s" : ""}`,
+ `${momentCount} key moment${momentCount !== 1 ? "s" : ""}`,
+ ];
+ if (technique.version_count > 0) {
+ parts.push(
+ `${technique.version_count} version${technique.version_count !== 1 ? "s" : ""}`,
+ );
+ }
+ parts.push(`Last updated ${updated}`);
+ return parts.join(" · ");
+ })()}
+
+
+
+ {versions.length > 0 && (
+
+
+
+ {versionLoading && (
+ Loading…
)}
)}
-
-
- {/* Meta stats line */}
-
- {(() => {
- const sourceCount = new Set(
- technique.key_moments
- .map((km) => km.video_filename)
- .filter(Boolean),
- ).size;
- const momentCount = technique.key_moments.length;
- const updated = new Date(technique.updated_at).toLocaleDateString(
- "en-US",
- { year: "numeric", month: "short", day: "numeric" },
- );
- const parts = [
- `Compiled from ${sourceCount} source${sourceCount !== 1 ? "s" : ""}`,
- `${momentCount} key moment${momentCount !== 1 ? "s" : ""}`,
- ];
- if (technique.version_count > 0) {
- parts.push(
- `${technique.version_count} version${technique.version_count !== 1 ? "s" : ""}`,
- );
- }
- parts.push(`Last updated ${updated}`);
- return parts.join(" · ");
- })()}
-
-
- {/* Version switcher */}
-
- {versions.length > 0 && (
-
-
-
- {versionLoading && (
- Loading…
- )}
-
- )}
-
-
+
{/* Pipeline metadata for historical versions */}
{isHistorical && versionDetail.pipeline_metadata && (
@@ -474,179 +423,181 @@ export default function TechniquePage() {
/>
)}
-
- {/* Tags + plugin pills — scoped to left column */}
- {((displayTags && displayTags.length > 0) || (displayPlugins && displayPlugins.length > 0)) && (
-
- {displayTags && displayTags.map((tag) => (
-
- {tag}
-
- ))}
- {displayPlugins && displayPlugins.length > 0 && displayPlugins.map((plugin) => (
-
- {plugin}
-
- ))}
-
- )}
-
- {/* Summary */}
- {displaySummary && (
-
+ {/* Tags + plugin pills */}
+ {((displayTags && displayTags.length > 0) || (displayPlugins && displayPlugins.length > 0)) && (
+
+ {displayTags && displayTags.map((tag) => (
+
+ {tag}
+
+ ))}
+ {displayPlugins && displayPlugins.length > 0 && displayPlugins.map((plugin) => (
+
+ {plugin}
+
+ ))}
+
)}
- {/* Study guide prose — body_sections */}
- {displaySections &&
- (Array.isArray(displaySections) ? displaySections.length > 0 : Object.keys(displaySections).length > 0) && (
-
- {displayFormat === "v2" && Array.isArray(displaySections) ? (
- <>
- {(displaySections as BodySectionV2[]).map((section) => {
- const sectionSlug = slugify(section.heading);
+ {/* Two-column layout: main content + ToC sidebar */}
+
+
+ {/* Summary */}
+ {displaySummary && (
+
+ )}
+
+ {/* Study guide prose — body_sections */}
+ {displaySections &&
+ (Array.isArray(displaySections) ? displaySections.length > 0 : Object.keys(displaySections).length > 0) && (
+
+ {displayFormat === "v2" && Array.isArray(displaySections) ? (
+ <>
+ {(displaySections as BodySectionV2[]).map((section) => {
+ const sectionSlug = slugify(section.heading);
+ return (
+
+
{section.heading}
+ {section.content && (
+
{parseCitations(section.content, technique.key_moments)}
+ )}
+ {section.subsections.map((sub) => {
+ const subSlug = `${sectionSlug}--${slugify(sub.heading)}`;
+ return (
+
+
{sub.heading}
+ {sub.content && (
+
{parseCitations(sub.content, technique.key_moments)}
+ )}
+
+ );
+ })}
+
+ );
+ })}
+ >
+ ) : (
+ /* V1 dict format */
+ Object.entries(displaySections as Record).map(
+ ([sectionTitle, content]: [string, unknown]) => (
+
+
{sectionTitle}
+ {typeof content === "string" ? (
+
{content as string}
+ ) : typeof content === "object" && content !== null ? (
+
+ {JSON.stringify(content, null, 2)}
+
+ ) : (
+
{String(content as string)}
+ )}
+
+ ),
+ )
+ )}
+
+ )}
+
+ {/* Key Moments — bibliography style */}
+ {technique.key_moments.length > 0 && (
+
+ Key Moments
+
+ {technique.key_moments.map((km, idx) => (
+ -
+ [{idx}]
+
+ {km.title}
+
+ {km.video_filename && (
+ {km.video_filename}
+ )}
+
+ {formatTime(km.start_time)}–{formatTime(km.end_time)}
+
+ {km.content_type}
+
+ {km.summary}
+
+
+ ))}
+
+
+ )}
+
+ {/* Signal chains */}
+ {displayChains &&
+ displayChains.length > 0 && (
+
+ Signal Chains
+ {displayChains.map((chain, i) => {
+ const chainObj = chain as Record;
+ const chainName =
+ typeof chainObj["name"] === "string"
+ ? chainObj["name"]
+ : `Chain ${i + 1}`;
+ const steps = Array.isArray(chainObj["steps"])
+ ? (chainObj["steps"] as string[])
+ : [];
return (
-
-
{section.heading}
- {section.content && (
-
{parseCitations(section.content, technique.key_moments)}
+
+
{chainName}
+ {steps.length > 0 && (
+
+ {steps.map((step, j) => (
+
+ {j > 0 && (
+
+ {" → "}
+
+ )}
+
+ {String(step)}
+
+
+ ))}
+
)}
- {section.subsections.map((sub) => {
- const subSlug = `${sectionSlug}--${slugify(sub.heading)}`;
- return (
-
-
{sub.heading}
- {sub.content && (
-
{parseCitations(sub.content, technique.key_moments)}
- )}
-
- );
- })}
);
})}
- >
- ) : (
- /* V1 dict format — original rendering */
- Object.entries(displaySections as Record
).map(
- ([sectionTitle, content]: [string, unknown]) => (
-
-
{sectionTitle}
- {typeof content === "string" ? (
-
{content as string}
- ) : typeof content === "object" && content !== null ? (
-
- {JSON.stringify(content, null, 2)}
-
- ) : (
-
{String(content as string)}
+
+ )}
+
+ {/* Related techniques */}
+ {technique.related_links.length > 0 && (
+
+ Related Techniques
+
+ {technique.related_links.map((link) => (
+
+
+ {link.target_title}
+
+ {link.creator_name && (
+
{link.creator_name}
+ )}
+ {link.topic_category && (
+
{link.topic_category}
+ )}
+ {link.reason && (
+
{link.reason}
)}
- ),
- )
- )}
-
- )}
-
-
-
- {/* Table of Contents — v2 pages only */}
- {displayFormat === "v2" && Array.isArray(displaySections) && (displaySections as BodySectionV2[]).length > 0 && (
-
- )}
- {/* Key moments (always from live data — not versioned) */}
- {technique.key_moments.length > 0 && (
-
- Key Moments
-
- {technique.key_moments.map((km) => (
- -
-
{km.title}
-
- {km.video_filename && (
-
- {km.video_filename}
-
- )}
-
- {formatTime(km.start_time)} – {formatTime(km.end_time)}
-
-
- {km.content_type}
-
-
- {km.summary}
-
- ))}
-
-
- )}
-
- {/* Signal chains */}
- {displayChains &&
- displayChains.length > 0 && (
-
- Signal Chains
- {displayChains.map((chain, i) => {
- const chainObj = chain as Record;
- const chainName =
- typeof chainObj["name"] === "string"
- ? chainObj["name"]
- : `Chain ${i + 1}`;
- const steps = Array.isArray(chainObj["steps"])
- ? (chainObj["steps"] as string[])
- : [];
- return (
-
-
{chainName}
- {steps.length > 0 && (
-
- {steps.map((step, j) => (
-
- {j > 0 && (
-
- {" → "}
-
- )}
-
- {String(step)}
-
-
- ))}
-
- )}
-
- );
- })}
-
- )}
-
- {/* Related techniques (always from live data) */}
- {technique.related_links.length > 0 && (
-
- Related Techniques
-
- {technique.related_links.map((link) => (
-
-
- {link.target_title}
-
- {link.creator_name && (
-
{link.creator_name}
- )}
- {link.topic_category && (
-
{link.topic_category}
- )}
- {link.reason && (
-
{link.reason}
- )}
+ ))}
- ))}
-
-
- )}
+
+ )}
+
+ {/* Sidebar: ToC only */}
+ {isV2 && (
+
+ )}
{/* Footer actions */}
diff --git a/frontend/tsconfig.app.tsbuildinfo b/frontend/tsconfig.app.tsbuildinfo
index 32fedc2..a04a926 100644
--- a/frontend/tsconfig.app.tsbuildinfo
+++ b/frontend/tsconfig.app.tsbuildinfo
@@ -1 +1 @@
-{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/public-client.ts","./src/components/AdminDropdown.tsx","./src/components/AppFooter.tsx","./src/components/CategoryIcons.tsx","./src/components/CopyLinkButton.tsx","./src/components/CreatorAvatar.tsx","./src/components/ReadingHeader.tsx","./src/components/ReportIssueModal.tsx","./src/components/SearchAutocomplete.tsx","./src/components/SortDropdown.tsx","./src/components/TableOfContents.tsx","./src/components/TagList.tsx","./src/hooks/useCountUp.ts","./src/hooks/useDocumentTitle.ts","./src/hooks/useSortPreference.ts","./src/pages/About.tsx","./src/pages/AdminPipeline.tsx","./src/pages/AdminReports.tsx","./src/pages/AdminTechniquePages.tsx","./src/pages/CreatorDetail.tsx","./src/pages/CreatorsBrowse.tsx","./src/pages/Home.tsx","./src/pages/SearchResults.tsx","./src/pages/SubTopicPage.tsx","./src/pages/TechniquePage.tsx","./src/pages/TopicsBrowse.tsx","./src/utils/catSlug.ts","./src/utils/citations.tsx"],"version":"5.6.3"}
\ No newline at end of file
+{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/public-client.ts","./src/components/AdminDropdown.tsx","./src/components/AppFooter.tsx","./src/components/CategoryIcons.tsx","./src/components/CopyLinkButton.tsx","./src/components/CreatorAvatar.tsx","./src/components/ReportIssueModal.tsx","./src/components/SearchAutocomplete.tsx","./src/components/SortDropdown.tsx","./src/components/TableOfContents.tsx","./src/components/TagList.tsx","./src/hooks/useCountUp.ts","./src/hooks/useDocumentTitle.ts","./src/hooks/useSortPreference.ts","./src/pages/About.tsx","./src/pages/AdminPipeline.tsx","./src/pages/AdminReports.tsx","./src/pages/AdminTechniquePages.tsx","./src/pages/CreatorDetail.tsx","./src/pages/CreatorsBrowse.tsx","./src/pages/Home.tsx","./src/pages/SearchResults.tsx","./src/pages/SubTopicPage.tsx","./src/pages/TechniquePage.tsx","./src/pages/TopicsBrowse.tsx","./src/utils/catSlug.ts","./src/utils/citations.tsx"],"version":"5.6.3"}
\ No newline at end of file