krz/domain-dig

an ios app for DNS & SSL analysis

clone: git clone https://gitbay.org/krz/domain-dig.git

ee2520f2c0c2d806fd284c3d6b8a81a3822f5270

verified · cmc

author: Christian Cleberg <hello@cleberg.net> · 2026-04-20T20:28:12Z

feat(v1.8): improve result structure, history, and consistency
- normalize sections and add summary card
- fix resolver usage in reverse DNS
- include custom port scans in history/export
- standardize error handling
- decompose ContentView into smaller views
 DomainDig/ContentView.swift          | 1184 ++++++++++++----------------
 DomainDig/DNSLookupService.swift     |   23 +-
 DomainDig/DomainViewModel.swift      | 1441 +++++++++++++++++++++++-----------
 DomainDig/EmailSecurityService.swift |    7 +-
 DomainDig/HTTPHeadersService.swift   |   61 +-
 DomainDig/HistoryView.swift          |  562 +++----------
 DomainDig/IPGeolocationService.swift |   19 +-
 DomainDig/Models.swift               |   69 +-
 DomainDig/PortScanService.swift      |   54 +-
 DomainDig/ReachabilityService.swift  |    5 +-
 DomainDig/RedirectChainService.swift |   13 +-
 DomainDig/ReverseDNSService.swift    |   49 +-
 DomainDig/SSLCheckService.swift      |   17 +-
 13 files changed, 1789 insertions(+), 1715 deletions(-)

diff --git a/DomainDig/ContentView.swift b/DomainDig/ContentView.swift
index d9c1299..68f3a6f 100644
--- a/DomainDig/ContentView.swift
+++ b/DomainDig/ContentView.swift
@@ -1,5 +1,5 @@
-import SwiftUI
 import MapKit
