Forráskód Böngészése

Fix profile page sections collapsing to the right.

Update profile layout constraints and width recalculation so personal information, education, and related form blocks stay full-width and left-aligned across window sizes.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 2 hónapja
szülő
commit
d2d4f852a6

+ 2 - 2
App for Indeed/Views/DashboardView.swift

@@ -1344,8 +1344,8 @@ final class DashboardView: NSView, NSTextFieldDelegate {
         myProfilePageView.translatesAutoresizingMaskIntoConstraints = false
         profilePageContainer.addSubview(myProfilePageView)
         NSLayoutConstraint.activate([
-            myProfilePageView.leadingAnchor.constraint(equalTo: profilePageContainer.leadingAnchor),
-            myProfilePageView.trailingAnchor.constraint(equalTo: profilePageContainer.trailingAnchor),
+            myProfilePageView.leftAnchor.constraint(equalTo: profilePageContainer.leftAnchor),
+            myProfilePageView.rightAnchor.constraint(equalTo: profilePageContainer.rightAnchor),
             myProfilePageView.topAnchor.constraint(equalTo: profilePageContainer.topAnchor),
             myProfilePageView.bottomAnchor.constraint(equalTo: profilePageContainer.bottomAnchor)
         ])

+ 904 - 25
App for Indeed/Views/MyProfilePageView.swift

@@ -17,14 +17,43 @@ private enum ProfilePagePalette {
     static let primaryText = NSColor(srgbRed: 45 / 255, green: 45 / 255, blue: 45 / 255, alpha: 1)
     static let secondaryText = NSColor(srgbRed: 118 / 255, green: 118 / 255, blue: 118 / 255, alpha: 1)
     static let border = NSColor(srgbRed: 212 / 255, green: 210 / 255, blue: 208 / 255, alpha: 1)
+    static let destructive = NSColor(srgbRed: 220 / 255, green: 38 / 255, blue: 38 / 255, alpha: 1)
+}
+
+/// Keeps profile text left-aligned and LTR so fields do not collapse to a narrow trailing strip under RTL / natural alignment.
+private enum ProfileLayoutEnforcement {
+    static func applyForcedLTR(to view: NSView) {
+        view.userInterfaceLayoutDirection = .leftToRight
+    }
+
+    static func applyLeftAlignedTextField(_ field: NSTextField) {
+        applyForcedLTR(to: field)
+        field.baseWritingDirection = .leftToRight
+        field.alignment = .left
+        if let cell = field.cell as? NSTextFieldCell {
+            cell.alignment = .left
+            cell.baseWritingDirection = .leftToRight
+        }
+    }
+
+    static func leftAlignedParagraphStyle() -> NSParagraphStyle {
+        let p = NSMutableParagraphStyle()
+        p.alignment = .left
+        p.baseWritingDirection = .leftToRight
+        return p
+    }
 }
 
 final class MyProfilePageView: NSView {
-    private static let compactFormWidth: CGFloat = 520
+    /// Below this form content width, two-column rows stack vertically.
+    private static let compactFormWidth: CGFloat = 640
+    private static let readableFormMaxWidth: CGFloat = 880
+    private static let horizontalPageInset: CGFloat = 24
 
     private let scrollView = NSScrollView()
     private let documentView = NSView()
     private let cardView = NSView()
+    private var readableFormWidthConstraint: NSLayoutConstraint!
     private let formStack = NSStackView()
 
     private let profileNameField = NSTextField()
@@ -34,13 +63,24 @@ final class MyProfilePageView: NSView {
     private let jobTitleField = NSTextField()
     private let addressField = NSTextField()
     private let careerField = NSTextField()
+    private let certificatesField = NSTextField()
+    private let interestsField = NSTextField()
+    private let languagesField = NSTextField()
+    private let referralField = NSTextField()
     private let saveButton = ProfilePrimaryButton(title: "Save Profile  →", target: nil, action: nil)
 
     private let nameEmailRow = NSStackView()
     private let phoneJobRow = NSStackView()
 
+    private let workExperienceRowsStack = NSStackView()
+    private var workExperienceEntries: [WorkExperienceEntryView] = []
+    private let educationRowsStack = NSStackView()
+    private var educationEntries: [EducationEntryView] = []
+
     private var lastCompactLayout: Bool?
 
+    private var referralHelperLabel: NSTextField?
+
     /// Force left-to-right geometry so profile fields span the full width even when the window uses RTL layout.
     override var userInterfaceLayoutDirection: NSUserInterfaceLayoutDirection {
         get { .leftToRight }
@@ -57,18 +97,77 @@ final class MyProfilePageView: NSView {
         setup()
     }
 
+    override func updateConstraints() {
+        updateReadableFormCardWidthConstraint()
+        super.updateConstraints()
+    }
+
+    override func viewDidMoveToWindow() {
+        super.viewDidMoveToWindow()
+        needsUpdateConstraints = true
+    }
+
     override func layout() {
         super.layout()
+        if let layer = cardView.layer, layer.shadowOpacity > 0 {
+            let r = layer.cornerRadius
+            layer.shadowPath = CGPath(roundedRect: cardView.bounds, cornerWidth: r, cornerHeight: r, transform: nil)
+        }
+        updateMultilinePreferredLayoutWidths()
         applyResponsiveRowsIfNeeded()
     }
 
+    /// Keeps the card width at `min(readable max, clip − horizontal insets)` before constraint passes. If the constant is ever wider than the clip, Auto Layout can slide the card to the trailing edge and the form looks compressed / right‑aligned.
+    private func updateReadableFormCardWidthConstraint() {
+        let clipW = max(bounds.width, scrollView.bounds.width, scrollView.contentView.bounds.width)
+        guard clipW > 1 else { return }
+        let horizontalCardInset = Self.horizontalPageInset * 2
+        let maxUsable = clipW - horizontalCardInset
+        let target = min(Self.readableFormMaxWidth, max(1, maxUsable))
+        if abs(readableFormWidthConstraint.constant - target) > 0.5 {
+            readableFormWidthConstraint.constant = target
+        }
+    }
+
+    /// Wrapping `NSTextField`s report a tiny intrinsic width until `preferredMaxLayoutWidth` tracks the chrome width, which otherwise collapses the stack to a narrow trailing column.
+    private func updateMultilinePreferredLayoutWidths() {
+        let horizontalInset: CGFloat = 24
+        applyPreferredWrapWidth(to: profileNameField, horizontalInset: horizontalInset)
+        applyPreferredWrapWidth(to: fullNameField, horizontalInset: horizontalInset)
+        applyPreferredWrapWidth(to: emailField, horizontalInset: horizontalInset)
+        applyPreferredWrapWidth(to: phoneField, horizontalInset: horizontalInset)
+        applyPreferredWrapWidth(to: jobTitleField, horizontalInset: horizontalInset)
+        applyPreferredWrapWidth(to: addressField, horizontalInset: horizontalInset)
+        applyPreferredWrapWidth(to: referralField, horizontalInset: horizontalInset)
+        applyPreferredWrapWidth(to: careerField, horizontalInset: horizontalInset)
+        applyPreferredWrapWidth(to: certificatesField, horizontalInset: horizontalInset)
+        applyPreferredWrapWidth(to: interestsField, horizontalInset: horizontalInset)
+        applyPreferredWrapWidth(to: languagesField, horizontalInset: horizontalInset)
+        if let helper = referralHelperLabel, let stack = helper.superview, stack.bounds.width > 2 {
+            let w = max(1, stack.bounds.width - 8)
+            if abs(helper.preferredMaxLayoutWidth - w) > 0.5 {
+                helper.preferredMaxLayoutWidth = w
+            }
+        }
+    }
+
+    private func applyPreferredWrapWidth(to field: NSTextField, horizontalInset: CGFloat) {
+        guard let wrap = field.superview, wrap.bounds.width > 2 else { return }
+        let w = max(1, wrap.bounds.width - horizontalInset)
+        if abs(field.preferredMaxLayoutWidth - w) > 0.5 {
+            field.preferredMaxLayoutWidth = w
+        }
+    }
+
     private func setup() {
         wantsLayer = true
         layer?.backgroundColor = ProfilePagePalette.pageBackground.cgColor
         userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: self)
 
         scrollView.translatesAutoresizingMaskIntoConstraints = false
         scrollView.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: scrollView)
         scrollView.hasVerticalScroller = true
         scrollView.hasHorizontalScroller = false
         scrollView.autohidesScrollers = true
@@ -77,28 +176,43 @@ final class MyProfilePageView: NSView {
         scrollView.scrollerStyle = .overlay
         scrollView.automaticallyAdjustsContentInsets = false
         scrollView.contentView.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: scrollView.contentView)
+        if #available(macOS 10.11, *) {
+            scrollView.horizontalScrollElasticity = .none
+        }
 
         documentView.translatesAutoresizingMaskIntoConstraints = false
         documentView.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: documentView)
 
         cardView.translatesAutoresizingMaskIntoConstraints = false
         cardView.wantsLayer = true
         cardView.layer?.backgroundColor = ProfilePagePalette.cardBackground.cgColor
-        cardView.layer?.cornerRadius = 16
+        cardView.layer?.cornerRadius = 18
         cardView.layer?.borderWidth = 1
         cardView.layer?.borderColor = ProfilePagePalette.border.cgColor
+        cardView.layer?.masksToBounds = false
+        cardView.layer?.shadowColor = NSColor.black.cgColor
+        cardView.layer?.shadowOpacity = 0.06
+        cardView.layer?.shadowRadius = 20
+        cardView.layer?.shadowOffset = CGSize(width: 0, height: 10)
         cardView.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: cardView)
         if #available(macOS 11.0, *) {
             cardView.layer?.cornerCurve = .continuous
         }
 
