@notionhive/contacts 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/bin/contacts.js +16 -0
  2. package/category.config.json +7 -0
  3. package/package.json +24 -0
  4. package/registry/contact-01.json +9 -0
  5. package/registry/contact-02.json +9 -0
  6. package/registry/contact-03.json +9 -0
  7. package/registry/contact-04.json +6 -0
  8. package/registry/contact-05.json +9 -0
  9. package/registry/contact-06.json +9 -0
  10. package/registry/contact-07.json +9 -0
  11. package/registry/contact-08.json +6 -0
  12. package/registry/contact-09.json +10 -0
  13. package/registry/contact-10.json +6 -0
  14. package/registry/index.json +88 -0
  15. package/templates/components/atoms/SafeImage/SafeImage.jsx +101 -0
  16. package/templates/components/atoms/SafeImage/index.js +1 -0
  17. package/templates/components/hooks/useCarousel.js +73 -0
  18. package/templates/components/organisms/Contact01/Contact01.jsx +363 -0
  19. package/templates/components/organisms/Contact01/Contact01.propTypes.js +105 -0
  20. package/templates/components/organisms/Contact01/index.js +1 -0
  21. package/templates/components/organisms/Contact02/Contact02.jsx +288 -0
  22. package/templates/components/organisms/Contact02/Contact02.propTypes.js +78 -0
  23. package/templates/components/organisms/Contact02/index.js +1 -0
  24. package/templates/components/organisms/Contact03/Contact03.jsx +94 -0
  25. package/templates/components/organisms/Contact03/Contact03.propTypes.js +25 -0
  26. package/templates/components/organisms/Contact03/index.js +1 -0
  27. package/templates/components/organisms/Contact04/Contact04.jsx +239 -0
  28. package/templates/components/organisms/Contact04/Contact04.propTypes.js +50 -0
  29. package/templates/components/organisms/Contact04/index.js +1 -0
  30. package/templates/components/organisms/Contact05/Contact05.jsx +272 -0
  31. package/templates/components/organisms/Contact05/Contact05.propTypes.js +49 -0
  32. package/templates/components/organisms/Contact05/index.js +1 -0
  33. package/templates/components/organisms/Contact06/Contact06.jsx +223 -0
  34. package/templates/components/organisms/Contact06/Contact06.propTypes.js +40 -0
  35. package/templates/components/organisms/Contact06/index.js +1 -0
  36. package/templates/components/organisms/Contact07/Contact07.jsx +348 -0
  37. package/templates/components/organisms/Contact07/Contact07.propTypes.js +92 -0
  38. package/templates/components/organisms/Contact07/index.js +1 -0
  39. package/templates/components/organisms/Contact08/Contact08.jsx +178 -0
  40. package/templates/components/organisms/Contact08/Contact08.propTypes.js +36 -0
  41. package/templates/components/organisms/Contact08/index.js +1 -0
  42. package/templates/components/organisms/Contact09/Contact09.jsx +345 -0
  43. package/templates/components/organisms/Contact09/Contact09.propTypes.js +96 -0
  44. package/templates/components/organisms/Contact09/index.js +1 -0
  45. package/templates/components/organisms/Contact10/Contact10.jsx +175 -0
  46. package/templates/components/organisms/Contact10/Contact10.propTypes.js +55 -0
  47. package/templates/components/organisms/Contact10/index.js +1 -0
  48. package/templates/public/contact/contact01/ellipse.svg +12 -0
  49. package/templates/public/contact/contact01/logo-accenture.svg +6 -0
  50. package/templates/public/contact/contact01/logo-amart.svg +12 -0
  51. package/templates/public/contact/contact01/logo-brand12.png +0 -0
  52. package/templates/public/contact/contact01/logo-brand5.png +0 -0
  53. package/templates/public/contact/contact01/logo-brand7.png +0 -0
  54. package/templates/public/contact/contact01/logo-brand9.svg +12 -0
  55. package/templates/public/contact/contact01/logo-great-eastern.png +0 -0
  56. package/templates/public/contact/contact01/logo-kenwood.png +0 -0
  57. package/templates/public/contact/contact01/logo-nissan.svg +12 -0
  58. package/templates/public/contact/contact01/logo-suncorp.png +0 -0
  59. package/templates/public/contact/contact01/logo-ticketmaster.png +0 -0
  60. package/templates/public/contact/contact01/logo-toyota.svg +5 -0
  61. package/templates/public/contact/contact02/map.jpg +0 -0
  62. package/templates/public/contact/contact03/bg.jpg +0 -0
  63. package/templates/public/contact/contact03/card-photo.jpg +0 -0
  64. package/templates/public/contact/contact05/hero.jpg +0 -0
  65. package/templates/public/contact/contact05/pattern.png +0 -0
  66. package/templates/public/contact/contact06/hero.jpg +0 -0
  67. package/templates/public/contact/contact07/hero.jpg +0 -0
  68. package/templates/public/contact/contact09/avatar.jpg +0 -0
