diff --git a/app/controllers/OrderApiController.asp b/app/controllers/OrderApiController.asp
index 748c821..d3ad008 100644
--- a/app/controllers/OrderApiController.asp
+++ b/app/controllers/OrderApiController.asp
@@ -119,6 +119,7 @@ Class OrderApiController_Class
' should not turn a successful submission into a failed one.
OrderMailer().SendOrderDetailsEmail order, model
+ Flash().Success = "Your order has been submitted. We'll be in touch with your quote soon."
Response.Write "{""success"":true}"
End Sub
diff --git a/app/controllers/RequestOrderController.asp b/app/controllers/RequestOrderController.asp
index b33e684..53ca498 100644
--- a/app/controllers/RequestOrderController.asp
+++ b/app/controllers/RequestOrderController.asp
@@ -93,7 +93,7 @@ Class RequestOrderController_Class
OrderMailer().SendContinuationEmail email, token
Flash().Success = "Check your email for a link to continue your order."
- Response.Redirect Routes().AppURL & "request-order"
+ Response.Redirect Routes().AppURL
End Sub
End Class
diff --git a/app/models/OrderDetailsAnswersMapper.asp b/app/models/OrderDetailsAnswersMapper.asp
index 3fb4860..2c3d0f8 100644
--- a/app/models/OrderDetailsAnswersMapper.asp
+++ b/app/models/OrderDetailsAnswersMapper.asp
@@ -19,6 +19,7 @@ Function ValidateOrderDetailsAnswers(answers)
RequireEnum errors, answers, "PurplePrintStyle", Array("ColorCoding", "BlackOnly")
RequireEnum errors, answers, "PurpleColorCodingBy", Array("Precinct", "Election")
RequireEnum errors, answers, "BluePermitOwnership", Array("KCIPermit", "OwnPermit")
+ RequireEnum errors, answers, "BlueHasNonprofitStatus", Array("Yes", "No", "Unsure")
RequireEnum errors, answers, "MailingPostageOption", Array("FirstClass", "NonprofitRate", "PresortStandard", "No")
RequireEnum errors, answers, "MailingHasNonprofitStatus", Array("Yes", "No", "NotSure")
@@ -27,9 +28,9 @@ Function ValidateOrderDetailsAnswers(answers)
RequireBoolean errors, answers, "PurpleTrackMIBallot"
RequireBoolean errors, answers, "BlueWantsQuote"
RequireBoolean errors, answers, "BluePrintPermit"
- RequireBoolean errors, answers, "BlueHasNonprofitStatus"
RequireBoolean errors, answers, "MailingWantsService"
+ RequireNonNegativeInteger errors, answers, "PurplePermanentBallotListCount"
RequireNonNegativeInteger errors, answers, "PurplePrecinctCount"
RequireNonNegativeInteger errors, answers, "PurpleExtraEnvelopeQty"
RequireNonNegativeInteger errors, answers, "MailingEstimatedQuantity"
@@ -65,6 +66,7 @@ Sub MapOrderDetailsAnswers(ByRef model, answers)
model.PurpleColorNames = DictValue(answers, "PurpleColorNames")
model.PurpleTrackMIBallot = DictValue(answers, "PurpleTrackMIBallot")
model.PurpleExtraEnvelopeQty = DictValue(answers, "PurpleExtraEnvelopeQty")
+ model.PurplePermanentBallotListCount = DictValue(answers, "PurplePermanentBallotListCount")
' Blue envelope
model.BlueWantsQuote = DictValue(answers, "BlueWantsQuote")
diff --git a/app/models/OrderDetailsLabels.asp b/app/models/OrderDetailsLabels.asp
index c33be53..042cbed 100644
--- a/app/models/OrderDetailsLabels.asp
+++ b/app/models/OrderDetailsLabels.asp
@@ -69,6 +69,7 @@ Function LabelNonprofitStatus(code)
Case "Yes" : LabelNonprofitStatus = "Yes"
Case "No" : LabelNonprofitStatus = "No"
Case "NotSure" : LabelNonprofitStatus = "I'm not sure"
+ Case "Unsure" : LabelNonprofitStatus = "Unsure (that's okay, we can help)"
Case Else : LabelNonprofitStatus = CStr(code & "")
End Select
End Function
diff --git a/app/models/OrderDetailsRepository.asp b/app/models/OrderDetailsRepository.asp
index 144d0ec..99f715b 100644
--- a/app/models/OrderDetailsRepository.asp
+++ b/app/models/OrderDetailsRepository.asp
@@ -1,6 +1,6 @@
<%
' Auto-generated Repository for table [OrderDetails]
-' Generated on 8/4/2026 3:40:46 PM
+' Generated on 8/20/2026 11:48:14 AM
' Generator: GenerateRepo.vbs v1.0
'
' Dependencies:
@@ -13,7 +13,7 @@
Class OrderDetailsRepository_Class
Public Function FindByID(id)
- Dim sql : sql = "Select [BlueDeliveryDate], [BlueEnvelopeProvider], [BlueHasNonprofitStatus], [BlueNonprofitAuthCode], [BluePermitCity], [BluePermitClass], [BluePermitNumber], [BluePermitOwnership], [BluePrintPermit], [BluePrintReturnAddress], [BlueWantsQuote], [ContactName], [FutureVoterStickerRolls], [IVotedStickerRolls], [MailingEstimatedQuantity], [MailingHasNonprofitStatus], [MailingPickupDate], [MailingPostageOption], [MailingWantsService], [MailingWantsSorting], [Municipality], [OrderDetailID], [OrderID], [Phone], [PurpleBlueProvider], [PurpleBlueProviderOther], [PurpleColorCodingBy], [PurpleColorNames], [PurpleDeliveryDate], [PurpleEnvelopeProvider], [PurpleEnvelopeStock], [PurpleExtraEnvelopeQty], [PurplePrecinctCount], [PurplePrintOption], [PurplePrintStyle], [PurpleTrackMIBallot], [PurpleWantsBlueEnvelopes], [PurpleWantsQuote], [SecrecySleevesQty], [SpecialRequests], [SubmittedAt] FROM [OrderDetails] WHERE [OrderDetailID] = ?"
+ Dim sql : sql = "Select [BlueDeliveryDate], [BlueEnvelopeProvider], [BlueHasNonprofitStatus], [BlueNonprofitAuthCode], [BluePermitCity], [BluePermitClass], [BluePermitNumber], [BluePermitOwnership], [BluePrintPermit], [BluePrintReturnAddress], [BlueWantsQuote], [ContactName], [FutureVoterStickerRolls], [IVotedStickerRolls], [MailingEstimatedQuantity], [MailingHasNonprofitStatus], [MailingPickupDate], [MailingPostageOption], [MailingWantsService], [MailingWantsSorting], [Municipality], [OrderDetailID], [OrderID], [Phone], [PurpleBlueProvider], [PurpleBlueProviderOther], [PurpleColorCodingBy], [PurpleColorNames], [PurpleDeliveryDate], [PurpleEnvelopeProvider], [PurpleEnvelopeStock], [PurpleExtraEnvelopeQty], [PurplePermanentBallotListCount], [PurplePrecinctCount], [PurplePrintOption], [PurplePrintStyle], [PurpleTrackMIBallot], [PurpleWantsBlueEnvelopes], [PurpleWantsQuote], [SecrecySleevesQty], [SpecialRequests], [SubmittedAt] FROM [OrderDetails] WHERE [OrderDetailID] = ?"
Dim rs : Set rs = DAL.Query(sql, Array(id))
If rs.EOF Then
Err.Raise 1, "OrderDetailsRepository_Class", RecordNotFoundException("OrderDetailID", id)
@@ -28,7 +28,7 @@ Class OrderDetailsRepository_Class
End Function
Public Function Find(where_kvarray, order_string_or_array)
- Dim sql : sql = "Select [BlueDeliveryDate], [BlueEnvelopeProvider], [BlueHasNonprofitStatus], [BlueNonprofitAuthCode], [BluePermitCity], [BluePermitClass], [BluePermitNumber], [BluePermitOwnership], [BluePrintPermit], [BluePrintReturnAddress], [BlueWantsQuote], [ContactName], [FutureVoterStickerRolls], [IVotedStickerRolls], [MailingEstimatedQuantity], [MailingHasNonprofitStatus], [MailingPickupDate], [MailingPostageOption], [MailingWantsService], [MailingWantsSorting], [Municipality], [OrderDetailID], [OrderID], [Phone], [PurpleBlueProvider], [PurpleBlueProviderOther], [PurpleColorCodingBy], [PurpleColorNames], [PurpleDeliveryDate], [PurpleEnvelopeProvider], [PurpleEnvelopeStock], [PurpleExtraEnvelopeQty], [PurplePrecinctCount], [PurplePrintOption], [PurplePrintStyle], [PurpleTrackMIBallot], [PurpleWantsBlueEnvelopes], [PurpleWantsQuote], [SecrecySleevesQty], [SpecialRequests], [SubmittedAt] FROM [OrderDetails]"
+ Dim sql : sql = "Select [BlueDeliveryDate], [BlueEnvelopeProvider], [BlueHasNonprofitStatus], [BlueNonprofitAuthCode], [BluePermitCity], [BluePermitClass], [BluePermitNumber], [BluePermitOwnership], [BluePrintPermit], [BluePrintReturnAddress], [BlueWantsQuote], [ContactName], [FutureVoterStickerRolls], [IVotedStickerRolls], [MailingEstimatedQuantity], [MailingHasNonprofitStatus], [MailingPickupDate], [MailingPostageOption], [MailingWantsService], [MailingWantsSorting], [Municipality], [OrderDetailID], [OrderID], [Phone], [PurpleBlueProvider], [PurpleBlueProviderOther], [PurpleColorCodingBy], [PurpleColorNames], [PurpleDeliveryDate], [PurpleEnvelopeProvider], [PurpleEnvelopeStock], [PurpleExtraEnvelopeQty], [PurplePermanentBallotListCount], [PurplePrecinctCount], [PurplePrintOption], [PurplePrintStyle], [PurpleTrackMIBallot], [PurpleWantsBlueEnvelopes], [PurpleWantsQuote], [SecrecySleevesQty], [SpecialRequests], [SubmittedAt] FROM [OrderDetails]"
Dim where_keys, where_values, i
If Not IsEmpty(where_kvarray) Then
KVUnzip where_kvarray, where_keys, where_values
@@ -52,7 +52,7 @@ Class OrderDetailsRepository_Class
End Function
Public Function FindPaged(where_kvarray, order_string_or_array, per_page, page_num, ByRef page_count, ByRef record_count)
- Dim sql : sql = "Select [BlueDeliveryDate], [BlueEnvelopeProvider], [BlueHasNonprofitStatus], [BlueNonprofitAuthCode], [BluePermitCity], [BluePermitClass], [BluePermitNumber], [BluePermitOwnership], [BluePrintPermit], [BluePrintReturnAddress], [BlueWantsQuote], [ContactName], [FutureVoterStickerRolls], [IVotedStickerRolls], [MailingEstimatedQuantity], [MailingHasNonprofitStatus], [MailingPickupDate], [MailingPostageOption], [MailingWantsService], [MailingWantsSorting], [Municipality], [OrderDetailID], [OrderID], [Phone], [PurpleBlueProvider], [PurpleBlueProviderOther], [PurpleColorCodingBy], [PurpleColorNames], [PurpleDeliveryDate], [PurpleEnvelopeProvider], [PurpleEnvelopeStock], [PurpleExtraEnvelopeQty], [PurplePrecinctCount], [PurplePrintOption], [PurplePrintStyle], [PurpleTrackMIBallot], [PurpleWantsBlueEnvelopes], [PurpleWantsQuote], [SecrecySleevesQty], [SpecialRequests], [SubmittedAt] FROM [OrderDetails]"
+ Dim sql : sql = "Select [BlueDeliveryDate], [BlueEnvelopeProvider], [BlueHasNonprofitStatus], [BlueNonprofitAuthCode], [BluePermitCity], [BluePermitClass], [BluePermitNumber], [BluePermitOwnership], [BluePrintPermit], [BluePrintReturnAddress], [BlueWantsQuote], [ContactName], [FutureVoterStickerRolls], [IVotedStickerRolls], [MailingEstimatedQuantity], [MailingHasNonprofitStatus], [MailingPickupDate], [MailingPostageOption], [MailingWantsService], [MailingWantsSorting], [Municipality], [OrderDetailID], [OrderID], [Phone], [PurpleBlueProvider], [PurpleBlueProviderOther], [PurpleColorCodingBy], [PurpleColorNames], [PurpleDeliveryDate], [PurpleEnvelopeProvider], [PurpleEnvelopeStock], [PurpleExtraEnvelopeQty], [PurplePermanentBallotListCount], [PurplePrecinctCount], [PurplePrintOption], [PurplePrintStyle], [PurpleTrackMIBallot], [PurpleWantsBlueEnvelopes], [PurpleWantsQuote], [SecrecySleevesQty], [SpecialRequests], [SubmittedAt] FROM [OrderDetails]"
Dim where_keys, where_values, i
If Not IsEmpty(where_kvarray) Then
KVUnzip where_kvarray, where_keys, where_values
@@ -77,7 +77,7 @@ Class OrderDetailsRepository_Class
End Function
Public Function SearchTablePaged(columns_array, search_value, order_string_or_array, per_page, page_num, ByRef page_count, ByRef record_count)
- Dim sql : sql = "Select [BlueDeliveryDate], [BlueEnvelopeProvider], [BlueHasNonprofitStatus], [BlueNonprofitAuthCode], [BluePermitCity], [BluePermitClass], [BluePermitNumber], [BluePermitOwnership], [BluePrintPermit], [BluePrintReturnAddress], [BlueWantsQuote], [ContactName], [FutureVoterStickerRolls], [IVotedStickerRolls], [MailingEstimatedQuantity], [MailingHasNonprofitStatus], [MailingPickupDate], [MailingPostageOption], [MailingWantsService], [MailingWantsSorting], [Municipality], [OrderDetailID], [OrderID], [Phone], [PurpleBlueProvider], [PurpleBlueProviderOther], [PurpleColorCodingBy], [PurpleColorNames], [PurpleDeliveryDate], [PurpleEnvelopeProvider], [PurpleEnvelopeStock], [PurpleExtraEnvelopeQty], [PurplePrecinctCount], [PurplePrintOption], [PurplePrintStyle], [PurpleTrackMIBallot], [PurpleWantsBlueEnvelopes], [PurpleWantsQuote], [SecrecySleevesQty], [SpecialRequests], [SubmittedAt] FROM [OrderDetails]"
+ Dim sql : sql = "Select [BlueDeliveryDate], [BlueEnvelopeProvider], [BlueHasNonprofitStatus], [BlueNonprofitAuthCode], [BluePermitCity], [BluePermitClass], [BluePermitNumber], [BluePermitOwnership], [BluePrintPermit], [BluePrintReturnAddress], [BlueWantsQuote], [ContactName], [FutureVoterStickerRolls], [IVotedStickerRolls], [MailingEstimatedQuantity], [MailingHasNonprofitStatus], [MailingPickupDate], [MailingPostageOption], [MailingWantsService], [MailingWantsSorting], [Municipality], [OrderDetailID], [OrderID], [Phone], [PurpleBlueProvider], [PurpleBlueProviderOther], [PurpleColorCodingBy], [PurpleColorNames], [PurpleDeliveryDate], [PurpleEnvelopeProvider], [PurpleEnvelopeStock], [PurpleExtraEnvelopeQty], [PurplePermanentBallotListCount], [PurplePrecinctCount], [PurplePrintOption], [PurplePrintStyle], [PurpleTrackMIBallot], [PurpleWantsBlueEnvelopes], [PurpleWantsQuote], [SecrecySleevesQty], [SpecialRequests], [SubmittedAt] FROM [OrderDetails]"
Dim i, params()
If IsArray(columns_array) And UBound(columns_array) >= 0 Then
sql = sql & " WHERE "
@@ -112,9 +112,9 @@ Class OrderDetailsRepository_Class
End Function
Public Sub AddNew(ByRef model)
- Dim sql : sql = "INSERT INTO [OrderDetails] ([BlueDeliveryDate], [BlueEnvelopeProvider], [BlueHasNonprofitStatus], [BlueNonprofitAuthCode], [BluePermitCity], [BluePermitClass], [BluePermitNumber], [BluePermitOwnership], [BluePrintPermit], [BluePrintReturnAddress], [BlueWantsQuote], [ContactName], [FutureVoterStickerRolls], [IVotedStickerRolls], [MailingEstimatedQuantity], [MailingHasNonprofitStatus], [MailingPickupDate], [MailingPostageOption], [MailingWantsService], [MailingWantsSorting], [Municipality], [OrderID], [Phone], [PurpleBlueProvider], [PurpleBlueProviderOther], [PurpleColorCodingBy], [PurpleColorNames], [PurpleDeliveryDate], [PurpleEnvelopeProvider], [PurpleEnvelopeStock], [PurpleExtraEnvelopeQty], [PurplePrecinctCount], [PurplePrintOption], [PurplePrintStyle], [PurpleTrackMIBallot], [PurpleWantsBlueEnvelopes], [PurpleWantsQuote], [SecrecySleevesQty], [SpecialRequests], [SubmittedAt]) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
- DAL.Execute sql, Array(model.BlueDeliveryDate, model.BlueEnvelopeProvider, model.BlueHasNonprofitStatus, model.BlueNonprofitAuthCode, model.BluePermitCity, model.BluePermitClass, model.BluePermitNumber, model.BluePermitOwnership, model.BluePrintPermit, model.BluePrintReturnAddress, model.BlueWantsQuote, model.ContactName, model.FutureVoterStickerRolls, model.IVotedStickerRolls, model.MailingEstimatedQuantity, model.MailingHasNonprofitStatus, model.MailingPickupDate, model.MailingPostageOption, model.MailingWantsService, model.MailingWantsSorting, model.Municipality, model.OrderID, model.Phone, model.PurpleBlueProvider, model.PurpleBlueProviderOther, model.PurpleColorCodingBy, model.PurpleColorNames, model.PurpleDeliveryDate, model.PurpleEnvelopeProvider, model.PurpleEnvelopeStock, model.PurpleExtraEnvelopeQty, model.PurplePrecinctCount, model.PurplePrintOption, model.PurplePrintStyle, model.PurpleTrackMIBallot, model.PurpleWantsBlueEnvelopes, model.PurpleWantsQuote, model.SecrecySleevesQty, model.SpecialRequests, model.SubmittedAt)
-
+ Dim sql : sql = "INSERT INTO [OrderDetails] ([BlueDeliveryDate], [BlueEnvelopeProvider], [BlueHasNonprofitStatus], [BlueNonprofitAuthCode], [BluePermitCity], [BluePermitClass], [BluePermitNumber], [BluePermitOwnership], [BluePrintPermit], [BluePrintReturnAddress], [BlueWantsQuote], [ContactName], [FutureVoterStickerRolls], [IVotedStickerRolls], [MailingEstimatedQuantity], [MailingHasNonprofitStatus], [MailingPickupDate], [MailingPostageOption], [MailingWantsService], [MailingWantsSorting], [Municipality], [OrderID], [Phone], [PurpleBlueProvider], [PurpleBlueProviderOther], [PurpleColorCodingBy], [PurpleColorNames], [PurpleDeliveryDate], [PurpleEnvelopeProvider], [PurpleEnvelopeStock], [PurpleExtraEnvelopeQty], [PurplePermanentBallotListCount], [PurplePrecinctCount], [PurplePrintOption], [PurplePrintStyle], [PurpleTrackMIBallot], [PurpleWantsBlueEnvelopes], [PurpleWantsQuote], [SecrecySleevesQty], [SpecialRequests], [SubmittedAt]) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
+ DAL.Execute sql, Array(model.BlueDeliveryDate, model.BlueEnvelopeProvider, model.BlueHasNonprofitStatus, model.BlueNonprofitAuthCode, model.BluePermitCity, model.BluePermitClass, model.BluePermitNumber, model.BluePermitOwnership, model.BluePrintPermit, model.BluePrintReturnAddress, model.BlueWantsQuote, model.ContactName, model.FutureVoterStickerRolls, model.IVotedStickerRolls, model.MailingEstimatedQuantity, model.MailingHasNonprofitStatus, model.MailingPickupDate, model.MailingPostageOption, model.MailingWantsService, model.MailingWantsSorting, model.Municipality, model.OrderID, model.Phone, model.PurpleBlueProvider, model.PurpleBlueProviderOther, model.PurpleColorCodingBy, model.PurpleColorNames, model.PurpleDeliveryDate, model.PurpleEnvelopeProvider, model.PurpleEnvelopeStock, model.PurpleExtraEnvelopeQty, model.PurplePermanentBallotListCount, model.PurplePrecinctCount, model.PurplePrintOption, model.PurplePrintStyle, model.PurpleTrackMIBallot, model.PurpleWantsBlueEnvelopes, model.PurpleWantsQuote, model.SecrecySleevesQty, model.SpecialRequests, model.SubmittedAt)
+
' Retrieve the newly inserted ID
On Error Resume Next
Dim rsId : Set rsId = DAL.Query("SELECT @@IDENTITY AS NewID", Empty)
@@ -124,7 +124,7 @@ Class OrderDetailsRepository_Class
Set rsId = DAL.Query("SELECT TOP 1 [OrderDetailID] FROM [OrderDetails] ORDER BY [OrderDetailID] DESC", Empty)
End If
On Error GoTo 0
-
+
If Not rsId.EOF Then
If Not IsNull(rsId(0)) Then model.OrderDetailID = rsId(0)
End If
@@ -132,8 +132,8 @@ Class OrderDetailsRepository_Class
End Sub
Public Sub Update(model)
- Dim sql : sql = "UPDATE [OrderDetails] SET [BlueDeliveryDate] = ?, [BlueEnvelopeProvider] = ?, [BlueHasNonprofitStatus] = ?, [BlueNonprofitAuthCode] = ?, [BluePermitCity] = ?, [BluePermitClass] = ?, [BluePermitNumber] = ?, [BluePermitOwnership] = ?, [BluePrintPermit] = ?, [BluePrintReturnAddress] = ?, [BlueWantsQuote] = ?, [ContactName] = ?, [FutureVoterStickerRolls] = ?, [IVotedStickerRolls] = ?, [MailingEstimatedQuantity] = ?, [MailingHasNonprofitStatus] = ?, [MailingPickupDate] = ?, [MailingPostageOption] = ?, [MailingWantsService] = ?, [MailingWantsSorting] = ?, [Municipality] = ?, [OrderID] = ?, [Phone] = ?, [PurpleBlueProvider] = ?, [PurpleBlueProviderOther] = ?, [PurpleColorCodingBy] = ?, [PurpleColorNames] = ?, [PurpleDeliveryDate] = ?, [PurpleEnvelopeProvider] = ?, [PurpleEnvelopeStock] = ?, [PurpleExtraEnvelopeQty] = ?, [PurplePrecinctCount] = ?, [PurplePrintOption] = ?, [PurplePrintStyle] = ?, [PurpleTrackMIBallot] = ?, [PurpleWantsBlueEnvelopes] = ?, [PurpleWantsQuote] = ?, [SecrecySleevesQty] = ?, [SpecialRequests] = ?, [SubmittedAt] = ? WHERE [OrderDetailID] = ?"
- DAL.Execute sql, Array(model.BlueDeliveryDate, model.BlueEnvelopeProvider, model.BlueHasNonprofitStatus, model.BlueNonprofitAuthCode, model.BluePermitCity, model.BluePermitClass, model.BluePermitNumber, model.BluePermitOwnership, model.BluePrintPermit, model.BluePrintReturnAddress, model.BlueWantsQuote, model.ContactName, model.FutureVoterStickerRolls, model.IVotedStickerRolls, model.MailingEstimatedQuantity, model.MailingHasNonprofitStatus, model.MailingPickupDate, model.MailingPostageOption, model.MailingWantsService, model.MailingWantsSorting, model.Municipality, model.OrderID, model.Phone, model.PurpleBlueProvider, model.PurpleBlueProviderOther, model.PurpleColorCodingBy, model.PurpleColorNames, model.PurpleDeliveryDate, model.PurpleEnvelopeProvider, model.PurpleEnvelopeStock, model.PurpleExtraEnvelopeQty, model.PurplePrecinctCount, model.PurplePrintOption, model.PurplePrintStyle, model.PurpleTrackMIBallot, model.PurpleWantsBlueEnvelopes, model.PurpleWantsQuote, model.SecrecySleevesQty, model.SpecialRequests, model.SubmittedAt, model.OrderDetailID)
+ Dim sql : sql = "UPDATE [OrderDetails] SET [BlueDeliveryDate] = ?, [BlueEnvelopeProvider] = ?, [BlueHasNonprofitStatus] = ?, [BlueNonprofitAuthCode] = ?, [BluePermitCity] = ?, [BluePermitClass] = ?, [BluePermitNumber] = ?, [BluePermitOwnership] = ?, [BluePrintPermit] = ?, [BluePrintReturnAddress] = ?, [BlueWantsQuote] = ?, [ContactName] = ?, [FutureVoterStickerRolls] = ?, [IVotedStickerRolls] = ?, [MailingEstimatedQuantity] = ?, [MailingHasNonprofitStatus] = ?, [MailingPickupDate] = ?, [MailingPostageOption] = ?, [MailingWantsService] = ?, [MailingWantsSorting] = ?, [Municipality] = ?, [OrderID] = ?, [Phone] = ?, [PurpleBlueProvider] = ?, [PurpleBlueProviderOther] = ?, [PurpleColorCodingBy] = ?, [PurpleColorNames] = ?, [PurpleDeliveryDate] = ?, [PurpleEnvelopeProvider] = ?, [PurpleEnvelopeStock] = ?, [PurpleExtraEnvelopeQty] = ?, [PurplePermanentBallotListCount] = ?, [PurplePrecinctCount] = ?, [PurplePrintOption] = ?, [PurplePrintStyle] = ?, [PurpleTrackMIBallot] = ?, [PurpleWantsBlueEnvelopes] = ?, [PurpleWantsQuote] = ?, [SecrecySleevesQty] = ?, [SpecialRequests] = ?, [SubmittedAt] = ? WHERE [OrderDetailID] = ?"
+ DAL.Execute sql, Array(model.BlueDeliveryDate, model.BlueEnvelopeProvider, model.BlueHasNonprofitStatus, model.BlueNonprofitAuthCode, model.BluePermitCity, model.BluePermitClass, model.BluePermitNumber, model.BluePermitOwnership, model.BluePrintPermit, model.BluePrintReturnAddress, model.BlueWantsQuote, model.ContactName, model.FutureVoterStickerRolls, model.IVotedStickerRolls, model.MailingEstimatedQuantity, model.MailingHasNonprofitStatus, model.MailingPickupDate, model.MailingPostageOption, model.MailingWantsService, model.MailingWantsSorting, model.Municipality, model.OrderID, model.Phone, model.PurpleBlueProvider, model.PurpleBlueProviderOther, model.PurpleColorCodingBy, model.PurpleColorNames, model.PurpleDeliveryDate, model.PurpleEnvelopeProvider, model.PurpleEnvelopeStock, model.PurpleExtraEnvelopeQty, model.PurplePermanentBallotListCount, model.PurplePrecinctCount, model.PurplePrintOption, model.PurplePrintStyle, model.PurpleTrackMIBallot, model.PurpleWantsBlueEnvelopes, model.PurpleWantsQuote, model.SecrecySleevesQty, model.SpecialRequests, model.SubmittedAt, model.OrderDetailID)
End Sub
Public Sub Delete(id)
diff --git a/app/models/OrderMailer.asp b/app/models/OrderMailer.asp
index 9c99c24..4747211 100644
--- a/app/models/OrderMailer.asp
+++ b/app/models/OrderMailer.asp
@@ -9,13 +9,18 @@ Class OrderMailer_Class
Dim continueUrl
continueUrl = Routes().AppURL & "order/continue?token=" & Server.URLEncode(token)
+ Dim inner
+ inner = "
Order Request Received
" & _
+ "Continue your election materials order " & _
+ "Click the button below to pick up where you left off.
" & _
+ "Continue your order
" & _
+ "Or copy and paste this link into your browser:" & H(continueUrl) & "
" & _
+ "This link expires in " & H(GetAppSetting("OrderTokenExpirationHours")) & " hours and can only be used once.
"
+
Dim mail : Set mail = NewConfiguredMail()
mail.Subject = "Continue your Purple Envelope order"
mail.IsBodyHTML = True
- mail.Body = "Click the link below to continue your order:
" & _
- "" & H(continueUrl) & "
" & _
- "This link expires in " & H(GetAppSetting("OrderTokenExpirationHours")) & _
- " hours and can only be used once.
"
+ mail.Body = EmailShell(inner)
mail.AddRecipient "To", email
mail.Send
End Sub
@@ -55,14 +60,7 @@ Class OrderMailer_Class
Private Function BuildOrderDetailsEmailBody(order, model)
Dim html
- html = "" & _
- "" & _
- "" & _
- "" & _
- "KCI Purple Envelope Order Confirmation " & _
- " " & _
- "" & _
- "Order Received
" & _
+ html = "Order Received
" & _
"Thanks! We've received your order details. " & _
"Jurisdiction number " & H(order("JurisdictionNumber")) & " " & _
" · submitted " & H(EmailDate(model.SubmittedAt)) & "
"
@@ -80,6 +78,7 @@ Class OrderMailer_Class
End If
html = html & EmailSection("Purple Ballot Envelopes", _
+ EmailRow("Permanent ballot list count", model.PurplePermanentBallotListCount) & _
EmailRow("Envelope stock", LabelEnvelopeStock(model.PurpleEnvelopeStock)) & _
EmailRow("Print option", LabelPrintOption(model.PurplePrintOption)) & _
EmailRow("Also wants KCI blue envelopes", EmailYesNo(model.PurpleWantsBlueEnvelopes)) & _
@@ -93,24 +92,25 @@ Class OrderMailer_Class
End If
If IsTrue(model.BlueWantsQuote) Then
- ' City/permit number are always collected for an own-organization permit - a
- ' permit lookup can't be done from the nonprofit authorization code alone, so
- ' that code is additional information when nonprofit status is Yes, not a
- ' replacement for city/permit number.
+ ' Nonprofit status (and the auth code that follows a "Yes") is asked for either
+ ' permit-ownership branch. City/permit number are additionally collected for an
+ ' own-organization permit - a permit lookup can't be done from the nonprofit
+ ' authorization code alone.
Dim bluePermitLine
bluePermitLine = ""
+ If CStr(model.BlueHasNonprofitStatus & "") = "Yes" Then
+ bluePermitLine = EmailRow("Nonprofit authorization code", model.BlueNonprofitAuthCode)
+ End If
If LabelPermitOwnership(model.BluePermitOwnership) = "My organization's permit" Then
- bluePermitLine = EmailRow("City of permit holder", model.BluePermitCity) & _
+ bluePermitLine = bluePermitLine & _
+ EmailRow("City of permit holder", model.BluePermitCity) & _
EmailRow("Permit number", model.BluePermitNumber)
- If IsTrue(model.BlueHasNonprofitStatus) Then
- bluePermitLine = bluePermitLine & EmailRow("Nonprofit authorization code", model.BlueNonprofitAuthCode)
- End If
End If
html = html & EmailSection("Blue AV Envelopes", _
EmailRow("Print permit", EmailYesNo(model.BluePrintPermit)) & _
EmailRow("Permit ownership", LabelPermitOwnership(model.BluePermitOwnership)) & _
- EmailRow("Confirmed nonprofit status with USPS", EmailYesNo(model.BlueHasNonprofitStatus)) & _
+ EmailRow("Confirmed nonprofit status with USPS", LabelNonprofitStatus(model.BlueHasNonprofitStatus)) & _
bluePermitLine)
End If
@@ -130,14 +130,27 @@ Class OrderMailer_Class
EmailRow("Special requests", model.SpecialRequests))
End If
- html = html & "Questions about this order? Just reply to this email.
" & _
+ html = html & "Questions about this order? Just reply to this email.
"
+
+ BuildOrderDetailsEmailBody = EmailShell(html)
+ End Function
+
+ ' Wraps inner body HTML in the same header/footer chrome used across the website - the
+ ' navy KCI Election Printing bar (matching the site header/footer) and white content card.
+ Private Function EmailShell(innerHtml)
+ Dim logoUrl : logoUrl = Routes().AppURL & "images/KCI%20ELECTION%20printing%20(white).png"
+ EmailShell = "" & _
+ "" & _
+ "" & _
+ "" & _
+ " " & _
" " & _
- "" & _
- "Purple Envelope · Addressing & Barcoding " & _
+ " " & innerHtml & " " & _
+ "" & _
+ "KCI Election Printing
" & _
+ "616.957.2120 · info@kentcommunications.com
" & _
" " & _
"
"
-
- BuildOrderDetailsEmailBody = html
End Function
Private Function EmailSection(sectionTitle, rowsHtml)
diff --git a/app/models/POBO_OrderDetails.asp b/app/models/POBO_OrderDetails.asp
index 15acc9e..fe26f47 100644
--- a/app/models/POBO_OrderDetails.asp
+++ b/app/models/POBO_OrderDetails.asp
@@ -42,6 +42,7 @@ Class POBO_OrderDetails
Private pPurpleEnvelopeProvider
Private pPurpleEnvelopeStock
Private pPurpleExtraEnvelopeQty
+ Private pPurplePermanentBallotListCount
Private pPurplePrecinctCount
Private pPurplePrintOption
Private pPurplePrintStyle
@@ -85,6 +86,7 @@ Class POBO_OrderDetails
pPurpleEnvelopeProvider = Null
pPurpleEnvelopeStock = Null
pPurpleExtraEnvelopeQty = 0
+ pPurplePermanentBallotListCount = 0
pPurplePrecinctCount = 0
pPurplePrintOption = Null
pPurplePrintStyle = Null
@@ -94,7 +96,7 @@ Class POBO_OrderDetails
pSecrecySleevesQty = 0
pSpecialRequests = Null
pSubmittedAt = #1/1/1970#
- Properties = Array("BlueDeliveryDate","BlueEnvelopeProvider","BlueHasNonprofitStatus","BlueNonprofitAuthCode","BluePermitCity","BluePermitClass","BluePermitNumber","BluePermitOwnership","BluePrintPermit","BluePrintReturnAddress","BlueWantsQuote","ContactName","FutureVoterStickerRolls","IVotedStickerRolls","MailingEstimatedQuantity","MailingHasNonprofitStatus","MailingPickupDate","MailingPostageOption","MailingWantsService","MailingWantsSorting","Municipality","OrderDetailID","OrderID","Phone","PurpleBlueProvider","PurpleBlueProviderOther","PurpleColorCodingBy","PurpleColorNames","PurpleDeliveryDate","PurpleEnvelopeProvider","PurpleEnvelopeStock","PurpleExtraEnvelopeQty","PurplePrecinctCount","PurplePrintOption","PurplePrintStyle","PurpleTrackMIBallot","PurpleWantsBlueEnvelopes","PurpleWantsQuote","SecrecySleevesQty","SpecialRequests","SubmittedAt")
+ Properties = Array("BlueDeliveryDate","BlueEnvelopeProvider","BlueHasNonprofitStatus","BlueNonprofitAuthCode","BluePermitCity","BluePermitClass","BluePermitNumber","BluePermitOwnership","BluePrintPermit","BluePrintReturnAddress","BlueWantsQuote","ContactName","FutureVoterStickerRolls","IVotedStickerRolls","MailingEstimatedQuantity","MailingHasNonprofitStatus","MailingPickupDate","MailingPostageOption","MailingWantsService","MailingWantsSorting","Municipality","OrderDetailID","OrderID","Phone","PurpleBlueProvider","PurpleBlueProviderOther","PurpleColorCodingBy","PurpleColorNames","PurpleDeliveryDate","PurpleEnvelopeProvider","PurpleEnvelopeStock","PurpleExtraEnvelopeQty","PurplePermanentBallotListCount","PurplePrecinctCount","PurplePrintOption","PurplePrintStyle","PurpleTrackMIBallot","PurpleWantsBlueEnvelopes","PurpleWantsQuote","SecrecySleevesQty","SpecialRequests","SubmittedAt")
End Sub
Public Property Get PrimaryKey()
@@ -145,10 +147,10 @@ Class POBO_OrderDetails
Public Property Let BlueHasNonprofitStatus(val)
On Error Resume Next
- If IsNull(val) Then
- pBlueHasNonprofitStatus = Null
+ If IsNumeric(val) Then
+ pBlueHasNonprofitStatus = CDbl(val)
Else
- pBlueHasNonprofitStatus = CBool(val)
+ pBlueHasNonprofitStatus = val
End If
If Err.Number <> 0 Then
Err.Raise Err.Number, "POBO_OrderDetails.BlueHasNonprofitStatus", "Invalid value for BlueHasNonprofitStatus: " & Err.Description
@@ -637,6 +639,23 @@ Class POBO_OrderDetails
On Error GoTo 0
End Property
+ Public Property Get PurplePermanentBallotListCount()
+ PurplePermanentBallotListCount = pPurplePermanentBallotListCount
+ End Property
+
+ Public Property Let PurplePermanentBallotListCount(val)
+ On Error Resume Next
+ If IsNumeric(val) Then
+ pPurplePermanentBallotListCount = CDbl(val)
+ Else
+ pPurplePermanentBallotListCount = val
+ End If
+ If Err.Number <> 0 Then
+ Err.Raise Err.Number, "POBO_OrderDetails.PurplePermanentBallotListCount", "Invalid value for PurplePermanentBallotListCount: " & Err.Description
+ End If
+ On Error GoTo 0
+ End Property
+
Public Property Get PurplePrecinctCount()
PurplePrecinctCount = pPurplePrecinctCount
End Property
diff --git a/app/views/Order/continue.asp b/app/views/Order/continue.asp
index cd9064f..3d06750 100644
--- a/app/views/Order/continue.asp
+++ b/app/views/Order/continue.asp
@@ -96,6 +96,13 @@
title: "Would you like for KCI to print your purple AV envelopes?",
isRequired: true
},
+ {
+ type: "text", name: "PurplePermanentBallotListCount",
+ title: "How many are on your permanent ballot list?",
+ inputType: "number", min: 0,
+ visibleIf: "{PurpleWantsQuote} = true",
+ isRequired: true
+ },
{
type: "radiogroup", name: "PurpleEnvelopeStock",
title: "Are you planning to use KCI's purple envelopes or from your own stock?",
@@ -229,15 +236,20 @@
html: "Please note, envelopes with KCI's permit MUST be mailed from KCI.
"
},
{
- type: "boolean", name: "BlueHasNonprofitStatus", labelTrue: "Yes", labelFalse: "No",
+ type: "radiogroup", name: "BlueHasNonprofitStatus",
title: "Does your organization have confirmed nonprofit status with USPS?",
- visibleIf: "{BluePermitOwnership} = 'OwnPermit'",
- isRequired: true
+ visibleIf: "{BluePrintPermit} = true",
+ isRequired: true,
+ choices: [
+ { value: "Yes", text: "Yes" },
+ { value: "No", text: "No" },
+ { value: "Unsure", text: "Unsure (That's okay, we can help you!)" }
+ ]
},
{
type: "text", name: "BlueNonprofitAuthCode",
title: "Please provide your nonprofit authorization code.",
- visibleIf: "{BlueHasNonprofitStatus} = true",
+ visibleIf: "{BlueHasNonprofitStatus} = 'Yes'",
isRequired: true
},
{
@@ -442,12 +454,8 @@
});
})
.then(function (result) {
- var container = document.getElementById("surveyContainer");
if (result.ok && result.body && result.body.success) {
- container.innerHTML =
- '' +
- "Thank you for submitting your order." +
- "
";
+ window.location.href = "<%= H(Routes().AppURL) %>";
} else {
alert((result.body && result.body.error) || "Something went wrong submitting your order. Please try again.");
}
diff --git a/app/views/RequestOrder/index.asp b/app/views/RequestOrder/index.asp
index f44dc35..15a7bde 100644
--- a/app/views/RequestOrder/index.asp
+++ b/app/views/RequestOrder/index.asp
@@ -1,32 +1,34 @@
-
-
Start your order
-
Request an order
-
- Enter your email address and jurisdiction number below. We'll email you a
- secure link to continue your order.
-
-
+
diff --git a/db/migrations/20260820114612_add_purple_ballot_list_count_and_blue_nonprofit_options.asp b/db/migrations/20260820114612_add_purple_ballot_list_count_and_blue_nonprofit_options.asp
new file mode 100644
index 0000000..6982db1
--- /dev/null
+++ b/db/migrations/20260820114612_add_purple_ballot_list_count_and_blue_nonprofit_options.asp
@@ -0,0 +1,44 @@
+<%
+'=======================================================================================================================
+' MIGRATION: add_purple_ballot_list_count_and_blue_nonprofit_options
+'=======================================================================================================================
+' This migration was auto-generated. Add your migration logic below.
+'
+' The migration object provides these helper methods:
+' - migration.ExecuteSQL(sql) - Execute raw SQL
+' - migration.CreateTable(name, columns) - Create a table
+' - migration.DropTable(name) - Drop a table
+' - migration.AddColumn(table, column, type) - Add a column
+' - migration.DropColumn(table, column) - Drop a column
+' - migration.CreateIndex(name, table, columns) - Create an index
+' - migration.DropIndex(name, table) - Drop an index
+'
+' For complex operations, use migration.DB to access the database directly:
+' migration.DB.Execute "INSERT INTO users (name) VALUES (?)", Array("John")
+'
+
+'-----------------------------------------------------------------------------------------------------------------------
+' UP - Apply the migration
+'-----------------------------------------------------------------------------------------------------------------------
+Sub Migration_Up(migration)
+ ' Fields added/changed by the revised order-form PDFs (docs/lucid/*"(2).pdf").
+
+ ' Purple Ballot Envelope section - new "permanent ballot list" count, asked
+ ' alongside PurpleEnvelopeStock/PurplePrintOption.
+ migration.AddColumn "OrderDetails", "PurplePermanentBallotListCount", "INTEGER"
+
+ ' Blue AV Envelope section - BlueHasNonprofitStatus gained a third "Unsure" choice and
+ ' is now asked for both permit-ownership branches, so it can no longer be YESNO.
+ ' Existing YESNO test data converts to "-1"/"0" text under this type change - there's no
+ ' real order data yet, so that's acceptable here.
+ migration.ExecuteSQL "ALTER TABLE OrderDetails ALTER COLUMN BlueHasNonprofitStatus VARCHAR(20)"
+End Sub
+
+'-----------------------------------------------------------------------------------------------------------------------
+' DOWN - Rollback the migration
+'-----------------------------------------------------------------------------------------------------------------------
+Sub Migration_Down(migration)
+ migration.ExecuteSQL "ALTER TABLE OrderDetails ALTER COLUMN BlueHasNonprofitStatus YESNO"
+ migration.DropColumn "OrderDetails", "PurplePermanentBallotListCount"
+End Sub
+%>
diff --git a/docs/lucid/Election Materials Order Form #1 (2).pdf b/docs/lucid/Election Materials Order Form #1 (2).pdf
new file mode 100644
index 0000000..5aa8f11
Binary files /dev/null and b/docs/lucid/Election Materials Order Form #1 (2).pdf differ
diff --git a/docs/lucid/Election Materials Request Form #2 (2).pdf b/docs/lucid/Election Materials Request Form #2 (2).pdf
new file mode 100644
index 0000000..df1a6c2
Binary files /dev/null and b/docs/lucid/Election Materials Request Form #2 (2).pdf differ
diff --git a/public/css/styles.css b/public/css/styles.css
index 2af128a..6bb8f95 100644
--- a/public/css/styles.css
+++ b/public/css/styles.css
@@ -847,7 +847,7 @@ details p {
}
.site-footer {
flex: 0 0 auto;
- padding: 6px 0 4px;
+ padding: 3px 0 2px;
color: rgba(255,255,255,0.84);
background: #254B74;
border-top: 1px solid rgba(255, 255, 255, 0.12);
@@ -855,19 +855,19 @@ details p {
.footer-shell {
display: flex;
flex-direction: column;
- gap: 4px;
+ gap: 2px;
}
.footer-compact-row {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
- gap: 10px;
+ gap: 6px;
}
.footer-logo-inline img {
display: block;
- width: 104px;
+ width: 56px;
height: auto;
}
@@ -875,9 +875,9 @@ details p {
display: flex;
align-items: center;
flex-wrap: wrap;
- gap: 6px 12px;
- font-size: 0.62rem;
- line-height: 1.2;
+ gap: 3px 8px;
+ font-size: 0.52rem;
+ line-height: 1.1;
}
.footer-inline-content strong,
@@ -892,7 +892,7 @@ details p {
.footer-badge-inline img {
display: block;
- width: 104px;
+ width: 56px;
height: auto;
}
@@ -900,21 +900,21 @@ details p {
display: flex;
justify-content: space-between;
gap: 8px;
- padding-top: 3px;
+ padding-top: 2px;
border-top: 1px solid rgba(255,255,255,0.1);
- font-size: 0.52rem;
- line-height: 1.2;
+ font-size: 0.46rem;
+ line-height: 1.1;
}
@media (max-width: 820px) {
.footer-compact-row {
grid-template-columns: 1fr;
- gap: 6px;
+ gap: 4px;
justify-items: start;
}
.footer-logo-inline img {
- width: 78px;
+ width: 56px;
}
.footer-bottom {
diff --git a/public/web.config b/public/web.config
index 73d2403..844067f 100644
--- a/public/web.config
+++ b/public/web.config
@@ -1,4 +1,4 @@
-
+
@@ -112,4 +112,13 @@
+
+
+
+
+
+
+
+
+
diff --git a/purple-envelope-order-site.zip b/purple-envelope-order-site.zip
deleted file mode 100644
index b640964..0000000
Binary files a/purple-envelope-order-site.zip and /dev/null differ
diff --git a/purple-envelope-order-site/index.html b/purple-envelope-order-site/index.html
deleted file mode 100644
index 8be3712..0000000
--- a/purple-envelope-order-site/index.html
+++ /dev/null
@@ -1,616 +0,0 @@
-
-
-
-
-
-
- Purple Envelope Addressing & Barcoding
-
-
-
-
-
-
-
-
-
-
-
-
-
Election mail production made simple
-
Accurate purple envelope addressing and USPS barcode preparation.
-
- Upload your voter or recipient list, select your production options,
- and receive a clear order total before submitting your job.
-
-
-
-
-
- Secure data handling
- Address formatting and validation
- Intelligent Mail barcode preparation
-
-
-
-
-
-
- ADDRESS SERVICE REQUESTED
- PRESORTED FIRST-CLASS MAIL
-
-
-
- JANE SAMPLE
- 1234 EXAMPLE AVENUE
- GRAND RAPIDS MI 49503-1234
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 99.9%
- production accuracy goal
-
-
-
- Fast
- proof approval workflow
-
-
-
-
-
-
-
-
- Address cleanup
- Standardized mailing fields
-
-
- USPS barcode setup
- IMb-ready production data
-
-
- Inkjet addressing
- Clear, machine-readable output
-
-
- Proof approval
- Review before production begins
-
-
-
-
-
-
-
-
Complete production support
-
Everything needed to prepare your ballot envelopes.
-
- Choose only the services you need, from basic addressing to a
- complete data-processing and mailing workflow.
-
-
-
-
-
- 01
- Recipient addressing
-
- Print recipient names, delivery addresses, city, state, ZIP Code,
- return-service endorsements, and optional identifiers.
-
-
-
-
- 02
- Barcode preparation
-
- Prepare and print Intelligent Mail barcode data using the mailing
- fields and identifiers supplied for your project.
-
-
-
-
- 03
- List processing
-
- Review column structure, remove obvious formatting problems,
- standardize capitalization, and identify incomplete records.
-
-
-
-
- 04
- Proof and production
-
- Receive a digital sample for approval before the full envelope
- run enters production.
-
-
-
-
-
-
-
-
-
- Simple four-step workflow
-
From uploaded list to completed order.
-
-
-
-
-
1
-
Enter job details
-
Provide quantity, envelope specifications, and due date.
-
-
-
2
-
Upload your files
-
Add recipient data, artwork, instructions, or a sample envelope.
-
-
-
3
-
Approve your proof
-
Review the address placement and sample barcode before printing.
-
-
-
4
-
Production begins
-
Your approved job is addressed, checked, packed, and delivered.
-
-
-
-
-
-
-
-
-
Online order request
-
Build your envelope addressing order.
-
- This form calculates an estimate. Final pricing can be confirmed
- after your data and production requirements are reviewed.
-
-
-
-
-
-
-
-
-
-
- Frequently asked questions
-
Ordering and production details.
-
-
-
-
- What file formats can I upload?
-
- CSV and Excel files are preferred. Clearly labeled columns should
- include the recipient name, delivery address, city, state, ZIP
- Code, and any barcode-related fields required for the project.
-
-
-
-
- Will I receive a proof?
-
- Yes. A digital proof can be provided before production. A printed
- physical proof can also be selected as an additional service.
-
-
-
-
- Does the estimate include postage?
-
- No. Postage depends on the mailing class, presort results, entry
- location, and current postal requirements. It should be quoted
- separately after the mailing data is reviewed.
-
-
-
-
- Can you supply the purple envelopes?
-
- Yes. Select “Please supply the envelopes” in the order form.
- Stock, color, size, window configuration, and availability will
- be confirmed before production.
-
-
-
-
-
-
-
-
-
- Need a custom production quote?
-
Large quantities and special ballot configurations are welcome.
-
-
Request pricing
-
-
-
-
-
-
-
-
-
×
-
✓
-
Order request ready
-
- The front-end form is working. Connect this form to your server-side
- order endpoint, payment system, CRM, or email workflow to process live orders.
-
-
Close
-
-
-
-
-
-
diff --git a/purple-envelope-order-site/script.js b/purple-envelope-order-site/script.js
deleted file mode 100644
index 89d2411..0000000
--- a/purple-envelope-order-site/script.js
+++ /dev/null
@@ -1,121 +0,0 @@
-const form = document.getElementById("orderForm");
-const quantityInput = document.getElementById("quantity");
-const turnaround = document.getElementById("turnaround");
-const envelopeSupply = document.getElementById("envelopeSupply");
-const serviceInputs = [...document.querySelectorAll('input[name="services"]')];
-
-const summaryQuantity = document.getElementById("summaryQuantity");
-const serviceSummary = document.getElementById("serviceSummary");
-const setupFeeElement = document.getElementById("setupFee");
-const rushLine = document.getElementById("rushLine");
-const rushFeeElement = document.getElementById("rushFee");
-const estimatedTotal = document.getElementById("estimatedTotal");
-const dialog = document.getElementById("confirmationDialog");
-
-const currency = new Intl.NumberFormat("en-US", {
- style: "currency",
- currency: "USD"
-});
-
-const number = new Intl.NumberFormat("en-US");
-
-function calculateOrder() {
- const quantity = Math.max(Number(quantityInput.value) || 0, 0);
- const setupFee = quantity > 0 ? 45 : 0;
- let subtotal = setupFee;
- const lines = [];
-
- serviceInputs.forEach((input) => {
- if (!input.checked) return;
-
- const rate = Number(input.dataset.rate || 0);
- const flat = Number(input.dataset.flat || 0);
- const amount = flat || quantity * rate;
- subtotal += amount;
-
- lines.push({
- label: input.dataset.label,
- amount
- });
- });
-
- if (envelopeSupply.value === "vendor") {
- const envelopeCost = quantity * 0.18;
- subtotal += envelopeCost;
- lines.push({
- label: "Purple envelope stock",
- amount: envelopeCost
- });
- }
-
- const rushFee = turnaround.value === "rush" ? subtotal * 0.25 : 0;
- const total = subtotal + rushFee;
-
- summaryQuantity.textContent = number.format(quantity);
- setupFeeElement.textContent = currency.format(setupFee);
- rushFeeElement.textContent = currency.format(rushFee);
- estimatedTotal.textContent = currency.format(total);
-
- rushLine.hidden = turnaround.value !== "rush";
-
- serviceSummary.innerHTML = lines
- .map(
- (line) => `
-
- ${line.label}
- ${currency.format(line.amount)}
-
- `
- )
- .join("");
-}
-
-function showSelectedFile(input) {
- const uploadBox = input.closest(".upload-box");
- const strong = uploadBox.querySelector("strong");
- const small = uploadBox.querySelector("small");
-
- if (input.files.length) {
- strong.textContent = input.files[0].name;
- small.textContent = "File selected";
- }
-}
-
-quantityInput.addEventListener("input", calculateOrder);
-turnaround.addEventListener("change", calculateOrder);
-envelopeSupply.addEventListener("change", calculateOrder);
-serviceInputs.forEach((input) => input.addEventListener("change", calculateOrder));
-
-document.querySelectorAll('input[type="file"]').forEach((input) => {
- input.addEventListener("change", () => showSelectedFile(input));
-});
-
-form.addEventListener("submit", (event) => {
- event.preventDefault();
-
- if (!form.checkValidity()) {
- form.reportValidity();
- return;
- }
-
- dialog.showModal();
-});
-
-dialog.querySelectorAll(".dialog-close, .dialog-button").forEach((button) => {
- button.addEventListener("click", () => dialog.close());
-});
-
-dialog.addEventListener("click", (event) => {
- const bounds = dialog.getBoundingClientRect();
- const clickedOutside =
- event.clientX < bounds.left ||
- event.clientX > bounds.right ||
- event.clientY < bounds.top ||
- event.clientY > bounds.bottom;
-
- if (clickedOutside) dialog.close();
-});
-
-document.getElementById("year").textContent = new Date().getFullYear();
-
-calculateOrder();
diff --git a/purple-envelope-order-site/styles.css b/purple-envelope-order-site/styles.css
deleted file mode 100644
index 8792e73..0000000
--- a/purple-envelope-order-site/styles.css
+++ /dev/null
@@ -1,1149 +0,0 @@
-:root {
- --purple-950: #24103a;
- --purple-900: #32134f;
- --purple-800: #4d1d78;
- --purple-700: #6529a0;
- --purple-600: #7c3bc0;
- --purple-500: #9658d5;
- --purple-200: #dcc9ef;
- --purple-100: #f0e7f8;
- --purple-50: #faf7fd;
- --ink: #201a27;
- --muted: #6d6574;
- --line: #e4dce9;
- --surface: #ffffff;
- --surface-soft: #f8f5fa;
- --success: #247a50;
- --shadow-sm: 0 10px 30px rgba(52, 25, 76, 0.08);
- --shadow-lg: 0 28px 80px rgba(52, 25, 76, 0.18);
- --radius-sm: 12px;
- --radius-md: 20px;
- --radius-lg: 32px;
- --container: 1180px;
-}
-
-* {
- box-sizing: border-box;
-}
-
-html {
- scroll-behavior: smooth;
-}
-
-body {
- margin: 0;
- color: var(--ink);
- background: var(--surface);
- font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
- line-height: 1.6;
-}
-
-button,
-input,
-select,
-textarea {
- font: inherit;
-}
-
-a {
- color: inherit;
- text-decoration: none;
-}
-
-img {
- max-width: 100%;
-}
-
-.container {
- width: min(calc(100% - 40px), var(--container));
- margin-inline: auto;
-}
-
-.site-header {
- position: sticky;
- top: 0;
- z-index: 50;
- background: rgba(255, 255, 255, 0.93);
- border-bottom: 1px solid rgba(228, 220, 233, 0.8);
- backdrop-filter: blur(16px);
-}
-
-.nav-wrap {
- min-height: 78px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 28px;
-}
-
-.brand {
- display: inline-flex;
- align-items: center;
- gap: 12px;
-}
-
-.brand-mark {
- width: 44px;
- height: 44px;
- display: grid;
- place-items: center;
- color: white;
- background: linear-gradient(135deg, var(--purple-800), var(--purple-500));
- border-radius: 14px;
- font-weight: 800;
- letter-spacing: -0.04em;
- box-shadow: 0 8px 18px rgba(101, 41, 160, 0.24);
-}
-
-.brand strong,
-.brand small {
- display: block;
-}
-
-.brand strong {
- font-size: 0.98rem;
- line-height: 1.25;
-}
-
-.brand small {
- margin-top: 2px;
- color: var(--muted);
- font-size: 0.72rem;
-}
-
-.main-nav {
- display: flex;
- align-items: center;
- gap: 30px;
- color: #4c4352;
- font-size: 0.92rem;
- font-weight: 600;
-}
-
-.main-nav a:hover {
- color: var(--purple-700);
-}
-
-.button {
- min-height: 50px;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- padding: 0 24px;
- border: 1px solid transparent;
- border-radius: 999px;
- cursor: pointer;
- font-weight: 700;
- transition:
- transform 160ms ease,
- box-shadow 160ms ease,
- background-color 160ms ease;
-}
-
-.button:hover {
- transform: translateY(-2px);
-}
-
-.button-small {
- min-height: 42px;
- padding-inline: 18px;
- font-size: 0.88rem;
-}
-
-.button-primary {
- color: white;
- background: linear-gradient(135deg, var(--purple-800), var(--purple-600));
- box-shadow: 0 12px 24px rgba(101, 41, 160, 0.2);
-}
-
-.button-primary:hover {
- box-shadow: 0 16px 30px rgba(101, 41, 160, 0.28);
-}
-
-.button-secondary {
- color: var(--purple-800);
- background: var(--purple-100);
-}
-
-.button-outline {
- color: var(--purple-800);
- border-color: var(--purple-200);
- background: white;
-}
-
-.button-light {
- color: var(--purple-900);
- background: white;
-}
-
-.button-full {
- width: 100%;
-}
-
-.hero {
- position: relative;
- overflow: hidden;
- padding: 95px 0 105px;
- background:
- radial-gradient(circle at 76% 22%, rgba(150, 88, 213, 0.18), transparent 30%),
- linear-gradient(180deg, #fff 0%, var(--purple-50) 100%);
-}
-
-.hero::before {
- content: "";
- position: absolute;
- width: 520px;
- height: 520px;
- left: -260px;
- bottom: -320px;
- border: 70px solid rgba(124, 59, 192, 0.06);
- border-radius: 50%;
-}
-
-.hero-grid {
- display: grid;
- grid-template-columns: 1.03fr 0.97fr;
- align-items: center;
- gap: 72px;
-}
-
-.eyebrow {
- display: inline-block;
- color: var(--purple-700);
- font-size: 0.75rem;
- font-weight: 800;
- letter-spacing: 0.14em;
- text-transform: uppercase;
-}
-
-.hero h1 {
- max-width: 760px;
- margin: 18px 0 22px;
- font-size: clamp(2.65rem, 5vw, 4.65rem);
- line-height: 1.02;
- letter-spacing: -0.055em;
-}
-
-.hero-lead {
- max-width: 665px;
- margin: 0;
- color: var(--muted);
- font-size: 1.1rem;
-}
-
-.hero-actions {
- display: flex;
- flex-wrap: wrap;
- gap: 14px;
- margin-top: 34px;
-}
-
-.trust-list {
- display: flex;
- flex-wrap: wrap;
- gap: 18px 28px;
- margin: 32px 0 0;
- padding: 0;
- list-style: none;
- color: #514859;
- font-size: 0.87rem;
- font-weight: 600;
-}
-
-.trust-list li::before {
- content: "✓";
- margin-right: 8px;
- color: var(--success);
- font-weight: 800;
-}
-
-.hero-visual {
- position: relative;
- min-height: 480px;
- display: grid;
- place-items: center;
-}
-
-.envelope-card {
- position: relative;
- width: min(100%, 550px);
- aspect-ratio: 1.56 / 1;
- overflow: hidden;
- padding: 38px;
- color: white;
- background:
- linear-gradient(160deg, rgba(255,255,255,0.11), transparent 30%),
- linear-gradient(135deg, #5c238f, #7c3bc0 54%, #512079);
- border: 1px solid rgba(255,255,255,0.34);
- border-radius: 24px;
- box-shadow: var(--shadow-lg);
- transform: rotate(-3deg);
-}
-
-.envelope-card::after {
- content: "";
- position: absolute;
- inset: 0;
- background-image:
- linear-gradient(rgba(255,255,255,0.028) 1px, transparent 1px),
- linear-gradient(90deg, rgba(255,255,255,0.028) 1px, transparent 1px);
- background-size: 16px 16px;
- pointer-events: none;
-}
-
-.envelope-top {
- display: flex;
- align-items: flex-start;
- justify-content: space-between;
- gap: 20px;
- font-size: 0.52rem;
- letter-spacing: 0.05em;
-}
-
-.permit {
- text-align: right;
- line-height: 1.25;
-}
-
-.recipient-block {
- position: absolute;
- left: 38%;
- top: 46%;
- display: flex;
- flex-direction: column;
- font-size: clamp(0.55rem, 1.25vw, 0.82rem);
- letter-spacing: 0.08em;
- line-height: 1.55;
-}
-
-.barcode {
- position: absolute;
- left: 38%;
- bottom: 28%;
- height: 33px;
- display: flex;
- align-items: flex-end;
- gap: 2px;
-}
-
-.barcode span {
- width: 2px;
- height: 20px;
- display: block;
- background: rgba(255,255,255,0.92);
-}
-
-.barcode span:nth-child(3n) {
- height: 31px;
-}
-
-.barcode span:nth-child(4n) {
- height: 13px;
-}
-
-.barcode span:nth-child(7n) {
- height: 25px;
-}
-
-.envelope-seal {
- position: absolute;
- inset: auto -8% -42% -8%;
- height: 65%;
- border-top: 1px solid rgba(255,255,255,0.22);
- transform: rotate(-3deg);
-}
-
-.stat-card {
- position: absolute;
- min-width: 165px;
- padding: 16px 18px;
- background: rgba(255,255,255,0.95);
- border: 1px solid var(--line);
- border-radius: 16px;
- box-shadow: var(--shadow-sm);
-}
-
-.stat-card strong,
-.stat-card span {
- display: block;
-}
-
-.stat-card strong {
- color: var(--purple-800);
- font-size: 1.15rem;
-}
-
-.stat-card span {
- color: var(--muted);
- font-size: 0.72rem;
-}
-
-.stat-card-one {
- left: -2%;
- bottom: 11%;
-}
-
-.stat-card-two {
- top: 11%;
- right: -2%;
-}
-
-.service-strip {
- color: white;
- background: var(--purple-950);
-}
-
-.service-strip-grid {
- display: grid;
- grid-template-columns: repeat(4, 1fr);
-}
-
-.service-strip-grid > div {
- min-height: 112px;
- display: flex;
- flex-direction: column;
- justify-content: center;
- padding: 24px;
- border-right: 1px solid rgba(255,255,255,0.11);
-}
-
-.service-strip-grid > div:last-child {
- border-right: 0;
-}
-
-.service-strip strong {
- font-size: 0.95rem;
-}
-
-.service-strip span {
- color: rgba(255,255,255,0.65);
- font-size: 0.78rem;
-}
-
-.section {
- padding: 105px 0;
-}
-
-.section-muted {
- background: var(--surface-soft);
-}
-
-.section-heading {
- max-width: 730px;
- margin-bottom: 48px;
-}
-
-.section-heading.centered {
- margin-inline: auto;
- text-align: center;
-}
-
-.section-heading h2 {
- margin: 12px 0 14px;
- font-size: clamp(2rem, 3.5vw, 3.25rem);
- line-height: 1.08;
- letter-spacing: -0.045em;
-}
-
-.section-heading p {
- margin: 0;
- color: var(--muted);
- font-size: 1rem;
-}
-
-.card-grid {
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- gap: 22px;
-}
-
-.feature-card {
- min-height: 300px;
- padding: 30px;
- background: white;
- border: 1px solid var(--line);
- border-radius: var(--radius-md);
- box-shadow: 0 10px 30px rgba(52, 25, 76, 0.035);
-}
-
-.feature-number {
- display: inline-flex;
- width: 48px;
- height: 48px;
- align-items: center;
- justify-content: center;
- color: var(--purple-700);
- background: var(--purple-100);
- border-radius: 14px;
- font-size: 0.82rem;
- font-weight: 800;
-}
-
-.feature-card h3 {
- margin: 35px 0 12px;
- font-size: 1.18rem;
-}
-
-.feature-card p {
- margin: 0;
- color: var(--muted);
- font-size: 0.9rem;
-}
-
-.process-grid {
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- gap: 1px;
- overflow: hidden;
- background: var(--line);
- border: 1px solid var(--line);
- border-radius: var(--radius-md);
-}
-
-.process-step {
- min-height: 270px;
- padding: 34px;
- background: white;
-}
-
-.process-step > span {
- width: 42px;
- height: 42px;
- display: inline-grid;
- place-items: center;
- color: white;
- background: var(--purple-800);
- border-radius: 50%;
- font-weight: 800;
-}
-
-.process-step h3 {
- margin: 48px 0 10px;
- font-size: 1.05rem;
-}
-
-.process-step p {
- margin: 0;
- color: var(--muted);
- font-size: 0.88rem;
-}
-
-.order-section {
- background:
- radial-gradient(circle at 100% 30%, rgba(124, 59, 192, 0.08), transparent 25%),
- white;
-}
-
-.order-layout {
- display: grid;
- grid-template-columns: minmax(0, 1fr) 370px;
- align-items: start;
- gap: 32px;
-}
-
-.form-panel {
- overflow: hidden;
- border: 1px solid var(--line);
- border-radius: var(--radius-md);
- background: white;
- box-shadow: var(--shadow-sm);
-}
-
-.form-section {
- margin: 0;
- padding: 34px;
- border: 0;
- border-bottom: 1px solid var(--line);
-}
-
-.form-section:last-child {
- border-bottom: 0;
-}
-
-.form-section legend {
- width: 100%;
- display: flex;
- align-items: center;
- gap: 13px;
- margin-bottom: 28px;
- padding: 0;
- font-size: 1.08rem;
- font-weight: 800;
-}
-
-.form-section legend span {
- width: 32px;
- height: 32px;
- display: inline-grid;
- place-items: center;
- color: white;
- background: var(--purple-800);
- border-radius: 50%;
- font-size: 0.76rem;
-}
-
-.form-grid {
- display: grid;
- gap: 22px;
-}
-
-.two-columns {
- grid-template-columns: repeat(2, minmax(0, 1fr));
-}
-
-label {
- color: #3e3545;
- font-size: 0.84rem;
- font-weight: 700;
-}
-
-input,
-select,
-textarea {
- width: 100%;
- margin-top: 8px;
- color: var(--ink);
- background: white;
- border: 1px solid #d9cfe0;
- border-radius: 12px;
- outline: none;
- transition:
- border-color 160ms ease,
- box-shadow 160ms ease;
-}
-
-input,
-select {
- height: 50px;
- padding: 0 14px;
-}
-
-textarea {
- resize: vertical;
- padding: 14px;
-}
-
-input:focus,
-select:focus,
-textarea:focus {
- border-color: var(--purple-500);
- box-shadow: 0 0 0 4px rgba(124, 59, 192, 0.1);
-}
-
-label small {
- display: block;
- margin-top: 7px;
- color: var(--muted);
- font-size: 0.71rem;
- font-weight: 500;
-}
-
-.option-grid {
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: 14px;
-}
-
-.option-card {
- min-height: 104px;
- display: grid;
- grid-template-columns: auto 1fr auto;
- align-items: start;
- gap: 12px;
- padding: 18px;
- background: white;
- border: 1px solid var(--line);
- border-radius: 14px;
- cursor: pointer;
-}
-
-.option-card:has(input:checked) {
- background: var(--purple-50);
- border-color: var(--purple-300, #c8afe2);
- box-shadow: inset 0 0 0 1px rgba(124,59,192,0.08);
-}
-
-.option-card input {
- width: 18px;
- height: 18px;
- margin: 2px 0 0;
- accent-color: var(--purple-700);
-}
-
-.option-card span,
-.option-card strong,
-.option-card small {
- display: block;
-}
-
-.option-card strong {
- color: var(--ink);
- font-size: 0.85rem;
-}
-
-.option-card small {
- margin-top: 4px;
- line-height: 1.35;
-}
-
-.option-card b {
- color: var(--purple-800);
- font-size: 0.73rem;
- white-space: nowrap;
-}
-
-.upload-grid {
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: 16px;
-}
-
-.upload-box {
- position: relative;
- min-height: 165px;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 24px;
- text-align: center;
- background: var(--purple-50);
- border: 1px dashed #b99bd7;
- border-radius: 16px;
- cursor: pointer;
-}
-
-.upload-box:hover {
- background: var(--purple-100);
-}
-
-.upload-box input {
- position: absolute;
- width: 1px;
- height: 1px;
- opacity: 0;
- pointer-events: none;
-}
-
-.upload-icon {
- width: 42px;
- height: 42px;
- display: grid;
- place-items: center;
- margin-bottom: 12px;
- color: var(--purple-800);
- background: white;
- border-radius: 50%;
- box-shadow: var(--shadow-sm);
- font-size: 1.3rem;
-}
-
-.upload-box strong {
- font-size: 0.86rem;
-}
-
-.upload-box small {
- margin-top: 4px;
-}
-
-.full-width {
- display: block;
- margin-top: 22px;
-}
-
-.agreement {
- display: flex;
- align-items: flex-start;
- gap: 10px;
- margin-top: 22px;
- color: var(--muted);
- line-height: 1.45;
- font-size: 0.77rem;
- font-weight: 500;
-}
-
-.agreement input {
- flex: 0 0 18px;
- width: 18px;
- height: 18px;
- margin: 2px 0 0;
- accent-color: var(--purple-700);
-}
-
-.summary-sticky {
- position: sticky;
- top: 105px;
- padding: 30px;
- color: white;
- background:
- radial-gradient(circle at 100% 0%, rgba(255,255,255,0.1), transparent 28%),
- var(--purple-950);
- border-radius: var(--radius-md);
- box-shadow: var(--shadow-lg);
-}
-
-.summary-sticky .eyebrow {
- color: #cfaeea;
-}
-
-.summary-sticky h3 {
- margin: 8px 0 24px;
- font-size: 1.55rem;
-}
-
-.summary-line {
- display: flex;
- justify-content: space-between;
- gap: 18px;
- padding: 12px 0;
- color: rgba(255,255,255,0.72);
- border-bottom: 1px solid rgba(255,255,255,0.11);
- font-size: 0.82rem;
-}
-
-.summary-line strong {
- color: white;
-}
-
-.service-summary:empty {
- display: none;
-}
-
-.summary-total {
- display: flex;
- justify-content: space-between;
- align-items: flex-end;
- gap: 18px;
- padding: 24px 0 10px;
-}
-
-.summary-total span {
- color: rgba(255,255,255,0.74);
- font-size: 0.8rem;
-}
-
-.summary-total strong {
- font-size: 2rem;
- letter-spacing: -0.04em;
-}
-
-.summary-note,
-.secure-note {
- color: rgba(255,255,255,0.58);
- font-size: 0.69rem;
- line-height: 1.5;
-}
-
-.summary-note {
- margin: 0 0 22px;
-}
-
-.secure-note {
- margin: 14px 0 0;
- text-align: center;
-}
-
-.faq-layout {
- display: grid;
- grid-template-columns: 0.78fr 1.22fr;
- gap: 70px;
-}
-
-.faq-list {
- display: grid;
- gap: 12px;
-}
-
-details {
- padding: 22px 24px;
- background: white;
- border: 1px solid var(--line);
- border-radius: 14px;
-}
-
-summary {
- cursor: pointer;
- font-weight: 750;
-}
-
-details p {
- margin: 14px 0 0;
- color: var(--muted);
- font-size: 0.87rem;
-}
-
-.cta-section {
- padding: 0 0 90px;
- background: var(--surface-soft);
-}
-
-.cta-card {
- min-height: 250px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 40px;
- padding: 52px;
- color: white;
- background:
- radial-gradient(circle at 85% 20%, rgba(255,255,255,0.13), transparent 25%),
- linear-gradient(135deg, var(--purple-950), var(--purple-700));
- border-radius: var(--radius-lg);
-}
-
-.cta-card .eyebrow {
- color: #d8bcea;
-}
-
-.cta-card h2 {
- max-width: 700px;
- margin: 10px 0 0;
- font-size: clamp(1.8rem, 3vw, 2.8rem);
- line-height: 1.1;
- letter-spacing: -0.04em;
-}
-
-.site-footer {
- padding: 65px 0 26px;
- color: rgba(255,255,255,0.68);
- background: #170b24;
-}
-
-.footer-grid {
- display: grid;
- grid-template-columns: 1.6fr repeat(3, 1fr);
- gap: 48px;
-}
-
-.footer-grid > div {
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- gap: 10px;
- font-size: 0.78rem;
-}
-
-.footer-grid > div:first-child {
- padding-right: 50px;
-}
-
-.footer-grid strong {
- color: white;
-}
-
-.footer-brand {
- color: white;
- margin-bottom: 8px;
-}
-
-.footer-brand small {
- color: rgba(255,255,255,0.5);
-}
-
-.footer-grid p {
- margin: 0;
-}
-
-.footer-grid a:hover {
- color: white;
-}
-
-.footer-bottom {
- display: flex;
- justify-content: space-between;
- gap: 30px;
- margin-top: 45px;
- padding-top: 24px;
- border-top: 1px solid rgba(255,255,255,0.1);
- font-size: 0.72rem;
-}
-
-dialog {
- width: min(calc(100% - 32px), 500px);
- padding: 0;
- border: 0;
- border-radius: 22px;
- box-shadow: var(--shadow-lg);
-}
-
-dialog::backdrop {
- background: rgba(21, 9, 32, 0.68);
- backdrop-filter: blur(4px);
-}
-
-.dialog-content {
- position: relative;
- padding: 42px;
- text-align: center;
-}
-
-.dialog-close {
- position: absolute;
- top: 12px;
- right: 14px;
- width: 36px;
- height: 36px;
- color: var(--muted);
- background: transparent;
- border: 0;
- border-radius: 50%;
- cursor: pointer;
- font-size: 1.5rem;
-}
-
-.dialog-icon {
- width: 62px;
- height: 62px;
- display: grid;
- place-items: center;
- margin: 0 auto 18px;
- color: white;
- background: var(--success);
- border-radius: 50%;
- font-size: 1.5rem;
-}
-
-.dialog-content h2 {
- margin: 0 0 10px;
-}
-
-.dialog-content p {
- margin: 0 0 24px;
- color: var(--muted);
- font-size: 0.9rem;
-}
-
-@media (max-width: 1020px) {
- .main-nav {
- display: none;
- }
-
- .hero-grid,
- .faq-layout {
- grid-template-columns: 1fr;
- }
-
- .hero-copy {
- max-width: 780px;
- }
-
- .hero-visual {
- min-height: 430px;
- }
-
- .card-grid,
- .process-grid {
- grid-template-columns: repeat(2, 1fr);
- }
-
- .order-layout {
- grid-template-columns: 1fr;
- }
-
- .summary-sticky {
- position: static;
- }
-
- .footer-grid {
- grid-template-columns: repeat(2, 1fr);
- }
-}
-
-@media (max-width: 720px) {
- .container {
- width: min(calc(100% - 26px), var(--container));
- }
-
- .site-header .button-outline {
- display: none;
- }
-
- .hero {
- padding: 68px 0 75px;
- }
-
- .hero h1 {
- font-size: 2.65rem;
- }
-
- .hero-visual {
- min-height: 330px;
- }
-
- .envelope-card {
- padding: 24px;
- border-radius: 18px;
- }
-
- .recipient-block,
- .barcode {
- left: 33%;
- }
-
- .barcode {
- transform: scale(0.72);
- transform-origin: left bottom;
- }
-
- .stat-card {
- display: none;
- }
-
- .service-strip-grid,
- .card-grid,
- .process-grid,
- .two-columns,
- .option-grid,
- .upload-grid,
- .footer-grid {
- grid-template-columns: 1fr;
- }
-
- .service-strip-grid > div {
- min-height: 90px;
- border-right: 0;
- border-bottom: 1px solid rgba(255,255,255,0.11);
- }
-
- .section {
- padding: 74px 0;
- }
-
- .feature-card,
- .process-step {
- min-height: auto;
- }
-
- .process-step h3 {
- margin-top: 25px;
- }
-
- .form-section {
- padding: 25px 20px;
- }
-
- .option-card {
- grid-template-columns: auto 1fr;
- }
-
- .option-card b {
- grid-column: 2;
- }
-
- .cta-card {
- min-height: 0;
- align-items: flex-start;
- flex-direction: column;
- padding: 36px 26px;
- border-radius: 22px;
- }
-
- .footer-grid > div:first-child {
- padding-right: 0;
- }
-
- .footer-bottom {
- align-items: flex-start;
- flex-direction: column;
- }
-}