|
@@ -32,17 +32,26 @@ struct CVTemplateCardPalette {
|
|
|
|
|
|
|
|
fileprivate enum CVPreviewDemoContent {
|
|
fileprivate enum CVPreviewDemoContent {
|
|
|
static let fullName = "Sarah Johnson"
|
|
static let fullName = "Sarah Johnson"
|
|
|
|
|
+ /// Shown in the header / contact band (broad role).
|
|
|
static let title = "Senior Product Manager"
|
|
static let title = "Senior Product Manager"
|
|
|
|
|
+ /// Scoped title under Experience so it is not a verbatim repeat of the header line.
|
|
|
|
|
+ static let experienceRole = "Group PM, Consumer Growth & Activation"
|
|
|
static let company = "Google"
|
|
static let company = "Google"
|
|
|
static let companyLine = "Google · Mountain View, CA · 2019 – Present"
|
|
static let companyLine = "Google · Mountain View, CA · 2019 – Present"
|
|
|
static let university = "Stanford University"
|
|
static let university = "Stanford University"
|
|
|
static let degree = "M.S. Management Science & Engineering"
|
|
static let degree = "M.S. Management Science & Engineering"
|
|
|
|
|
+ static let educationYears = "2014 – 2016"
|
|
|
static let email = "sarah.johnson@email.com"
|
|
static let email = "sarah.johnson@email.com"
|
|
|
static let phone = "(415) 555-0198"
|
|
static let phone = "(415) 555-0198"
|
|
|
static let location = "Mountain View, CA"
|
|
static let location = "Mountain View, CA"
|
|
|
static let summary = "Product leader shipping roadmap, discovery, and analytics for high-scale consumer experiences."
|
|
static let summary = "Product leader shipping roadmap, discovery, and analytics for high-scale consumer experiences."
|
|
|
static let bullet1 = "Defined multi-year platform strategy with exec stakeholders and quarterly OKRs."
|
|
static let bullet1 = "Defined multi-year platform strategy with exec stakeholders and quarterly OKRs."
|
|
|
static let bullet2 = "Partnered with engineering and design to launch experiments improving activation by 12%."
|
|
static let bullet2 = "Partnered with engineering and design to launch experiments improving activation by 12%."
|
|
|
|
|
+ static let bullet3 = "Stood up quarterly business reviews with finance and GTM, aligning spend to north-star metrics."
|
|
|
|
|
+ /// Sidebar “highlights” blurb kept distinct from the experience bullets.
|
|
|
|
|
+ static let careerHighlights = "Presented roadmap shifts to the leadership team and translated trade-offs into clear investment asks."
|
|
|
|
|
+ /// Single tools line reused wherever the résumé lists a stack (avoids scattered near-duplicate strings).
|
|
|
|
|
+ static let toolsLine = "Figma · SQL · Amplitude · Jira · BigQuery"
|
|
|
static let skillsList = ["Product Strategy", "SQL", "Figma", "A/B Testing", "Roadmapping"]
|
|
static let skillsList = ["Product Strategy", "SQL", "Figma", "A/B Testing", "Roadmapping"]
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -300,7 +309,7 @@ final class CVTemplatePreviewView: NSView {
|
|
|
}
|
|
}
|
|
|
if variant % 7 == 3 {
|
|
if variant % 7 == 3 {
|
|
|
inner.addArrangedSubview(sectionHeading("TOOLS"))
|
|
inner.addArrangedSubview(sectionHeading("TOOLS"))
|
|
|
- inner.addArrangedSubview(makeLabel("Amplitude · Jira · BigQuery", font: .systemFont(ofSize: 5.9), color: palette.previewMuted, alignment: .left, maxLines: 1))
|
|
|
|
|
|
|
+ inner.addArrangedSubview(makeLabel(CVPreviewDemoContent.toolsLine, font: .systemFont(ofSize: 5.9), color: palette.previewMuted, alignment: .left, maxLines: 1))
|
|
|
}
|
|
}
|
|
|
box.addArrangedSubview(inner)
|
|
box.addArrangedSubview(inner)
|
|
|
return box
|
|
return box
|
|
@@ -319,10 +328,11 @@ final class CVTemplatePreviewView: NSView {
|
|
|
}
|
|
}
|
|
|
let experienceBlock: () -> Void = {
|
|
let experienceBlock: () -> Void = {
|
|
|
stack.addArrangedSubview(self.sectionHeading("EXPERIENCE"))
|
|
stack.addArrangedSubview(self.sectionHeading("EXPERIENCE"))
|
|
|
- stack.addArrangedSubview(self.makeLabel(CVPreviewDemoContent.title, font: .systemFont(ofSize: 6.8, weight: .semibold), color: self.palette.previewInk, alignment: .left, maxLines: 1))
|
|
|
|
|
|
|
+ stack.addArrangedSubview(self.makeLabel(CVPreviewDemoContent.experienceRole, font: .systemFont(ofSize: 6.8, weight: .semibold), color: self.palette.previewInk, alignment: .left, maxLines: 1))
|
|
|
stack.addArrangedSubview(self.makeLabel(CVPreviewDemoContent.companyLine, font: .systemFont(ofSize: 6.2, weight: .medium), color: self.template.themeColor, alignment: .left, maxLines: 1))
|
|
stack.addArrangedSubview(self.makeLabel(CVPreviewDemoContent.companyLine, font: .systemFont(ofSize: 6.2, weight: .medium), color: self.template.themeColor, alignment: .left, maxLines: 1))
|
|
|
stack.addArrangedSubview(self.bulletRow(CVPreviewDemoContent.bullet1, size: sp))
|
|
stack.addArrangedSubview(self.bulletRow(CVPreviewDemoContent.bullet1, size: sp))
|
|
|
stack.addArrangedSubview(self.bulletRow(CVPreviewDemoContent.bullet2, size: sp))
|
|
stack.addArrangedSubview(self.bulletRow(CVPreviewDemoContent.bullet2, size: sp))
|
|
|
|
|
+ stack.addArrangedSubview(self.bulletRow(CVPreviewDemoContent.bullet3, size: sp))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if experienceFirst {
|
|
if experienceFirst {
|
|
@@ -334,7 +344,7 @@ final class CVTemplatePreviewView: NSView {
|
|
|
}
|
|
}
|
|
|
stack.addArrangedSubview(sectionHeading("EDUCATION"))
|
|
stack.addArrangedSubview(sectionHeading("EDUCATION"))
|
|
|
stack.addArrangedSubview(makeLabel(CVPreviewDemoContent.university, font: .systemFont(ofSize: 6.6, weight: .semibold), color: palette.previewInk, alignment: .left, maxLines: 1))
|
|
stack.addArrangedSubview(makeLabel(CVPreviewDemoContent.university, font: .systemFont(ofSize: 6.6, weight: .semibold), color: palette.previewInk, alignment: .left, maxLines: 1))
|
|
|
- stack.addArrangedSubview(makeLabel(CVPreviewDemoContent.degree, font: .systemFont(ofSize: 6.2), color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
|
|
|
|
|
+ stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.degree) · \(CVPreviewDemoContent.educationYears)", font: .systemFont(ofSize: 6.2), color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
|
return stack
|
|
return stack
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -508,7 +518,7 @@ final class CVTemplatePreviewView: NSView {
|
|
|
box.addArrangedSubview(modernSectionRow(symbol: "person.crop.circle", title: "About", theme: theme))
|
|
box.addArrangedSubview(modernSectionRow(symbol: "person.crop.circle", title: "About", theme: theme))
|
|
|
box.addArrangedSubview(makeLabel(CVPreviewDemoContent.summary, font: .systemFont(ofSize: 6), color: palette.previewInk, alignment: .left, maxLines: 4))
|
|
box.addArrangedSubview(makeLabel(CVPreviewDemoContent.summary, font: .systemFont(ofSize: 6), color: palette.previewInk, alignment: .left, maxLines: 4))
|
|
|
box.addArrangedSubview(modernSectionRow(symbol: "star.fill", title: "Highlights", theme: theme))
|
|
box.addArrangedSubview(modernSectionRow(symbol: "star.fill", title: "Highlights", theme: theme))
|
|
|
- box.addArrangedSubview(makeLabel("Launched growth experiments, SQL deep dives, design QA.", font: .systemFont(ofSize: 6), color: palette.previewMuted, alignment: .left, maxLines: 3))
|
|
|
|
|
|
|
+ box.addArrangedSubview(makeLabel(CVPreviewDemoContent.careerHighlights, font: .systemFont(ofSize: 6), color: palette.previewMuted, alignment: .left, maxLines: 3))
|
|
|
return box
|
|
return box
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -533,11 +543,11 @@ final class CVTemplatePreviewView: NSView {
|
|
|
stack.spacing = 5
|
|
stack.spacing = 5
|
|
|
stack.alignment = .leading
|
|
stack.alignment = .leading
|
|
|
stack.addArrangedSubview(modernSectionRow(symbol: "briefcase.fill", title: "Experience", theme: theme))
|
|
stack.addArrangedSubview(modernSectionRow(symbol: "briefcase.fill", title: "Experience", theme: theme))
|
|
|
- stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.title) — \(CVPreviewDemoContent.company)", font: .systemFont(ofSize: 6.6, weight: .semibold), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
|
|
- stack.addArrangedSubview(makeLabel("2019 – Present · Led cross-functional pods from discovery to launch.", font: .systemFont(ofSize: 6.1), color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
|
|
|
|
|
+ stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.experienceRole) — \(CVPreviewDemoContent.company)", font: .systemFont(ofSize: 6.6, weight: .semibold), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
|
|
+ stack.addArrangedSubview(makeLabel("2019 – Present · Led cross-functional pods from discovery through launch and post-ship learning.", font: .systemFont(ofSize: 6.1), color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
|
stack.addArrangedSubview(tagRow(theme: theme))
|
|
stack.addArrangedSubview(tagRow(theme: theme))
|
|
|
stack.addArrangedSubview(modernSectionRow(symbol: "graduationcap.fill", title: "Education", theme: theme))
|
|
stack.addArrangedSubview(modernSectionRow(symbol: "graduationcap.fill", title: "Education", theme: theme))
|
|
|
- stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.university), \(CVPreviewDemoContent.degree)", font: .systemFont(ofSize: 6.2), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
|
|
|
|
+ stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.university), \(CVPreviewDemoContent.degree) (\(CVPreviewDemoContent.educationYears))", font: .systemFont(ofSize: 6.2), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
return stack
|
|
return stack
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -636,7 +646,7 @@ final class CVTemplatePreviewView: NSView {
|
|
|
|
|
|
|
|
let edu: () -> Void = {
|
|
let edu: () -> Void = {
|
|
|
stack.addArrangedSubview(self.sectionHeading("EDUCATION"))
|
|
stack.addArrangedSubview(self.sectionHeading("EDUCATION"))
|
|
|
- stack.addArrangedSubview(self.makeLabel("\(CVPreviewDemoContent.university) — \(CVPreviewDemoContent.degree)", font: .systemFont(ofSize: 6.1, weight: .light), color: self.palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
|
|
|
|
+ stack.addArrangedSubview(self.makeLabel("\(CVPreviewDemoContent.university) — \(CVPreviewDemoContent.degree) · \(CVPreviewDemoContent.educationYears)", font: .systemFont(ofSize: 6.1, weight: .light), color: self.palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
}
|
|
}
|
|
|
let prof: () -> Void = {
|
|
let prof: () -> Void = {
|
|
|
stack.addArrangedSubview(self.sectionHeading("PROFILE"))
|
|
stack.addArrangedSubview(self.sectionHeading("PROFILE"))
|
|
@@ -644,8 +654,9 @@ final class CVTemplatePreviewView: NSView {
|
|
|
}
|
|
}
|
|
|
let exp: () -> Void = {
|
|
let exp: () -> Void = {
|
|
|
stack.addArrangedSubview(self.sectionHeading("EXPERIENCE"))
|
|
stack.addArrangedSubview(self.sectionHeading("EXPERIENCE"))
|
|
|
- stack.addArrangedSubview(self.makeLabel("\(CVPreviewDemoContent.title), \(CVPreviewDemoContent.company)", font: .systemFont(ofSize: 6.5, weight: .regular), color: self.palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
|
|
|
|
+ stack.addArrangedSubview(self.makeLabel("\(CVPreviewDemoContent.experienceRole) · \(CVPreviewDemoContent.company)", font: .systemFont(ofSize: 6.5, weight: .regular), color: self.palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
stack.addArrangedSubview(self.makeLabel(CVPreviewDemoContent.bullet1, font: .systemFont(ofSize: 6, weight: .light), color: self.palette.previewMuted, alignment: .left, maxLines: 2))
|
|
stack.addArrangedSubview(self.makeLabel(CVPreviewDemoContent.bullet1, font: .systemFont(ofSize: 6, weight: .light), color: self.palette.previewMuted, alignment: .left, maxLines: 2))
|
|
|
|
|
+ stack.addArrangedSubview(self.makeLabel(CVPreviewDemoContent.bullet2, font: .systemFont(ofSize: 6, weight: .light), color: self.palette.previewMuted, alignment: .left, maxLines: 2))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if educationBeforeExperience {
|
|
if educationBeforeExperience {
|
|
@@ -744,11 +755,12 @@ final class CVTemplatePreviewView: NSView {
|
|
|
stack.addArrangedSubview(makeLabel(CVPreviewDemoContent.summary, font: serif, color: ink, alignment: .left, maxLines: 3))
|
|
stack.addArrangedSubview(makeLabel(CVPreviewDemoContent.summary, font: serif, color: ink, alignment: .left, maxLines: 3))
|
|
|
stack.addArrangedSubview(sectionHeading("SELECTED EXPERIENCE"))
|
|
stack.addArrangedSubview(sectionHeading("SELECTED EXPERIENCE"))
|
|
|
let jobFont = NSFontManager.shared.convert(serif, toHaveTrait: .boldFontMask)
|
|
let jobFont = NSFontManager.shared.convert(serif, toHaveTrait: .boldFontMask)
|
|
|
- stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.title), \(CVPreviewDemoContent.company)", font: jobFont, color: ink, alignment: .left, maxLines: 2))
|
|
|
|
|
|
|
+ stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.experienceRole), \(CVPreviewDemoContent.company)", font: jobFont, color: ink, alignment: .left, maxLines: 2))
|
|
|
stack.addArrangedSubview(makeLabel("2019 – Present", font: serif, color: theme, alignment: .left, maxLines: 1))
|
|
stack.addArrangedSubview(makeLabel("2019 – Present", font: serif, color: theme, alignment: .left, maxLines: 1))
|
|
|
stack.addArrangedSubview(makeLabel(CVPreviewDemoContent.bullet1, font: serif, color: muted, alignment: .left, maxLines: 2))
|
|
stack.addArrangedSubview(makeLabel(CVPreviewDemoContent.bullet1, font: serif, color: muted, alignment: .left, maxLines: 2))
|
|
|
|
|
+ stack.addArrangedSubview(makeLabel(CVPreviewDemoContent.bullet2, font: serif, color: muted, alignment: .left, maxLines: 2))
|
|
|
stack.addArrangedSubview(sectionHeading("EDUCATION"))
|
|
stack.addArrangedSubview(sectionHeading("EDUCATION"))
|
|
|
- stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.university), \(CVPreviewDemoContent.degree)", font: serif, color: ink, alignment: .left, maxLines: 2))
|
|
|
|
|
|
|
+ stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.university), \(CVPreviewDemoContent.degree) · \(CVPreviewDemoContent.educationYears)", font: serif, color: ink, alignment: .left, maxLines: 2))
|
|
|
return stack
|
|
return stack
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -771,7 +783,7 @@ final class CVTemplatePreviewView: NSView {
|
|
|
stack.addArrangedSubview(makeLabel("· \(s)", font: serif, color: palette.previewInk, alignment: .left, maxLines: 1))
|
|
stack.addArrangedSubview(makeLabel("· \(s)", font: serif, color: palette.previewInk, alignment: .left, maxLines: 1))
|
|
|
}
|
|
}
|
|
|
stack.addArrangedSubview(sectionHeading("TOOLS"))
|
|
stack.addArrangedSubview(sectionHeading("TOOLS"))
|
|
|
- stack.addArrangedSubview(makeLabel("Figma, SQL, Amplitude, Jira", font: serif, color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
|
|
|
|
|
+ stack.addArrangedSubview(makeLabel(CVPreviewDemoContent.toolsLine, font: serif, color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
|
if showMetrics {
|
|
if showMetrics {
|
|
|
stack.addArrangedSubview(sectionHeading("IMPACT"))
|
|
stack.addArrangedSubview(sectionHeading("IMPACT"))
|
|
|
stack.addArrangedSubview(makeLabel("+12% activation · $4.2M ARR influenced", font: serif, color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
stack.addArrangedSubview(makeLabel("+12% activation · $4.2M ARR influenced", font: serif, color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
@@ -814,10 +826,12 @@ final class CVTemplatePreviewView: NSView {
|
|
|
main.addArrangedSubview(sectionHeading("PROFILE"))
|
|
main.addArrangedSubview(sectionHeading("PROFILE"))
|
|
|
main.addArrangedSubview(makeLabel(CVPreviewDemoContent.summary, font: .systemFont(ofSize: 6.2), color: palette.previewInk, alignment: .left, maxLines: 3))
|
|
main.addArrangedSubview(makeLabel(CVPreviewDemoContent.summary, font: .systemFont(ofSize: 6.2), color: palette.previewInk, alignment: .left, maxLines: 3))
|
|
|
main.addArrangedSubview(sectionHeading("IMPACT"))
|
|
main.addArrangedSubview(sectionHeading("IMPACT"))
|
|
|
- main.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.company) — \(CVPreviewDemoContent.title)", font: .systemFont(ofSize: 6.6, weight: .heavy), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
|
|
|
|
+ main.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.company) — \(CVPreviewDemoContent.experienceRole)", font: .systemFont(ofSize: 6.6, weight: .heavy), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
let bMark = (idVariant % 2 == 0) ? "— " : "▸ "
|
|
let bMark = (idVariant % 2 == 0) ? "— " : "▸ "
|
|
|
main.addArrangedSubview(makeLabel("\(bMark)\(CVPreviewDemoContent.bullet1)", font: .systemFont(ofSize: 6), color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
main.addArrangedSubview(makeLabel("\(bMark)\(CVPreviewDemoContent.bullet1)", font: .systemFont(ofSize: 6), color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
|
main.addArrangedSubview(makeLabel("\(bMark)\(CVPreviewDemoContent.bullet2)", font: .systemFont(ofSize: 6), color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
main.addArrangedSubview(makeLabel("\(bMark)\(CVPreviewDemoContent.bullet2)", font: .systemFont(ofSize: 6), color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
|
|
|
+ main.addArrangedSubview(sectionHeading("EDUCATION"))
|
|
|
|
|
+ main.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.university) · \(CVPreviewDemoContent.degree) · \(CVPreviewDemoContent.educationYears)", font: .systemFont(ofSize: 6.1), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
|
|
|
|
|
let sidebarMult = 0.32 + CGFloat(idVariant % 3) * 0.02
|
|
let sidebarMult = 0.32 + CGFloat(idVariant % 3) * 0.02
|
|
|
|
|
|