+        // Start narrow so the first constraint pass cannot exceed a small host; `updateConstraints` sets the real width.
+        readableFormWidthConstraint = cardView.widthAnchor.constraint(equalToConstant: 200)
+
         formStack.translatesAutoresizingMaskIntoConstraints = false
         formStack.orientation = .vertical
         formStack.alignment = .width
         formStack.distribution = .fill
-        formStack.spacing = 20
-        formStack.edgeInsets = NSEdgeInsets(top: 28, left: 22, bottom: 28, right: 22)
+        formStack.spacing = 24
+        formStack.edgeInsets = NSEdgeInsets(top: 32, left: 28, bottom: 32, right: 28)
         formStack.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: formStack)
         formStack.setContentHuggingPriority(.defaultLow, for: .horizontal)
         formStack.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
 
@@ -108,24 +222,26 @@ final class MyProfilePageView: NSView {
         cardView.addSubview(formStack)
 
         NSLayoutConstraint.activate([
-            scrollView.leadingAnchor.constraint(equalTo: leadingAnchor),
-            scrollView.trailingAnchor.constraint(equalTo: trailingAnchor),
+            scrollView.leftAnchor.constraint(equalTo: leftAnchor),
+            scrollView.rightAnchor.constraint(equalTo: rightAnchor),
             scrollView.topAnchor.constraint(equalTo: topAnchor),
             scrollView.bottomAnchor.constraint(equalTo: bottomAnchor),
 
-            // Pin left and right to the clip view’s geometric edges so the document spans the full visible
-            // width (leading/trailing + width alone can leave a narrow strip on the wrong side in edge cases).
+            // Pin the document to the clip view’s geometric width so LTR/RTL semantics cannot slide the form.
             documentView.leftAnchor.constraint(equalTo: scrollView.contentView.leftAnchor),
             documentView.rightAnchor.constraint(equalTo: scrollView.contentView.rightAnchor),
+            documentView.widthAnchor.constraint(equalTo: scrollView.contentView.widthAnchor),
             documentView.topAnchor.constraint(equalTo: scrollView.contentView.topAnchor),
+            documentView.bottomAnchor.constraint(equalTo: cardView.bottomAnchor, constant: Self.horizontalPageInset),
 
-            cardView.leadingAnchor.constraint(equalTo: documentView.leadingAnchor, constant: 20),
-            cardView.trailingAnchor.constraint(equalTo: documentView.trailingAnchor, constant: -20),
-            cardView.topAnchor.constraint(equalTo: documentView.topAnchor, constant: 16),
-            cardView.bottomAnchor.constraint(equalTo: documentView.bottomAnchor, constant: -24),
+            cardView.leadingAnchor.constraint(equalTo: documentView.leadingAnchor, constant: Self.horizontalPageInset),
+            cardView.trailingAnchor.constraint(lessThanOrEqualTo: documentView.trailingAnchor, constant: -Self.horizontalPageInset),
+            cardView.topAnchor.constraint(equalTo: documentView.topAnchor, constant: Self.horizontalPageInset),
+            readableFormWidthConstraint,
 
             formStack.leadingAnchor.constraint(equalTo: cardView.leadingAnchor),
             formStack.trailingAnchor.constraint(equalTo: cardView.trailingAnchor),
+            formStack.widthAnchor.constraint(equalTo: cardView.widthAnchor),
             formStack.topAnchor.constraint(equalTo: cardView.topAnchor),
             formStack.bottomAnchor.constraint(equalTo: cardView.bottomAnchor)
         ])
@@ -145,12 +261,51 @@ final class MyProfilePageView: NSView {
         pinEqualColumnWidths(in: phoneJobRow)
         formStack.addArrangedSubview(phoneJobRow)
 
+        ProfileLayoutEnforcement.applyForcedLTR(to: nameEmailRow)
+        ProfileLayoutEnforcement.applyForcedLTR(to: phoneJobRow)
+
         formStack.addArrangedSubview(labeledGroup(title: "Address", field: addressField, placeholder: "123 Main St, City, State, ZIP"))
         formStack.addArrangedSubview(careerSummaryBlock())
+        formStack.addArrangedSubview(horizontalSeparator())
+        formStack.addArrangedSubview(workExperienceSection())
+        formStack.addArrangedSubview(horizontalSeparator())
+        formStack.addArrangedSubview(educationSection())
+        formStack.addArrangedSubview(horizontalSeparator())
+        formStack.addArrangedSubview(
+            multilineProfileBlock(
+                title: "Certificates / Rewards",
+                placeholder: "List your certificates and awards...",
+                field: certificatesField,
+                minHeight: 100
+            )
+        )
+        formStack.addArrangedSubview(horizontalSeparator())
+        formStack.addArrangedSubview(
+            multilineProfileBlock(
+                title: "Interests",
+                placeholder: "List your interests and hobbies...",
+                field: interestsField,
+                minHeight: 100
+            )
+        )
+        formStack.addArrangedSubview(horizontalSeparator())
+        formStack.addArrangedSubview(
+            multilineProfileBlock(
+                title: "Languages",
+                placeholder: "List languages you speak (e.g., English - Native, Spanish - Fluent)...",
+                field: languagesField,
+                minHeight: 100
+            )
+        )
+        formStack.addArrangedSubview(horizontalSeparator())
+        formStack.addArrangedSubview(referralBlock())
         formStack.addArrangedSubview(saveButtonHost())
 
         saveButton.target = self
         saveButton.action = #selector(didTapSave)
+
+        appendWorkExperienceEntry()
+        appendEducationEntry()
     }
 
     private func applyResponsiveRowsIfNeeded() {
@@ -168,6 +323,12 @@ final class MyProfilePageView: NSView {
         phoneJobRow.spacing = rowSpacing
         nameEmailRow.distribution = compact ? .fill : .fillEqually
         phoneJobRow.distribution = compact ? .fill : .fillEqually
+        for entry in workExperienceEntries {
+            entry.applyCompactLayout(compact)
+        }
+        for entry in educationEntries {
+            entry.applyCompactLayout(compact)
+        }
     }
 
     /// Keeps two columns the same width when the row is horizontal; avoids NSTextField intrinsic width fighting the stack.
@@ -185,6 +346,7 @@ final class MyProfilePageView: NSView {
         row.distribution = .fillEqually
         row.alignment = .top
         row.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: row)
         row.setContentHuggingPriority(.defaultLow, for: .horizontal)
         row.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
         row.addArrangedSubview(left)
@@ -195,10 +357,10 @@ final class MyProfilePageView: NSView {
         let label = NSTextField(labelWithString: text)
         label.font = .systemFont(ofSize: 15, weight: .semibold)
         label.textColor = ProfilePagePalette.primaryText
-        label.alignment = .left
         label.baseWritingDirection = .leftToRight
         label.translatesAutoresizingMaskIntoConstraints = false
         label.setContentHuggingPriority(.defaultHigh, for: .horizontal)
+        ProfileLayoutEnforcement.applyLeftAlignedTextField(label)
 
         let spacer = NSView()
         spacer.translatesAutoresizingMaskIntoConstraints = false
@@ -207,8 +369,10 @@ final class MyProfilePageView: NSView {
         let row = NSStackView(views: [label, spacer])
         row.orientation = .horizontal
         row.alignment = .centerY
+        row.distribution = .fill
         row.spacing = 0
         row.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: row)
         row.translatesAutoresizingMaskIntoConstraints = false
         return row
     }
@@ -217,10 +381,9 @@ final class MyProfilePageView: NSView {
         let label = NSTextField(labelWithString: title)
         label.font = .systemFont(ofSize: 12, weight: .medium)
         label.textColor = ProfilePagePalette.secondaryText
-        label.alignment = .left
-        label.baseWritingDirection = .leftToRight
         label.translatesAutoresizingMaskIntoConstraints = false
         label.setContentHuggingPriority(.defaultHigh, for: .horizontal)
+        ProfileLayoutEnforcement.applyLeftAlignedTextField(label)
 
         styleSingleLineField(field, placeholder: placeholder)
         let wrap = roundedFieldChrome(containing: field, minHeight: 40)
@@ -228,10 +391,11 @@ final class MyProfilePageView: NSView {
         let stack = NSStackView(views: [label, wrap])
         stack.orientation = .vertical
         stack.spacing = 8
-        // Leading keeps the title at the left edge; width match keeps the field full width.
-        stack.alignment = .leading
+        // `.width` stretches label + field chrome to the row width; `.leading` collapses to intrinsic width and caused right-edge compression.
+        stack.alignment = .width
         stack.translatesAutoresizingMaskIntoConstraints = false
         stack.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: stack)
         stack.setContentHuggingPriority(.defaultLow, for: .horizontal)
         wrap.setContentHuggingPriority(.defaultLow, for: .horizontal)
         wrap.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
@@ -250,18 +414,19 @@ final class MyProfilePageView: NSView {
         field.textColor = ProfilePagePalette.primaryText
         field.setContentHuggingPriority(.defaultLow, for: .horizontal)
         field.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
+        let paragraph = ProfileLayoutEnforcement.leftAlignedParagraphStyle()
         field.placeholderAttributedString = NSAttributedString(
             string: placeholder,
             attributes: [
                 .foregroundColor: ProfilePagePalette.secondaryText,
-                .font: NSFont.systemFont(ofSize: 14, weight: .regular)
+                .font: NSFont.systemFont(ofSize: 14, weight: .regular),
+                .paragraphStyle: paragraph
             ]
         )
         field.cell?.usesSingleLineMode = true
         field.cell?.wraps = false
         field.cell?.isScrollable = true
-        field.baseWritingDirection = .leftToRight
-        field.alignment = .left
+        ProfileLayoutEnforcement.applyLeftAlignedTextField(field)
     }
 
     private func roundedFieldChrome(containing field: NSTextField, minHeight: CGFloat) -> NSView {
@@ -276,6 +441,7 @@ final class MyProfilePageView: NSView {
             wrap.layer?.cornerCurve = .continuous
         }
         wrap.addSubview(field)
+        ProfileLayoutEnforcement.applyForcedLTR(to: wrap)
         NSLayoutConstraint.activate([
             field.leftAnchor.constraint(equalTo: wrap.leftAnchor, constant: 12),
             field.rightAnchor.constraint(equalTo: wrap.rightAnchor, constant: -12),
@@ -289,8 +455,8 @@ final class MyProfilePageView: NSView {
         let label = NSTextField(labelWithString: "Career Summary")
         label.font = .systemFont(ofSize: 12, weight: .medium)
         label.textColor = ProfilePagePalette.secondaryText
-        label.alignment = .left
         label.translatesAutoresizingMaskIntoConstraints = false
+        ProfileLayoutEnforcement.applyLeftAlignedTextField(label)
 
         careerField.translatesAutoresizingMaskIntoConstraints = false
         careerField.isEditable = true
@@ -307,15 +473,15 @@ final class MyProfilePageView: NSView {
         careerField.stringValue = ""
         careerField.setContentHuggingPriority(.defaultLow, for: .horizontal)
         careerField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
-        careerField.baseWritingDirection = .leftToRight
-        careerField.alignment = .left
         careerField.placeholderAttributedString = NSAttributedString(
             string: "Brief overview of your professional background and key achievements...",
             attributes: [
                 .foregroundColor: ProfilePagePalette.secondaryText,
-                .font: NSFont.systemFont(ofSize: 14, weight: .regular)
+                .font: NSFont.systemFont(ofSize: 14, weight: .regular),
+                .paragraphStyle: ProfileLayoutEnforcement.leftAlignedParagraphStyle()
             ]
         )
+        ProfileLayoutEnforcement.applyLeftAlignedTextField(careerField)
 
         let wrap = NSView()
         wrap.translatesAutoresizingMaskIntoConstraints = false
@@ -328,12 +494,13 @@ final class MyProfilePageView: NSView {
             wrap.layer?.cornerCurve = .continuous
         }
         wrap.addSubview(careerField)
+        ProfileLayoutEnforcement.applyForcedLTR(to: wrap)
         NSLayoutConstraint.activate([
             careerField.leftAnchor.constraint(equalTo: wrap.leftAnchor, constant: 12),
             careerField.rightAnchor.constraint(equalTo: wrap.rightAnchor, constant: -12),
             careerField.topAnchor.constraint(equalTo: wrap.topAnchor, constant: 10),
             careerField.bottomAnchor.constraint(equalTo: wrap.bottomAnchor, constant: -10),
-            wrap.heightAnchor.constraint(greaterThanOrEqualToConstant: 120)
+            wrap.heightAnchor.constraint(greaterThanOrEqualToConstant: 168)
         ])
 
         let stack = NSStackView(views: [label, wrap])
@@ -342,16 +509,296 @@ final class MyProfilePageView: NSView {
         stack.alignment = .width
         stack.translatesAutoresizingMaskIntoConstraints = false
         stack.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: stack)
         stack.setContentHuggingPriority(.defaultLow, for: .horizontal)
         wrap.setContentHuggingPriority(.defaultLow, for: .horizontal)
         return stack
     }
 
+    private func multilineProfileBlock(title: String, placeholder: String, field: NSTextField, minHeight: CGFloat) -> NSView {
+        let label = NSTextField(labelWithString: title)
+        label.font = .systemFont(ofSize: 12, weight: .medium)
+        label.textColor = ProfilePagePalette.secondaryText
+        label.translatesAutoresizingMaskIntoConstraints = false
+        ProfileLayoutEnforcement.applyLeftAlignedTextField(label)
+
+        field.translatesAutoresizingMaskIntoConstraints = false
+        field.isEditable = true
+        field.isSelectable = true
+        field.isBordered = false
+        field.drawsBackground = false
+        field.focusRingType = .none
+        field.font = .systemFont(ofSize: 14, weight: .regular)
+        field.textColor = ProfilePagePalette.primaryText
+        field.maximumNumberOfLines = 0
+        field.cell?.wraps = true
+        field.cell?.isScrollable = false
+        field.cell?.usesSingleLineMode = false
+        field.stringValue = ""
+        field.setContentHuggingPriority(.defaultLow, for: .horizontal)
+        field.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
+        field.placeholderAttributedString = NSAttributedString(
+            string: placeholder,
+            attributes: [
+                .foregroundColor: ProfilePagePalette.secondaryText,
+                .font: NSFont.systemFont(ofSize: 14, weight: .regular),
+                .paragraphStyle: ProfileLayoutEnforcement.leftAlignedParagraphStyle()
+            ]
+        )
+        ProfileLayoutEnforcement.applyLeftAlignedTextField(field)
+
+        let wrap = NSView()
+        wrap.translatesAutoresizingMaskIntoConstraints = false
+        wrap.wantsLayer = true
+        wrap.layer?.backgroundColor = ProfilePagePalette.fieldFill.cgColor
+        wrap.layer?.cornerRadius = 10
+        wrap.layer?.borderWidth = 1
+        wrap.layer?.borderColor = ProfilePagePalette.border.cgColor
+        if #available(macOS 11.0, *) {
+            wrap.layer?.cornerCurve = .continuous
+        }
+        wrap.addSubview(field)
+        ProfileLayoutEnforcement.applyForcedLTR(to: wrap)
+        NSLayoutConstraint.activate([
+            field.leftAnchor.constraint(equalTo: wrap.leftAnchor, constant: 12),
+            field.rightAnchor.constraint(equalTo: wrap.rightAnchor, constant: -12),
+            field.topAnchor.constraint(equalTo: wrap.topAnchor, constant: 10),
+            field.bottomAnchor.constraint(equalTo: wrap.bottomAnchor, constant: -10),
+            wrap.heightAnchor.constraint(greaterThanOrEqualToConstant: minHeight)
+        ])
+
+        let stack = NSStackView(views: [label, wrap])
+        stack.orientation = .vertical
+        stack.spacing = 8
+        stack.alignment = .width
+        stack.translatesAutoresizingMaskIntoConstraints = false
+        stack.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: stack)
+        stack.setContentHuggingPriority(.defaultLow, for: .horizontal)
+        wrap.setContentHuggingPriority(.defaultLow, for: .horizontal)
+        return stack
+    }
+
+    private func referralBlock() -> NSView {
+        let label = NSTextField(labelWithString: "Referral (Optional)")
+        label.font = .systemFont(ofSize: 12, weight: .medium)
+        label.textColor = ProfilePagePalette.secondaryText
+        label.translatesAutoresizingMaskIntoConstraints = false
+        ProfileLayoutEnforcement.applyLeftAlignedTextField(label)
+
+        styleSingleLineField(referralField, placeholder: "Referred by (Company/Person Name)")
+        let wrap = roundedFieldChrome(containing: referralField, minHeight: 40)
+
+        let helper = NSTextField(wrappingLabelWithString: "If someone referred you for this job, enter their name or company here")
+        helper.font = .systemFont(ofSize: 11, weight: .regular)
+        helper.textColor = ProfilePagePalette.secondaryText
+        helper.translatesAutoresizingMaskIntoConstraints = false
+        ProfileLayoutEnforcement.applyLeftAlignedTextField(helper)
+        referralHelperLabel = helper
+
+        let stack = NSStackView(views: [label, wrap, helper])
+        stack.orientation = .vertical
+        stack.spacing = 8
+        stack.alignment = .width
+        stack.translatesAutoresizingMaskIntoConstraints = false
+        stack.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: stack)
+        stack.setContentHuggingPriority(.defaultLow, for: .horizontal)
+        wrap.setContentHuggingPriority(.defaultLow, for: .horizontal)
+        if #available(macOS 10.11, *) {
+            stack.setCustomSpacing(6, after: wrap)
+        }
+        return stack
+    }
+
+    private func horizontalSeparator() -> NSView {
+        let box = NSBox()
+        box.boxType = .separator
+        box.translatesAutoresizingMaskIntoConstraints = false
+        return box
+    }
+
+    private func workExperienceSection() -> NSView {
+        let title = NSTextField(labelWithString: "Work Experience")
+        title.font = .systemFont(ofSize: 15, weight: .semibold)
+        title.textColor = ProfilePagePalette.primaryText
+        title.translatesAutoresizingMaskIntoConstraints = false
+        title.setContentHuggingPriority(.defaultLow, for: .horizontal)
+        ProfileLayoutEnforcement.applyLeftAlignedTextField(title)
+
+        let addButton = NSButton(title: "+ Add Another", target: self, action: #selector(didTapAddWorkExperience))
+        addButton.translatesAutoresizingMaskIntoConstraints = false
+        addButton.bezelStyle = .rounded
+        addButton.isBordered = true
+        addButton.font = .systemFont(ofSize: 12, weight: .medium)
+        addButton.controlSize = .regular
+        addButton.setContentHuggingPriority(.required, for: .horizontal)
+
+        let spacer = NSView()
+        spacer.translatesAutoresizingMaskIntoConstraints = false
+        spacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
+
+        let headerRow = NSStackView(views: [title, spacer, addButton])
+        headerRow.orientation = .horizontal
+        headerRow.alignment = .centerY
+        headerRow.distribution = .fill
+        headerRow.spacing = 12
+        headerRow.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: headerRow)
+        headerRow.translatesAutoresizingMaskIntoConstraints = false
+
+        workExperienceRowsStack.translatesAutoresizingMaskIntoConstraints = false
+        workExperienceRowsStack.orientation = .vertical
+        workExperienceRowsStack.spacing = 20
+        workExperienceRowsStack.alignment = .width
+        workExperienceRowsStack.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: workExperienceRowsStack)
+
+        let outer = NSStackView(views: [headerRow, workExperienceRowsStack])
+        outer.orientation = .vertical
+        outer.spacing = 16
+        outer.alignment = .width
+        outer.translatesAutoresizingMaskIntoConstraints = false
+        outer.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: outer)
+        return outer
+    }
+
+    private func educationSection() -> NSView {
+        let title = NSTextField(labelWithString: "Education")
+        title.font = .systemFont(ofSize: 15, weight: .semibold)
+        title.textColor = ProfilePagePalette.primaryText
+        title.translatesAutoresizingMaskIntoConstraints = false
+        title.setContentHuggingPriority(.defaultLow, for: .horizontal)
+        ProfileLayoutEnforcement.applyLeftAlignedTextField(title)
+
+        let addButton = NSButton(title: "+ Add Another", target: self, action: #selector(didTapAddEducation))
+        addButton.translatesAutoresizingMaskIntoConstraints = false
+        addButton.bezelStyle = .rounded
+        addButton.isBordered = true
+        addButton.font = .systemFont(ofSize: 12, weight: .medium)
+        addButton.setContentHuggingPriority(.required, for: .horizontal)
+
+        let spacer = NSView()
+        spacer.translatesAutoresizingMaskIntoConstraints = false
+        spacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
+
+        let headerRow = NSStackView(views: [title, spacer, addButton])
+        headerRow.orientation = .horizontal
+        headerRow.alignment = .centerY
+        headerRow.distribution = .fill
+        headerRow.spacing = 12
+        headerRow.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: headerRow)
+        headerRow.translatesAutoresizingMaskIntoConstraints = false
+
+        educationRowsStack.translatesAutoresizingMaskIntoConstraints = false
+        educationRowsStack.orientation = .vertical
+        educationRowsStack.spacing = 16
+        educationRowsStack.alignment = .width
+        educationRowsStack.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: educationRowsStack)
+
+        let outer = NSStackView(views: [headerRow, educationRowsStack])
+        outer.orientation = .vertical
+        outer.spacing = 16
+        outer.alignment = .width
+        outer.translatesAutoresizingMaskIntoConstraints = false
+        outer.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: outer)
+        return outer
+    }
+
+    private func appendWorkExperienceEntry() {
+        let entry = WorkExperienceEntryView()
+        entry.translatesAutoresizingMaskIntoConstraints = false
+        if let compact = lastCompactLayout {
+            entry.applyCompactLayout(compact)
+        }
+        entry.onDelete = { [weak self, weak entry] in
+            guard let self, let entry else { return }
+            self.removeWorkExperienceEntry(entry)
+        }
+        workExperienceEntries.append(entry)
+        workExperienceRowsStack.addArrangedSubview(entry)
+        renumberWorkExperienceEntries()
+        refreshWorkExperienceDeleteButtons()
+    }
+
+    private func removeWorkExperienceEntry(_ entry: WorkExperienceEntryView) {
+        guard workExperienceEntries.count > 1 else { return }
+        workExperienceEntries.removeAll { $0 === entry }
+        workExperienceRowsStack.removeArrangedSubview(entry)
+        entry.removeFromSuperview()
+        renumberWorkExperienceEntries()
+        refreshWorkExperienceDeleteButtons()
+    }
+
+    private func renumberWorkExperienceEntries() {
+        for (i, entry) in workExperienceEntries.enumerated() {
+            entry.setExperienceIndex(i + 1)
+        }
+    }
+
+    private func refreshWorkExperienceDeleteButtons() {
+        let hide = workExperienceEntries.count <= 1
+        for entry in workExperienceEntries {
+            entry.setDeleteHidden(hide)
+        }
+    }
+
+    private func appendEducationEntry() {
+        let entry = EducationEntryView()
+        entry.translatesAutoresizingMaskIntoConstraints = false
+        if let compact = lastCompactLayout {
+            entry.applyCompactLayout(compact)
+        }
+        entry.onDelete = { [weak self, weak entry] in
+            guard let self, let entry else { return }
+            self.removeEducationEntry(entry)
+        }
+        educationEntries.append(entry)
+        educationRowsStack.addArrangedSubview(entry)
+        renumberEducationEntries()
+        refreshEducationDeleteButtons()
+    }
+
+    private func removeEducationEntry(_ entry: EducationEntryView) {
+        guard educationEntries.count > 1 else { return }
+        educationEntries.removeAll { $0 === entry }
+        educationRowsStack.removeArrangedSubview(entry)
+        entry.removeFromSuperview()
+        renumberEducationEntries()
+        refreshEducationDeleteButtons()
+    }
+
+    private func renumberEducationEntries() {
+        for (i, entry) in educationEntries.enumerated() {
+            entry.setEducationIndex(i + 1)
+        }
+    }
+
+    private func refreshEducationDeleteButtons() {
+        let hide = educationEntries.count <= 1
+        for entry in educationEntries {
+            entry.setDeleteHidden(hide)
+        }
+    }
+
+    @objc private func didTapAddWorkExperience() {
+        appendWorkExperienceEntry()
+    }
+
+    @objc private func didTapAddEducation() {
+        appendEducationEntry()
+    }
+
     private func saveButtonHost() -> NSView {
         saveButton.translatesAutoresizingMaskIntoConstraints = false
         let host = NSView()
         host.translatesAutoresizingMaskIntoConstraints = false
         host.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: host)
         host.addSubview(saveButton)
         NSLayoutConstraint.activate([
             saveButton.leadingAnchor.constraint(equalTo: host.leadingAnchor),
@@ -368,6 +815,438 @@ final class MyProfilePageView: NSView {
     }
 }
 
+// MARK: - Work experience & education rows
+
+private final class WorkExperienceEntryView: NSView {
+    var onDelete: (() -> Void)?
+
+    private let subtitleLabel = NSTextField(labelWithString: "Experience 1")
+    private let deleteButton = NSButton()
+    private let jobTitleField = NSTextField()
+    private let companyField = NSTextField()
+    private let durationField = NSTextField()
+    private let descriptionField = NSTextField()
+    private let jobCompanyRow = NSStackView()
+
+    override init(frame frameRect: NSRect) {
+        super.init(frame: frameRect)
+        configure()
+    }
+
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    func setExperienceIndex(_ index: Int) {
+        subtitleLabel.stringValue = "Experience \(index)"
+    }
+
+    func setDeleteHidden(_ hidden: Bool) {
+        deleteButton.isHidden = hidden
+    }
+
+    func applyCompactLayout(_ compact: Bool) {
+        jobCompanyRow.orientation = compact ? .vertical : .horizontal
+        jobCompanyRow.spacing = compact ? 12 : 12
+        jobCompanyRow.distribution = compact ? .fill : .fillEqually
+    }
+
+    private func configure() {
+        userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: self)
+        wantsLayer = true
+        layer?.cornerRadius = 14
+        layer?.borderWidth = 1
+        layer?.borderColor = ProfilePagePalette.border.cgColor
+        layer?.backgroundColor = ProfilePagePalette.cardBackground.cgColor
+        layer?.masksToBounds = false
+        layer?.shadowColor = NSColor.black.cgColor
+        layer?.shadowOpacity = 0.05
+        layer?.shadowRadius = 12
+        layer?.shadowOffset = CGSize(width: 0, height: 6)
+        if #available(macOS 11.0, *) {
+            layer?.cornerCurve = .continuous
+        }
+
+        subtitleLabel.font = .systemFont(ofSize: 12, weight: .medium)
+        subtitleLabel.textColor = ProfilePagePalette.secondaryText
+        subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
+        ProfileLayoutEnforcement.applyLeftAlignedTextField(subtitleLabel)
+
+        deleteButton.translatesAutoresizingMaskIntoConstraints = false
+        deleteButton.isBordered = false
+        deleteButton.bezelStyle = .regularSquare
+        deleteButton.focusRingType = .none
+        deleteButton.contentTintColor = ProfilePagePalette.destructive
+        deleteButton.target = self
+        deleteButton.action = #selector(didTapDelete)
+        if #available(macOS 11.0, *) {
+            deleteButton.image = NSImage(systemSymbolName: "trash", accessibilityDescription: "Remove experience")
+            deleteButton.imagePosition = .imageOnly
+        } else {
+            deleteButton.title = "Remove"
+            deleteButton.font = .systemFont(ofSize: 12, weight: .medium)
+        }
+
+        let headerSpacer = NSView()
+        headerSpacer.translatesAutoresizingMaskIntoConstraints = false
+        headerSpacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
+        let headerRow = NSStackView(views: [subtitleLabel, headerSpacer, deleteButton])
+        headerRow.orientation = .horizontal
+        headerRow.alignment = .centerY
+        headerRow.distribution = .fill
+        headerRow.spacing = 8
+        headerRow.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: headerRow)
+        headerRow.translatesAutoresizingMaskIntoConstraints = false
+
+        let jobGroup = Self.labeledFieldStack(title: "Job Title *", field: jobTitleField, placeholder: "e.g., Software Engineer")
+        let companyGroup = Self.labeledFieldStack(title: "Company Name *", field: companyField, placeholder: "e.g., Google")
+        configureTwoColumnRow(jobCompanyRow, left: jobGroup, right: companyGroup)
+        pinEqualColumnWidths(in: jobCompanyRow)
+
+        let durationGroup = Self.labeledFieldStack(title: "Duration *", field: durationField, placeholder: "e.g., Jan 2020 - Present")
+        let descriptionGroup = Self.multilineLabeledStack(
+            title: "Description",
+            field: descriptionField,
+            placeholder: "Describe your responsibilities and achievements...",
+            minHeight: 120
+        )
+
+        let inner = NSStackView(views: [headerRow, jobCompanyRow, durationGroup, descriptionGroup])
+        inner.orientation = .vertical
+        inner.spacing = 16
+        inner.alignment = .width
+        inner.translatesAutoresizingMaskIntoConstraints = false
+        inner.edgeInsets = NSEdgeInsets(top: 16, left: 18, bottom: 16, right: 18)
+        inner.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: inner)
+
+        addSubview(inner)
+        NSLayoutConstraint.activate([
+            inner.leftAnchor.constraint(equalTo: leftAnchor),
+            inner.rightAnchor.constraint(equalTo: rightAnchor),
+            inner.topAnchor.constraint(equalTo: topAnchor),
+            inner.bottomAnchor.constraint(equalTo: bottomAnchor)
+        ])
+    }
+
+    private func configureTwoColumnRow(_ row: NSStackView, left: NSView, right: NSView) {
+        row.translatesAutoresizingMaskIntoConstraints = false
+        row.orientation = .horizontal
+        row.spacing = 12
+        row.distribution = .fillEqually
+        row.alignment = .top
+        row.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: row)
+        row.setContentHuggingPriority(.defaultLow, for: .horizontal)
+        row.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
+        row.addArrangedSubview(left)
+        row.addArrangedSubview(right)
+    }
+
+    override func layout() {
+        super.layout()
+        for field in [jobTitleField, companyField, durationField, descriptionField] {
+            if let wrap = field.superview, wrap.bounds.width > 2 {
+                let w = max(1, wrap.bounds.width - 24)
+                if abs(field.preferredMaxLayoutWidth - w) > 0.5 {
+                    field.preferredMaxLayoutWidth = w
+                }
+            }
+        }
+        guard let layer = layer, layer.shadowOpacity > 0 else { return }
+        let r = layer.cornerRadius
+        layer.shadowPath = CGPath(roundedRect: bounds, cornerWidth: r, cornerHeight: r, transform: nil)
+    }
+
+    private func pinEqualColumnWidths(in row: NSStackView) {
+        guard row.arrangedSubviews.count == 2 else { return }
+        let left = row.arrangedSubviews[0]
+        let right = row.arrangedSubviews[1]
+        left.widthAnchor.constraint(equalTo: right.widthAnchor).isActive = true
+    }
+
+    @objc private func didTapDelete() {
+        onDelete?()
+    }
+
+    fileprivate static func labeledFieldStack(title: String, field: NSTextField, placeholder: String) -> NSView {
+        let label = NSTextField(labelWithString: title)
+        label.font = .systemFont(ofSize: 12, weight: .medium)
+        label.textColor = ProfilePagePalette.secondaryText
+        label.translatesAutoresizingMaskIntoConstraints = false
+        ProfileLayoutEnforcement.applyLeftAlignedTextField(label)
+
+        styleSingleLineField(field, placeholder: placeholder)
+        let wrap = roundedChrome(around: field, minHeight: 40)
+
+        let stack = NSStackView(views: [label, wrap])
+        stack.orientation = .vertical
+        stack.spacing = 8
+        stack.alignment = .width
+        stack.translatesAutoresizingMaskIntoConstraints = false
+        stack.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: stack)
+        NSLayoutConstraint.activate([wrap.widthAnchor.constraint(equalTo: stack.widthAnchor)])
+        return stack
+    }
+
+    private static func multilineLabeledStack(title: String, field: NSTextField, placeholder: String, minHeight: CGFloat) -> NSView {
+        let label = NSTextField(labelWithString: title)
+        label.font = .systemFont(ofSize: 12, weight: .medium)
+        label.textColor = ProfilePagePalette.secondaryText
+        label.translatesAutoresizingMaskIntoConstraints = false
+        ProfileLayoutEnforcement.applyLeftAlignedTextField(label)
+
+        field.translatesAutoresizingMaskIntoConstraints = false
+        field.isEditable = true
+        field.isSelectable = true
+        field.isBordered = false
+        field.drawsBackground = false
+        field.focusRingType = .none
+        field.font = .systemFont(ofSize: 14, weight: .regular)
+        field.textColor = ProfilePagePalette.primaryText
+        field.maximumNumberOfLines = 0
+        field.cell?.wraps = true
+        field.cell?.isScrollable = false
+        field.cell?.usesSingleLineMode = false
+        field.placeholderAttributedString = NSAttributedString(
+            string: placeholder,
+            attributes: [
+                .foregroundColor: ProfilePagePalette.secondaryText,
+                .font: NSFont.systemFont(ofSize: 14, weight: .regular),
+                .paragraphStyle: ProfileLayoutEnforcement.leftAlignedParagraphStyle()
+            ]
+        )
+        ProfileLayoutEnforcement.applyLeftAlignedTextField(field)
+
+        let wrap = NSView()
+        wrap.translatesAutoresizingMaskIntoConstraints = false
+        wrap.wantsLayer = true
+        wrap.layer?.backgroundColor = ProfilePagePalette.fieldFill.cgColor
+        wrap.layer?.cornerRadius = 10
+        wrap.layer?.borderWidth = 1
+        wrap.layer?.borderColor = ProfilePagePalette.border.cgColor
+        if #available(macOS 11.0, *) {
+            wrap.layer?.cornerCurve = .continuous
+        }
+        wrap.addSubview(field)
+        ProfileLayoutEnforcement.applyForcedLTR(to: wrap)
+        NSLayoutConstraint.activate([
+            field.leftAnchor.constraint(equalTo: wrap.leftAnchor, constant: 12),
+            field.rightAnchor.constraint(equalTo: wrap.rightAnchor, constant: -12),
+            field.topAnchor.constraint(equalTo: wrap.topAnchor, constant: 10),
+            field.bottomAnchor.constraint(equalTo: wrap.bottomAnchor, constant: -10),
+            wrap.heightAnchor.constraint(greaterThanOrEqualToConstant: minHeight)
+        ])
+
+        let stack = NSStackView(views: [label, wrap])
+        stack.orientation = .vertical
+        stack.spacing = 8
+        stack.alignment = .width
+        stack.translatesAutoresizingMaskIntoConstraints = false
+        stack.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: stack)
+        return stack
+    }
+
+    private static func styleSingleLineField(_ field: NSTextField, placeholder: String) {
+        field.translatesAutoresizingMaskIntoConstraints = false
+        field.isBordered = false
+        field.drawsBackground = false
+        field.focusRingType = .none
+        field.font = .systemFont(ofSize: 14, weight: .regular)
+        field.textColor = ProfilePagePalette.primaryText
+        field.placeholderAttributedString = NSAttributedString(
+            string: placeholder,
+            attributes: [
+                .foregroundColor: ProfilePagePalette.secondaryText,
+                .font: NSFont.systemFont(ofSize: 14, weight: .regular),
+                .paragraphStyle: ProfileLayoutEnforcement.leftAlignedParagraphStyle()
+            ]
+        )
+        field.cell?.usesSingleLineMode = true
+        field.cell?.wraps = false
+        field.cell?.isScrollable = true
+        ProfileLayoutEnforcement.applyLeftAlignedTextField(field)
+    }
+
+    private static func roundedChrome(around field: NSTextField, minHeight: CGFloat) -> NSView {
+        let wrap = NSView()
+        wrap.translatesAutoresizingMaskIntoConstraints = false
+        wrap.wantsLayer = true
+        wrap.layer?.backgroundColor = ProfilePagePalette.fieldFill.cgColor
+        wrap.layer?.cornerRadius = 10
+        wrap.layer?.borderWidth = 1
+        wrap.layer?.borderColor = ProfilePagePalette.border.cgColor
+        if #available(macOS 11.0, *) {
+            wrap.layer?.cornerCurve = .continuous
+        }
+        wrap.addSubview(field)
+        ProfileLayoutEnforcement.applyForcedLTR(to: wrap)
+        NSLayoutConstraint.activate([
+            field.leftAnchor.constraint(equalTo: wrap.leftAnchor, constant: 12),
+            field.rightAnchor.constraint(equalTo: wrap.rightAnchor, constant: -12),
+            field.centerYAnchor.constraint(equalTo: wrap.centerYAnchor),
+            wrap.heightAnchor.constraint(greaterThanOrEqualToConstant: minHeight)
+        ])
+        return wrap
+    }
+}
+
+private final class EducationEntryView: NSView {
+    var onDelete: (() -> Void)?
+
+    private let subtitleLabel = NSTextField(labelWithString: "Education 1")
+    private let deleteButton = NSButton()
+    private let degreeField = NSTextField()
+    private let institutionField = NSTextField()
+    private let yearField = NSTextField()
+    private let degreeInstitutionRow = NSStackView()
+
+    override init(frame frameRect: NSRect) {
+        super.init(frame: frameRect)
+        configure()
+    }
+
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    func setEducationIndex(_ index: Int) {
+        subtitleLabel.stringValue = "Education \(index)"
+    }
+
+    func setDeleteHidden(_ hidden: Bool) {
+        deleteButton.isHidden = hidden
+    }
+
+    func applyCompactLayout(_ compact: Bool) {
+        degreeInstitutionRow.orientation = compact ? .vertical : .horizontal
+        degreeInstitutionRow.spacing = 12
+        degreeInstitutionRow.distribution = compact ? .fill : .fillEqually
+    }
+
+    private func configure() {
+        userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: self)
+        wantsLayer = true
+        layer?.cornerRadius = 14
+        layer?.borderWidth = 1
+        layer?.borderColor = ProfilePagePalette.border.cgColor
+        layer?.backgroundColor = ProfilePagePalette.cardBackground.cgColor
+        layer?.masksToBounds = false
+        layer?.shadowColor = NSColor.black.cgColor
+        layer?.shadowOpacity = 0.04
+        layer?.shadowRadius = 10
+        layer?.shadowOffset = CGSize(width: 0, height: 5)
+        if #available(macOS 11.0, *) {
+            layer?.cornerCurve = .continuous
+        }
+
+        subtitleLabel.font = .systemFont(ofSize: 12, weight: .medium)
+        subtitleLabel.textColor = ProfilePagePalette.secondaryText
+        subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
+        ProfileLayoutEnforcement.applyLeftAlignedTextField(subtitleLabel)
+
+        deleteButton.translatesAutoresizingMaskIntoConstraints = false
+        deleteButton.isBordered = false
+        deleteButton.bezelStyle = .regularSquare
+        deleteButton.focusRingType = .none
+        deleteButton.contentTintColor = ProfilePagePalette.destructive
+        deleteButton.target = self
+        deleteButton.action = #selector(didTapDelete)
+        if #available(macOS 11.0, *) {
+            deleteButton.image = NSImage(systemSymbolName: "trash", accessibilityDescription: "Remove education")
+            deleteButton.imagePosition = .imageOnly
+        } else {
+            deleteButton.title = "Remove"
+            deleteButton.font = .systemFont(ofSize: 12, weight: .medium)
+        }
+
+        let headerSpacer = NSView()
+        headerSpacer.translatesAutoresizingMaskIntoConstraints = false
+        headerSpacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
+        let headerRow = NSStackView(views: [subtitleLabel, headerSpacer, deleteButton])
+        headerRow.orientation = .horizontal
+        headerRow.alignment = .centerY
+        headerRow.distribution = .fill
+        headerRow.spacing = 8
+        headerRow.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: headerRow)
+        headerRow.translatesAutoresizingMaskIntoConstraints = false
+
+        let degreeGroup = WorkExperienceEntryView.labeledFieldStack(
+            title: "Degree / program *",
+            field: degreeField,
+            placeholder: "e.g., BSc Computer Science"
+        )
+        let institutionGroup = WorkExperienceEntryView.labeledFieldStack(
+            title: "Institution *",
+            field: institutionField,
+            placeholder: "e.g., MIT"
+        )
+        configureTwoColumnRow(degreeInstitutionRow, left: degreeGroup, right: institutionGroup)
+        pinEqualColumnWidths(in: degreeInstitutionRow)
+
+        let yearGroup = WorkExperienceEntryView.labeledFieldStack(
+            title: "Year *",
+            field: yearField,
+            placeholder: "e.g., 2020"
+        )
+
+        let inner = NSStackView(views: [headerRow, degreeInstitutionRow, yearGroup])
+        inner.orientation = .vertical
+        inner.spacing = 14
+        inner.alignment = .width
+        inner.translatesAutoresizingMaskIntoConstraints = false
+        inner.edgeInsets = NSEdgeInsets(top: 14, left: 18, bottom: 14, right: 18)
+        inner.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: inner)
+
+        addSubview(inner)
+        NSLayoutConstraint.activate([
+            inner.leftAnchor.constraint(equalTo: leftAnchor),
+            inner.rightAnchor.constraint(equalTo: rightAnchor),
+            inner.topAnchor.constraint(equalTo: topAnchor),
+            inner.bottomAnchor.constraint(equalTo: bottomAnchor)
+        ])
+    }
+
+    private func configureTwoColumnRow(_ row: NSStackView, left: NSView, right: NSView) {
+        row.translatesAutoresizingMaskIntoConstraints = false
+        row.orientation = .horizontal
+        row.spacing = 12
+        row.distribution = .fillEqually
+        row.alignment = .top
+        row.userInterfaceLayoutDirection = .leftToRight
+        ProfileLayoutEnforcement.applyForcedLTR(to: row)
+        row.setContentHuggingPriority(.defaultLow, for: .horizontal)
+        row.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
+        row.addArrangedSubview(left)
+        row.addArrangedSubview(right)
+    }
+
+    override func layout() {
+        super.layout()
+        guard let layer = layer, layer.shadowOpacity > 0 else { return }
+        let r = layer.cornerRadius
+        layer.shadowPath = CGPath(roundedRect: bounds, cornerWidth: r, cornerHeight: r, transform: nil)
+    }
+
+    private func pinEqualColumnWidths(in row: NSStackView) {
+        guard row.arrangedSubviews.count == 2 else { return }
+        let left = row.arrangedSubviews[0]
+        let right = row.arrangedSubviews[1]
+        left.widthAnchor.constraint(equalTo: right.widthAnchor).isActive = true
+    }
+
+    @objc private func didTapDelete() {
+        onDelete?()
+    }
+}
+
 // MARK: - Primary CTA
 
 private final class ProfilePrimaryButton: NSButton {