@@ -0,0 +1,223 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import SafeImage from "../../atoms/SafeImage";
5
+ import {
6
+ contact06DefaultProps,
7
+ contact06PropTypes,
8
+ } from "./Contact06.propTypes";
9
+
10
+ const underlineInputClass =
11
+ "w-full bg-transparent text-base text-[#2c2c2c] outline-none transition-colors duration-200 ease-out placeholder:text-transparent focus:placeholder:text-[#2c2c2c]/40";
12
+
13
+ function ChevronDownIcon({ className = "" }) {
14
+ return (
15
+ <svg viewBox="0 0 20 20" fill="none" aria-hidden="true" className={className}>
16
+ <path
17
+ d="M5 8l5 5 5-5"
18
+ stroke="currentColor"
19
+ strokeWidth="1.5"
20
+ strokeLinecap="round"
21
+ strokeLinejoin="round"
22
+ />
23
+ </svg>
24
+ );
25
+ }
26
+
27
+ function ArrowUpRightIcon({ className = "" }) {
28
+ return (
29
+ <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className={className}>
30
+ <path
31
+ d="M7 17L17 7M17 7H9M17 7V15"
32
+ stroke="currentColor"
33
+ strokeWidth="1.5"
34
+ strokeLinecap="round"
35
+ strokeLinejoin="round"
36
+ />
37
+ </svg>
38
+ );
39
+ }
40
+
41
+ function UnderlineField({ label, required = false, children }) {
42
+ return (
43
+ <div className="flex h-[60px] flex-col justify-center border-b border-[rgba(44,44,44,0.1)] py-5">
44
+ <label className="text-xs font-medium uppercase tracking-[0.96px] text-[#2c2c2c]/60">
45
+ {label}
46
+ {required ? "*" : ""}
47
+ </label>
48
+ {children}
49
+ </div>
50
+ );
51
+ }
52
+
53
+ /**
54
+ * Contact06 — Split hero image + underline form panel.
55
+ * Figma node 26:15636 at 1920px.
56
+ *
57
+ * @param {object} props - See Contact06.propTypes.js.
58
+ */
59
+ export function Contact06({
60
+ headline = contact06DefaultProps.headline,
61
+ description = contact06DefaultProps.description,
62
+ heroImage = contact06DefaultProps.heroImage,
63
+ heroAlt = contact06DefaultProps.heroAlt,
64
+ subjectOptions = contact06DefaultProps.subjectOptions,
65
+ submitText = contact06DefaultProps.submitText,
66
+ formData: controlledForm,
67
+ onFieldChange,
68
+ onSubmit,
69
+ className = "",
70
+ }) {
71
+ const [internalForm, setInternalForm] = useState({
72
+ firstName: "",
73
+ lastName: "",
74
+ email: "",
75
+ phone: "",
76
+ subject: "",
77
+ message: "",
78
+ });
79
+
80
+ const form = controlledForm ?? internalForm;
81
+
82
+ const setField = (field, value) => {
83
+ onFieldChange?.(field, value);
84
+ if (!controlledForm) {
85
+ setInternalForm((prev) => ({ ...prev, [field]: value }));
86
+ }
87
+ };
88
+
89
+ const handleSubmit = (event) => {
90
+ event.preventDefault();
91
+ onSubmit?.(form);
92
+ };
93
+
94
+ return (
95
+ <section
96
+ className={["relative w-full overflow-hidden bg-[#fafafa]", className]
97
+ .filter(Boolean)
98
+ .join(" ")}
99
+ data-contact="contact06"
100
+ >
101
+ <div className="relative mx-auto flex w-full max-w-[1920px] flex-col lg:min-h-[700px] lg:flex-row xl:min-h-[1043px]">
102
+ {/* Left — hero image */}
103
+ <div className="relative h-[50vh] w-full shrink-0 lg:h-auto lg:min-h-[700px] lg:w-1/2 xl:min-h-[1043px]">
104
+ <SafeImage
105
+ src={heroImage}
106
+ alt={heroAlt}
107
+ fill
108
+ className="object-cover object-center"
109
+ sizes="(max-width: 1024px) 100vw, 50vw"
110
+ priority
111
+ />
112
+ </div>
113
+
114
+ {/* Right — form panel */}
115
+ <div className="flex w-full items-center bg-[#fafafa] px-4 py-12 sm:px-6 md:px-10 lg:w-1/2 lg:px-16 lg:py-16 xl:px-[80px] xl:py-20">
116
+ <form
117
+ onSubmit={handleSubmit}
118
+ className="flex w-full max-w-[608px] flex-col gap-10 lg:gap-[60px]"
119
+ >
120
+ <div className="flex flex-col gap-5 text-[#2c2c2c]">
121
+ <h2 className="font-serif text-3xl leading-[1.1] tracking-[-0.96px] sm:text-4xl xl:text-[48px]">
122
+ {headline}
123
+ </h2>
124
+ <p className="font-display text-base leading-[1.4] tracking-[-0.36px] opacity-80 sm:text-lg">
125
+ {description}
126
+ </p>
127
+ </div>
128
+
129
+ <div className="flex flex-col gap-5">
130
+ <div className="grid grid-cols-1 gap-5 sm:grid-cols-2">
131
+ <UnderlineField label="First name" required>
132
+ <input
133
+ type="text"
134
+ value={form.firstName}
135
+ onChange={(e) => setField("firstName", e.target.value)}
136
+ className={underlineInputClass}
137
+ required
138
+ />
139
+ </UnderlineField>
140
+ <UnderlineField label="Last name" required>
141
+ <input
142
+ type="text"
143
+ value={form.lastName}
144
+ onChange={(e) => setField("lastName", e.target.value)}
145
+ className={underlineInputClass}
146
+ required
147
+ />
148
+ </UnderlineField>
149
+ </div>
150
+
151
+ <div className="grid grid-cols-1 gap-5 sm:grid-cols-2">
152
+ <UnderlineField label="Email address" required>
153
+ <input
154
+ type="email"
155
+ value={form.email}
156
+ onChange={(e) => setField("email", e.target.value)}
157
+ className={underlineInputClass}
158
+ required
159
+ />
160
+ </UnderlineField>
161
+ <UnderlineField label="Phone number">
162
+ <input
163
+ type="tel"
164
+ value={form.phone}
165
+ onChange={(e) => setField("phone", e.target.value)}
166
+ className={underlineInputClass}
167
+ />
168
+ </UnderlineField>
169
+ </div>
170
+
171
+ <div className="relative flex h-[60px] items-center border-b border-[rgba(44,44,44,0.1)] py-5">
172
+ <label className="sr-only">Select subject</label>
173
+ <select
174
+ value={form.subject}
175
+ onChange={(e) => setField("subject", e.target.value)}
176
+ className={`${underlineInputClass} appearance-none pr-8 text-xs font-medium uppercase tracking-[0.96px] text-[#2c2c2c]/60`}
177
+ >
178
+ <option value="" disabled>
179
+ Select subject
180
+ </option>
181
+ {subjectOptions.map((option) => (
182
+ <option key={option} value={option} className="normal-case">
183
+ {option}
184
+ </option>
185
+ ))}
186
+ </select>
187
+ <ChevronDownIcon className="pointer-events-none absolute right-0 size-5 text-[#2c2c2c]" />
188
+ </div>
189
+
190
+ <div className="flex min-h-[111px] flex-col border-b border-[rgba(44,44,44,0.1)] py-5">
191
+ <label className="text-xs font-medium uppercase tracking-[0.96px] text-[#2c2c2c]/60">
192
+ Write your message here
193
+ </label>
194
+ <textarea
195
+ value={form.message}
196
+ onChange={(e) => setField("message", e.target.value)}
197
+ rows={3}
198
+ className={`${underlineInputClass} mt-1 min-h-[60px] resize-none`}
199
+ />
200
+ </div>
201
+ </div>
202
+
203
+ <button
204
+ type="submit"
205
+ className="flex h-12 w-max max-w-full items-stretch overflow-hidden transition-opacity duration-200 ease-out hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2"
206
+ >
207
+ <span className="flex items-center justify-center bg-[#146d38] px-6 py-3 font-display text-sm font-semibold uppercase tracking-[1.12px] text-white">
208
+ {submitText}
209
+ </span>
210
+ <span className="flex size-12 shrink-0 items-center justify-center bg-[#146d38] text-white">
211
+ <ArrowUpRightIcon className="size-6" />
212
+ </span>
213
+ </button>
214
+ </form>
215
+ </div>
216
+ </div>
217
+ </section>
218
+ );
219
+ }
220
+
221
+ Contact06.propTypes = contact06PropTypes;
222
+
223
+ export default Contact06;
@@ -0,0 +1,40 @@
1
+ import PropTypes from "prop-types";
2
+
3
+ export const contact06PropTypes = {
4
+ headline: PropTypes.string,
5
+ description: PropTypes.string,
6
+ heroImage: PropTypes.string,
7
+ heroAlt: PropTypes.string,
8
+ subjectOptions: PropTypes.arrayOf(PropTypes.string),
9
+ submitText: PropTypes.string,
10
+ formData: PropTypes.shape({
11
+ firstName: PropTypes.string,
12
+ lastName: PropTypes.string,
13
+ email: PropTypes.string,
14
+ phone: PropTypes.string,
15
+ subject: PropTypes.string,
16
+ message: PropTypes.string,
17
+ }),
18
+ onFieldChange: PropTypes.func,
19
+ onSubmit: PropTypes.func,
20
+ className: PropTypes.string,
21
+ };
22
+
23
+ export const contact06DefaultProps = {
24
+ headline: "Prefer to reach out online",
25
+ description:
26
+ "Submit your message and we'll connect you with the right specialist to support your project.",
27
+ heroImage: "/contact/contact06/hero.jpg",
28
+ heroAlt: "Luxury spa bathroom with tropical view",
29
+ subjectOptions: [
30
+ "General inquiry",
31
+ "Project consultation",
32
+ "Partnership",
33
+ "Support",
34
+ ],
35
+ submitText: "submit your inquires",
36
+ formData: undefined,
37
+ onFieldChange: undefined,
38
+ onSubmit: undefined,
39
+ className: "",
40
+ };
@@ -0,0 +1 @@
1
+ export { Contact06, default } from "./Contact06";
@@ -0,0 +1,348 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import SafeImage from "../../atoms/SafeImage";
5
+ import {
6
+ contact07DefaultProps,
7
+ contact07PropTypes,
8
+ } from "./Contact07.propTypes";
9
+
10
+ const fieldClassName =
11
+ "h-[52px] w-full bg-white px-5 text-sm tracking-[0.14px] text-[#1b1e2e] outline-none transition-colors duration-200 ease-out placeholder:text-[#7e8088] focus:ring-1 focus:ring-[#020617]/20";
12
+
13
+ function ChevronDownIcon({ className = "" }) {
14
+ return (
15
+ <svg viewBox="0 0 18 18" fill="none" aria-hidden="true" className={className}>
16
+ <path
17
+ d="M4.5 7l4.5 4.5L13.5 7"
18
+ stroke="currentColor"
19
+ strokeWidth="1.5"
20
+ strokeLinecap="round"
21
+ strokeLinejoin="round"
22
+ />
23
+ </svg>
24
+ );
25
+ }
26
+
27
+ function ChevronUpIcon({ className = "" }) {
28
+ return (
29
+ <svg viewBox="0 0 18 18" fill="none" aria-hidden="true" className={className}>
30
+ <path
31
+ d="M4.5 11l4.5-4.5L13.5 11"
32
+ stroke="currentColor"
33
+ strokeWidth="1.5"
34
+ strokeLinecap="round"
35
+ strokeLinejoin="round"
36
+ />
37
+ </svg>
38
+ );
39
+ }
40
+
41
+ function MailIcon({ className = "" }) {
42
+ return (
43
+ <svg viewBox="0 0 14 14" fill="none" aria-hidden="true" className={className}>
44
+ <rect x="1" y="2.5" width="12" height="9" rx="1" stroke="currentColor" strokeWidth="1" />
45
+ <path d="M1 4l6 4 6-4" stroke="currentColor" strokeWidth="1" strokeLinecap="round" />
46
+ </svg>
47
+ );
48
+ }
49
+
50
+ function PhoneIcon({ className = "" }) {
51
+ return (
52
+ <svg viewBox="0 0 14 14" fill="none" aria-hidden="true" className={className}>
53
+ <path
54
+ d="M3.5 1.5h2l1 3-1.5 1a6 6 0 003 3L9 7l3 1v2a1 1 0 01-1 1A9.5 9.5 0 011.5 3.5a1 1 0 011-2z"
55
+ stroke="currentColor"
56
+ strokeWidth="1"
57
+ strokeLinecap="round"
58
+ strokeLinejoin="round"
59
+ />
60
+ </svg>
61
+ );
62
+ }
63
+
64
+ function ClockIcon({ className = "" }) {
65
+ return (
66
+ <svg viewBox="0 0 14 14" fill="none" aria-hidden="true" className={className}>
67
+ <circle cx="7" cy="7" r="5.5" stroke="currentColor" strokeWidth="1" />
68
+ <path d="M7 4v3l2 1.5" stroke="currentColor" strokeWidth="1" strokeLinecap="round" />
69
+ </svg>
70
+ );
71
+ }
72
+
73
+ function OfficeColumn({ office }) {
74
+ return (
75
+ <div className="flex min-w-0 flex-1 flex-col gap-3">
76
+ <p className="text-xs font-medium uppercase tracking-[0.96px] text-[#020617]">
77
+ {office.name}
78
+ </p>
79
+ <div className="h-px w-full bg-[#e1e2e3]" />
80
+ <div className="flex flex-col gap-2.5 text-sm leading-[1.2] tracking-[0.14px] text-[#343744]">
81
+ {office.email ? (
82
+ <div className="flex items-center gap-2">
83
+ <MailIcon className="size-3.5 shrink-0 text-[#343744]" />
84
+ <a
85
+ href={`mailto:${office.email}`}
86
+ className="transition-opacity duration-200 hover:opacity-80"
87
+ >
88
+ {office.email}
89
+ </a>
90
+ </div>
91
+ ) : null}
92
+ {office.phone ? (
93
+ <div className="flex items-center gap-2">
94
+ <PhoneIcon className="size-3.5 shrink-0 text-[#343744]" />
95
+ <a
96
+ href={`tel:${office.phone.replace(/\s/g, "")}`}
97
+ className="transition-opacity duration-200 hover:opacity-80"
98
+ >
99
+ {office.phone}
100
+ </a>
101
+ </div>
102
+ ) : null}
103
+ {office.hours ? (
104
+ <div className="flex items-center gap-2">
105
+ <ClockIcon className="size-3.5 shrink-0 text-[#343744]" />
106
+ <span>{office.hours}</span>
107
+ </div>
108
+ ) : null}
109
+ </div>
110
+ </div>
111
+ );
112
+ }
113
+
114
+ /**
115
+ * Contact07 — Gentle Park split contact with overlaid form and FAQ.
116
+ * Figma node 26:15698 at 1920px (navbar excluded).
117
+ *
118
+ * @param {object} props - See Contact07.propTypes.js.
119
+ */
120
+ export function Contact07({
121
+ headline = contact07DefaultProps.headline,
122
+ description = contact07DefaultProps.description,
123
+ heroImage = contact07DefaultProps.heroImage,
124
+ heroAlt = contact07DefaultProps.heroAlt,
125
+ offices = contact07DefaultProps.offices,
126
+ topicOptions = contact07DefaultProps.topicOptions,
127
+ submitText = contact07DefaultProps.submitText,
128
+ faqTitle = contact07DefaultProps.faqTitle,
129
+ faqItems = contact07DefaultProps.faqItems,
130
+ defaultOpenFaq = contact07DefaultProps.defaultOpenFaq,
131
+ activeFaq: controlledFaq,
132
+ onFaqChange,
133
+ formData: controlledForm,
134
+ onFieldChange,
135
+ onSubmit,
136
+ showFaq = contact07DefaultProps.showFaq,
137
+ className = "",
138
+ }) {
139
+ const [internalFaq, setInternalFaq] = useState(defaultOpenFaq);
140
+ const [internalForm, setInternalForm] = useState({
141
+ fullName: "",
142
+ email: "",
143
+ phone: "",
144
+ orderId: "",
145
+ topic: "",
146
+ message: "",
147
+ });
148
+
149
+ const activeFaq = controlledFaq ?? internalFaq;
150
+ const form = controlledForm ?? internalForm;
151
+
152
+ const setField = (field, value) => {
153
+ onFieldChange?.(field, value);
154
+ if (!controlledForm) {
155
+ setInternalForm((prev) => ({ ...prev, [field]: value }));
156
+ }
157
+ };
158
+
159
+ const toggleFaq = (index) => {
160
+ const next = activeFaq === index ? -1 : index;
161
+ onFaqChange?.(next);
162
+ if (controlledFaq === undefined) setInternalFaq(next);
163
+ };
164
+
165
+ const handleSubmit = (event) => {
166
+ event.preventDefault();
167
+ onSubmit?.(form);
168
+ };
169
+
170
+ return (
171
+ <section
172
+ className={["relative w-full overflow-hidden bg-[#fafafa]", className]
173
+ .filter(Boolean)
174
+ .join(" ")}
175
+ data-contact="contact07"
176
+ >
177
+ {/* Hero + form */}
178
+ <div className="relative mx-auto w-full max-w-[1920px]">
179
+ <div className="relative flex flex-col lg:min-h-[800px] lg:flex-row xl:min-h-[1032px]">
180
+ {/* Left — copy + offices */}
181
+ <div className="relative z-10 flex w-full flex-col justify-center gap-8 bg-[#fafafa] px-4 py-12 sm:px-6 md:px-10 lg:w-1/2 lg:gap-[34px] lg:px-10 lg:py-16 xl:px-[160px] xl:py-20">
182
+ <div className="flex max-w-[633px] flex-col gap-[34px]">
183
+ <h2 className="text-4xl font-light capitalize leading-none tracking-[-1px] text-[#020617] sm:text-5xl xl:text-[50px]">
184
+ {headline}
185
+ </h2>
186
+ <p className="text-base leading-[1.1] tracking-[0.16px] text-[#343744]">
187
+ {description}
188
+ </p>
189
+ </div>
190
+
191
+ <div className="flex max-w-[633px] flex-col gap-[34px]">
192
+ <div className="grid grid-cols-1 gap-8 sm:grid-cols-2 sm:gap-[34px]">
193
+ {offices.map((office) => (
194
+ <OfficeColumn key={office.name} office={office} />
195
+ ))}
196
+ </div>
197
+ </div>
198
+ </div>
199
+
200
+ {/* Right — hero image + overlaid form */}
201
+ <div className="relative w-full lg:w-1/2 lg:min-h-[800px] xl:min-h-[1032px]">
202
+ <div className="relative h-[50vh] w-full lg:absolute lg:inset-0 lg:h-full">
203
+ <SafeImage
204
+ src={heroImage}
205
+ alt={heroAlt}
206
+ fill
207
+ className="object-cover object-center"
208
+ sizes="(max-width: 1024px) 100vw, 50vw"
209
+ priority
210
+ />
211
+ </div>
212
+
213
+ {/* Form overlay — flows below image on mobile, absolute on desktop */}
214
+ <div className="relative z-10 px-4 py-8 sm:px-6 md:px-10 lg:absolute lg:inset-0 lg:flex lg:items-center lg:justify-end lg:px-10 lg:py-0 xl:pr-[68px]">
215
+ <form
216
+ onSubmit={handleSubmit}
217
+ className="flex w-full max-w-[500px] flex-col gap-2.5 bg-[#fafafa]/95 p-4 backdrop-blur-sm lg:bg-transparent lg:p-0 lg:backdrop-blur-none"
218
+ >
219
+ <input
220
+ type="text"
221
+ value={form.fullName}
222
+ onChange={(e) => setField("fullName", e.target.value)}
223
+ placeholder="Full Name*"
224
+ className={fieldClassName}
225
+ required
226
+ />
227
+ <input
228
+ type="email"
229
+ value={form.email}
230
+ onChange={(e) => setField("email", e.target.value)}
231
+ placeholder="Email Address*"
232
+ className={fieldClassName}
233
+ required
234
+ />
235
+ <input
236
+ type="tel"
237
+ value={form.phone}
238
+ onChange={(e) => setField("phone", e.target.value)}
239
+ placeholder="Phone Number (Optional)"
240
+ className={fieldClassName}
241
+ />
242
+ <input
243
+ type="text"
244
+ value={form.orderId}
245
+ onChange={(e) => setField("orderId", e.target.value)}
246
+ placeholder="Order ID (Optional)"
247
+ className={fieldClassName}
248
+ />
249
+ <div className="relative">
250
+ <select
251
+ value={form.topic}
252
+ onChange={(e) => setField("topic", e.target.value)}
253
+ className={`${fieldClassName} appearance-none pr-10 text-[#7e8088]`}
254
+ >
255
+ <option value="" disabled>
256
+ Select topic
257
+ </option>
258
+ {topicOptions.map((option) => (
259
+ <option key={option} value={option} className="text-[#1b1e2e]">
260
+ {option}
261
+ </option>
262
+ ))}
263
+ </select>
264
+ <ChevronDownIcon className="pointer-events-none absolute right-5 top-1/2 size-[18px] -translate-y-1/2 text-[#7e8088]" />
265
+ </div>
266
+ <textarea
267
+ value={form.message}
268
+ onChange={(e) => setField("message", e.target.value)}
269
+ placeholder="Your message"
270
+ rows={5}
271
+ className="min-h-[150px] w-full resize-none bg-white px-5 py-4 text-sm tracking-[0.14px] text-[#1b1e2e] outline-none transition-colors duration-200 ease-out placeholder:text-[#7e8088] focus:ring-1 focus:ring-[#020617]/20"
272
+ />
273
+ <button
274
+ type="submit"
275
+ className="flex h-[52px] w-full items-center justify-center bg-[#1b1e2e] px-10 py-[17px] text-sm font-semibold uppercase tracking-[0.98px] text-white transition-colors duration-200 ease-out hover:bg-[#020617] focus-visible:outline-2 focus-visible:outline-offset-2"
276
+ >
277
+ {submitText}
278
+ </button>
279
+ </form>
280
+ </div>
281
+ </div>
282
+ </div>
283
+ </div>
284
+
285
+ {/* FAQ */}
286
+ {showFaq && faqItems?.length ? (
287
+ <div className="px-4 py-16 sm:px-6 md:px-10 lg:px-20 lg:py-24 xl:px-[160px]">
288
+ <div className="mx-auto flex w-full max-w-[955px] flex-col items-center gap-10 lg:gap-[60px]">
289
+ <h2 className="w-full text-center text-4xl font-light capitalize leading-none tracking-[-1px] text-[#020617] sm:text-5xl xl:text-[50px]">
290
+ {faqTitle}
291
+ </h2>
292
+
293
+ <div className="flex w-full flex-col gap-[30px]">
294
+ {faqItems.map((item, index) => {
295
+ const isOpen = activeFaq === index;
296
+ const isLast = index === faqItems.length - 1;
297
+ return (
298
+ <div key={item.question} className="flex flex-col gap-[30px]">
299
+ <div className="flex flex-col gap-[30px]">
300
+ <button
301
+ type="button"
302
+ onClick={() => toggleFaq(index)}
303
+ className="flex w-full items-center justify-between gap-4 text-left"
304
+ aria-expanded={isOpen}
305
+ >
306
+ <span className="text-lg font-medium leading-none text-[#020617] sm:text-2xl">
307
+ {item.question}
308
+ </span>
309
+ <span className="shrink-0 text-[#020617]">
310
+ {isOpen ? (
311
+ <ChevronUpIcon className="size-[18px]" />
312
+ ) : (
313
+ <ChevronDownIcon className="size-[18px]" />
314
+ )}
315
+ </span>
316
+ </button>
317
+ <div
318
+ className={[
319
+ "grid motion-reduce:transition-none transition-all duration-300 ease-out",
320
+ isOpen && item.answer
321
+ ? "grid-rows-[1fr] opacity-100"
322
+ : "grid-rows-[0fr] opacity-0",
323
+ ].join(" ")}
324
+ >
325
+ <div className="overflow-hidden">
326
+ {item.answer ? (
327
+ <p className="text-base leading-[1.1] tracking-[0.16px] text-[#343744]">
328
+ {item.answer}
329
+ </p>
330
+ ) : null}
331
+ </div>
332
+ </div>
333
+ </div>
334
+ {!isLast ? <div className="h-px w-full bg-[#e1e2e3]" /> : null}
335
+ </div>
336
+ );
337
+ })}
338
+ </div>
339
+ </div>
340
+ </div>
341
+ ) : null}
342
+ </section>
343
+ );
344
+ }
345
+
346
+ Contact07.propTypes = contact07PropTypes;
347
+
348
+ export default Contact07;