|
|
@@ -32,17 +32,26 @@ struct CVTemplateCardPalette {
|
|
32
|
32
|
|
|
33
|
33
|
fileprivate enum CVPreviewDemoContent {
|
|
34
|
34
|
static let fullName = "Sarah Johnson"
|
|
|
35
|
+ /// Shown in the header / contact band (broad role).
|
|
35
|
36
|
static let title = "Senior Product Manager"
|
|
|
37
|
+ /// Scoped title under Experience so it is not a verbatim repeat of the header line.
|
|
|
38
|
+ static let experienceRole = "Group PM, Consumer Growth & Activation"
|
|
36
|
39
|
static let company = "Google"
|
|
37
|
40
|
static let companyLine = "Google · Mountain View, CA · 2019 – Present"
|
|
38
|
41
|
static let university = "Stanford University"
|
|
39
|
42
|
static let degree = "M.S. Management Science & Engineering"
|
|
|
43
|
+ static let educationYears = "2014 – 2016"
|
|
40
|
44
|
static let email = "sarah.johnson@email.com"
|
|
41
|
45
|
static let phone = "(415) 555-0198"
|
|
42
|
46
|
static let location = "Mountain View, CA"
|
|
43
|
47
|
static let summary = "Product leader shipping roadmap, discovery, and analytics for high-scale consumer experiences."
|
|
44
|
48
|
static let bullet1 = "Defined multi-year platform strategy with exec stakeholders and quarterly OKRs."
|
|
45
|
49
|
static let bullet2 = "Partnered with engineering and design to launch experiments improving activation by 12%."
|
|
|
50
|
+ static let bullet3 = "Stood up quarterly business reviews with finance and GTM, aligning spend to north-star metrics."
|
|
|
51
|
+ /// Sidebar “highlights” blurb kept distinct from the experience bullets.
|
|
|
52
|
+ static let careerHighlights = "Presented roadmap shifts to the leadership team and translated trade-offs into clear investment asks."
|
|
|
53
|
+ /// Single tools line reused wherever the résumé lists a stack (avoids scattered near-duplicate strings).
|
|
|
54
|
+ static let toolsLine = "Figma · SQL · Amplitude · Jira · BigQuery"
|
|
46
|
55
|
static let skillsList = ["Product Strategy", "SQL", "Figma", "A/B Testing", "Roadmapping"]
|
|
47
|
56
|
}
|
|
48
|
57
|
|
|
|
@@ -300,7 +309,7 @@ final class CVTemplatePreviewView: NSView {
|
|
300
|
309
|
}
|
|
301
|
310
|
if variant % 7 == 3 {
|
|
302
|
311
|
inner.addArrangedSubview(sectionHeading("TOOLS"))
|
|
303
|
|
- inner.addArrangedSubview(makeLabel("Amplitude · Jira · BigQuery", font: .systemFont(ofSize: 5.9), color: palette.previewMuted, alignment: .left, maxLines: 1))
|
|
|
312
|
+ inner.addArrangedSubview(makeLabel(CVPreviewDemoContent.toolsLine, font: .systemFont(ofSize: 5.9), color: palette.previewMuted, alignment: .left, maxLines: 1))
|
|
304
|
313
|
}
|
|
305
|
314
|
box.addArrangedSubview(inner)
|
|
306
|
315
|
return box
|
|
|
@@ -319,10 +328,11 @@ final class CVTemplatePreviewView: NSView {
|
|
319
|
328
|
}
|
|
320
|
329
|
let experienceBlock: () -> Void = {
|
|
321
|
330
|
stack.addArrangedSubview(self.sectionHeading("EXPERIENCE"))
|
|
322
|
|
- stack.addArrangedSubview(self.makeLabel(CVPreviewDemoContent.title, font: .systemFont(ofSize: 6.8, weight: .semibold), color: self.palette.previewInk, alignment: .left, maxLines: 1))
|
|
|
331
|
+ stack.addArrangedSubview(self.makeLabel(CVPreviewDemoContent.experienceRole, font: .systemFont(ofSize: 6.8, weight: .semibold), color: self.palette.previewInk, alignment: .left, maxLines: 1))
|
|
323
|
332
|
stack.addArrangedSubview(self.makeLabel(CVPreviewDemoContent.companyLine, font: .systemFont(ofSize: 6.2, weight: .medium), color: self.template.themeColor, alignment: .left, maxLines: 1))
|
|
324
|
333
|
stack.addArrangedSubview(self.bulletRow(CVPreviewDemoContent.bullet1, size: sp))
|
|
325
|
334
|
stack.addArrangedSubview(self.bulletRow(CVPreviewDemoContent.bullet2, size: sp))
|
|
|
335
|
+ stack.addArrangedSubview(self.bulletRow(CVPreviewDemoContent.bullet3, size: sp))
|
|
326
|
336
|
}
|
|
327
|
337
|
|
|
328
|
338
|
if experienceFirst {
|
|
|
@@ -334,7 +344,7 @@ final class CVTemplatePreviewView: NSView {
|
|
334
|
344
|
}
|
|
335
|
345
|
stack.addArrangedSubview(sectionHeading("EDUCATION"))
|
|
336
|
346
|
stack.addArrangedSubview(makeLabel(CVPreviewDemoContent.university, font: .systemFont(ofSize: 6.6, weight: .semibold), color: palette.previewInk, alignment: .left, maxLines: 1))
|
|
337
|
|
- stack.addArrangedSubview(makeLabel(CVPreviewDemoContent.degree, font: .systemFont(ofSize: 6.2), color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
|
347
|
+ stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.degree) · \(CVPreviewDemoContent.educationYears)", font: .systemFont(ofSize: 6.2), color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
338
|
348
|
return stack
|
|
339
|
349
|
}
|
|
340
|
350
|
|
|
|
@@ -508,7 +518,7 @@ final class CVTemplatePreviewView: NSView {
|
|
508
|
518
|
box.addArrangedSubview(modernSectionRow(symbol: "person.crop.circle", title: "About", theme: theme))
|
|
509
|
519
|
box.addArrangedSubview(makeLabel(CVPreviewDemoContent.summary, font: .systemFont(ofSize: 6), color: palette.previewInk, alignment: .left, maxLines: 4))
|
|
510
|
520
|
box.addArrangedSubview(modernSectionRow(symbol: "star.fill", title: "Highlights", theme: theme))
|
|
511
|
|
- box.addArrangedSubview(makeLabel("Launched growth experiments, SQL deep dives, design QA.", font: .systemFont(ofSize: 6), color: palette.previewMuted, alignment: .left, maxLines: 3))
|
|
|
521
|
+ box.addArrangedSubview(makeLabel(CVPreviewDemoContent.careerHighlights, font: .systemFont(ofSize: 6), color: palette.previewMuted, alignment: .left, maxLines: 3))
|
|
512
|
522
|
return box
|
|
513
|
523
|
}
|
|
514
|
524
|
|
|
|
@@ -533,11 +543,11 @@ final class CVTemplatePreviewView: NSView {
|
|
533
|
543
|
stack.spacing = 5
|
|
534
|
544
|
stack.alignment = .leading
|
|
535
|
545
|
stack.addArrangedSubview(modernSectionRow(symbol: "briefcase.fill", title: "Experience", theme: theme))
|
|
536
|
|
- stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.title) — \(CVPreviewDemoContent.company)", font: .systemFont(ofSize: 6.6, weight: .semibold), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
537
|
|
- 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))
|
|
|
546
|
+ stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.experienceRole) — \(CVPreviewDemoContent.company)", font: .systemFont(ofSize: 6.6, weight: .semibold), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
547
|
+ 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))
|
|
538
|
548
|
stack.addArrangedSubview(tagRow(theme: theme))
|
|
539
|
549
|
stack.addArrangedSubview(modernSectionRow(symbol: "graduationcap.fill", title: "Education", theme: theme))
|
|
540
|
|
- stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.university), \(CVPreviewDemoContent.degree)", font: .systemFont(ofSize: 6.2), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
550
|
+ stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.university), \(CVPreviewDemoContent.degree) (\(CVPreviewDemoContent.educationYears))", font: .systemFont(ofSize: 6.2), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
541
|
551
|
return stack
|
|
542
|
552
|
}
|
|
543
|
553
|
|
|
|
@@ -636,7 +646,7 @@ final class CVTemplatePreviewView: NSView {
|
|
636
|
646
|
|
|
637
|
647
|
let edu: () -> Void = {
|
|
638
|
648
|
stack.addArrangedSubview(self.sectionHeading("EDUCATION"))
|
|
639
|
|
- stack.addArrangedSubview(self.makeLabel("\(CVPreviewDemoContent.university) — \(CVPreviewDemoContent.degree)", font: .systemFont(ofSize: 6.1, weight: .light), color: self.palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
649
|
+ 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))
|
|
640
|
650
|
}
|
|
641
|
651
|
let prof: () -> Void = {
|
|
642
|
652
|
stack.addArrangedSubview(self.sectionHeading("PROFILE"))
|
|
|
@@ -644,8 +654,9 @@ final class CVTemplatePreviewView: NSView {
|
|
644
|
654
|
}
|
|
645
|
655
|
let exp: () -> Void = {
|
|
646
|
656
|
stack.addArrangedSubview(self.sectionHeading("EXPERIENCE"))
|
|
647
|
|
- stack.addArrangedSubview(self.makeLabel("\(CVPreviewDemoContent.title), \(CVPreviewDemoContent.company)", font: .systemFont(ofSize: 6.5, weight: .regular), color: self.palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
657
|
+ stack.addArrangedSubview(self.makeLabel("\(CVPreviewDemoContent.experienceRole) · \(CVPreviewDemoContent.company)", font: .systemFont(ofSize: 6.5, weight: .regular), color: self.palette.previewInk, alignment: .left, maxLines: 2))
|
|
648
|
658
|
stack.addArrangedSubview(self.makeLabel(CVPreviewDemoContent.bullet1, font: .systemFont(ofSize: 6, weight: .light), color: self.palette.previewMuted, alignment: .left, maxLines: 2))
|
|
|
659
|
+ stack.addArrangedSubview(self.makeLabel(CVPreviewDemoContent.bullet2, font: .systemFont(ofSize: 6, weight: .light), color: self.palette.previewMuted, alignment: .left, maxLines: 2))
|
|
649
|
660
|
}
|
|
650
|
661
|
|
|
651
|
662
|
if educationBeforeExperience {
|
|
|
@@ -744,11 +755,12 @@ final class CVTemplatePreviewView: NSView {
|
|
744
|
755
|
stack.addArrangedSubview(makeLabel(CVPreviewDemoContent.summary, font: serif, color: ink, alignment: .left, maxLines: 3))
|
|
745
|
756
|
stack.addArrangedSubview(sectionHeading("SELECTED EXPERIENCE"))
|
|
746
|
757
|
let jobFont = NSFontManager.shared.convert(serif, toHaveTrait: .boldFontMask)
|
|
747
|
|
- stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.title), \(CVPreviewDemoContent.company)", font: jobFont, color: ink, alignment: .left, maxLines: 2))
|
|
|
758
|
+ stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.experienceRole), \(CVPreviewDemoContent.company)", font: jobFont, color: ink, alignment: .left, maxLines: 2))
|
|
748
|
759
|
stack.addArrangedSubview(makeLabel("2019 – Present", font: serif, color: theme, alignment: .left, maxLines: 1))
|
|
749
|
760
|
stack.addArrangedSubview(makeLabel(CVPreviewDemoContent.bullet1, font: serif, color: muted, alignment: .left, maxLines: 2))
|
|
|
761
|
+ stack.addArrangedSubview(makeLabel(CVPreviewDemoContent.bullet2, font: serif, color: muted, alignment: .left, maxLines: 2))
|
|
750
|
762
|
stack.addArrangedSubview(sectionHeading("EDUCATION"))
|
|
751
|
|
- stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.university), \(CVPreviewDemoContent.degree)", font: serif, color: ink, alignment: .left, maxLines: 2))
|
|
|
763
|
+ stack.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.university), \(CVPreviewDemoContent.degree) · \(CVPreviewDemoContent.educationYears)", font: serif, color: ink, alignment: .left, maxLines: 2))
|
|
752
|
764
|
return stack
|
|
753
|
765
|
}
|
|
754
|
766
|
|
|
|
@@ -771,7 +783,7 @@ final class CVTemplatePreviewView: NSView {
|
|
771
|
783
|
stack.addArrangedSubview(makeLabel("· \(s)", font: serif, color: palette.previewInk, alignment: .left, maxLines: 1))
|
|
772
|
784
|
}
|
|
773
|
785
|
stack.addArrangedSubview(sectionHeading("TOOLS"))
|
|
774
|
|
- stack.addArrangedSubview(makeLabel("Figma, SQL, Amplitude, Jira", font: serif, color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
|
786
|
+ stack.addArrangedSubview(makeLabel(CVPreviewDemoContent.toolsLine, font: serif, color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
775
|
787
|
if showMetrics {
|
|
776
|
788
|
stack.addArrangedSubview(sectionHeading("IMPACT"))
|
|
777
|
789
|
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 {
|
|
814
|
826
|
main.addArrangedSubview(sectionHeading("PROFILE"))
|
|
815
|
827
|
main.addArrangedSubview(makeLabel(CVPreviewDemoContent.summary, font: .systemFont(ofSize: 6.2), color: palette.previewInk, alignment: .left, maxLines: 3))
|
|
816
|
828
|
main.addArrangedSubview(sectionHeading("IMPACT"))
|
|
817
|
|
- main.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.company) — \(CVPreviewDemoContent.title)", font: .systemFont(ofSize: 6.6, weight: .heavy), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
829
|
+ main.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.company) — \(CVPreviewDemoContent.experienceRole)", font: .systemFont(ofSize: 6.6, weight: .heavy), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
818
|
830
|
let bMark = (idVariant % 2 == 0) ? "— " : "▸ "
|
|
819
|
831
|
main.addArrangedSubview(makeLabel("\(bMark)\(CVPreviewDemoContent.bullet1)", font: .systemFont(ofSize: 6), color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
820
|
832
|
main.addArrangedSubview(makeLabel("\(bMark)\(CVPreviewDemoContent.bullet2)", font: .systemFont(ofSize: 6), color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
|
833
|
+ main.addArrangedSubview(sectionHeading("EDUCATION"))
|
|
|
834
|
+ main.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.university) · \(CVPreviewDemoContent.degree) · \(CVPreviewDemoContent.educationYears)", font: .systemFont(ofSize: 6.1), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
821
|
835
|
|
|
822
|
836
|
let sidebarMult = 0.32 + CGFloat(idVariant % 3) * 0.02
|
|
823
|
837
|
|