+import SwiftUI
 
 struct ContentView: View {
     @State private var viewModel = DomainViewModel()
@@ -14,14 +14,59 @@ struct ContentView: View {
                     inputSection
                     if viewModel.hasRun {
                         actionButtons
-                        reachabilitySection
-                        redirectChainSection
-                        dnsResultsSection
-                        emailSecuritySection
-                        sslResultsSection
-                        httpHeadersSection
-                        ipGeolocationSection
-                        portScanSection
+                        SummaryView(fields: viewModel.summaryFields)
+                            .padding(.top, 8)
+                        DomainSectionView(rows: viewModel.domainRows)
+                            .padding(.top, 16)
+                        DNSSectionView(
+                            dnssecLabel: viewModel.dnssecLabel,
+                            sections: viewModel.dnsRows,
+                            ptrMessage: viewModel.ptrMessage,
+                            loading: viewModel.dnsLoading || viewModel.ptrLoading,
+                            sectionError: viewModel.dnsError
+                        )
+                        .padding(.top, 16)
+                        WebSectionView(
+                            certificateRows: viewModel.webCertificateRows,
+                            sslInfo: viewModel.sslInfo,
+                            sslLoading: viewModel.sslLoading || viewModel.hstsLoading,
+                            sslError: viewModel.sslError,
+                            responseRows: viewModel.webResponseRows,
+                            headers: viewModel.httpHeaders,
+                            headersLoading: viewModel.httpHeadersLoading,
+                            headersError: viewModel.httpHeadersError,
+                            redirects: viewModel.redirectRows,
+                            redirectLoading: viewModel.redirectChainLoading,
+                            redirectError: viewModel.redirectChainError,
+                            finalURL: viewModel.currentSnapshot.redirectChain.last?.url
+                        )
+                        .padding(.top, 16)
+                        EmailSectionView(
+                            rows: viewModel.emailRows,
+                            loading: viewModel.emailSecurityLoading,
+                            error: viewModel.emailSecurityError
+                        )
+                        .padding(.top, 16)
+                        NetworkSectionView(
+                            reachabilityRows: viewModel.reachabilityRows,
+                            reachabilityLoading: viewModel.reachabilityLoading,
+                            reachabilityError: viewModel.reachabilityError,
+                            locationRows: viewModel.locationRows,
+                            geolocation: viewModel.ipGeolocation,
+                            geolocationLoading: viewModel.ipGeolocationLoading,
+                            geolocationError: viewModel.ipGeolocationError,
+                            standardPortRows: viewModel.standardPortRows,
+                            customPortRows: viewModel.customPortRows,
+                            portScanLoading: viewModel.portScanLoading,
+                            portScanError: viewModel.portScanError,
+                            customPortScanLoading: viewModel.customPortScanLoading,
+                            customPortScanError: viewModel.customPortScanError,
+                            isCloudflareProxied: viewModel.isCloudflareProxied,
+                            customPortsExpanded: $customPortsExpanded,
+                            customPortInput: $customPortInput,
+                            onScanCustomPorts: runCustomPortScan
+                        )
+                        .padding(.top, 16)
                     } else if !viewModel.recentSearches.isEmpty {
                         recentSearchesSection
                     }
@@ -55,8 +100,6 @@ struct ContentView: View {
                         Image(systemName: "clock.arrow.trianglehead.counterclockwise.rotate.90")
                             .foregroundStyle(.secondary)
                     }
-                }
-                ToolbarItem(placement: .topBarTrailing) {
                     NavigationLink {
                         SettingsView()
                     } label: {
@@ -71,8 +114,6 @@ struct ContentView: View {
         }
     }
 
-    // MARK: - Input
-
     private var inputSection: some View {
         VStack(spacing: 12) {
             TextField("e.g. cleberg.net", text: $viewModel.domain)
@@ -101,8 +142,6 @@ struct ContentView: View {
         .padding(.vertical, 16)
     }
 
-    // MARK: - Action Buttons (Share + Bookmark)
-
     private var actionButtons: some View {
         HStack {
             Spacer()
@@ -123,11 +162,8 @@ struct ContentView: View {
                 }
             }
         }
-        .padding(.top, 8)
     }
 
-    // MARK: - Recent Searches
-
     private var recentSearchesSection: some View {
         VStack(alignment: .leading, spacing: 8) {
             HStack {
@@ -162,778 +198,605 @@ struct ContentView: View {
         .padding(.top, 8)
     }
 
-    // MARK: - Reachability
-
-    private var reachabilitySection: some View {
-        VStack(alignment: .leading, spacing: 12) {
-            sectionHeader("Reachability")
-
-            if viewModel.reachabilityLoading {
-                ProgressView("Checking ports…")
-                    .frame(maxWidth: .infinity, alignment: .center)
-                    .padding()
-            } else if let error = viewModel.reachabilityError {
-                errorLabel(error)
-            } else {
-                VStack(alignment: .leading, spacing: 4) {
-                    ForEach(viewModel.reachabilityResults) { result in
-                        HStack(spacing: 8) {
-                            Circle()
-                                .fill(result.reachable ? Color.green : Color.red)
-                                .frame(width: 8, height: 8)
-                            Text("Port \(result.port)")
-                                .font(.system(.caption, design: .monospaced))
-                            if result.reachable, let ms = result.latencyMs {
-                                Text("\(ms)ms")
-                                    .font(.system(.caption, design: .monospaced))
-                                    .foregroundStyle(.secondary)
-                            } else if !result.reachable {
-                                Text("—")
-                                    .font(.system(.caption, design: .monospaced))
-                                    .foregroundStyle(.secondary)
-                            }
-                            Spacer()
-                            Text(result.reachable ? "Reachable" : "Unreachable")
-                                .font(.system(.caption, design: .monospaced))
-                                .foregroundStyle(result.reachable ? .green : .red)
-                        }
-                    }
-                }
-                .padding(10)
-                .background(Color(.systemGray6).opacity(0.5))
-                .cornerRadius(6)
-            }
+    private func runCustomPortScan() {
+        let ports = parsedCustomPorts(from: customPortInput)
+        Task {
+            await viewModel.runCustomPortScan(ports: ports)
         }
-        .padding(.top, 8)
     }
 
-    // MARK: - Redirect Chain
-
-    private var redirectChainSection: some View {
-        VStack(alignment: .leading, spacing: 12) {
-            sectionHeader("Redirect Chain")
-
-            if viewModel.redirectChainLoading {
-                ProgressView("Tracing redirects…")
-                    .frame(maxWidth: .infinity, alignment: .center)
-                    .padding()
-            } else if let error = viewModel.redirectChainError {
-                errorLabel(error)
-            } else if viewModel.redirectChain.isEmpty {
-                Text("No redirect data")
-                    .font(.system(.caption, design: .monospaced))
-                    .foregroundStyle(.secondary)
-                    .padding(8)
-            } else if viewModel.redirectChain.count == 1,
-                      let only = viewModel.redirectChain.first,
-                      only.isFinal, !(300...399).contains(only.statusCode) {
-                Text("No redirects — direct connection")
-                    .font(.system(.caption, design: .monospaced))
-                    .foregroundStyle(.secondary)
-                    .padding(10)
-                    .frame(maxWidth: .infinity, alignment: .leading)
-                    .background(Color(.systemGray6).opacity(0.5))
-                    .cornerRadius(6)
-            } else {
-                horizontallyScrollableCard {
-                    ForEach(viewModel.redirectChain) { hop in
-                        HStack(alignment: .top, spacing: 6) {
-                            Text("\(hop.stepNumber)")
-                                .font(.system(.caption, design: .monospaced))
-                                .foregroundStyle(.secondary)
-                                .frame(width: 16, alignment: .trailing)
-                            Text("\(hop.statusCode)")
-                                .font(.system(.caption, design: .monospaced))
-                                .foregroundStyle(.cyan)
-                                .frame(width: 30, alignment: .leading)
-                            Text(hop.url)
-                                .font(.system(.caption, design: .monospaced))
-                                .foregroundStyle(.primary)
-                                .textSelection(.enabled)
-                            if hop.isFinal {
-                                Text("(final)")
-                                    .font(.system(.caption2, design: .monospaced))
-                                    .foregroundStyle(.secondary)
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        .padding(.top, 16)
-    }
-
-    // MARK: - DNS Results
+    private func parsedCustomPorts(from input: String) -> [UInt16] {
+        let parts = input.split(separator: ",", omittingEmptySubsequences: true)
+        var seen = Set<UInt16>()
+        var ports: [UInt16] = []
 
-    private var dnsResultsSection: some View {
-        VStack(alignment: .leading, spacing: 12) {
-            HStack(alignment: .top, spacing: 8) {
-                sectionHeader("DNS Records")
-                Spacer()
-                if let dnssecSigned = dnssecStatus {
-                    Text(dnssecSigned ? "DNSSEC ✓" : "DNSSEC ✗")
-                        .font(.system(.caption2, design: .monospaced))
-                        .foregroundStyle(dnssecSigned ? .green : .red)
-                        .padding(.top, 1)
-                }
+        for part in parts {
+            let trimmed = part.trimmingCharacters(in: .whitespacesAndNewlines)
+            guard let value = UInt16(trimmed), seen.insert(value).inserted else {
+                continue
             }
-
-            if viewModel.dnsLoading {
-                ProgressView("Querying DNS…")
-                    .frame(maxWidth: .infinity, alignment: .center)
-                    .padding()
-            } else if let error = viewModel.dnsError {
-                errorLabel(error)
-            } else {
-                ForEach(viewModel.dnsSections) { section in
-                    dnsRecordSection(section)
-                    if section.recordType == .A {
-                        ptrRow
-                    }
-                }
+            ports.append(value)
+            if ports.count == 20 {
+                break
             }
         }
-        .padding(.top, 16)
-    }
 
-    private var ptrRow: some View {
-        horizontallyScrollableCard {
-            Text("PTR (Reverse DNS)")
-                .font(.system(.subheadline, design: .monospaced))
-                .fontWeight(.semibold)
-                .foregroundStyle(.cyan)
-
-            if viewModel.ptrLoading {
-                ProgressView()
-                    .frame(maxWidth: .infinity, alignment: .center)
-                    .padding(4)
-            } else if let ptr = viewModel.ptrRecord {
-                Text(ptr)
-                    .font(.system(.caption, design: .monospaced))
-                    .foregroundStyle(.primary)
-                    .textSelection(.enabled)
-            } else {
-                Text("No PTR record found")
-                    .font(.system(.caption, design: .monospaced))
-                    .foregroundStyle(.secondary)
-            }
-        }
+        return ports
     }
 
-    private func dnsRecordSection(_ section: DNSSection) -> some View {
-        horizontallyScrollableCard {
-            Text(section.recordType.rawValue)
-                .font(.system(.subheadline, design: .monospaced))
-                .fontWeight(.semibold)
-                .foregroundStyle(.cyan)
-
-            if let error = section.error {
-                errorLabel(error)
-            } else if section.records.isEmpty {
-                Text("No records found")
-                    .font(.system(.caption, design: .monospaced))
-                    .foregroundStyle(.secondary)
-            } else {
-                dnsRecordRows(section.records)
-            }
-
-            if !section.wildcardRecords.isEmpty {
-                Text("*.\(viewModel.searchedDomain)")
-                    .font(.system(.caption, design: .monospaced))
-                    .fontWeight(.medium)
-                    .foregroundStyle(.cyan.opacity(0.7))
-                    .padding(.top, 4)
+    private func shareResults() {
+        let text = viewModel.exportText()
+        let dateFmt = DateFormatter()
+        dateFmt.dateFormat = "yyyyMMdd_HHmmss"
+        let timestamp = dateFmt.string(from: Date())
+        let filename = "\(timestamp)_domaindigresults.txt"
+        let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(filename)
 
-                dnsRecordRows(section.wildcardRecords)
-            }
+        do {
+            try text.write(to: tempURL, atomically: true, encoding: .utf8)
+        } catch {
+            return
         }
-    }
 
-    private func dnsRecordRows(_ records: [DNSRecord]) -> some View {
-        ForEach(records) { record in
-            HStack(alignment: .top) {
-                Text(record.value)
-                    .font(.system(.caption, design: .monospaced))
-                    .foregroundStyle(.primary)
-                    .textSelection(.enabled)
-                Spacer()
-                Text("TTL \(record.ttl)")
-                    .font(.system(.caption2, design: .monospaced))
-                    .foregroundStyle(.secondary)
-            }
+        let activityVC = UIActivityViewController(activityItems: [tempURL], applicationActivities: nil)
+        guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
+              let rootVC = windowScene.keyWindow?.rootViewController else { return }
+        var presenter = rootVC
+        while let presented = presenter.presentedViewController {
+            presenter = presented
         }
+        activityVC.popoverPresentationController?.sourceView = presenter.view
+        presenter.present(activityVC, animated: true)
     }
+}
 
-    // MARK: - Email Security
-
-    @State private var expandedEmailField: String?
+struct SummaryView: View {
+    let fields: [SummaryFieldViewData]
 
-    private var emailSecuritySection: some View {
+    var body: some View {
         VStack(alignment: .leading, spacing: 12) {
-            sectionHeader("Email Security")
-
-            if viewModel.emailSecurityLoading {
-                ProgressView("Checking email records…")
-                    .frame(maxWidth: .infinity, alignment: .center)
-                    .padding()
-            } else if let error = viewModel.emailSecurityError {
-                errorLabel(error)
-            } else if let email = viewModel.emailSecurity {
-                horizontallyScrollableCard(spacing: 6) {
-                    emailSecurityRow("SPF", record: email.spf)
-                    emailSecurityRow("DMARC", record: email.dmarc)
-                    emailSecurityRow("DKIM", record: email.dkim)
-                    emailSecurityRow("MTA-STS", mtaSts: email.mtaSts)
-                    emailSecurityRow("BIMI", record: email.bimi)
-                }
-            }
-        }
-        .frame(maxWidth: .infinity, alignment: .leading)
-        .padding(.top, 16)
-    }
-
-    private func emailSecurityRow(_ label: String, record: EmailSecurityRecord) -> some View {
-        VStack(alignment: .leading, spacing: 2) {
-            HStack(spacing: 8) {
-                Text(label)
-                    .font(.system(.caption, design: .monospaced))
-                    .fontWeight(.semibold)
-                    .frame(width: 72, alignment: .leading)
-                Text(record.found ? "✓" : "✗")
-                    .font(.system(.caption, design: .monospaced))
-                    .foregroundStyle(record.found ? .green : .red)
-                if let value = record.value {
-                    let isExpanded = expandedEmailField == label
-                    let displayValue = isExpanded ? value : String(value.prefix(80))
-                    Text(displayValue)
-                        .font(.system(.caption2, design: .monospaced))
-                        .foregroundStyle(.primary)
-                        .textSelection(.enabled)
-                        .lineLimit(isExpanded ? nil : 1)
-                        .onTapGesture {
-                            withAnimation {
-                                expandedEmailField = isExpanded ? nil : label
-                            }
-                        }
-                    if let selector = record.matchedSelector {
-                        Text("(selector: \(selector))")
+            SectionTitleView(title: "Summary")
+            LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 8) {
+                ForEach(fields) { field in
+                    VStack(alignment: .leading, spacing: 4) {
+                        Text(field.label)
                             .font(.system(.caption2, design: .monospaced))
                             .foregroundStyle(.secondary)
+                        Text(field.value)
+                            .font(.system(.caption, design: .monospaced))
+                            .foregroundStyle(ResultColors.color(for: field.tone))
+                            .lineLimit(2)
+                            .textSelection(.enabled)
                     }
-                } else {
-                    Text("No record found")
-                        .font(.system(.caption2, design: .monospaced))
-                        .foregroundStyle(.secondary)
-                }
-            }
-        }
-    }
-
-    private func emailSecurityRow(_ label: String, mtaSts: MTASTSResult?) -> some View {
-        VStack(alignment: .leading, spacing: 2) {
-            HStack(spacing: 8) {
-                Text(label)
-                    .font(.system(.caption, design: .monospaced))
-                    .fontWeight(.semibold)
-                    .frame(width: 72, alignment: .leading)
-                Text(mtaSts?.txtFound == true ? "✓" : "✗")
-                    .font(.system(.caption, design: .monospaced))
-                    .foregroundStyle(mtaSts?.txtFound == true ? .green : .red)
-                if let policyMode = mtaSts?.policyMode {
-                    Text(policyMode)
-                        .font(.system(.caption2, design: .monospaced))
-                        .foregroundStyle(.primary)
-                        .textSelection(.enabled)
-                } else {
-                    Text(mtaSts?.txtFound == true ? "Policy unavailable" : "No record found")
-                        .font(.system(.caption2, design: .monospaced))
-                        .foregroundStyle(.secondary)
+                    .frame(maxWidth: .infinity, alignment: .leading)
+                    .padding(10)
+                    .background(Color(.systemGray6).opacity(0.5))
+                    .cornerRadius(6)
                 }
             }
         }
     }
+}
 
-    // MARK: - SSL Results
+struct DomainSectionView: View {
+    let rows: [InfoRowViewData]
 
-    private var sslResultsSection: some View {
+    var body: some View {
         VStack(alignment: .leading, spacing: 12) {
-            sectionHeader("SSL / TLS Certificate")
-
-            if viewModel.sslLoading {
-                ProgressView("Checking certificate…")
-                    .frame(maxWidth: .infinity, alignment: .center)
-                    .padding()
-            } else if let error = viewModel.sslError {
-                errorLabel(error)
-            } else if let info = viewModel.sslInfo {
-                sslDetail(info, domain: viewModel.searchedDomain)
+            SectionTitleView(title: "Domain")
+            CardView {
+                ForEach(rows) { row in
+                    LabeledValueRow(row: row)
+                }
             }
         }
-        .padding(.top, 16)
     }
+}
 
-    private func sslDetail(_ info: SSLCertificateInfo, domain: String) -> some View {
-        horizontallyScrollableCard(spacing: 8) {
-            certRow("Common Name", info.commonName)
-            certRow("Issuer", info.issuer)
+struct DNSSectionView: View {
+    let dnssecLabel: String?
+    let sections: [DNSRecordSectionViewData]
+    let ptrMessage: SectionMessageViewData?
+    let loading: Bool
+    let sectionError: String?
 
-            VStack(alignment: .leading, spacing: 2) {
-                Text("SANs")
-                    .font(.system(.caption2, design: .monospaced))
-                    .foregroundStyle(.secondary)
-                ForEach(info.subjectAltNames, id: \.self) { san in
-                    Text(san)
-                        .font(.system(.caption, design: .monospaced))
-                        .textSelection(.enabled)
+    var body: some View {
+        VStack(alignment: .leading, spacing: 12) {
+            HStack(alignment: .top, spacing: 8) {
+                SectionTitleView(title: "DNS")
+                Spacer()
+                if let dnssecLabel {
+                    Text(dnssecLabel)
+                        .font(.system(.caption2, design: .monospaced))
+                        .foregroundStyle(.secondary)
+                        .multilineTextAlignment(.trailing)
                 }
             }
 
-            let formatter = DateFormatter.certDate
-            certRow("Valid From", formatter.string(from: info.validFrom))
-            certRow("Valid Until", formatter.string(from: info.validUntil))
+            if loading {
+                LoadingCardView(text: "Querying DNS…")
+            } else if let sectionError, sections.isEmpty {
+                MessageCardView(text: sectionError, isError: true)
+            } else {
+                ForEach(sections) { section in
+                    CardView {
+                        Text(section.title)
+                            .font(.system(.subheadline, design: .monospaced))
+                            .fontWeight(.semibold)
+                            .foregroundStyle(.cyan)
+
+                        if let message = section.message {
+                            MessageRowView(text: message.text, isError: message.isError)
+                        }
 
-            VStack(alignment: .leading, spacing: 2) {
-                Text("Days Until Expiry")
-                    .font(.system(.caption2, design: .monospaced))
-                    .foregroundStyle(.secondary)
-                Text("\(info.daysUntilExpiry)")
-                    .font(.system(.caption, design: .monospaced))
-                    .fontWeight(.bold)
-                    .foregroundStyle(expiryColor(info.daysUntilExpiry))
-            }
+                        ForEach(section.rows) { row in
+                            LabeledValueRow(row: row)
+                        }
 
-            certRow("Chain Depth", "\(info.chainDepth)")
-            if viewModel.hstsLoading {
-                hstsLoadingRow
-            } else if let hstsPreloaded = viewModel.hstsPreloaded {
-                hstsStatusRow(hstsPreloaded)
-            }
-            if let tlsVersion = info.tlsVersion {
-                certRow("TLS Version", tlsVersion)
-            }
-            if let cipherSuite = info.cipherSuite {
-                certRow("Cipher Suite", cipherSuite)
-            }
-            if !info.chain.isEmpty {
-                VStack(alignment: .leading, spacing: 6) {
-                    Text("Certificate Chain")
-                        .font(.system(.caption2, design: .monospaced))
-                        .foregroundStyle(.secondary)
-                    ForEach(Array(info.chain.enumerated()), id: \.offset) { index, certificate in
-                        DisclosureGroup {
-                            Text(certificate.issuer)
+                        if let wildcardTitle = section.wildcardTitle {
+                            Text(wildcardTitle)
                                 .font(.system(.caption, design: .monospaced))
                                 .foregroundStyle(.secondary)
-                                .textSelection(.enabled)
-                        } label: {
-                            Text(certificate.subject)
-                                .font(.system(.caption, design: .monospaced))
-                                .foregroundStyle(.primary)
-                                .textSelection(.enabled)
+                                .padding(.top, 4)
+                            ForEach(section.wildcardRows) { row in
+                                LabeledValueRow(row: row)
+                            }
                         }
-                        .tint(index == 0 ? .cyan : .secondary)
+                    }
+                }
+
+                if let ptrMessage {
+                    CardView {
+                        Text("PTR")
+                            .font(.system(.subheadline, design: .monospaced))
+                            .fontWeight(.semibold)
+                            .foregroundStyle(.cyan)
+                        MessageRowView(text: ptrMessage.text, isError: ptrMessage.isError)
                     }
                 }
             }
-            Link("View on crt.sh →", destination: URL(string: "https://crt.sh/?q=\(domain)")!)
-                .font(.system(.caption, design: .monospaced))
-                .foregroundStyle(.cyan)
         }
     }
+}
 
-    // MARK: - HTTP Headers
+struct WebSectionView: View {
+    let certificateRows: [InfoRowViewData]
+    let sslInfo: SSLCertificateInfo?
+    let sslLoading: Bool
+    let sslError: String?
+    let responseRows: [InfoRowViewData]
+    let headers: [HTTPHeader]
+    let headersLoading: Bool
+    let headersError: String?
+    let redirects: [RedirectHopViewData]
+    let redirectLoading: Bool
+    let redirectError: String?
+    let finalURL: String?
 
-    private var httpHeadersSection: some View {
+    var body: some View {
         VStack(alignment: .leading, spacing: 12) {
-            HStack(spacing: 8) {
-                sectionHeader("HTTP Headers")
-                if let grade = viewModel.httpSecurityGrade {
-                    Text(grade)
-                        .font(.system(.caption, design: .monospaced))
-                        .foregroundStyle(httpSecurityGradeColor(for: grade))
-                        .padding(.horizontal, 8)
-                        .padding(.vertical, 2)
-                        .background(httpSecurityGradeColor(for: grade).opacity(0.18))
-                        .clipShape(RoundedRectangle(cornerRadius: 6, style: .continuous))
+            SectionTitleView(title: "Web")
+
+            CardView {
+                Text("TLS")
+                    .font(.system(.subheadline, design: .monospaced))
+                    .fontWeight(.semibold)
+                    .foregroundStyle(.cyan)
+                if sslLoading {
+                    ProgressView("Checking certificate…")
+                } else if let sslError {
+                    MessageRowView(text: sslError, isError: true)
+                } else {
+                    ForEach(certificateRows) { row in
+                        LabeledValueRow(row: row)
+                    }
+                    if let sslInfo, !sslInfo.subjectAltNames.isEmpty {
+                        Text("SANs")
+                            .font(.system(.caption2, design: .monospaced))
+                            .foregroundStyle(.secondary)
+                        ForEach(sslInfo.subjectAltNames, id: \.self) { san in
+                            Text(san)
+                                .font(.system(.caption, design: .monospaced))
+                                .textSelection(.enabled)
+                        }
+                    }
                 }
-                Spacer()
             }
 
-            if viewModel.httpHeadersLoading {
-                ProgressView("Fetching headers…")
-                    .frame(maxWidth: .infinity, alignment: .center)
-                    .padding()
-            } else if let error = viewModel.httpHeadersError {
-                errorLabel(error)
-            } else {
-                horizontallyScrollableCard {
-                    if !httpStatusSummaryParts.isEmpty || http3AvailabilityNote != nil {
-                        HStack(alignment: .top, spacing: 0) {
-                            ForEach(Array(httpStatusSummaryParts.enumerated()), id: \.offset) { index, part in
-                                if index > 0 {
-                                    Text("  ")
-                                        .font(.system(.caption, design: .monospaced))
-                                }
-                                Text(part.text)
-                                    .font(.system(.caption, design: .monospaced))
-                                    .foregroundStyle(part.color)
-                            }
-                            if let http3AvailabilityNote {
-                                Text("  ")
+            CardView {
+                Text("Headers")
+                    .font(.system(.subheadline, design: .monospaced))
+                    .fontWeight(.semibold)
+                    .foregroundStyle(.cyan)
+                if headersLoading {
+                    ProgressView("Fetching headers…")
+                } else if let headersError {
+                    MessageRowView(text: headersError, isError: true)
+                } else {
+                    ForEach(responseRows) { row in
+                        LabeledValueRow(row: row)
+                    }
+                    if headers.isEmpty {
+                        MessageRowView(text: "No HTTP headers returned", isError: false)
+                    } else {
+                        ForEach(headers) { header in
+                            HStack(alignment: .top, spacing: 4) {
+                                Text(header.name + ":")
                                     .font(.system(.caption, design: .monospaced))
-                                Text(http3AvailabilityNote)
+                                    .foregroundStyle(header.isSecurityHeader ? .yellow : .cyan)
+                                Text(header.value)
                                     .font(.system(.caption, design: .monospaced))
-                                    .foregroundStyle(.secondary)
+                                    .foregroundStyle(.primary)
+                                    .textSelection(.enabled)
                             }
                         }
                     }
-                    ForEach(viewModel.httpHeaders) { header in
-                        HStack(alignment: .top, spacing: 4) {
-                            Text(header.name + ":")
+                }
+            }
+
+            CardView {
+                Text("Redirects")
+                    .font(.system(.subheadline, design: .monospaced))
+                    .fontWeight(.semibold)
+                    .foregroundStyle(.cyan)
+                if redirectLoading {
+                    ProgressView("Tracing redirects…")
+                } else if let redirectError {
+                    MessageRowView(text: redirectError, isError: true)
+                } else if redirects.isEmpty {
+                    MessageRowView(text: "No redirect data available", isError: false)
+                } else {
+                    if let finalURL {
+                        LabeledValueRow(row: InfoRowViewData(label: "Final URL", value: finalURL, tone: .secondary))
+                    }
+                    ForEach(redirects) { redirect in
+                        HStack(alignment: .top, spacing: 6) {
+                            Text(redirect.stepLabel)
                                 .font(.system(.caption, design: .monospaced))
-                                .foregroundStyle(header.isSecurityHeader ? .yellow : .cyan)
-                            Text(header.value)
+                                .foregroundStyle(.secondary)
+                                .frame(width: 16, alignment: .trailing)
+                            Text(redirect.statusCode)
+                                .font(.system(.caption, design: .monospaced))
+                                .foregroundStyle(.cyan)
+                                .frame(width: 36, alignment: .leading)
+                            Text(redirect.url)
                                 .font(.system(.caption, design: .monospaced))
-                                .foregroundStyle(.primary)
                                 .textSelection(.enabled)
+                            if redirect.isFinal {
+                                Text("(final)")
+                                    .font(.system(.caption2, design: .monospaced))
+                                    .foregroundStyle(.secondary)
+                            }
                         }
                     }
                 }
             }
         }
-        .padding(.top, 16)
     }
+}
 
-    // MARK: - IP Geolocation
+struct EmailSectionView: View {
+    let rows: [EmailRowViewData]
+    let loading: Bool
+    let error: String?
 
-    private var ipGeolocationSection: some View {
+    var body: some View {
         VStack(alignment: .leading, spacing: 12) {
-            sectionHeader("IP Location")
-
-            if viewModel.ipGeolocationLoading {
-                ProgressView("Looking up location…")
-                    .frame(maxWidth: .infinity, alignment: .center)
-                    .padding()
-            } else if let geo = viewModel.ipGeolocation {
-                ipGeolocationDetail(geo)
-            } else if let error = viewModel.ipGeolocationError {
-                if error == "No A record available" {
-                    Text("No location data available")
-                        .font(.system(.caption, design: .monospaced))
-                        .foregroundStyle(.secondary)
-                        .padding(8)
+            SectionTitleView(title: "Email")
+            CardView {
+                if loading {
+                    ProgressView("Checking email records…")
+                } else if let error {
+                    MessageRowView(text: error, isError: true)
+                } else if rows.isEmpty {
+                    MessageRowView(text: "No email security records found", isError: false)
                 } else {
-                    errorLabel(error)
-                }
-            }
-        }
-        .padding(.top, 16)
-    }
-
-    private func ipGeolocationDetail(_ geo: IPGeolocation) -> some View {
-        VStack(alignment: .leading, spacing: 6) {
-            horizontallyScrollableContent(spacing: 6) {
-                certRow("IP", geo.ip)
-                if let org = geo.org {
-                    certRow("Org / ISP", org)
-                }
-                let location = [geo.city, geo.region, geo.country_name].compactMap { $0 }.joined(separator: ", ")
-                if !location.isEmpty {
-                    certRow("Location", location)
-                }
-            }
-
-            if let lat = geo.latitude, let lon = geo.longitude {
-                let coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lon)
-                Map(initialPosition: .region(MKCoordinateRegion(
-                    center: coordinate,
-                    span: MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1)
-                ))) {
-                    Marker(geo.ip, coordinate: coordinate)
+                    ForEach(rows) { row in
+                        VStack(alignment: .leading, spacing: 4) {
+                            HStack(spacing: 8) {
+                                Text(row.label)
+                                    .font(.system(.caption, design: .monospaced))
+                                    .foregroundStyle(.cyan)
+                                    .frame(width: 76, alignment: .leading)
+                                Text(row.status)
+                                    .font(.system(.caption, design: .monospaced))
+                                    .foregroundStyle(ResultColors.color(for: row.statusTone))
+                            }
+                            Text(row.detail)
+                                .font(.system(.caption2, design: .monospaced))
+                                .foregroundStyle(.primary)
+                                .textSelection(.enabled)
+                            if let auxiliaryDetail = row.auxiliaryDetail {
+                                Text(auxiliaryDetail)
+                                    .font(.system(.caption2, design: .monospaced))
+                                    .foregroundStyle(.secondary)
+                            }
+                        }
+                    }
                 }
-                .mapStyle(.standard)
-                .frame(maxWidth: .infinity)
-                .frame(height: 180)
-                .cornerRadius(8)
             }
         }
-        .padding(10)
-        .background(Color(.systemGray6).opacity(0.5))
-        .cornerRadius(6)
     }
+}
 
-    // MARK: - Port Scan
+struct NetworkSectionView: View {
+    let reachabilityRows: [ReachabilityRowViewData]
+    let reachabilityLoading: Bool
+    let reachabilityError: String?
+    let locationRows: [InfoRowViewData]
+    let geolocation: IPGeolocation?
+    let geolocationLoading: Bool
+    let geolocationError: String?
+    let standardPortRows: [PortScanRowViewData]
+    let customPortRows: [PortScanRowViewData]
+    let portScanLoading: Bool
+    let portScanError: String?
+    let customPortScanLoading: Bool
+    let customPortScanError: String?
+    let isCloudflareProxied: Bool
+    @Binding var customPortsExpanded: Bool
+    @Binding var customPortInput: String
+    let onScanCustomPorts: () -> Void
 
-    private var portScanSection: some View {
+    var body: some View {
         VStack(alignment: .leading, spacing: 12) {
-            sectionHeader("Open Ports")
-
-            if viewModel.portScanLoading {
-                ProgressView("Scanning ports…")
-                    .frame(maxWidth: .infinity, alignment: .center)
-                    .padding()
-            } else if let error = viewModel.portScanError {
-                errorLabel(error)
-            } else {
-                VStack(alignment: .leading, spacing: 12) {
-                    if viewModel.isCloudflareProxied {
-                        Text("Domain is behind Cloudflare's proxy. Results reflect what CF's edge exposes, not the origin. CF only proxies ports: 80, 443, 2052–2053, 2082–2083, 2086–2087, 2095–2096, 8080, 8443, 8880.")
-                            .font(.system(.caption2, design: .monospaced))
-                            .foregroundStyle(.orange)
-                            .padding(8)
-                            .background(Color.orange.opacity(0.1))
-                            .cornerRadius(6)
-                    }
-                    portScanResultsCard(viewModel.portScanResults)
+            SectionTitleView(title: "Network")
 
-                    DisclosureGroup("Custom Ports", isExpanded: $customPortsExpanded) {
-                        VStack(alignment: .leading, spacing: 10) {
-                            TextField("8888, 9000, 27017", text: $customPortInput)
+            CardView {
+                Text("Reachability")
+                    .font(.system(.subheadline, design: .monospaced))
+                    .fontWeight(.semibold)
+                    .foregroundStyle(.cyan)
+                if reachabilityLoading {
+                    ProgressView("Checking ports…")
+                } else if let reachabilityError {
+                    MessageRowView(text: reachabilityError, isError: true)
+                } else {
+                    ForEach(reachabilityRows) { row in
+                        HStack {
+                            Text(row.portLabel)
                                 .font(.system(.caption, design: .monospaced))
-                                .textInputAutocapitalization(.never)
-                                .autocorrectionDisabled()
-                                .keyboardType(.numberPad)
-                                .padding(10)
-                                .background(Color(.systemGray6).opacity(0.5))
-                                .cornerRadius(6)
-
-                            Button("Scan") {
-                                let ports = parsedCustomPorts(from: customPortInput)
-                                Task {
-                                    await viewModel.runCustomPortScan(ports: ports)
-                                }
-                            }
-                            .buttonStyle(.borderedProminent)
-                            .tint(.blue)
-                            .disabled(viewModel.customPortScanLoading)
-
-                            if viewModel.customPortScanLoading {
-                                ProgressView("Scanning custom ports…")
-                                    .font(.system(.caption, design: .monospaced))
-                            } else if let error = viewModel.customPortScanError {
-                                errorLabel(error)
-                            } else if !viewModel.customPortResults.isEmpty {
-                                portScanResultsCard(viewModel.customPortResults)
-                            }
+                            Spacer()
+                            Text(row.latencyLabel)
+                                .font(.system(.caption2, design: .monospaced))
+                                .foregroundStyle(.secondary)
+                            Text(row.statusLabel)
+                                .font(.system(.caption, design: .monospaced))
+                                .foregroundStyle(ResultColors.color(for: row.statusTone))
                         }
-                        .padding(.top, 8)
                     }
-                    .font(.system(.caption, design: .monospaced))
-                    .tint(.secondary)
                 }
-                .padding(10)
-                .background(Color(.systemGray6).opacity(0.5))
-                .cornerRadius(6)
             }
-        }
-        .padding(.top, 16)
-    }
 
-    // MARK: - Helpers
+            CardView {
+                Text("Location")
+                    .font(.system(.subheadline, design: .monospaced))
+                    .fontWeight(.semibold)
+                    .foregroundStyle(.cyan)
+                if geolocationLoading {
+                    ProgressView("Looking up location…")
+                } else if let geolocationError, geolocation == nil {
+                    MessageRowView(text: geolocationError, isError: geolocationError != "No A record available")
+                } else if let geolocation {
+                    ForEach(locationRows) { row in
+                        LabeledValueRow(row: row)
+                    }
+                    if let latitude = geolocation.latitude, let longitude = geolocation.longitude {
+                        let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
+                        Map(initialPosition: .region(MKCoordinateRegion(
+                            center: coordinate,
+                            span: MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1)
+                        ))) {
+                            Marker(geolocation.ip, coordinate: coordinate)
+                        }
+                        .mapStyle(.standard)
+                        .frame(height: 180)
+                        .cornerRadius(8)
+                    }
+                } else {
+                    MessageRowView(text: "No location data available", isError: false)
+                }
+            }
 
-    private func sectionHeader(_ title: String) -> some View {
-        Text(title)
-            .font(.system(.headline, design: .default))
-            .foregroundStyle(.white)
-    }
+            CardView {
+                Text("Port Scan")
+                    .font(.system(.subheadline, design: .monospaced))
+                    .fontWeight(.semibold)
+                    .foregroundStyle(.cyan)
 
-    private var dnssecStatus: Bool? {
-        viewModel.dnsSections.compactMap(\.dnssecSigned).first
-    }
+                if isCloudflareProxied {
+                    Text("Domain is behind Cloudflare's proxy. Results reflect the edge, not the origin.")
+                        .font(.system(.caption2, design: .monospaced))
+                        .foregroundStyle(.orange)
+                }
 
-    private func certRow(_ label: String, _ value: String) -> some View {
-        VStack(alignment: .leading, spacing: 2) {
-            Text(label)
-                .font(.system(.caption2, design: .monospaced))
-                .foregroundStyle(.secondary)
-            Text(value)
-                .font(.system(.caption, design: .monospaced))
-                .textSelection(.enabled)
-        }
-    }
+                if portScanLoading {
+                    ProgressView("Scanning ports…")
+                } else if let portScanError, standardPortRows.isEmpty {
+                    MessageRowView(text: portScanError, isError: true)
+                } else {
+                    Text("Standard Ports")
+                        .font(.system(.caption, design: .monospaced))
+                        .foregroundStyle(.secondary)
+                    PortRowsView(rows: standardPortRows)
+                }
 
-    private var hstsLoadingRow: some View {
-        VStack(alignment: .leading, spacing: 2) {
-            Text("HSTS Preload")
-                .font(.system(.caption2, design: .monospaced))
-                .foregroundStyle(.secondary)
-            ProgressView()
-                .controlSize(.small)
-        }
-    }
+                DisclosureGroup("Custom Ports", isExpanded: $customPortsExpanded) {
+                    VStack(alignment: .leading, spacing: 10) {
+                        TextField("8888, 9000, 27017", text: $customPortInput)
+                            .font(.system(.caption, design: .monospaced))
+                            .textInputAutocapitalization(.never)
+                            .autocorrectionDisabled()
+                            .keyboardType(.numberPad)
+                            .padding(10)
+                            .background(Color(.systemGray6).opacity(0.5))
+                            .cornerRadius(6)
 
-    private func hstsStatusRow(_ isPreloaded: Bool) -> some View {
-        VStack(alignment: .leading, spacing: 2) {
-            Text("HSTS Preload")
-                .font(.system(.caption2, design: .monospaced))
-                .foregroundStyle(.secondary)
-            Text(isPreloaded ? "Preloaded" : "Not preloaded")
+                        Button("Scan") {
+                            onScanCustomPorts()
+                        }
+                        .buttonStyle(.borderedProminent)
+                        .disabled(customPortScanLoading)
+
+                        if customPortScanLoading {
+                            ProgressView("Scanning custom ports…")
+                        } else if let customPortScanError {
+                            MessageRowView(text: customPortScanError, isError: true)
+                        } else {
+                            PortRowsView(rows: customPortRows)
+                        }
+                    }
+                    .padding(.top, 8)
+                }
                 .font(.system(.caption, design: .monospaced))
-                .foregroundStyle(isPreloaded ? .green : .secondary)
-        }
-    }
-
-    private func horizontallyScrollableCard<Content: View>(
-        spacing: CGFloat = 4,
-        @ViewBuilder content: () -> Content
-    ) -> some View {
-        horizontallyScrollableContent(spacing: spacing) {
-            content()
-        }
-        .padding(10)
-        .background(Color(.systemGray6).opacity(0.5))
-        .cornerRadius(6)
-    }
-
-    private func horizontallyScrollableContent<Content: View>(
-        spacing: CGFloat = 4,
-        @ViewBuilder content: () -> Content
-    ) -> some View {
-        ScrollView(.horizontal) {
-            VStack(alignment: .leading, spacing: spacing) {
-                content()
+                .tint(.secondary)
             }
-            .scrollTargetLayout()
         }
-        .scrollBounceBehavior(.basedOnSize, axes: .horizontal)
-        .frame(maxWidth: .infinity, alignment: .leading)
     }
+}
 
-    private func errorLabel(_ message: String) -> some View {
-        Label(message, systemImage: "exclamationmark.triangle.fill")
-            .font(.system(.caption, design: .monospaced))
-            .foregroundStyle(.red)
-            .padding(8)
-    }
+struct PortRowsView: View {
+    let rows: [PortScanRowViewData]
 
-    private func portScanResultsCard(_ results: [PortScanResult]) -> some View {
-        VStack(alignment: .leading, spacing: 4) {
-            ForEach(results) { result in
+    var body: some View {
+        if rows.isEmpty {
+            MessageRowView(text: "No results", isError: false)
+        } else {
+            ForEach(rows) { row in
                 VStack(alignment: .leading, spacing: 2) {
-                    HStack(spacing: 8) {
-                        Circle()
-                            .fill(result.open ? Color.green : Color(.systemGray4))
-                            .frame(width: 8, height: 8)
-                        Text("\(result.port)")
+                    HStack {
+                        Text(row.portLabel)
                             .font(.system(.caption, design: .monospaced))
-                            .lineLimit(1)
                             .frame(width: 52, alignment: .leading)
-                        Text(result.service)
+                        Text(row.service)
                             .font(.system(.caption, design: .monospaced))
-                            .foregroundStyle(result.open ? .primary : .secondary)
+                            .foregroundStyle(.primary)
                         Spacer()
-                        if result.open {
-                            Text("Open")
+                        if let durationLabel = row.durationLabel {
+                            Text(durationLabel)
                                 .font(.system(.caption2, design: .monospaced))
-                                .foregroundStyle(.green)
+                                .foregroundStyle(.secondary)
                         }
+                        Text(row.statusLabel)
+                            .font(.system(.caption2, design: .monospaced))
+                            .foregroundStyle(ResultColors.color(for: row.statusTone))
                     }
-
-                    if let banner = result.banner {
+                    if let banner = row.banner {
                         Text(banner)
                             .font(.system(.caption2, design: .monospaced))
                             .foregroundStyle(.secondary)
-                            .lineLimit(1)
-                            .padding(.leading, 16)
+                            .padding(.leading, 8)
                     }
                 }
             }
         }
     }
+}
 
-    private func parsedCustomPorts(from input: String) -> [UInt16] {
-        let parts = input.split(separator: ",", omittingEmptySubsequences: true)
-        var seen = Set<UInt16>()
-        var ports: [UInt16] = []
-
-        for part in parts {
-            let trimmed = part.trimmingCharacters(in: .whitespacesAndNewlines)
-            guard let value = UInt16(trimmed), seen.insert(value).inserted else {
-                continue
-            }
-            ports.append(value)
-            if ports.count == 20 {
-                break
-            }
-        }
+struct SectionTitleView: View {
+    let title: String
 
-        return ports
+    var body: some View {
+        Text(title)
+            .font(.system(.headline))
+            .foregroundStyle(.white)
     }
+}
 
-    private var httpStatusSummaryParts: [(text: String, color: Color)] {
-        var parts: [(text: String, color: Color)] = []
+struct CardView<Content: View>: View {
+    let content: Content
 
-        if let statusCode = viewModel.httpStatusCode {
-            parts.append(("HTTP \(statusCode)", .cyan))
-        }
-        if let responseTimeMs = viewModel.httpResponseTimeMs {
-            parts.append(("\(responseTimeMs)ms", .secondary))
-        }
-        if let httpProtocol = viewModel.httpProtocol {
-            parts.append((httpProtocol, .secondary))
-        }
+    init(@ViewBuilder content: () -> Content) {
+        self.content = content()
+    }
 
-        return parts
+    var body: some View {
+        ScrollView(.horizontal) {
+            VStack(alignment: .leading, spacing: 6) {
+                content
+            }
+            .scrollTargetLayout()
+        }
+        .scrollBounceBehavior(.basedOnSize, axes: .horizontal)
+        .frame(maxWidth: .infinity, alignment: .leading)
+        .padding(10)
+        .background(Color(.systemGray6).opacity(0.5))
+        .cornerRadius(6)
     }
+}
 
-    private var http3AvailabilityNote: String? {
-        guard viewModel.http3Advertised, viewModel.httpProtocol != "HTTP/3" else {
-            return nil
+struct LoadingCardView: View {
+    let text: String
+
+    var body: some View {
+        CardView {
+            ProgressView(text)
+                .frame(maxWidth: .infinity, alignment: .center)
         }
-        return "(HTTP/3 available)"
     }
+}
+
+struct MessageCardView: View {
+    let text: String
+    let isError: Bool
 
-    private func httpSecurityGradeColor(for grade: String) -> Color {
-        switch grade {
-        case "A", "B":
-            .green
-        case "C":
-            .yellow
-        case "D", "F":
-            .red
-        default:
-            .secondary
+    var body: some View {
+        CardView {
+            MessageRowView(text: text, isError: isError)
         }
     }
+}
+
+struct MessageRowView: View {
+    let text: String
+    let isError: Bool
 
-    private func expiryColor(_ days: Int) -> Color {
-        if days < 30 { return .red }
-        if days < 60 { return .yellow }
-        return .green
+    var body: some View {
+        Label(text, systemImage: isError ? "exclamationmark.triangle.fill" : "info.circle")
+            .font(.system(.caption, design: .monospaced))
+            .foregroundStyle(isError ? .red : .secondary)
     }
+}
 
-    private func shareResults() {
-        let text = viewModel.exportText()
-        let dateFmt = DateFormatter()
-        dateFmt.dateFormat = "yyyyMMdd_HHmmss"
-        let timestamp = dateFmt.string(from: Date())
-        let filename = "\(timestamp)_domaindigresults.txt"
-        let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(filename)
+struct LabeledValueRow: View {
+    let row: InfoRowViewData
 
-        do {
-            try text.write(to: tempURL, atomically: true, encoding: .utf8)
-        } catch {
-            return
+    var body: some View {
+        VStack(alignment: .leading, spacing: 2) {
+            Text(row.label)
+                .font(.system(.caption2, design: .monospaced))
+                .foregroundStyle(.secondary)
+            Text(row.value)
+                .font(.system(.caption, design: .monospaced))
+                .foregroundStyle(ResultColors.color(for: row.tone))
+                .textSelection(.enabled)
         }
+    }
+}
 
-        let activityVC = UIActivityViewController(activityItems: [tempURL], applicationActivities: nil)
-        guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
-              let rootVC = windowScene.keyWindow?.rootViewController else { return }
-        var presenter = rootVC
-        while let presented = presenter.presentedViewController {
-            presenter = presented
+enum ResultColors {
+    static func color(for tone: ResultTone) -> Color {
+        switch tone {
+        case .primary:
+            return .primary
+        case .secondary:
+            return .secondary
+        case .success:
+            return .green
+        case .warning:
+            return .yellow
+        case .failure:
+            return .red
         }
-        activityVC.popoverPresentationController?.sourceView = presenter.view
-        presenter.present(activityVC, animated: true)
     }
 }
 
 extension DateFormatter {
     static let certDate: DateFormatter = {
-        let f = DateFormatter()
-        f.dateStyle = .medium
-        f.timeStyle = .short
-        return f
+        let formatter = DateFormatter()
+        formatter.dateStyle = .medium
+        formatter.timeStyle = .short
+        return formatter
     }()
 }
 
@@ -948,10 +811,7 @@ private struct SettingsView: View {
         guard resolverOption == .custom else {
             return nil
         }
-
-        return DNSResolverOption.isValidCustomURL(customResolverURL)
-            ? nil
-            : "Resolver URL must start with https://"
+        return DNSResolverOption.isValidCustomURL(customResolverURL) ? nil : "Resolver URL must start with https://"
     }
 
     var body: some View {
@@ -981,9 +841,7 @@ private struct SettingsView: View {
         .onAppear {
             let currentResolverURL = storedResolverURL.trimmingCharacters(in: .whitespacesAndNewlines)
             resolverOption = DNSResolverOption.option(for: currentResolverURL)
-            customResolverURL = resolverOption == .custom
-                ? currentResolverURL
-                : DNSResolverOption.defaultURLString
+            customResolverURL = resolverOption == .custom ? currentResolverURL : DNSResolverOption.defaultURLString
         }
         .onChange(of: resolverOption) { _, newValue in
             guard let presetURL = newValue.urlString else {
@@ -993,9 +851,7 @@ private struct SettingsView: View {
             storedResolverURL = presetURL
         }
         .onChange(of: customResolverURL) { _, newValue in
-            guard resolverOption == .custom else {
-                return
-            }
+            guard resolverOption == .custom else { return }
             storedResolverURL = newValue.trimmingCharacters(in: .whitespacesAndNewlines)
         }
     }
diff --git a/DomainDig/DNSLookupService.swift b/DomainDig/DNSLookupService.swift
index a59126f..8c8d53c 100644
--- a/DomainDig/DNSLookupService.swift
+++ b/DomainDig/DNSLookupService.swift
@@ -57,8 +57,6 @@ enum DNSResolverOption: String, CaseIterable, Identifiable {
 }
 
 struct DNSLookupService {
-    private static let rrsigQueryType = 46
-    private static let dnskeyQueryType = 48
     private static let internetClass = 1
 
     static func lookup(domain: String, recordType: DNSRecordType) async throws -> [DNSRecord] {
@@ -93,7 +91,7 @@ struct DNSLookupService {
             }
     }
 
-    static func lookupAll(domain: String) async -> [DNSSection] {
+    static func lookupAll(domain: String) async -> ServiceResult<[DNSSection]> {
         typealias Result = (
             type: DNSRecordType,
             records: [DNSRecord],
@@ -109,8 +107,11 @@ struct DNSLookupService {
             resolverURLString: resolverURLString
         )
 
-        return await withTaskGroup(of: Result.self, returning: [DNSSection].self) { group in
+        let sections = await withTaskGroup(of: Result.self, returning: [DNSSection].self) { group in
             for recordType in DNSRecordType.allCases {
+                guard recordType != .PTR else {
+                    continue
+                }
                 let shouldQueryWildcard = wildcardTypes.contains(recordType)
                 group.addTask {
                     var apexRecords: [DNSRecord] = []
@@ -159,6 +160,12 @@ struct DNSLookupService {
                 (order.firstIndex(of: a.recordType) ?? 0) < (order.firstIndex(of: b.recordType) ?? 0)
             }
         }
+
+        if sections.contains(where: { !$0.records.isEmpty || !$0.wildcardRecords.isEmpty || $0.error != nil }) {
+            return .success(sections)
+        }
+
+        return .empty("No DNS records found")
     }
 
     private static func lookupResponse(
@@ -218,11 +225,17 @@ struct DNSLookupService {
         return response.authenticatedData
     }
 
-    private static func currentResolverURLString() -> String {
+    static func currentResolverURLString() -> String {
         let storedValue = UserDefaults.standard.string(forKey: DNSResolverOption.userDefaultsKey)
         return DNSResolverOption.resolvedURLString(from: storedValue)
     }
 
+    static func currentResolverDisplayName() -> String {
+        let resolverURLString = currentResolverURLString()
+        let option = DNSResolverOption.option(for: resolverURLString)
+        return option == .custom ? resolverURLString : option.title
+    }
+
     private static func validatedResolverURL(from urlString: String) throws -> URL {
         guard let url = URL(string: urlString) else {
             throw URLError(.badURL)
diff --git a/DomainDig/DomainViewModel.swift b/DomainDig/DomainViewModel.swift
index 314703f..638a96b 100644
--- a/DomainDig/DomainViewModel.swift
+++ b/DomainDig/DomainViewModel.swift
@@ -1,24 +1,162 @@
 import Foundation
 import SwiftUI
 
+enum ResultTone {
+    case primary
+    case secondary
+    case success
+    case warning
+    case failure
+}
+
+struct SummaryFieldViewData: Identifiable {
+    let id = UUID()
+    let label: String
+    let value: String
+    let tone: ResultTone
+}
+
+struct InfoRowViewData: Identifiable {
+    let id = UUID()
+    let label: String
+    let value: String
+    let tone: ResultTone
+}
+
+struct SectionMessageViewData {
+    let text: String
+    let isError: Bool
+}
+
+struct DNSRecordSectionViewData: Identifiable {
+    let id = UUID()
+    let title: String
+    let rows: [InfoRowViewData]
+    let wildcardRows: [InfoRowViewData]
+    let wildcardTitle: String?
+    let message: SectionMessageViewData?
+}
+
+struct EmailRowViewData: Identifiable {
+    let id = UUID()
+    let label: String
+    let status: String
+    let statusTone: ResultTone
+    let detail: String
+    let auxiliaryDetail: String?
+}
+
+struct RedirectHopViewData: Identifiable {
+    let id = UUID()
+    let stepLabel: String
+    let statusCode: String
+    let url: String
+    let isFinal: Bool
+}
+
+struct ReachabilityRowViewData: Identifiable {
+    let id = UUID()
+    let portLabel: String
+    let latencyLabel: String
+    let statusLabel: String
+    let statusTone: ResultTone
+}
+
+struct PortScanRowViewData: Identifiable {
+    let id = UUID()
+    let portLabel: String
+    let service: String
+    let statusLabel: String
+    let statusTone: ResultTone
+    let banner: String?
+    let durationLabel: String?
+}
+
+struct LookupSnapshot {
+    let domain: String
+    let timestamp: Date
+    let resolverDisplayName: String
+    let resolverURLString: String
+    let totalLookupDurationMs: Int?
+    let dnsSections: [DNSSection]
+    let dnsError: String?
+    let sslInfo: SSLCertificateInfo?
+    let sslError: String?
+    let hstsPreloaded: Bool?
+    let httpHeaders: [HTTPHeader]
+    let httpSecurityGrade: String?
+    let httpStatusCode: Int?
+    let httpResponseTimeMs: Int?
+    let httpProtocol: String?
+    let http3Advertised: Bool
+    let httpHeadersError: String?
+    let reachabilityResults: [PortReachability]
+    let reachabilityError: String?
+    let ipGeolocation: IPGeolocation?
+    let ipGeolocationError: String?
+    let emailSecurity: EmailSecurityResult?
+    let emailSecurityError: String?
+    let ptrRecord: String?
+    let ptrError: String?
+    let redirectChain: [RedirectHop]
+    let redirectChainError: String?
+    let portScanResults: [PortScanResult]
+    let portScanError: String?
+    let isLive: Bool
+}
+
+extension HistoryEntry {
+    var snapshot: LookupSnapshot {
+        LookupSnapshot(
+            domain: domain,
+            timestamp: timestamp,
+            resolverDisplayName: resolverDisplayName,
+            resolverURLString: resolverURLString,
+            totalLookupDurationMs: totalLookupDurationMs,
+            dnsSections: dnsSections,
+            dnsError: nil,
+            sslInfo: sslInfo,
+            sslError: sslError,
+            hstsPreloaded: hstsPreloaded,
+            httpHeaders: httpHeaders,
+            httpSecurityGrade: HTTPSecurityGrade.grade(for: httpHeaders).rawValue,
+            httpStatusCode: nil,
+            httpResponseTimeMs: nil,
+            httpProtocol: nil,
+            http3Advertised: false,
+            httpHeadersError: httpHeadersError,
+            reachabilityResults: reachabilityResults,
+            reachabilityError: reachabilityError,
+            ipGeolocation: ipGeolocation,
+            ipGeolocationError: ipGeolocationError,
+            emailSecurity: emailSecurity,
+            emailSecurityError: emailSecurityError,
+            ptrRecord: ptrRecord,
+            ptrError: ptrError,
+            redirectChain: redirectChain,
+            redirectChainError: redirectChainError,
+            portScanResults: portScanResults,
+            portScanError: portScanError,
+            isLive: false
+        )
+    }
+}
+
 @MainActor
 @Observable
 final class DomainViewModel {
     var domain: String = ""
 
-    // DNS
     var dnsSections: [DNSSection] = []
     var dnsLoading = false
     var dnsError: String?
 
-    // SSL
     var sslInfo: SSLCertificateInfo?
     var sslLoading = false
     var sslError: String?
     var hstsPreloaded: Bool?
     var hstsLoading = false
 
-    // HTTP Headers
     var httpHeaders: [HTTPHeader] = []
     var httpSecurityGrade: String?
     var httpStatusCode: Int?
@@ -28,32 +166,26 @@ final class DomainViewModel {
     var httpHeadersLoading = false
     var httpHeadersError: String?
 
-    // Reachability
     var reachabilityResults: [PortReachability] = []
     var reachabilityLoading = false
     var reachabilityError: String?
 
-    // IP Geolocation
     var ipGeolocation: IPGeolocation?
     var ipGeolocationLoading = false
     var ipGeolocationError: String?
 
-    // Email Security
     var emailSecurity: EmailSecurityResult?
     var emailSecurityLoading = false
     var emailSecurityError: String?
 
-    // PTR / Reverse DNS
     var ptrRecord: String?
     var ptrLoading = false
     var ptrError: String?
 
-    // Redirect Chain
     var redirectChain: [RedirectHop] = []
     var redirectChainLoading = false
     var redirectChainError: String?
 
-    // Port Scan
     var portScanResults: [PortScanResult] = []
     var portScanLoading = false
     var portScanError: String?
@@ -63,368 +195,494 @@ final class DomainViewModel {
 
     var hasRun = false
     private(set) var searchedDomain: String = ""
+    private(set) var lastLookupDurationMs: Int?
 
-    // MARK: - Recent Searches
+    private var lookupTask: Task<Void, Never>?
+    private var customPortScanTask: Task<Void, Never>?
+    private var activeLookupID = UUID()
+    private var lookupStartedAt: Date?
 
     private static let recentSearchesKey = "recentSearches"
     private static let maxRecent = 20
-
     var recentSearches: [String] = UserDefaults.standard.stringArray(forKey: recentSearchesKey) ?? []
 
-    // MARK: - Saved Domains
-
     private static let savedDomainsKey = "savedDomains"
-
     var savedDomains: [String] = UserDefaults.standard.stringArray(forKey: savedDomainsKey) ?? []
 
-    var isCurrentDomainSaved: Bool {
-        !searchedDomain.isEmpty && savedDomains.contains(where: { $0.lowercased() == searchedDomain.lowercased() })
+    private static let historyKey = "lookupHistory"
+    private static let maxHistory = 50
+    var history: [HistoryEntry] = {
+        guard let data = UserDefaults.standard.data(forKey: historyKey),
+              let entries = try? JSONDecoder().decode([HistoryEntry].self, from: data) else {
+            return []
+        }
+        return entries
+    }()
+
+    var trimmedDomain: String {
+        domain
+            .trimmingCharacters(in: .whitespacesAndNewlines)
+            .replacingOccurrences(of: "https://", with: "")
+            .replacingOccurrences(of: "http://", with: "")
+            .components(separatedBy: "/").first ?? ""
     }
 
-    func toggleSavedDomain() {
-        if isCurrentDomainSaved {
-            savedDomains.removeAll { $0.lowercased() == searchedDomain.lowercased() }
-        } else {
-            savedDomains.append(searchedDomain)
-        }
-        UserDefaults.standard.set(savedDomains, forKey: Self.savedDomainsKey)
+    var resultsLoaded: Bool {
+        hasRun &&
+            !dnsLoading &&
+            !sslLoading &&
+            !hstsLoading &&
+            !httpHeadersLoading &&
+            !reachabilityLoading &&
+            !ipGeolocationLoading &&
+            !emailSecurityLoading &&
+            !ptrLoading &&
+            !redirectChainLoading &&
+            !portScanLoading &&
+            !customPortScanLoading
     }
 
-    func removeSavedDomain(_ domain: String) {
-        savedDomains.removeAll { $0 == domain }
-        UserDefaults.standard.set(savedDomains, forKey: Self.savedDomainsKey)
+    var isCloudflareProxied: Bool {
+        httpHeaders.contains { $0.name.lowercased() == "cf-ray" }
     }
 
-    func removeSavedDomains(at offsets: IndexSet) {
-        savedDomains.remove(atOffsets: offsets)
-        UserDefaults.standard.set(savedDomains, forKey: Self.savedDomainsKey)
+    var isCurrentDomainSaved: Bool {
+        !searchedDomain.isEmpty && savedDomains.contains(where: { $0.lowercased() == searchedDomain.lowercased() })
     }
 
-    // MARK: - History
+    var resolverDisplayName: String {
+        DNSLookupService.currentResolverDisplayName()
+    }
 
-    private static let historyKey = "lookupHistory"
-    private static let maxHistory = 50
+    var resolverURLString: String {
+        DNSLookupService.currentResolverURLString()
+    }
 
-    var history: [HistoryEntry] = {
-        guard let data = UserDefaults.standard.data(forKey: "lookupHistory"),
-              let entries = try? JSONDecoder().decode([HistoryEntry].self, from: data) else {
-            return []
+    var allPortScanResults: [PortScanResult] {
+        (portScanResults + customPortResults).sorted {
+            if $0.kind == $1.kind {
+                return $0.port < $1.port
+            }
+            return $0.kind == .standard
         }
-        return entries
-    }()
+    }
 
-    private func saveHistoryEntry() {
-        let entry = HistoryEntry(
+    var currentSnapshot: LookupSnapshot {
+        LookupSnapshot(
             domain: searchedDomain,
             timestamp: Date(),
+            resolverDisplayName: resolverDisplayName,
+            resolverURLString: resolverURLString,
+            totalLookupDurationMs: lastLookupDurationMs,
             dnsSections: dnsSections,
+            dnsError: dnsError,
             sslInfo: sslInfo,
+            sslError: sslError,
+            hstsPreloaded: hstsPreloaded,
             httpHeaders: httpHeaders,
+            httpSecurityGrade: httpSecurityGrade,
+            httpStatusCode: httpStatusCode,
+            httpResponseTimeMs: httpResponseTimeMs,
+            httpProtocol: httpProtocol,
+            http3Advertised: http3Advertised,
+            httpHeadersError: httpHeadersError,
             reachabilityResults: reachabilityResults,
+            reachabilityError: reachabilityError,
             ipGeolocation: ipGeolocation,
+            ipGeolocationError: ipGeolocationError,
             emailSecurity: emailSecurity,
-            mtaSts: emailSecurity?.mtaSts,
+            emailSecurityError: emailSecurityError,
             ptrRecord: ptrRecord,
+            ptrError: ptrError,
             redirectChain: redirectChain,
-            portScanResults: portScanResults,
-            hstsPreloaded: hstsPreloaded
+            redirectChainError: redirectChainError,
+            portScanResults: allPortScanResults,
+            portScanError: combinedPortScanError,
+            isLive: true
         )
-        history.insert(entry, at: 0)
-        if history.count > Self.maxHistory {
-            history = Array(history.prefix(Self.maxHistory))
-        }
-        if let data = try? JSONEncoder().encode(history) {
-            UserDefaults.standard.set(data, forKey: Self.historyKey)
-        }
     }
 
-    func removeHistoryEntries(at offsets: IndexSet) {
-        history.remove(atOffsets: offsets)
-        if let data = try? JSONEncoder().encode(history) {
-            UserDefaults.standard.set(data, forKey: Self.historyKey)
-        }
+    var summaryFields: [SummaryFieldViewData] {
+        Self.summaryFields(from: currentSnapshot)
     }
 
-    // MARK: - Computed
+    var domainRows: [InfoRowViewData] {
+        Self.domainRows(from: currentSnapshot)
+    }
 
-    var trimmedDomain: String {
-        domain
-            .trimmingCharacters(in: .whitespacesAndNewlines)
-            .replacingOccurrences(of: "https://", with: "")
-            .replacingOccurrences(of: "http://", with: "")
-            .components(separatedBy: "/").first ?? ""
+    var dnsRows: [DNSRecordSectionViewData] {
+        Self.dnsRows(from: currentSnapshot)
     }
 
-    /// True when all lookups have finished (regardless of success/failure).
-    var resultsLoaded: Bool {
-        hasRun && !dnsLoading && !sslLoading && !hstsLoading && !httpHeadersLoading && !reachabilityLoading
-            && !ipGeolocationLoading && !emailSecurityLoading && !ptrLoading
-            && !redirectChainLoading && !portScanLoading
+    var dnssecLabel: String? {
+        Self.dnssecLabel(from: currentSnapshot)
     }
 
-    /// True when response headers indicate the domain is behind Cloudflare's proxy.
-    /// Cloudflare injects cf-ray on all proxied (orange-cloud) responses. Grey-cloud
-    /// (DNS-only) domains won't have this header because traffic doesn't pass through CF's edge.
-    var isCloudflareProxied: Bool {
-        httpHeaders.contains { $0.name.lowercased() == "cf-ray" }
+    var ptrMessage: SectionMessageViewData? {
+        Self.ptrMessage(from: currentSnapshot)
+    }
+
+    var webCertificateRows: [InfoRowViewData] {
+        Self.webCertificateRows(from: currentSnapshot)
+    }
+
+    var webResponseRows: [InfoRowViewData] {
+        Self.webResponseRows(from: currentSnapshot)
     }
 
-    // MARK: - Reset
+    var redirectRows: [RedirectHopViewData] {
+        Self.redirectRows(from: currentSnapshot)
+    }
+
+    var emailRows: [EmailRowViewData] {
+        Self.emailRows(from: currentSnapshot)
+    }
+
+    var reachabilityRows: [ReachabilityRowViewData] {
+        Self.reachabilityRows(from: currentSnapshot)
+    }
+
+    var locationRows: [InfoRowViewData] {
+        Self.locationRows(from: currentSnapshot)
+    }
+
+    var standardPortRows: [PortScanRowViewData] {
+        Self.portRows(from: currentSnapshot, kind: .standard)
+    }
+
+    var customPortRows: [PortScanRowViewData] {
+        Self.portRows(from: currentSnapshot, kind: .custom)
+    }
+
+    var combinedPortScanError: String? {
+        [portScanError, customPortScanError].compactMap { $0 }.joined(separator: "\n").nilIfEmpty
+    }
+
+    func toggleSavedDomain() {
+        if isCurrentDomainSaved {
+            savedDomains.removeAll { $0.lowercased() == searchedDomain.lowercased() }
+        } else {
+            savedDomains.append(searchedDomain)
+        }
+        UserDefaults.standard.set(savedDomains, forKey: Self.savedDomainsKey)
+    }
+
+    func removeSavedDomains(at offsets: IndexSet) {
+        savedDomains.remove(atOffsets: offsets)
+        UserDefaults.standard.set(savedDomains, forKey: Self.savedDomainsKey)
+    }
+
+    func removeHistoryEntries(at offsets: IndexSet) {
+        history.remove(atOffsets: offsets)
+        persistHistory()
+    }
+
+    func clearRecentSearches() {
+        recentSearches.removeAll()
+        UserDefaults.standard.removeObject(forKey: Self.recentSearchesKey)
+    }
+
+    func rerunLookup(from entry: HistoryEntry) {
+        UserDefaults.standard.set(entry.resolverURLString, forKey: DNSResolverOption.userDefaultsKey)
+        domain = entry.domain
+        run()
+    }
 
     func reset() {
+        lookupTask?.cancel()
+        customPortScanTask?.cancel()
         hasRun = false
         searchedDomain = ""
-        dnsSections = []
-        dnsError = nil
-        dnsLoading = false
-        sslInfo = nil
-        sslError = nil
-        sslLoading = false
-        hstsPreloaded = nil
-        hstsLoading = false
-        httpHeaders = []
-        httpSecurityGrade = nil
-        httpStatusCode = nil
-        httpResponseTimeMs = nil
-        httpProtocol = nil
-        http3Advertised = false
-        httpHeadersError = nil
-        httpHeadersLoading = false
-        reachabilityResults = []
-        reachabilityError = nil
-        reachabilityLoading = false
-        ipGeolocation = nil
-        ipGeolocationError = nil
-        ipGeolocationLoading = false
-        emailSecurity = nil
-        emailSecurityError = nil
-        emailSecurityLoading = false
-        ptrRecord = nil
-        ptrError = nil
-        ptrLoading = false
-        redirectChain = []
-        redirectChainError = nil
-        redirectChainLoading = false
-        portScanResults = []
-        portScanError = nil
-        portScanLoading = false
-        customPortResults = []
-        customPortScanError = nil
-        customPortScanLoading = false
+        lastLookupDurationMs = nil
+        clearLookupState()
     }
 
-    // MARK: - Run
-
     func run() {
         let target = trimmedDomain
         guard !target.isEmpty else { return }
 
+        lookupTask?.cancel()
+        customPortScanTask?.cancel()
+
+        let lookupID = UUID()
+        activeLookupID = lookupID
+        lookupStartedAt = Date()
+        lastLookupDurationMs = nil
         addRecentSearch(target)
         searchedDomain = target
         hasRun = true
+        clearLookupState()
+        setAllLoadingStates(true)
+        customPortScanLoading = false
 
-        // Reset all state
-        dnsSections = []
-        dnsError = nil
-        dnsLoading = true
-        sslInfo = nil
-        sslError = nil
-        sslLoading = true
-        hstsPreloaded = nil
-        hstsLoading = true
-        httpHeaders = []
-        httpSecurityGrade = nil
-        httpStatusCode = nil
-        httpResponseTimeMs = nil
-        httpProtocol = nil
-        http3Advertised = false
-        httpHeadersError = nil
-        httpHeadersLoading = true
-        reachabilityResults = []
-        reachabilityError = nil
-        reachabilityLoading = true
-        ipGeolocation = nil
-        ipGeolocationError = nil
-        ipGeolocationLoading = true
-        emailSecurity = nil
-        emailSecurityError = nil
-        emailSecurityLoading = true
-        ptrRecord = nil
-        ptrError = nil
-        ptrLoading = true
-        redirectChain = []
-        redirectChainError = nil
-        redirectChainLoading = true
-        portScanResults = []
-        portScanError = nil
-        portScanLoading = true
-        customPortResults = []
+        lookupTask = Task { [weak self] in
+            guard let self else { return }
+            await self.performLookup(domain: target, lookupID: lookupID)
+        }
+    }
+
+    func runCustomPortScan(ports: [UInt16]) async {
+        guard !searchedDomain.isEmpty else {
+            customPortScanError = "Run a domain lookup first"
+            return
+        }
+
+        guard !ports.isEmpty else {
+            customPortScanError = "Enter at least one valid port"
+            customPortResults = []
+            return
+        }
+
+        customPortScanTask?.cancel()
+        let domain = searchedDomain
+        let lookupID = activeLookupID
+
+        customPortScanLoading = true
         customPortScanError = nil
-        customPortScanLoading = false
+        customPortResults = []
 
-        Task {
-            await withTaskGroup(of: Void.self) { group in
-                // DNS → chained: email security, PTR, geolocation
-                group.addTask { @MainActor in
-                    await self.runDNS(domain: target)
-                    // These depend on DNS results and run in parallel after DNS
-                    await withTaskGroup(of: Void.self) { postDNS in
-                        postDNS.addTask { @MainActor in
-                            await self.runEmailSecurity(domain: target)
-                        }
-                        postDNS.addTask { @MainActor in
-                            await self.runReverseDNS()
-                        }
-                        postDNS.addTask { @MainActor in
-                            await self.runIPGeolocation()
-                        }
-                    }
-                }
-                group.addTask { @MainActor in
-                    await self.runSSL(domain: target)
-                }
-                group.addTask { @MainActor in
-                    await self.runHSTSPreload(domain: target)
-                }
-                group.addTask { @MainActor in
-                    await self.runHTTPHeaders(domain: target)
-                }
-                group.addTask { @MainActor in
-                    await self.runReachability(domain: target)
-                }
-                group.addTask { @MainActor in
-                    await self.runRedirectChain(domain: target)
-                }
-                group.addTask { @MainActor in
-                    await self.runPortScan(domain: target)
-                }
-            }
-            // Save history after all lookups complete so the snapshot is complete
-            saveHistoryEntry()
+        customPortScanTask = Task { [weak self] in
+            guard let self else { return }
+            let result = await PortScanService.scanPorts(domain: domain, ports: ports, timeout: 3.0)
+            guard !Task.isCancelled, self.isCurrentLookup(lookupID) else { return }
+            self.applyCustomPortResult(result)
         }
     }
 
-    // MARK: - Lookup Methods
+    func exportText() -> String {
+        Self.formatExportText(from: currentSnapshot)
+    }
+
+    private func performLookup(domain: String, lookupID: UUID) async {
+        await withTaskGroup(of: Void.self) { group in
+            group.addTask { await self.runDNS(domain: domain, lookupID: lookupID) }
+            group.addTask { await self.runSSL(domain: domain, lookupID: lookupID) }
+            group.addTask { await self.runHSTSPreload(domain: domain, lookupID: lookupID) }
+            group.addTask { await self.runHTTPHeaders(domain: domain, lookupID: lookupID) }
+            group.addTask { await self.runReachability(domain: domain, lookupID: lookupID) }
+            group.addTask { await self.runRedirectChain(domain: domain, lookupID: lookupID) }
+            group.addTask { await self.runPortScan(domain: domain, lookupID: lookupID) }
+        }
+
+        guard !Task.isCancelled, isCurrentLookup(lookupID) else { return }
+
+        let txtRecords = dnsSections.first(where: { $0.recordType == .TXT })?.records ?? []
+        let primaryIP = primaryIPAddress(from: dnsSections)
+
+        await withTaskGroup(of: Void.self) { group in
+            group.addTask { await self.runEmailSecurity(domain: domain, txtRecords: txtRecords, lookupID: lookupID) }
+            if let primaryIP {
+                group.addTask { await self.runReverseDNS(ip: primaryIP, lookupID: lookupID) }
+                group.addTask { await self.runIPGeolocation(ip: primaryIP, lookupID: lookupID) }
+            } else {
+                group.addTask { await self.finishDependentWithoutPrimaryIP(lookupID: lookupID) }
+            }
+        }
+
+        guard !Task.isCancelled, isCurrentLookup(lookupID) else { return }
+        lastLookupDurationMs = lookupStartedAt.map { Int(Date().timeIntervalSince($0) * 1000) }
+        saveHistoryEntry(replaceLatest: false)
+    }
 
-    private func runDNS(domain: String) async {
-        do {
-            let sections = await DNSLookupService.lookupAll(domain: domain)
+    private func runDNS(domain: String, lookupID: UUID) async {
+        let result = await DNSLookupService.lookupAll(domain: domain)
+        guard !Task.isCancelled, isCurrentLookup(lookupID) else { return }
+        switch result {
+        case let .success(sections):
             dnsSections = sections
+            dnsError = nil
+        case let .empty(message):
+            dnsSections = []
+            dnsError = message
+        case let .error(message):
+            dnsSections = []
+            dnsError = message
         }
         dnsLoading = false
     }
 
-    private func runSSL(domain: String) async {
-        do {
-            let info = try await SSLCheckService.check(domain: domain)
+    private func runSSL(domain: String, lookupID: UUID) async {
+        let result = await SSLCheckService.check(domain: domain)
+        guard !Task.isCancelled, isCurrentLookup(lookupID) else { return }
+        switch result {
+        case let .success(info):
             sslInfo = info
-        } catch {
-            sslError = error.localizedDescription
+            sslError = nil
+        case let .empty(message):
+            sslInfo = nil
+            sslError = message
+        case let .error(message):
+            sslInfo = nil
+            sslError = message
         }
         sslLoading = false
     }
 
-    private func runHSTSPreload(domain: String) async {
-        hstsPreloaded = await SSLCheckService.checkHSTSPreload(domain: domain)
+    private func runHSTSPreload(domain: String, lookupID: UUID) async {
+        let result = await SSLCheckService.checkHSTSPreload(domain: domain)
+        guard !Task.isCancelled, isCurrentLookup(lookupID) else { return }
+        hstsPreloaded = result
         hstsLoading = false
     }
 
-    private func runHTTPHeaders(domain: String) async {
-        do {
-            let result = try await HTTPHeadersService.fetch(domain: domain)
-            httpHeaders = result.headers
-            httpSecurityGrade = HTTPSecurityGrade.grade(for: result.headers).rawValue
-            httpStatusCode = result.statusCode
-            httpResponseTimeMs = result.responseTimeMs
-            httpProtocol = result.httpProtocol
-            http3Advertised = result.http3Advertised
-        } catch {
-            httpHeadersError = error.localizedDescription
+    private func runHTTPHeaders(domain: String, lookupID: UUID) async {
+        let result = await HTTPHeadersService.fetch(domain: domain)
+        guard !Task.isCancelled, isCurrentLookup(lookupID) else { return }
+        switch result {
+        case let .success(headersResult):
+            httpHeaders = headersResult.headers
+            httpSecurityGrade = HTTPSecurityGrade.grade(for: headersResult.headers).rawValue
+            httpStatusCode = headersResult.statusCode
+            httpResponseTimeMs = headersResult.responseTimeMs
+            httpProtocol = headersResult.httpProtocol
+            http3Advertised = headersResult.http3Advertised
+            httpHeadersError = nil
+        case let .empty(message):
+            httpHeaders = []
+            httpSecurityGrade = nil
+            httpStatusCode = nil
+            httpResponseTimeMs = nil
+            httpProtocol = nil
+            http3Advertised = false
+            httpHeadersError = message
+        case let .error(message):
+            httpHeaders = []
+            httpSecurityGrade = nil
+            httpStatusCode = nil
+            httpResponseTimeMs = nil
+            httpProtocol = nil
+            http3Advertised = false
+            httpHeadersError = message
         }
         httpHeadersLoading = false
     }
 
-    private func runReachability(domain: String) async {
-        let results = await ReachabilityService.checkAll(domain: domain)
-        reachabilityResults = results
-        reachabilityLoading = false
-    }
-
-    private func runIPGeolocation() async {
-        // Find the first A record IP
-        guard let aSection = dnsSections.first(where: { $0.recordType == .A }),
-              let firstIP = aSection.records.first?.value else {
-            ipGeolocationError = "No A record available"
-            ipGeolocationLoading = false
-            return
+    private func runReachability(domain: String, lookupID: UUID) async {
+        let result = await ReachabilityService.checkAll(domain: domain)
+        guard !Task.isCancelled, isCurrentLookup(lookupID) else { return }
+        switch result {
+        case let .success(results):
+            reachabilityResults = results
+            reachabilityError = nil
+        case let .empty(message):
+            reachabilityResults = []
+            reachabilityError = message
+        case let .error(message):
+            reachabilityResults = []
+            reachabilityError = message
         }
-        do {
-            let geo = try await IPGeolocationService.lookup(ip: firstIP)
-            ipGeolocation = geo
-        } catch {
-            ipGeolocationError = error.localizedDescription
-        }
-        ipGeolocationLoading = false
+        reachabilityLoading = false
     }
 
-    private func runEmailSecurity(domain: String) async {
-        // Extract TXT records from already-fetched DNS sections
-        let txtRecords = dnsSections.first(where: { $0.recordType == .TXT })?.records ?? []
+    private func runEmailSecurity(domain: String, txtRecords: [DNSRecord], lookupID: UUID) async {
         let result = await EmailSecurityService.analyze(domain: domain, txtRecords: txtRecords)
-        emailSecurity = result
+        guard !Task.isCancelled, isCurrentLookup(lookupID) else { return }
+        switch result {
+        case let .success(emailResult):
+            emailSecurity = emailResult
+            emailSecurityError = nil
+        case let .empty(message):
+            emailSecurity = nil
+            emailSecurityError = message
+        case let .error(message):
+            emailSecurity = nil
+            emailSecurityError = message
+        }
         emailSecurityLoading = false
     }
 
-    private func runReverseDNS() async {
-        guard let aSection = dnsSections.first(where: { $0.recordType == .A }),
-              let firstIP = aSection.records.first?.value else {
-            ptrError = "No A record available"
-            ptrLoading = false
-            return
-        }
-        let result = await ReverseDNSService.lookup(ip: firstIP)
-        ptrRecord = result
-        if result == nil {
-            ptrError = "No PTR record found"
+    private func runReverseDNS(ip: String, lookupID: UUID) async {
+        let result = await ReverseDNSService.lookup(ip: ip, resolverURLString: resolverURLString)
+        guard !Task.isCancelled, isCurrentLookup(lookupID) else { return }
+        switch result {
+        case let .success(record):
+            ptrRecord = record
+            ptrError = nil
+        case let .empty(message):
+            ptrRecord = nil
+            ptrError = message
+        case let .error(message):
+            ptrRecord = nil
+            ptrError = message
         }
         ptrLoading = false
     }
 
-    private func runRedirectChain(domain: String) async {
-        do {
-            let hops = try await RedirectChainService.trace(domain: domain)
+    private func runRedirectChain(domain: String, lookupID: UUID) async {
+        let result = await RedirectChainService.trace(domain: domain)
+        guard !Task.isCancelled, isCurrentLookup(lookupID) else { return }
+        switch result {
+        case let .success(hops):
             redirectChain = hops
-        } catch {
-            redirectChainError = error.localizedDescription
+            redirectChainError = nil
+        case let .empty(message):
+            redirectChain = []
+            redirectChainError = message
+        case let .error(message):
+            redirectChain = []
+            redirectChainError = message
         }
         redirectChainLoading = false
     }
 
-    private func runPortScan(domain: String) async {
-        let results = await PortScanService.scanAll(domain: domain)
-        let enrichedResults = await enrichOpenPortBanners(in: results, domain: domain)
-        portScanResults = enrichedResults
+    private func runPortScan(domain: String, lookupID: UUID) async {
+        let result = await PortScanService.scanAll(domain: domain)
+        switch result {
+        case let .success(results):
+            let enrichedResults = await enrichOpenPortBanners(in: results, domain: domain)
+            guard !Task.isCancelled, isCurrentLookup(lookupID) else { return }
+            portScanResults = enrichedResults
+            portScanError = nil
+        case let .empty(message):
+            guard !Task.isCancelled, isCurrentLookup(lookupID) else { return }
+            portScanResults = []
+            portScanError = message
+        case let .error(message):
+            guard !Task.isCancelled, isCurrentLookup(lookupID) else { return }
+            portScanResults = []
+            portScanError = message
+        }
         portScanLoading = false
     }
 
-    func runCustomPortScan(ports: [UInt16]) async {
-        guard !searchedDomain.isEmpty else {
-            customPortScanError = "Run a domain lookup first"
-            return
+    private func runIPGeolocation(ip: String, lookupID: UUID) async {
+        let result = await IPGeolocationService.lookup(ip: ip)
+        guard !Task.isCancelled, isCurrentLookup(lookupID) else { return }
+        switch result {
+        case let .success(geolocation):
+            ipGeolocation = geolocation
+            ipGeolocationError = nil
+        case let .empty(message):
+            ipGeolocation = nil
+            ipGeolocationError = message
+        case let .error(message):
+            ipGeolocation = nil
+            ipGeolocationError = message
         }
+        ipGeolocationLoading = false
+    }
 
-        guard !ports.isEmpty else {
-            customPortScanError = "Enter at least one valid port"
+    private func finishDependentWithoutPrimaryIP(lookupID: UUID) async {
+        guard !Task.isCancelled, isCurrentLookup(lookupID) else { return }
+        ptrLoading = false
+        ptrError = "No A record available"
+        ipGeolocationLoading = false
+        ipGeolocationError = "No A record available"
+    }
+
+    private func applyCustomPortResult(_ result: ServiceResult<[PortScanResult]>) {
+        switch result {
+        case let .success(results):
+            customPortResults = results
+            customPortScanError = nil
+            saveHistoryEntry(replaceLatest: true)
+        case let .empty(message):
             customPortResults = []
-            return
+            customPortScanError = message
+        case let .error(message):
+            customPortResults = []
+            customPortScanError = message
         }
-
-        customPortScanLoading = true
-        customPortScanError = nil
-        customPortResults = []
-
-        let results = await PortScanService.scanPorts(domain: searchedDomain, ports: ports, timeout: 3.0)
-        customPortResults = results
         customPortScanLoading = false
     }
 
@@ -453,271 +711,502 @@ final class DomainViewModel {
         }
     }
 
-    // MARK: - Export
-
-    func exportText() -> String {
-        return Self.formatExportText(
+    private func saveHistoryEntry(replaceLatest: Bool) {
+        guard !searchedDomain.isEmpty else { return }
+        let entry = HistoryEntry(
             domain: searchedDomain,
-            date: Date(),
+            timestamp: Date(),
             dnsSections: dnsSections,
             sslInfo: sslInfo,
-            sslError: sslError,
-            hstsPreloaded: hstsPreloaded,
             httpHeaders: httpHeaders,
-            httpSecurityGrade: httpSecurityGrade,
-            httpStatusCode: httpStatusCode,
-            httpResponseTimeMs: httpResponseTimeMs,
-            httpProtocol: httpProtocol,
-            http3Advertised: http3Advertised,
-            httpHeadersError: httpHeadersError,
             reachabilityResults: reachabilityResults,
             ipGeolocation: ipGeolocation,
-            ipGeolocationError: ipGeolocationError,
             emailSecurity: emailSecurity,
+            mtaSts: emailSecurity?.mtaSts,
             ptrRecord: ptrRecord,
             redirectChain: redirectChain,
-            portScanResults: portScanResults
+            portScanResults: allPortScanResults,
+            hstsPreloaded: hstsPreloaded,
+            resolverDisplayName: resolverDisplayName,
+            resolverURLString: resolverURLString,
+            totalLookupDurationMs: lastLookupDurationMs,
+            sslError: sslError,
+            httpHeadersError: httpHeadersError,
+            reachabilityError: reachabilityError,
+            ipGeolocationError: ipGeolocationError,
+            emailSecurityError: emailSecurityError,
+            ptrError: ptrError,
+            redirectChainError: redirectChainError,
+            portScanError: combinedPortScanError
         )
+
+        if replaceLatest, !history.isEmpty, history[0].domain.caseInsensitiveCompare(searchedDomain) == .orderedSame {
+            history[0] = entry
+        } else {
+            history.insert(entry, at: 0)
+            if history.count > Self.maxHistory {
+                history = Array(history.prefix(Self.maxHistory))
+            }
+        }
+        persistHistory()
+    }
+
+    private func persistHistory() {
+        if let data = try? JSONEncoder().encode(history) {
+            UserDefaults.standard.set(data, forKey: Self.historyKey)
+        }
+    }
+
+    private func addRecentSearch(_ domain: String) {
+        recentSearches.removeAll { $0.lowercased() == domain.lowercased() }
+        recentSearches.insert(domain, at: 0)
+        if recentSearches.count > Self.maxRecent {
+            recentSearches = Array(recentSearches.prefix(Self.maxRecent))
+        }
+        UserDefaults.standard.set(recentSearches, forKey: Self.recentSearchesKey)
+    }
+
+    private func clearLookupState() {
+        dnsSections = []
+        dnsError = nil
+        dnsLoading = false
+        sslInfo = nil
+        sslError = nil
+        sslLoading = false
+        hstsPreloaded = nil
+        hstsLoading = false
+        httpHeaders = []
+        httpSecurityGrade = nil
+        httpStatusCode = nil
+        httpResponseTimeMs = nil
+        httpProtocol = nil
+        http3Advertised = false
+        httpHeadersError = nil
+        httpHeadersLoading = false
+        reachabilityResults = []
+        reachabilityError = nil
+        reachabilityLoading = false
+        ipGeolocation = nil
+        ipGeolocationError = nil
+        ipGeolocationLoading = false
+        emailSecurity = nil
+        emailSecurityError = nil
+        emailSecurityLoading = false
+        ptrRecord = nil
+        ptrError = nil
+        ptrLoading = false
+        redirectChain = []
+        redirectChainError = nil
+        redirectChainLoading = false
+        portScanResults = []
+        portScanError = nil
+        portScanLoading = false
+        customPortResults = []
+        customPortScanError = nil
+        customPortScanLoading = false
+    }
+
+    private func setAllLoadingStates(_ loading: Bool) {
+        dnsLoading = loading
+        sslLoading = loading
+        hstsLoading = loading
+        httpHeadersLoading = loading
+        reachabilityLoading = loading
+        ipGeolocationLoading = loading
+        emailSecurityLoading = loading
+        ptrLoading = loading
+        redirectChainLoading = loading
+        portScanLoading = loading
+    }
+
+    private func primaryIPAddress(from sections: [DNSSection]) -> String? {
+        sections.first(where: { $0.recordType == .A })?.records.first?.value
+    }
+
+    private func isCurrentLookup(_ lookupID: UUID) -> Bool {
+        activeLookupID == lookupID
+    }
+
+    static func summaryFields(from snapshot: LookupSnapshot) -> [SummaryFieldViewData] {
+        [
+            SummaryFieldViewData(label: "Domain", value: snapshot.domain.nonEmpty ?? "Unavailable", tone: .primary),
+            SummaryFieldViewData(label: "Primary IP", value: primaryIPAddress(from: snapshot) ?? "Unavailable", tone: .primary),
+            SummaryFieldViewData(label: "HTTPS", value: httpsSummary(from: snapshot), tone: httpsSummaryTone(from: snapshot)),
+            SummaryFieldViewData(label: "Redirect", value: finalRedirectTarget(from: snapshot) ?? "Unavailable", tone: .secondary),
+            SummaryFieldViewData(label: "Email", value: emailSummary(from: snapshot), tone: .secondary)
+        ]
+    }
+
+    static func domainRows(from snapshot: LookupSnapshot) -> [InfoRowViewData] {
+        [
+            InfoRowViewData(label: "Domain", value: snapshot.domain, tone: .primary),
+            InfoRowViewData(label: "Resolver", value: snapshot.resolverDisplayName, tone: .secondary),
+            InfoRowViewData(label: snapshot.isLive ? "Result" : "Snapshot", value: snapshot.isLive ? "Live" : "Snapshot", tone: snapshot.isLive ? .success : .warning),
+            InfoRowViewData(label: "Lookup Duration", value: durationLabel(snapshot.totalLookupDurationMs), tone: .secondary)
+        ]
+    }
+
+    static func dnsRows(from snapshot: LookupSnapshot) -> [DNSRecordSectionViewData] {
+        snapshot.dnsSections.map { section in
+            DNSRecordSectionViewData(
+                title: section.recordType.rawValue,
+                rows: section.records.map { InfoRowViewData(label: "TTL \($0.ttl)", value: $0.value, tone: .primary) },
+                wildcardRows: section.wildcardRecords.map { InfoRowViewData(label: "TTL \($0.ttl)", value: $0.value, tone: .primary) },
+                wildcardTitle: section.wildcardRecords.isEmpty ? nil : "*.\(snapshot.domain)",
+                message: section.error.map { SectionMessageViewData(text: $0, isError: true) } ??
+                    ((section.records.isEmpty && section.wildcardRecords.isEmpty) ? SectionMessageViewData(text: "No records found", isError: false) : nil)
+            )
+        }
+    }
+
+    static func dnssecLabel(from snapshot: LookupSnapshot) -> String? {
+        guard let signed = snapshot.dnsSections.compactMap(\.dnssecSigned).first else { return nil }
+        return "Resolver-reported DNSSEC (not full validation): \(signed ? "Yes" : "No")"
+    }
+
+    static func ptrMessage(from snapshot: LookupSnapshot) -> SectionMessageViewData? {
+        if let ptrRecord = snapshot.ptrRecord {
+            return SectionMessageViewData(text: ptrRecord, isError: false)
+        }
+        if let ptrError = snapshot.ptrError {
+            return SectionMessageViewData(text: ptrError, isError: ptrError != "No A record available" && ptrError != "No PTR record found")
+        }
+        return nil
+    }
+
+    static func webCertificateRows(from snapshot: LookupSnapshot) -> [InfoRowViewData] {
+        guard let sslInfo = snapshot.sslInfo else { return [] }
+        var rows = [
+            InfoRowViewData(label: "Common Name", value: sslInfo.commonName, tone: .primary),
+            InfoRowViewData(label: "Issuer", value: sslInfo.issuer, tone: .primary),
+            InfoRowViewData(label: "Valid From", value: DateFormatter.certDate.string(from: sslInfo.validFrom), tone: .secondary),
+            InfoRowViewData(label: "Valid Until", value: DateFormatter.certDate.string(from: sslInfo.validUntil), tone: .secondary),
+            InfoRowViewData(label: "Days Until Expiry", value: "\(sslInfo.daysUntilExpiry)", tone: sslInfo.daysUntilExpiry < 30 ? .failure : (sslInfo.daysUntilExpiry < 60 ? .warning : .success)),
+            InfoRowViewData(label: "Chain Depth", value: "\(sslInfo.chainDepth)", tone: .secondary)
+        ]
+        if let tlsVersion = sslInfo.tlsVersion {
+            rows.append(InfoRowViewData(label: "TLS Version", value: tlsVersion, tone: .secondary))
+        }
+        if let cipherSuite = sslInfo.cipherSuite {
+            rows.append(InfoRowViewData(label: "Cipher Suite", value: cipherSuite, tone: .secondary))
+        }
+        if let hstsPreloaded = snapshot.hstsPreloaded {
+            rows.append(InfoRowViewData(label: "HSTS Preload", value: hstsPreloaded ? "Preloaded" : "Not preloaded", tone: hstsPreloaded ? .success : .secondary))
+        }
+        return rows
+    }
+
+    static func webResponseRows(from snapshot: LookupSnapshot) -> [InfoRowViewData] {
+        var rows: [InfoRowViewData] = []
+        if let httpStatusCode = snapshot.httpStatusCode {
+            rows.append(InfoRowViewData(label: "Status", value: "\(httpStatusCode)", tone: .primary))
+        }
+        if let httpResponseTimeMs = snapshot.httpResponseTimeMs {
+            rows.append(InfoRowViewData(label: "Response Time", value: "\(httpResponseTimeMs) ms", tone: .secondary))
+        }
+        if let httpProtocol = snapshot.httpProtocol {
+            rows.append(InfoRowViewData(label: "Protocol", value: httpProtocol, tone: .secondary))
+        }
+        if let httpSecurityGrade = snapshot.httpSecurityGrade {
+            rows.append(InfoRowViewData(label: "Security Grade", value: httpSecurityGrade, tone: securityGradeTone(httpSecurityGrade)))
+        }
+        if snapshot.http3Advertised {
+            rows.append(InfoRowViewData(label: "HTTP/3", value: "Advertised", tone: .secondary))
+        }
+        return rows
+    }
+
+    static func redirectRows(from snapshot: LookupSnapshot) -> [RedirectHopViewData] {
+        snapshot.redirectChain.map {
+            RedirectHopViewData(
+                stepLabel: "\($0.stepNumber)",
+                statusCode: "\($0.statusCode)",
+                url: $0.url,
+                isFinal: $0.isFinal
+            )
+        }
     }
 
-    static func formatExportText(
-        domain: String,
-        date: Date,
-        dnsSections: [DNSSection],
-        sslInfo: SSLCertificateInfo?,
-        sslError: String? = nil,
-        hstsPreloaded: Bool? = nil,
-        httpHeaders: [HTTPHeader],
-        httpSecurityGrade: String? = nil,
-        httpStatusCode: Int? = nil,
-        httpResponseTimeMs: Int? = nil,
-        httpProtocol: String? = nil,
-        http3Advertised: Bool = false,
-        httpHeadersError: String? = nil,
-        reachabilityResults: [PortReachability],
-        ipGeolocation: IPGeolocation?,
-        ipGeolocationError: String? = nil,
-        emailSecurity: EmailSecurityResult? = nil,
-        ptrRecord: String? = nil,
-        redirectChain: [RedirectHop] = [],
-        portScanResults: [PortScanResult] = []
-    ) -> String {
-        let dateFmt = DateFormatter()
-        dateFmt.dateFormat = "yyyy-MM-dd HH:mm"
+    static func emailRows(from snapshot: LookupSnapshot) -> [EmailRowViewData] {
+        guard let emailSecurity = snapshot.emailSecurity else { return [] }
+        return [
+            EmailRowViewData(label: "SPF", status: emailSecurity.spf.found ? "Present" : "Missing", statusTone: emailSecurity.spf.found ? .success : .warning, detail: emailSecurity.spf.value ?? "No record found", auxiliaryDetail: nil),
+            EmailRowViewData(label: "DMARC", status: emailSecurity.dmarc.found ? "Present" : "Missing", statusTone: emailSecurity.dmarc.found ? .success : .warning, detail: emailSecurity.dmarc.value ?? "No record found", auxiliaryDetail: nil),
+            EmailRowViewData(label: "DKIM", status: emailSecurity.dkim.found ? "Present" : "Missing", statusTone: emailSecurity.dkim.found ? .success : .warning, detail: emailSecurity.dkim.value ?? "No record found", auxiliaryDetail: emailSecurity.dkim.matchedSelector.map { "Selector: \($0)" }),
+            EmailRowViewData(label: "MTA-STS", status: emailSecurity.mtaSts?.txtFound == true ? "Present" : "Missing", statusTone: emailSecurity.mtaSts?.txtFound == true ? .success : .warning, detail: emailSecurity.mtaSts?.policyMode ?? (emailSecurity.mtaSts?.txtFound == true ? "Policy unavailable" : "No record found"), auxiliaryDetail: nil),
+            EmailRowViewData(label: "BIMI", status: emailSecurity.bimi.found ? "Present" : "Missing", statusTone: emailSecurity.bimi.found ? .success : .warning, detail: emailSecurity.bimi.value ?? "No record found", auxiliaryDetail: nil)
+        ]
+    }
+
+    static func reachabilityRows(from snapshot: LookupSnapshot) -> [ReachabilityRowViewData] {
+        snapshot.reachabilityResults.map {
+            ReachabilityRowViewData(
+                portLabel: "Port \($0.port)",
+                latencyLabel: $0.latencyMs.map { "\($0) ms" } ?? "—",
+                statusLabel: $0.reachable ? "Reachable" : "Unreachable",
+                statusTone: $0.reachable ? .success : .failure
+            )
+        }
+    }
+
+    static func locationRows(from snapshot: LookupSnapshot) -> [InfoRowViewData] {
+        guard let ipGeolocation = snapshot.ipGeolocation else { return [] }
+        var rows = [InfoRowViewData(label: "IP", value: ipGeolocation.ip, tone: .primary)]
+        if let org = ipGeolocation.org {
+            rows.append(InfoRowViewData(label: "Org / ISP", value: org, tone: .secondary))
+        }
+        let location = [ipGeolocation.city, ipGeolocation.region, ipGeolocation.country_name].compactMap { $0 }.joined(separator: ", ")
+        if !location.isEmpty {
+            rows.append(InfoRowViewData(label: "Location", value: location, tone: .secondary))
+        }
+        if let latitude = ipGeolocation.latitude, let longitude = ipGeolocation.longitude {
+            rows.append(InfoRowViewData(label: "Coordinates", value: "\(latitude), \(longitude)", tone: .secondary))
+        }
+        return rows
+    }
+
+    static func portRows(from snapshot: LookupSnapshot, kind: PortScanKind) -> [PortScanRowViewData] {
+        snapshot.portScanResults
+            .filter { $0.kind == kind }
+            .map {
+                PortScanRowViewData(
+                    portLabel: "\($0.port)",
+                    service: $0.service,
+                    statusLabel: $0.open ? "Open" : "Closed",
+                    statusTone: $0.open ? .success : .secondary,
+                    banner: $0.banner,
+                    durationLabel: $0.durationMs.map { "\($0) ms" }
+                )
+            }
+    }
+
+    static func formatExportText(from snapshot: LookupSnapshot) -> String {
+        let exportDateFormatter = DateFormatter()
+        exportDateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
 
         var lines: [String] = [
             "DomainDig Export",
-            "Domain: \(domain)",
-            "Date: \(dateFmt.string(from: date))",
+            "Domain: \(snapshot.domain)",
+            "Date: \(exportDateFormatter.string(from: snapshot.timestamp))",
+            "Mode: \(snapshot.isLive ? "Live" : "Snapshot")",
+            "Resolver: \(snapshot.resolverDisplayName)",
+            "Lookup Duration: \(durationLabel(snapshot.totalLookupDurationMs))"
         ]
 
-        // Reachability
-        if !reachabilityResults.isEmpty {
+        func appendSection(_ title: String, body: () -> Void) {
             lines.append("")
-            lines.append("Reachability")
-            lines.append("------------")
-            for result in reachabilityResults {
-                if result.reachable, let ms = result.latencyMs {
-                    lines.append("  Port \(result.port)  \(ms)ms  Reachable")
-                } else {
-                    lines.append("  Port \(result.port)  —  Unreachable")
-                }
+            lines.append(title)
+            lines.append(String(repeating: "-", count: title.count))
+            body()
+        }
+
+        appendSection("Summary") {
+            for item in summaryFields(from: snapshot) {
+                lines.append("  \(item.label): \(item.value)")
             }
         }
 
-        // Redirect Chain
-        if !redirectChain.isEmpty {
-            lines.append("")
-            lines.append("Redirect Chain")
-            lines.append("--------------")
-            if redirectChain.count == 1 && redirectChain[0].isFinal && !(300...399).contains(redirectChain[0].statusCode) {
-                lines.append("  No redirects — direct connection")
-            } else {
-                for hop in redirectChain {
-                    let final = hop.isFinal ? "  (final)" : ""
-                    lines.append("  \(hop.stepNumber)  \(hop.statusCode)  \(hop.url)\(final)")
-                }
+        appendSection("Domain") {
+            for row in domainRows(from: snapshot) {
+                lines.append("  \(row.label): \(row.value)")
             }
         }
 
-        // DNS
-        lines.append("")
-        lines.append("DNS Records")
-        lines.append("-----------")
-        for section in dnsSections {
-            lines.append(section.recordType.rawValue)
-            if let error = section.error {
-                lines.append("  Error: \(error)")
-            } else if section.records.isEmpty {
-                lines.append("  No records found")
-            } else {
-                for record in section.records {
-                    lines.append("  \(record.value)  TTL \(record.ttl)")
-                }
+        appendSection("DNS") {
+            if let dnsError = snapshot.dnsError {
+                lines.append("  Error: \(dnsError)")
+            }
+            if let dnssecLabel = dnssecLabel(from: snapshot) {
+                lines.append("  \(dnssecLabel)")
             }
-            if !section.wildcardRecords.isEmpty {
-                lines.append("*.\(domain)")
-                for record in section.wildcardRecords {
-                    lines.append("  \(record.value)  TTL \(record.ttl)")
+            for section in dnsRows(from: snapshot) {
+                lines.append("  \(section.title)")
+                if let message = section.message {
+                    lines.append("    \(message.isError ? "Error" : "Info"): \(message.text)")
                 }
+                for row in section.rows {
+                    lines.append("    \(row.value) (\(row.label))")
+                }
+                if let wildcardTitle = section.wildcardTitle {
+                    lines.append("    \(wildcardTitle)")
+                    for row in section.wildcardRows {
+                        lines.append("      \(row.value) (\(row.label))")
+                    }
+                }
+            }
+            if let ptrRecord = snapshot.ptrRecord {
+                lines.append("  PTR: \(ptrRecord)")
+            } else if let ptrError = snapshot.ptrError {
+                lines.append("  PTR Error: \(ptrError)")
             }
         }
 
-        // PTR
-        if let ptr = ptrRecord {
-            lines.append("PTR (Reverse DNS)")
-            lines.append("  \(ptr)")
-        }
+        appendSection("Web") {
+            if let sslError = snapshot.sslError {
+                lines.append("  TLS Error: \(sslError)")
+            } else {
+                for row in webCertificateRows(from: snapshot) {
+                    lines.append("  \(row.label): \(row.value)")
+                }
+            }
 
-        // Email Security
-        if let email = emailSecurity {
-            lines.append("")
-            lines.append("Email Security")
-            lines.append("--------------")
-            lines.append("  SPF:   \(email.spf.found ? "✓" : "✗")  \(email.spf.value ?? "No record found")")
-            lines.append("  DMARC: \(email.dmarc.found ? "✓" : "✗")  \(email.dmarc.value ?? "No record found")")
-            let dkimValue = if let selector = email.dkim.matchedSelector,
-                               let value = email.dkim.value {
-                "\(value) (selector: \(selector))"
+            if let httpHeadersError = snapshot.httpHeadersError {
+                lines.append("  Headers Error: \(httpHeadersError)")
             } else {
-                email.dkim.value ?? "No record found"
+                for row in webResponseRows(from: snapshot) {
+                    lines.append("  \(row.label): \(row.value)")
+                }
+                if snapshot.httpHeaders.isEmpty {
+                    lines.append("  Headers: No headers returned")
+                } else {
+                    lines.append("  Headers:")
+                    for header in snapshot.httpHeaders {
+                        lines.append("    \(header.name): \(header.value)")
+                    }
+                }
             }
-            lines.append("  DKIM:  \(email.dkim.found ? "✓" : "✗")  \(dkimValue)")
-            let mtaDescription = if let mode = email.mtaSts?.policyMode {
-                "mode: \(mode)"
-            } else if email.mtaSts?.txtFound == true {
-                "Policy unavailable"
+
+            if let redirectChainError = snapshot.redirectChainError {
+                lines.append("  Redirect Error: \(redirectChainError)")
+            } else if snapshot.redirectChain.isEmpty {
+                lines.append("  Redirects: No redirect data available")
             } else {
-                "No record found"
+                lines.append("  Redirects:")
+                for hop in redirectRows(from: snapshot) {
+                    lines.append("    \(hop.stepLabel). \(hop.statusCode) \(hop.url)\(hop.isFinal ? " (final)" : "")")
+                }
             }
-            lines.append("  MTA-STS: \(email.mtaSts?.txtFound == true ? "✓" : "✗")  \(mtaDescription)")
-            lines.append("  BIMI:  \(email.bimi.found ? "✓" : "✗")  \(email.bimi.value ?? "No record found")")
         }
 
-        // SSL
-        if let info = sslInfo {
-            let certDateFmt = DateFormatter()
-            certDateFmt.dateStyle = .medium
-            certDateFmt.timeStyle = .none
-
-            lines.append("")
-            lines.append("SSL / TLS Certificate")
-            lines.append("---------------------")
-            lines.append("Common Name: \(info.commonName)")
-            lines.append("Issuer: \(info.issuer)")
-            lines.append("SANs: \(info.subjectAltNames.joined(separator: ", "))")
-            lines.append("Valid From: \(certDateFmt.string(from: info.validFrom))")
-            lines.append("Valid Until: \(certDateFmt.string(from: info.validUntil))")
-            lines.append("Days Until Expiry: \(info.daysUntilExpiry)")
-            lines.append("Chain Depth: \(info.chainDepth)")
-            if let tlsVersion = info.tlsVersion {
-                lines.append("TLS Version: \(tlsVersion)")
-            }
-            if let cipherSuite = info.cipherSuite {
-                lines.append("Cipher Suite: \(cipherSuite)")
-            }
-            if let hstsPreloaded {
-                lines.append("HSTS Preload: \(hstsPreloaded ? "Preloaded" : "Not preloaded")")
-            }
-            if !info.chain.isEmpty {
-                lines.append("Certificate Chain:")
-                for certificate in info.chain {
-                    lines.append("  Subject: \(certificate.subject)")
-                    lines.append("  Issuer: \(certificate.issuer)")
+        appendSection("Email") {
+            if let emailSecurityError = snapshot.emailSecurityError {
+                lines.append("  Error: \(emailSecurityError)")
+            } else if emailRows(from: snapshot).isEmpty {
+                lines.append("  No email security records found")
+            } else {
+                for row in emailRows(from: snapshot) {
+                    lines.append("  \(row.label): \(row.status)")
+                    lines.append("    \(row.detail)")
+                    if let auxiliaryDetail = row.auxiliaryDetail {
+                        lines.append("    \(auxiliaryDetail)")
+                    }
                 }
             }
-        } else if let error = sslError {
-            lines.append("")
-            lines.append("SSL / TLS Certificate")
-            lines.append("---------------------")
-            lines.append("Error: \(error)")
         }
 
-        // HTTP Headers
-        if !httpHeaders.isEmpty {
-            lines.append("")
-            lines.append("HTTP Headers")
-            lines.append("------------")
-            for header in httpHeaders {
-                lines.append("  \(header.name): \(header.value)")
-            }
-            if let httpSecurityGrade {
-                lines.append("Grade: \(httpSecurityGrade)")
-            }
-            if let httpStatusCode {
-                lines.append("Status: \(httpStatusCode)")
-            }
-            if let httpResponseTimeMs {
-                lines.append("Response Time: \(httpResponseTimeMs)ms")
-            }
-            if let httpProtocol {
-                lines.append("Protocol: \(httpProtocol)")
+        appendSection("Network") {
+            if let reachabilityError = snapshot.reachabilityError {
+                lines.append("  Reachability Error: \(reachabilityError)")
+            } else if reachabilityRows(from: snapshot).isEmpty {
+                lines.append("  Reachability: No results")
+            } else {
+                lines.append("  Reachability:")
+                for row in reachabilityRows(from: snapshot) {
+                    lines.append("    \(row.portLabel): \(row.statusLabel) \(row.latencyLabel)")
+                }
             }
-            if http3Advertised {
-                lines.append("HTTP/3 Advertised: Yes")
+
+            if let ipGeolocationError = snapshot.ipGeolocationError, snapshot.ipGeolocation == nil {
+                lines.append("  Location Error: \(ipGeolocationError)")
+            } else if locationRows(from: snapshot).isEmpty {
+                lines.append("  Location: No data")
+            } else {
+                lines.append("  Location:")
+                for row in locationRows(from: snapshot) {
+                    lines.append("    \(row.label): \(row.value)")
+                }
             }
-        } else if let error = httpHeadersError {
-            lines.append("")
-            lines.append("HTTP Headers")
-            lines.append("------------")
-            lines.append("Error: \(error)")
-        }
 
-        // IP Geolocation
-        if let geo = ipGeolocation {
-            lines.append("")
-            lines.append("IP Location")
-            lines.append("-----------")
-            lines.append("IP: \(geo.ip)")
-            if let org = geo.org { lines.append("Org: \(org)") }
-            let location = [geo.city, geo.region, geo.country_name].compactMap { $0 }.joined(separator: ", ")
-            if !location.isEmpty { lines.append("Location: \(location)") }
-            if let lat = geo.latitude, let lon = geo.longitude {
-                lines.append("Coordinates: \(lat), \(lon)")
+            if let portScanError = snapshot.portScanError, snapshot.portScanResults.isEmpty {
+                lines.append("  Port Scan Error: \(portScanError)")
             }
-        } else if let error = ipGeolocationError, error != "No A record available" {
-            lines.append("")
-            lines.append("IP Location")
-            lines.append("-----------")
-            lines.append("Error: \(error)")
-        }
 
-        // Open Ports
-        if !portScanResults.isEmpty {
-            lines.append("")
-            lines.append("Open Ports")
-            lines.append("----------")
-            let openPorts = portScanResults.filter { $0.open }
-            if openPorts.isEmpty {
-                lines.append("  No open ports detected")
+            lines.append("  Standard Ports:")
+            let standardRows = portRows(from: snapshot, kind: .standard)
+            if standardRows.isEmpty {
+                lines.append("    No results")
             } else {
-                for port in openPorts {
-                    let bannerSuffix = port.banner.map { "  \($0)" } ?? ""
-                    lines.append("  \(port.port)  \(port.service)\(bannerSuffix)")
+                for row in standardRows {
+                    lines.append("    \(row.portLabel) \(row.service): \(row.statusLabel)\(row.durationLabel.map { " \($0)" } ?? "")")
+                    if let banner = row.banner {
+                        lines.append("      Banner: \(banner)")
+                    }
                 }
             }
-            let closedPorts = portScanResults.filter { !$0.open }
-            if !closedPorts.isEmpty {
-                lines.append("Closed: \(closedPorts.map { "\($0.port)" }.joined(separator: ", "))")
+
+            lines.append("  Custom Ports:")
+            let customRows = portRows(from: snapshot, kind: .custom)
+            if customRows.isEmpty {
+                lines.append("    No results")
+            } else {
+                for row in customRows {
+                    lines.append("    \(row.portLabel) \(row.service): \(row.statusLabel)\(row.durationLabel.map { " \($0)" } ?? "")")
+                    if let banner = row.banner {
+                        lines.append("      Banner: \(banner)")
+                    }
+                }
             }
         }
 
         return lines.joined(separator: "\n")
     }
 
-    // MARK: - Recent Searches
+    private static func primaryIPAddress(from snapshot: LookupSnapshot) -> String? {
+        snapshot.dnsSections.first(where: { $0.recordType == .A })?.records.first?.value
+    }
 
-    private func addRecentSearch(_ domain: String) {
-        recentSearches.removeAll { $0.lowercased() == domain.lowercased() }
-        recentSearches.insert(domain, at: 0)
-        if recentSearches.count > Self.maxRecent {
-            recentSearches = Array(recentSearches.prefix(Self.maxRecent))
+    private static func finalRedirectTarget(from snapshot: LookupSnapshot) -> String? {
+        snapshot.redirectChain.last?.url
+    }
+
+    private static func httpsSummary(from snapshot: LookupSnapshot) -> String {
+        if snapshot.sslInfo != nil {
+            return "Valid"
         }
-        UserDefaults.standard.set(recentSearches, forKey: Self.recentSearchesKey)
+        if let sslError = snapshot.sslError {
+            return sslError.localizedCaseInsensitiveContains("certificate") ? "Invalid" : "Failed"
+        }
+        return "Unavailable"
     }
 
-    func clearRecentSearches() {
-        recentSearches.removeAll()
-        UserDefaults.standard.removeObject(forKey: Self.recentSearchesKey)
+    private static func httpsSummaryTone(from snapshot: LookupSnapshot) -> ResultTone {
+        if snapshot.sslInfo != nil {
+            return .success
+        }
+        return snapshot.sslError == nil ? .secondary : .failure
+    }
+
+    private static func emailSummary(from snapshot: LookupSnapshot) -> String {
+        guard let emailSecurity = snapshot.emailSecurity else {
+            return snapshot.emailSecurityError ?? "Unavailable"
+        }
+        return "SPF \(emailSecurity.spf.found ? "Yes" : "No") / DMARC \(emailSecurity.dmarc.found ? "Yes" : "No")"
+    }
+
+    private static func securityGradeTone(_ grade: String) -> ResultTone {
+        switch grade {
+        case "A", "B":
+            return .success
+        case "C":
+            return .warning
+        case "D", "F":
+            return .failure
+        default:
+            return .secondary
+        }
+    }
+
+    private static func durationLabel(_ durationMs: Int?) -> String {
+        durationMs.map { "\($0) ms" } ?? "Unavailable"
+    }
+}
+
+private extension String {
+    var nonEmpty: String? {
+        isEmpty ? nil : self
+    }
+
+    var nilIfEmpty: String? {
+        isEmpty ? nil : self
     }
 }
diff --git a/DomainDig/EmailSecurityService.swift b/DomainDig/EmailSecurityService.swift
index 50e5f73..c80d0f7 100644
--- a/DomainDig/EmailSecurityService.swift
+++ b/DomainDig/EmailSecurityService.swift
@@ -8,7 +8,7 @@ struct EmailSecurityService {
 
     /// Analyze email security records. SPF is parsed from existing TXT records;
     /// DMARC and DKIM require additional DoH queries.
-    static func analyze(domain: String, txtRecords: [DNSRecord]) async -> EmailSecurityResult {
+    static func analyze(domain: String, txtRecords: [DNSRecord]) async -> ServiceResult<EmailSecurityResult> {
         // SPF: prefer the already-fetched apex TXT records, but fall back to a direct lookup
         // in case the earlier DNS section missed or normalized the record differently.
         let localSPFRecord = txtRecords.first(where: { isMatchingTXTRecord($0.value, prefix: "v=spf1") })?.value
@@ -49,13 +49,16 @@ struct EmailSecurityService {
             value: bimiValue
         )
 
-        return EmailSecurityResult(
+        let result = EmailSecurityResult(
             spf: spf,
             dmarc: dmarc,
             dkim: dkim,
             bimi: bimi,
             mtaSts: mtaSts
         )
+
+        let hasAnyRecord = result.spf.found || result.dmarc.found || result.dkim.found || result.bimi.found || result.mtaSts?.txtFound == true
+        return hasAnyRecord ? .success(result) : .empty("No email security records found")
     }
 
     /// Query a TXT record for the given subdomain via DoH.
diff --git a/DomainDig/HTTPHeadersService.swift b/DomainDig/HTTPHeadersService.swift
index 51e7b5b..4bf175a 100644
--- a/DomainDig/HTTPHeadersService.swift
+++ b/DomainDig/HTTPHeadersService.swift
@@ -35,41 +35,46 @@ struct HTTPHeadersResult {
 }
 
 struct HTTPHeadersService {
-    static func fetch(domain: String) async throws -> HTTPHeadersResult {
+    static func fetch(domain: String) async -> ServiceResult<HTTPHeadersResult> {
         let url = URL(string: "https://\(domain)")!
         var request = URLRequest(url: url, timeoutInterval: 10)
         request.httpMethod = "HEAD"
         let metricsDelegate = TaskMetricsDelegate()
         let startTime = Date()
 
-        let (_, response) = try await URLSession.shared.data(for: request, delegate: metricsDelegate)
-        let responseTimeMs = max(0, Int(Date().timeIntervalSince(startTime) * 1000))
-
-        guard let httpResponse = response as? HTTPURLResponse else {
-            throw URLError(.badServerResponse)
-        }
-
-        let headers = httpResponse.allHeaderFields.compactMap { entry -> HTTPHeader? in
-            guard let name = entry.key as? String,
-                  let value = entry.value as? String else { return nil }
-            return HTTPHeader(name: name, value: value)
+        do {
+            let (_, response) = try await URLSession.shared.data(for: request, delegate: metricsDelegate)
+            let responseTimeMs = max(0, Int(Date().timeIntervalSince(startTime) * 1000))
+
+            guard let httpResponse = response as? HTTPURLResponse else {
+                return .error(URLError(.badServerResponse).localizedDescription)
+            }
+
+            let headers = httpResponse.allHeaderFields.compactMap { entry -> HTTPHeader? in
+                guard let name = entry.key as? String,
+                      let value = entry.value as? String else { return nil }
+                return HTTPHeader(name: name, value: value)
+            }
+            .sorted { $0.name.lowercased() < $1.name.lowercased() }
+
+            let networkProtocolName = metricsDelegate.metrics?.transactionMetrics
+                .compactMap { $0.networkProtocolName }
+                .last
+            let detectedProtocol = protocolLabel(for: networkProtocolName)
+            let altSvcValue = headerValue(named: "alt-svc", in: httpResponse)
+            let http3Advertised = altSvcValue?.localizedCaseInsensitiveContains("h3") == true
+            let result = HTTPHeadersResult(
+                headers: headers,
+                statusCode: httpResponse.statusCode,
+                responseTimeMs: responseTimeMs,
+                httpProtocol: detectedProtocol,
+                http3Advertised: http3Advertised
+            )
+
+            return headers.isEmpty ? .empty("No HTTP headers returned") : .success(result)
+        } catch {
+            return .error(error.localizedDescription)
         }
-        .sorted { $0.name.lowercased() < $1.name.lowercased() }
-
-        let networkProtocolName = metricsDelegate.metrics?.transactionMetrics
-            .compactMap { $0.networkProtocolName }
-            .last
-        let detectedProtocol = protocolLabel(for: networkProtocolName)
-        let altSvcValue = headerValue(named: "alt-svc", in: httpResponse)
-        let http3Advertised = altSvcValue?.localizedCaseInsensitiveContains("h3") == true
-
-        return HTTPHeadersResult(
-            headers: headers,
-            statusCode: httpResponse.statusCode,
-            responseTimeMs: responseTimeMs,
-            httpProtocol: detectedProtocol,
-            http3Advertised: http3Advertised
-        )
     }
 
     private static func protocolLabel(for networkProtocolName: String?) -> String? {
diff --git a/DomainDig/HistoryView.swift b/DomainDig/HistoryView.swift
index 52b0070..cc4321f 100644
--- a/DomainDig/HistoryView.swift
+++ b/DomainDig/HistoryView.swift
@@ -1,15 +1,13 @@
 import SwiftUI
-import MapKit
 
 struct HistoryView: View {
     @Bindable var viewModel: DomainViewModel
-    @Environment(\.dismiss) private var dismiss
 
-    private let dateFmt: DateFormatter = {
-        let f = DateFormatter()
-        f.dateStyle = .medium
-        f.timeStyle = .short
-        return f
+    private let dateFormatter: DateFormatter = {
+        let formatter = DateFormatter()
+        formatter.dateStyle = .medium
+        formatter.timeStyle = .short
+        return formatter
     }()
 
     var body: some View {
@@ -22,15 +20,22 @@ struct HistoryView: View {
             } else {
                 ForEach(viewModel.history) { entry in
                     NavigationLink {
-                        HistoryDetailView(entry: entry)
+                        HistoryDetailView(viewModel: viewModel, entry: entry)
                     } label: {
-                        VStack(alignment: .leading, spacing: 2) {
+                        VStack(alignment: .leading, spacing: 4) {
                             Text(entry.domain)
                                 .font(.system(.callout, design: .monospaced))
                                 .foregroundStyle(.primary)
-                            Text(dateFmt.string(from: entry.timestamp))
-                                .font(.system(.caption2, design: .monospaced))
-                                .foregroundStyle(.secondary)
+                            HStack(spacing: 8) {
+                                Text(dateFormatter.string(from: entry.timestamp))
+                                Text("Snapshot")
+                                Text(entry.resolverDisplayName)
+                                if let totalLookupDurationMs = entry.totalLookupDurationMs {
+                                    Text("\(totalLookupDurationMs) ms")
+                                }
+                            }
+                            .font(.system(.caption2, design: .monospaced))
+                            .foregroundStyle(.secondary)
                         }
                     }
                     .listRowBackground(Color(.systemGray6).opacity(0.5))
@@ -52,47 +57,102 @@ struct HistoryView: View {
     }
 }
 
-// MARK: - History Detail View (Read-Only Cached Results)
-
 struct HistoryDetailView: View {
+    @Bindable var viewModel: DomainViewModel
     let entry: HistoryEntry
 
-    private let dateFmt: DateFormatter = {
-        let f = DateFormatter()
-        f.dateStyle = .medium
-        f.timeStyle = .short
-        return f
+    private let dateFormatter: DateFormatter = {
+        let formatter = DateFormatter()
+        formatter.dateStyle = .medium
+        formatter.timeStyle = .short
+        return formatter
     }()
 
+    private var snapshot: LookupSnapshot {
+        entry.snapshot
+    }
+
     var body: some View {
         ScrollView(.vertical) {
             VStack(alignment: .leading, spacing: 0) {
-                cachedBanner
-                reachabilitySection
-                redirectChainSection
-                dnsSection
-                emailSecuritySection
-                sslSection
-                httpHeadersSection
-                ipGeolocationSection
-                portScanSection
+                snapshotBanner
+                SummaryView(fields: DomainViewModel.summaryFields(from: snapshot))
+                    .padding(.top, 8)
+                DomainSectionView(rows: DomainViewModel.domainRows(from: snapshot))
+                    .padding(.top, 16)
+                DNSSectionView(
+                    dnssecLabel: DomainViewModel.dnssecLabel(from: snapshot),
+                    sections: DomainViewModel.dnsRows(from: snapshot),
+                    ptrMessage: DomainViewModel.ptrMessage(from: snapshot),
+                    loading: false,
+                    sectionError: snapshot.dnsError
+                )
+                .padding(.top, 16)
+                WebSectionView(
+                    certificateRows: DomainViewModel.webCertificateRows(from: snapshot),
+                    sslInfo: snapshot.sslInfo,
+                    sslLoading: false,
+                    sslError: snapshot.sslError,
+                    responseRows: DomainViewModel.webResponseRows(from: snapshot),
+                    headers: snapshot.httpHeaders,
+                    headersLoading: false,
+                    headersError: snapshot.httpHeadersError,
+                    redirects: DomainViewModel.redirectRows(from: snapshot),
+                    redirectLoading: false,
+                    redirectError: snapshot.redirectChainError,
+                    finalURL: snapshot.redirectChain.last?.url
+                )
+                .padding(.top, 16)
+                EmailSectionView(
+                    rows: DomainViewModel.emailRows(from: snapshot),
+                    loading: false,
+                    error: snapshot.emailSecurityError
+                )
+                .padding(.top, 16)
+                NetworkSectionView(
+                    reachabilityRows: DomainViewModel.reachabilityRows(from: snapshot),
+                    reachabilityLoading: false,
+                    reachabilityError: snapshot.reachabilityError,
+                    locationRows: DomainViewModel.locationRows(from: snapshot),
+                    geolocation: snapshot.ipGeolocation,
+                    geolocationLoading: false,
+                    geolocationError: snapshot.ipGeolocationError,
+                    standardPortRows: DomainViewModel.portRows(from: snapshot, kind: .standard),
+                    customPortRows: DomainViewModel.portRows(from: snapshot, kind: .custom),
+                    portScanLoading: false,
+                    portScanError: snapshot.portScanError,
+                    customPortScanLoading: false,
+                    customPortScanError: nil,
+                    isCloudflareProxied: snapshot.httpHeaders.contains(where: { $0.name.lowercased() == "cf-ray" }),
+                    customPortsExpanded: .constant(false),
+                    customPortInput: .constant(""),
+                    onScanCustomPorts: {}
+                )
+                .padding(.top, 16)
             }
             .padding(.horizontal)
             .padding(.bottom, 32)
         }
         .background(Color.black)
         .navigationTitle(entry.domain)
+        .toolbar {
+            Button("Re-run") {
+                viewModel.rerunLookup(from: entry)
+            }
+        }
         .preferredColorScheme(.dark)
     }
 
-    // MARK: - Cached Banner
-
-    private var cachedBanner: some View {
-        HStack(spacing: 6) {
+    private var snapshotBanner: some View {
+        HStack(spacing: 8) {
             Image(systemName: "archivebox")
                 .font(.caption)
-            Text("Cached result from \(dateFmt.string(from: entry.timestamp))")
+            Text("Snapshot from \(dateFormatter.string(from: entry.timestamp))")
                 .font(.system(.caption, design: .monospaced))
+            Spacer()
+            Text("Live re-run available")
+                .font(.system(.caption2, design: .monospaced))
+                .foregroundStyle(.secondary)
         }
         .foregroundStyle(.secondary)
         .padding(8)
@@ -101,440 +161,4 @@ struct HistoryDetailView: View {
         .cornerRadius(6)
         .padding(.vertical, 12)
     }
-
-    // MARK: - Reachability
-
-    private var reachabilitySection: some View {
-        VStack(alignment: .leading, spacing: 12) {
-            if !entry.reachabilityResults.isEmpty {
-                sectionHeader("Reachability")
-                VStack(alignment: .leading, spacing: 4) {
-                    ForEach(entry.reachabilityResults) { result in
-                        HStack(spacing: 8) {
-                            Circle()
-                                .fill(result.reachable ? Color.green : Color.red)
-                                .frame(width: 8, height: 8)
-                            Text("Port \(result.port)")
-                                .font(.system(.caption, design: .monospaced))
-                            if result.reachable, let ms = result.latencyMs {
-                                Text("\(ms)ms")
-                                    .font(.system(.caption, design: .monospaced))
-                                    .foregroundStyle(.secondary)
-                            } else if !result.reachable {
-                                Text("—")
-                                    .font(.system(.caption, design: .monospaced))
-                                    .foregroundStyle(.secondary)
-                            }
-                            Spacer()
-                            Text(result.reachable ? "Reachable" : "Unreachable")
-                                .font(.system(.caption, design: .monospaced))
-                                .foregroundStyle(result.reachable ? .green : .red)
-                        }
-                    }
-                }
-                .padding(10)
-                .background(Color(.systemGray6).opacity(0.5))
-                .cornerRadius(6)
-            }
-        }
-        .padding(.top, 8)
-    }
-
-    // MARK: - Redirect Chain
-
-    private var redirectChainSection: some View {
-        VStack(alignment: .leading, spacing: 12) {
-            if !entry.redirectChain.isEmpty {
-                sectionHeader("Redirect Chain")
-                if entry.redirectChain.count == 1,
-                   let only = entry.redirectChain.first,
-                   only.isFinal, !(300...399).contains(only.statusCode) {
-                    Text("No redirects — direct connection")
-                        .font(.system(.caption, design: .monospaced))
-                        .foregroundStyle(.secondary)
-                        .padding(10)
-                        .frame(maxWidth: .infinity, alignment: .leading)
-                        .background(Color(.systemGray6).opacity(0.5))
-                        .cornerRadius(6)
-                } else {
-                    horizontallyScrollableCard {
-                        ForEach(entry.redirectChain) { hop in
-                            HStack(alignment: .top, spacing: 6) {
-                                Text("\(hop.stepNumber)")
-                                    .font(.system(.caption, design: .monospaced))
-                                    .foregroundStyle(.secondary)
-                                    .frame(width: 16, alignment: .trailing)
-                                Text("\(hop.statusCode)")
-                                    .font(.system(.caption, design: .monospaced))
-                                    .foregroundStyle(.cyan)
-                                    .frame(width: 30, alignment: .leading)
-                                Text(hop.url)
-                                    .font(.system(.caption, design: .monospaced))
-                                    .foregroundStyle(.primary)
-                                    .textSelection(.enabled)
-                                if hop.isFinal {
-                                    Text("(final)")
-                                        .font(.system(.caption2, design: .monospaced))
-                                        .foregroundStyle(.secondary)
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        .padding(.top, 16)
-    }
-
-    // MARK: - DNS
-
-    private var dnsSection: some View {
-        VStack(alignment: .leading, spacing: 12) {
-            sectionHeader("DNS Records")
-            ForEach(entry.dnsSections) { section in
-                horizontallyScrollableCard {
-                    Text(section.recordType.rawValue)
-                        .font(.system(.subheadline, design: .monospaced))
-                        .fontWeight(.semibold)
-                        .foregroundStyle(.cyan)
-
-                    if let error = section.error {
-                        errorLabel(error)
-                    } else if section.records.isEmpty {
-                        Text("No records found")
-                            .font(.system(.caption, design: .monospaced))
-                            .foregroundStyle(.secondary)
-                    } else {
-                        recordRows(section.records)
-                    }
-
-                    if !section.wildcardRecords.isEmpty {
-                        Text("*.\(entry.domain)")
-                            .font(.system(.caption, design: .monospaced))
-                            .fontWeight(.medium)
-                            .foregroundStyle(.cyan.opacity(0.7))
-                            .padding(.top, 4)
-                        recordRows(section.wildcardRecords)
-                    }
-                }
-
-                if section.recordType == .A {
-                    horizontallyScrollableCard {
-                        Text("PTR (Reverse DNS)")
-                            .font(.system(.subheadline, design: .monospaced))
-                            .fontWeight(.semibold)
-                            .foregroundStyle(.cyan)
-
-                        if let ptr = entry.ptrRecord {
-                            Text(ptr)
-                                .font(.system(.caption, design: .monospaced))
-                                .foregroundStyle(.primary)
-                                .textSelection(.enabled)
-                        } else {
-                            Text("No PTR record found")
-                                .font(.system(.caption, design: .monospaced))
-                                .foregroundStyle(.secondary)
-                        }
-                    }
-                }
-            }
-        }
-        .padding(.top, 16)
-    }
-
-    // MARK: - Email Security
-
-    @State private var expandedEmailField: String?
-
-    private var emailSecuritySection: some View {
-        VStack(alignment: .leading, spacing: 12) {
-            if let email = entry.emailSecurity {
-                sectionHeader("Email Security")
-                horizontallyScrollableCard(spacing: 6) {
-                    historyEmailRow("SPF", record: email.spf)
-                    historyEmailRow("DMARC", record: email.dmarc)
-                    historyEmailRow("DKIM", record: email.dkim)
-                    historyEmailRow("MTA-STS", mtaSts: entry.mtaSts ?? email.mtaSts)
-                    historyEmailRow("BIMI", record: email.bimi)
-                }
-            }
-        }
-        .frame(maxWidth: .infinity, alignment: .leading)
-        .padding(.top, 16)
-    }
-
-    private func historyEmailRow(_ label: String, record: EmailSecurityRecord) -> some View {
-        VStack(alignment: .leading, spacing: 2) {
-            HStack(spacing: 8) {
-                Text(label)
-                    .font(.system(.caption, design: .monospaced))
-                    .fontWeight(.semibold)
-                    .frame(width: 72, alignment: .leading)
-                Text(record.found ? "✓" : "✗")
-                    .font(.system(.caption, design: .monospaced))
-                    .foregroundStyle(record.found ? .green : .red)
-                if let value = record.value {
-                    let isExpanded = expandedEmailField == label
-                    let displayValue = isExpanded ? value : String(value.prefix(80))
-                    Text(displayValue)
-                        .font(.system(.caption2, design: .monospaced))
-                        .foregroundStyle(.primary)
-                        .textSelection(.enabled)
-                        .lineLimit(isExpanded ? nil : 1)
-                        .onTapGesture {
-                            withAnimation {
-                                expandedEmailField = isExpanded ? nil : label
-                            }
-                        }
-                    if let selector = record.matchedSelector {
-                        Text("(selector: \(selector))")
-                            .font(.system(.caption2, design: .monospaced))
-                            .foregroundStyle(.secondary)
-                    }
-                } else {
-                    Text("No record found")
-                        .font(.system(.caption2, design: .monospaced))
-                        .foregroundStyle(.secondary)
-                }
-            }
-        }
-    }
-
-    private func historyEmailRow(_ label: String, mtaSts: MTASTSResult?) -> some View {
-        VStack(alignment: .leading, spacing: 2) {
-            HStack(spacing: 8) {
-                Text(label)
-                    .font(.system(.caption, design: .monospaced))
-                    .fontWeight(.semibold)
-                    .frame(width: 72, alignment: .leading)
-                Text(mtaSts?.txtFound == true ? "✓" : "✗")
-                    .font(.system(.caption, design: .monospaced))
-                    .foregroundStyle(mtaSts?.txtFound == true ? .green : .red)
-                if let policyMode = mtaSts?.policyMode {
-                    Text(policyMode)
-                        .font(.system(.caption2, design: .monospaced))
-                        .foregroundStyle(.primary)
-                        .textSelection(.enabled)
-                } else {
-                    Text(mtaSts?.txtFound == true ? "Policy unavailable" : "No record found")
-                        .font(.system(.caption2, design: .monospaced))
-                        .foregroundStyle(.secondary)
-                }
-            }
-        }
-    }
-
-    // MARK: - SSL
-
-    private var sslSection: some View {
-        VStack(alignment: .leading, spacing: 12) {
-            if let info = entry.sslInfo {
-                sectionHeader("SSL / TLS Certificate")
-                horizontallyScrollableCard(spacing: 8) {
-                    labelRow("Common Name", info.commonName)
-                    labelRow("Issuer", info.issuer)
-
-                    VStack(alignment: .leading, spacing: 2) {
-                        Text("SANs")
-                            .font(.system(.caption2, design: .monospaced))
-                            .foregroundStyle(.secondary)
-                        ForEach(info.subjectAltNames, id: \.self) { san in
-                            Text(san)
-                                .font(.system(.caption, design: .monospaced))
-                                .textSelection(.enabled)
-                        }
-                    }
-
-                    labelRow("Valid From", DateFormatter.certDate.string(from: info.validFrom))
-                    labelRow("Valid Until", DateFormatter.certDate.string(from: info.validUntil))
-
-                    HStack {
-                        Text("Days Until Expiry")
-                            .font(.system(.caption2, design: .monospaced))
-                            .foregroundStyle(.secondary)
-                        Spacer()
-                        Text("\(info.daysUntilExpiry)")
-                            .font(.system(.caption, design: .monospaced))
-                            .fontWeight(.bold)
-                            .foregroundStyle(expiryColor(info.daysUntilExpiry))
-                    }
-
-                    labelRow("Chain Depth", "\(info.chainDepth)")
-                }
-            }
-        }
-        .padding(.top, 16)
-    }
-
-    // MARK: - HTTP Headers
-
-    private var httpHeadersSection: some View {
-        VStack(alignment: .leading, spacing: 12) {
-            if !entry.httpHeaders.isEmpty {
-                sectionHeader("HTTP Headers")
-                horizontallyScrollableCard {
-                    ForEach(entry.httpHeaders) { header in
-                        HStack(alignment: .top, spacing: 4) {
-                            Text(header.name + ":")
-                                .font(.system(.caption, design: .monospaced))
-                                .foregroundStyle(header.isSecurityHeader ? .yellow : .cyan)
-                            Text(header.value)
-                                .font(.system(.caption, design: .monospaced))
-                                .foregroundStyle(.primary)
-                                .textSelection(.enabled)
-                        }
-                    }
-                }
-            }
-        }
-        .padding(.top, 16)
-    }
-
-    // MARK: - IP Geolocation
-
-    private var ipGeolocationSection: some View {
-        VStack(alignment: .leading, spacing: 12) {
-            if let geo = entry.ipGeolocation {
-                sectionHeader("IP Location")
-                VStack(alignment: .leading, spacing: 6) {
-                    horizontallyScrollableContent(spacing: 6) {
-                        labelRow("IP", geo.ip)
-                        if let org = geo.org {
-                            labelRow("Org / ISP", org)
-                        }
-                        let location = [geo.city, geo.region, geo.country_name].compactMap { $0 }.joined(separator: ", ")
-                        if !location.isEmpty {
-                            labelRow("Location", location)
-                        }
-                    }
-
-                    if let lat = geo.latitude, let lon = geo.longitude {
-                        let coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lon)
-                        Map(initialPosition: .region(MKCoordinateRegion(
-                            center: coordinate,
-                            span: MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1)
-                        ))) {
-                            Marker(geo.ip, coordinate: coordinate)
-                        }
-                        .mapStyle(.standard)
-                        .frame(maxWidth: .infinity)
-                        .frame(height: 180)
-                        .cornerRadius(8)
-                    }
-                }
-                .padding(10)
-                .background(Color(.systemGray6).opacity(0.5))
-                .cornerRadius(6)
-            }
-        }
-        .padding(.top, 16)
-    }
-
-    // MARK: - Port Scan
-
-    private var portScanSection: some View {
-        VStack(alignment: .leading, spacing: 12) {
-            if !entry.portScanResults.isEmpty {
-                sectionHeader("Open Ports")
-                VStack(alignment: .leading, spacing: 4) {
-                    ForEach(entry.portScanResults) { result in
-                        HStack(spacing: 8) {
-                            Circle()
-                                .fill(result.open ? Color.green : Color(.systemGray4))
-                                .frame(width: 8, height: 8)
-                            Text("\(result.port)")
-                                .font(.system(.caption, design: .monospaced))
-                                .frame(width: 44, alignment: .leading)
-                            Text(result.service)
-                                .font(.system(.caption, design: .monospaced))
-                                .foregroundStyle(result.open ? .primary : .secondary)
-                            Spacer()
-                            if result.open {
-                                Text("Open")
-                                    .font(.system(.caption2, design: .monospaced))
-                                    .foregroundStyle(.green)
-                            }
-                        }
-                    }
-                }
-                .padding(10)
-                .background(Color(.systemGray6).opacity(0.5))
-                .cornerRadius(6)
-            }
-        }
-        .padding(.top, 16)
-    }
-
-    // MARK: - Helpers
-
-    private func sectionHeader(_ title: String) -> some View {
-        Text(title)
-            .font(.system(.headline, design: .default))
-            .foregroundStyle(.white)
-    }
-
-    private func labelRow(_ label: String, _ value: String) -> some View {
-        VStack(alignment: .leading, spacing: 2) {
-            Text(label)
-                .font(.system(.caption2, design: .monospaced))
-                .foregroundStyle(.secondary)
-            Text(value)
-                .font(.system(.caption, design: .monospaced))
-                .textSelection(.enabled)
-        }
-    }
-
-    private func recordRows(_ records: [DNSRecord]) -> some View {
-        ForEach(records) { record in
-            HStack(alignment: .top) {
-                Text(record.value)
-                    .font(.system(.caption, design: .monospaced))
-                    .foregroundStyle(.primary)
-                    .textSelection(.enabled)
-                Spacer()
-                Text("TTL \(record.ttl)")
-                    .font(.system(.caption2, design: .monospaced))
-                    .foregroundStyle(.secondary)
-            }
-        }
-    }
-
-    private func horizontallyScrollableCard<Content: View>(
-        spacing: CGFloat = 4,
-        @ViewBuilder content: () -> Content
-    ) -> some View {
-        horizontallyScrollableContent(spacing: spacing) {
-            content()
-        }
-        .padding(10)
-        .background(Color(.systemGray6).opacity(0.5))
-        .cornerRadius(6)
-    }
-
-    private func horizontallyScrollableContent<Content: View>(
-        spacing: CGFloat = 4,
-        @ViewBuilder content: () -> Content
-    ) -> some View {
-        ScrollView(.horizontal) {
-            VStack(alignment: .leading, spacing: spacing) {
-                content()
-            }
-            .scrollTargetLayout()
-        }
-        .scrollBounceBehavior(.basedOnSize, axes: .horizontal)
-        .frame(maxWidth: .infinity, alignment: .leading)
-    }
-
-    private func errorLabel(_ message: String) -> some View {
-        Label(message, systemImage: "exclamationmark.triangle.fill")
-            .font(.system(.caption, design: .monospaced))
-            .foregroundStyle(.red)
-            .padding(8)
-    }
-
-    private func expiryColor(_ days: Int) -> Color {
-        if days < 30 { return .red }
-        if days < 60 { return .yellow }
-        return .green
-    }
 }
diff --git a/DomainDig/IPGeolocationService.swift b/DomainDig/IPGeolocationService.swift
index 6c60295..809dd28 100644
--- a/DomainDig/IPGeolocationService.swift
+++ b/DomainDig/IPGeolocationService.swift
@@ -1,17 +1,22 @@
 import Foundation
 
 struct IPGeolocationService {
-    static func lookup(ip: String) async throws -> IPGeolocation {
+    static func lookup(ip: String) async -> ServiceResult<IPGeolocation> {
         let url = URL(string: "https://ipapi.co/\(ip)/json/")!
         let request = URLRequest(url: url, timeoutInterval: 10)
 
-        let (data, response) = try await URLSession.shared.data(for: request)
+        do {
+            let (data, response) = try await URLSession.shared.data(for: request)
 
-        guard let httpResponse = response as? HTTPURLResponse,
-              httpResponse.statusCode == 200 else {
-            throw URLError(.badServerResponse)
-        }
+            guard let httpResponse = response as? HTTPURLResponse,
+                  httpResponse.statusCode == 200 else {
+                return .error(URLError(.badServerResponse).localizedDescription)
+            }
 
-        return try JSONDecoder().decode(IPGeolocation.self, from: data)
+            let geolocation = try JSONDecoder().decode(IPGeolocation.self, from: data)
+            return .success(geolocation)
+        } catch {
+            return .error(error.localizedDescription)
+        }
     }
 }
diff --git a/DomainDig/Models.swift b/DomainDig/Models.swift
index 8a84adf..d854137 100644
--- a/DomainDig/Models.swift
+++ b/DomainDig/Models.swift
@@ -1,5 +1,11 @@
 import Foundation
 
+enum ServiceResult<Value> {
+    case success(Value)
+    case empty(String)
+    case error(String)
+}
+
 // MARK: - DNS Models
 
 enum DNSRecordType: String, CaseIterable, Codable {
@@ -13,6 +19,7 @@ enum DNSRecordType: String, CaseIterable, Codable {
     case SRV
     case CAA
     case DS
+    case PTR
 
     var queryType: Int {
         switch self {
@@ -26,6 +33,7 @@ enum DNSRecordType: String, CaseIterable, Codable {
         case .SRV: return 33
         case .CAA: return 257
         case .DS: return 43
+        case .PTR: return 12
         }
     }
 
@@ -223,18 +231,34 @@ struct RedirectHop: Identifiable, Codable {
 
 // MARK: - Port Scan Models
 
+enum PortScanKind: String, Codable {
+    case standard
+    case custom
+}
+
 struct PortScanResult: Identifiable, Codable {
     var id = UUID()
     let port: UInt16
     let service: String
     let open: Bool
     var banner: String?
-
-    nonisolated init(port: UInt16, service: String, open: Bool, banner: String? = nil) {
+    let kind: PortScanKind
+    let durationMs: Int?
+
+    nonisolated init(
+        port: UInt16,
+        service: String,
+        open: Bool,
+        banner: String? = nil,
+        kind: PortScanKind = .standard,
+        durationMs: Int? = nil
+    ) {
         self.port = port
         self.service = service
         self.open = open
         self.banner = banner
+        self.kind = kind
+        self.durationMs = durationMs
     }
 
     init(from decoder: Decoder) throws {
@@ -244,6 +268,8 @@ struct PortScanResult: Identifiable, Codable {
         service = try container.decode(String.self, forKey: .service)
         open = try container.decode(Bool.self, forKey: .open)
         banner = try container.decodeIfPresent(String.self, forKey: .banner)
+        kind = try container.decodeIfPresent(PortScanKind.self, forKey: .kind) ?? .standard
+        durationMs = try container.decodeIfPresent(Int.self, forKey: .durationMs)
     }
 }
 
@@ -264,13 +290,28 @@ struct HistoryEntry: Identifiable, Codable {
     var redirectChain: [RedirectHop]
     var portScanResults: [PortScanResult]
     var hstsPreloaded: Bool?
+    var resolverDisplayName: String
+    var resolverURLString: String
+    var totalLookupDurationMs: Int?
+    var sslError: String?
+    var httpHeadersError: String?
+    var reachabilityError: String?
+    var ipGeolocationError: String?
+    var emailSecurityError: String?
+    var ptrError: String?
+    var redirectChainError: String?
+    var portScanError: String?
 
     init(domain: String, timestamp: Date, dnsSections: [DNSSection],
          sslInfo: SSLCertificateInfo?, httpHeaders: [HTTPHeader],
          reachabilityResults: [PortReachability], ipGeolocation: IPGeolocation?,
          emailSecurity: EmailSecurityResult? = nil, mtaSts: MTASTSResult? = nil, ptrRecord: String? = nil,
          redirectChain: [RedirectHop] = [], portScanResults: [PortScanResult] = [],
-         hstsPreloaded: Bool? = nil) {
+         hstsPreloaded: Bool? = nil, resolverDisplayName: String, resolverURLString: String,
+         totalLookupDurationMs: Int? = nil, sslError: String? = nil, httpHeadersError: String? = nil,
+         reachabilityError: String? = nil, ipGeolocationError: String? = nil,
+         emailSecurityError: String? = nil, ptrError: String? = nil,
+         redirectChainError: String? = nil, portScanError: String? = nil) {
         self.domain = domain
         self.timestamp = timestamp
         self.dnsSections = dnsSections
@@ -284,6 +325,17 @@ struct HistoryEntry: Identifiable, Codable {
         self.redirectChain = redirectChain
         self.portScanResults = portScanResults
         self.hstsPreloaded = hstsPreloaded
+        self.resolverDisplayName = resolverDisplayName
+        self.resolverURLString = resolverURLString
+        self.totalLookupDurationMs = totalLookupDurationMs
+        self.sslError = sslError
+        self.httpHeadersError = httpHeadersError
+        self.reachabilityError = reachabilityError
+        self.ipGeolocationError = ipGeolocationError
+        self.emailSecurityError = emailSecurityError
+        self.ptrError = ptrError
+        self.redirectChainError = redirectChainError
+        self.portScanError = portScanError
     }
 
     init(from decoder: Decoder) throws {
@@ -302,6 +354,17 @@ struct HistoryEntry: Identifiable, Codable {
         redirectChain = try container.decodeIfPresent([RedirectHop].self, forKey: .redirectChain) ?? []
         portScanResults = try container.decodeIfPresent([PortScanResult].self, forKey: .portScanResults) ?? []
         hstsPreloaded = try container.decodeIfPresent(Bool.self, forKey: .hstsPreloaded)
+        resolverDisplayName = try container.decodeIfPresent(String.self, forKey: .resolverDisplayName) ?? "Cloudflare"
+        resolverURLString = try container.decodeIfPresent(String.self, forKey: .resolverURLString) ?? DNSResolverOption.defaultURLString
+        totalLookupDurationMs = try container.decodeIfPresent(Int.self, forKey: .totalLookupDurationMs)
+        sslError = try container.decodeIfPresent(String.self, forKey: .sslError)
+        httpHeadersError = try container.decodeIfPresent(String.self, forKey: .httpHeadersError)
+        reachabilityError = try container.decodeIfPresent(String.self, forKey: .reachabilityError)
+        ipGeolocationError = try container.decodeIfPresent(String.self, forKey: .ipGeolocationError)
+        emailSecurityError = try container.decodeIfPresent(String.self, forKey: .emailSecurityError)
+        ptrError = try container.decodeIfPresent(String.self, forKey: .ptrError)
+        redirectChainError = try container.decodeIfPresent(String.self, forKey: .redirectChainError)
+        portScanError = try container.decodeIfPresent(String.self, forKey: .portScanError)
     }
 }
 
diff --git a/DomainDig/PortScanService.swift b/DomainDig/PortScanService.swift
index 4dc9001..36c2827 100644
--- a/DomainDig/PortScanService.swift
+++ b/DomainDig/PortScanService.swift
@@ -20,12 +20,18 @@ struct PortScanService {
         PortInfo(port: 8443, service: "HTTPS Alt"),
     ]
 
-    static func scanAll(domain: String) async -> [PortScanResult] {
+    static func scanAll(domain: String) async -> ServiceResult<[PortScanResult]> {
         await withTaskGroup(of: PortScanResult.self, returning: [PortScanResult].self) { group in
             for info in ports {
                 group.addTask {
-                    let open = await probe(domain: domain, port: info.port)
-                    return PortScanResult(port: info.port, service: info.service, open: open)
+                    let result = await probe(domain: domain, port: info.port)
+                    return PortScanResult(
+                        port: info.port,
+                        service: info.service,
+                        open: result.open,
+                        kind: .standard,
+                        durationMs: result.durationMs
+                    )
                 }
             }
 
@@ -35,20 +41,24 @@ struct PortScanService {
             }
 
             // Sort by port number
-            return results.sorted { $0.port < $1.port }
+            let sorted = results.sorted { $0.port < $1.port }
+            return sorted.isEmpty ? [] : sorted
         }
+        .pipe { $0.isEmpty ? .empty("No port scan results") : .success($0) }
     }
 
-    static func scanPorts(domain: String, ports: [UInt16], timeout: TimeInterval) async -> [PortScanResult] {
+    static func scanPorts(domain: String, ports: [UInt16], timeout: TimeInterval) async -> ServiceResult<[PortScanResult]> {
         await withTaskGroup(of: PortScanResult.self, returning: [PortScanResult].self) { group in
             for port in ports {
                 let service = self.ports.first(where: { $0.port == port })?.service ?? "Custom"
                 group.addTask {
-                    let open = await probe(domain: domain, port: port, timeout: timeout)
+                    let result = await probe(domain: domain, port: port, timeout: timeout)
                     return PortScanResult(
                         port: port,
                         service: service,
-                        open: open
+                        open: result.open,
+                        kind: .custom,
+                        durationMs: result.durationMs
                     )
                 }
             }
@@ -58,8 +68,10 @@ struct PortScanService {
                 results.append(result)
             }
 
-            return results.sorted { $0.port < $1.port }
+            let sorted = results.sorted { $0.port < $1.port }
+            return sorted.isEmpty ? [] : sorted
         }
+        .pipe { $0.isEmpty ? .empty("No custom port scan results") : .success($0) }
     }
 
     static func grabBanner(host: String, port: UInt16, timeout: TimeInterval = 3.0) async -> String? {
@@ -111,11 +123,11 @@ struct PortScanService {
         }
     }
 
-    private static func probe(domain: String, port: UInt16) async -> Bool {
+    private static func probe(domain: String, port: UInt16) async -> PortProbeResult {
         await probe(domain: domain, port: port, timeout: 5)
     }
 
-    private static func probe(domain: String, port: UInt16, timeout: TimeInterval) async -> Bool {
+    private static func probe(domain: String, port: UInt16, timeout: TimeInterval) async -> PortProbeResult {
         await withCheckedContinuation { continuation in
             let host = NWEndpoint.Host(domain)
             let nwPort = NWEndpoint.Port(rawValue: port)!
@@ -143,13 +155,19 @@ struct PortScanService {
     }
 }
 
+private struct PortProbeResult: Sendable {
+    let open: Bool
+    let durationMs: Int?
+}
+
 private final class ProbeContext: @unchecked Sendable {
     private let connection: NWConnection
-    private let continuation: CheckedContinuation<Bool, Never>
+    private let continuation: CheckedContinuation<PortProbeResult, Never>
+    private let start = CFAbsoluteTimeGetCurrent()
     private let lock = NSLock()
     private nonisolated(unsafe) var resumed = false
 
-    init(connection: NWConnection, continuation: CheckedContinuation<Bool, Never>) {
+    init(connection: NWConnection, continuation: CheckedContinuation<PortProbeResult, Never>) {
         self.connection = connection
         self.continuation = continuation
     }
@@ -164,7 +182,17 @@ private final class ProbeContext: @unchecked Sendable {
         lock.unlock()
 
         connection.cancel()
-        continuation.resume(returning: open)
+        let elapsedMs = Int((CFAbsoluteTimeGetCurrent() - start) * 1000)
+        continuation.resume(returning: PortProbeResult(
+            open: open,
+            durationMs: elapsedMs >= 0 ? elapsedMs : nil
+        ))
+    }
+}
+
+private extension Array {
+    func pipe<T>(_ transform: (Self) -> T) -> T {
+        transform(self)
     }
 }
 
diff --git a/DomainDig/ReachabilityService.swift b/DomainDig/ReachabilityService.swift
index 0bb30ae..3fb7dd9 100644
--- a/DomainDig/ReachabilityService.swift
+++ b/DomainDig/ReachabilityService.swift
@@ -29,10 +29,11 @@ struct ReachabilityService {
         }
     }
 
-    static func checkAll(domain: String) async -> [PortReachability] {
+    static func checkAll(domain: String) async -> ServiceResult<[PortReachability]> {
         async let port443 = check(domain: domain, port: 443)
         async let port80 = check(domain: domain, port: 80)
-        return await [port443, port80]
+        let results = await [port443, port80]
+        return results.isEmpty ? .empty("No reachability results") : .success(results)
     }
 }
 
diff --git a/DomainDig/RedirectChainService.swift b/DomainDig/RedirectChainService.swift
index 541e118..d55a4aa 100644
--- a/DomainDig/RedirectChainService.swift
+++ b/DomainDig/RedirectChainService.swift
@@ -1,12 +1,17 @@
 import Foundation
 
 struct RedirectChainService {
-    static func trace(domain: String) async throws -> [RedirectHop] {
-        // Try HTTPS first (avoids ATS issues), fall back to HTTP if it fails entirely
+    static func trace(domain: String) async -> ServiceResult<[RedirectHop]> {
         do {
-            return try await followChain(startingURL: URL(string: "https://\(domain)")!)
+            let hops = try await followChain(startingURL: URL(string: "https://\(domain)")!)
+            return hops.isEmpty ? .empty("No redirect data available") : .success(hops)
         } catch {
-            return try await followChain(startingURL: URL(string: "http://\(domain)")!)
+            do {
+                let hops = try await followChain(startingURL: URL(string: "http://\(domain)")!)
+                return hops.isEmpty ? .empty("No redirect data available") : .success(hops)
+            } catch {
+                return .error(error.localizedDescription)
+            }
         }
     }
 
diff --git a/DomainDig/ReverseDNSService.swift b/DomainDig/ReverseDNSService.swift
index caab0f7..3b212a8 100644
--- a/DomainDig/ReverseDNSService.swift
+++ b/DomainDig/ReverseDNSService.swift
@@ -1,48 +1,27 @@
 import Foundation
 
 struct ReverseDNSService {
-    /// Look up the PTR record for an IPv4 address via Cloudflare DoH.
-    static func lookup(ip: String) async -> String? {
+    static func lookup(ip: String, resolverURLString: String) async -> ServiceResult<String> {
         let octets = ip.split(separator: ".")
-        guard octets.count == 4 else { return nil }
+        guard octets.count == 4 else {
+            return .error("Invalid IPv4 address")
+        }
 
         let reversed = octets.reversed().joined(separator: ".")
         let ptrDomain = "\(reversed).in-addr.arpa"
 
-        // PTR record type = 12
         do {
-            let records = try await lookupPTR(domain: ptrDomain)
-            return records.first
+            let records = try await DNSLookupService.lookup(
+                domain: ptrDomain,
+                recordType: .PTR,
+                resolverURLString: resolverURLString
+            )
+            if let record = records.first?.value {
+                return .success(record)
+            }
+            return .empty("No PTR record found")
         } catch {
-            return nil
+            return .error(error.localizedDescription)
         }
     }
-
-    private static func lookupPTR(domain: String) async throws -> [String] {
-        var components = URLComponents(string: "https://cloudflare-dns.com/dns-query")!
-        components.queryItems = [
-            URLQueryItem(name: "name", value: domain),
-            URLQueryItem(name: "type", value: "12") // PTR
-        ]
-
-        var request = URLRequest(url: components.url!)
-        request.setValue("application/dns-json", forHTTPHeaderField: "Accept")
-
-        let (data, response) = try await URLSession.shared.data(for: request)
-
-        guard let httpResponse = response as? HTTPURLResponse,
-              httpResponse.statusCode == 200 else {
-            throw URLError(.badServerResponse)
-        }
-
-        let dnsResponse = try JSONDecoder().decode(CloudflareDNSResponse.self, from: data)
-
-        guard let answers = dnsResponse.Answer else {
-            return []
-        }
-
-        return answers
-            .filter { $0.type == 12 }
-            .map { $0.data.trimmingCharacters(in: CharacterSet(charactersIn: "\"")) }
-    }
 }
diff --git a/DomainDig/SSLCheckService.swift b/DomainDig/SSLCheckService.swift
index 6cf8ec3..e180d4e 100644
--- a/DomainDig/SSLCheckService.swift
+++ b/DomainDig/SSLCheckService.swift
@@ -3,7 +3,7 @@ import Security
 
 struct SSLCheckService {
 
-    static func check(domain: String) async throws -> SSLCertificateInfo {
+    static func check(domain: String) async -> ServiceResult<SSLCertificateInfo> {
         let delegate = SSLSessionDelegate()
         let session = URLSession(
             configuration: .ephemeral,
@@ -15,14 +15,17 @@ struct SSLCheckService {
         let url = URL(string: "https://\(domain)")!
         let request = URLRequest(url: url, timeoutInterval: 10)
 
-        // We only need to establish the connection to grab the cert
-        _ = try await session.data(for: request)
+        do {
+            _ = try await session.data(for: request)
 
-        guard let trust = delegate.serverTrust else {
-            throw SSLError.noCertificate
-        }
+            guard let trust = delegate.serverTrust else {
+                return .empty(SSLError.noCertificate.localizedDescription)
+            }
 
-        return try extractCertificateInfo(from: trust, metadata: delegate.tlsMetadata)
+            return .success(try extractCertificateInfo(from: trust, metadata: delegate.tlsMetadata))
+        } catch {
+            return .error(error.localizedDescription)
+        }
     }
 
     static func checkHSTSPreload(domain: String) async -> Bool? {