You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1040 lines
27 KiB

3 years ago
  1. <template>
  2. <div class="white-bkg">
  3. <header>
  4. <div class="header__col">
  5. <button class="btn btn--primary" @click="goBack">
  6. <svg
  7. xmlns="http://www.w3.org/2000/svg"
  8. class="icon icon-tabler icon-tabler-chevrons-left"
  9. width="24"
  10. height="24"
  11. viewBox="0 0 24 24"
  12. stroke-width="3"
  13. stroke="#ffffff"
  14. fill="none"
  15. stroke-linecap="round"
  16. stroke-linejoin="round"
  17. >
  18. <path stroke="none" d="M0 0h24v24H0z" fill="none" />
  19. <polyline points="11 7 6 12 11 17" />
  20. <polyline points="17 7 12 12 17 17" />
  21. </svg>
  22. back
  23. </button>
  24. </div>
  25. <div class="header__col">
  26. <Logo />
  27. </div>
  28. <div class="header__col" />
  29. </header>
  30. <main class="main-with-header">
  31. <ChosenFlights
  32. :selected-orig="selectedOrig"
  33. :selected-dest="selectedDest"
  34. :departure="formattedDate($route.params.departure)"
  35. :departure-time="
  36. selectedSchedule &&
  37. selectedSchedule.fields &&
  38. selectedSchedule.fields.Departure_TimeL
  39. "
  40. style="margin: 0 1rem;"
  41. />
  42. <div class="schedule-wrapper">
  43. <ul class="schedule-grid">
  44. <li
  45. v-for="schedule in schedules"
  46. :key="schedule.fields.Record_ID"
  47. :class="[
  48. 'grid__row',
  49. { grid__row: selectedSchedule.id == schedule.id },
  50. ]"
  51. >
  52. <div class="row__check">
  53. <label class="radio radio--sched" :for="schedule.id">
  54. <input :id="schedule.id" type="radio" name="schedules" />
  55. <span @click="selectSchedule(schedule)">
  56. <svg
  57. xmlns="http://www.w3.org/2000/svg"
  58. class="icon icon-tabler icon-tabler-check"
  59. width="24"
  60. height="24"
  61. viewBox="0 0 24 24"
  62. stroke-width="3"
  63. stroke="#ffffff"
  64. fill="none"
  65. stroke-linecap="round"
  66. stroke-linejoin="round"
  67. >
  68. <path stroke="none" d="M0 0h24v24H0z" fill="none" />
  69. <path d="M5 12l5 5l10 -10" />
  70. </svg>
  71. </span>
  72. </label>
  73. </div>
  74. <div class="row__dep">
  75. <label class="text--muni">
  76. {{ selectedOrig.municipality }}
  77. </label>
  78. <h1 class="text--time">
  79. {{ schedule.fields.Departure_TimeL }}
  80. </h1>
  81. </div>
  82. <div class="row__trip">
  83. <svg
  84. xmlns="http://www.w3.org/2000/svg"
  85. class="icon icon-tabler icon-tabler-arrow-right-circle"
  86. width="100"
  87. height="100"
  88. viewBox="0 0 24 24"
  89. stroke-width="1.5"
  90. stroke="#007fff"
  91. fill="none"
  92. stroke-linecap="round"
  93. stroke-linejoin="round"
  94. >
  95. <path stroke="none" d="M0 0h24v24H0z" fill="none" />
  96. <path d="M18 15l3 -3l-3 -3" />
  97. <circle cx="5" cy="12" r="2" />
  98. <path d="M7 12h14" />
  99. </svg>
  100. </div>
  101. <div class="row__arr">
  102. <label class="text--muni">
  103. {{ selectedDest.municipality }}
  104. </label>
  105. <h1 class="text--time">
  106. {{ schedule.fields.Arrival_TimeL }}
  107. </h1>
  108. </div>
  109. <div class="row__info">
  110. <h2 class="text--carrier">
  111. {{ schedule.fields.Carrier_Name }}
  112. </h2>
  113. <span
  114. v-if="schedule.fields.Flight_Number"
  115. class="text--flight-num"
  116. >
  117. Flight No. {{ schedule.fields.Flight_Number }}
  118. </span>
  119. <span class="text--dow">
  120. {{ schedule.fields.Frequency_Convert }}
  121. </span>
  122. <span class="badge--avail">
  123. Available until
  124. <strong>{{
  125. formattedDate(schedule.fields.Effective_End)
  126. }}</strong>
  127. </span>
  128. </div>
  129. </li>
  130. </ul>
  131. </div>
  132. <div
  133. :class="[
  134. 'flyout-container',
  135. { 'flyout-container--hide': !selectedSchedule.id },
  136. { 'flyout-container--show': selectedSchedule.id },
  137. ]"
  138. >
  139. <div class="flyout">
  140. <button
  141. v-if="
  142. selectedSchedule &&
  143. selectedSchedule.fields &&
  144. selectedSchedule.fields.IsExpedia &&
  145. selectedSchedule.fields.IsExpedia[0] !== 'true' &&
  146. selectedSchedule.fields.Carrier_Name !==
  147. 'Copper Valley Air Service'
  148. "
  149. class="btn btn--primary"
  150. @click="goToCarrier"
  151. >
  152. see flights
  153. </button>
  154. <div
  155. v-if="
  156. selectedSchedule &&
  157. selectedSchedule.fields &&
  158. selectedSchedule.fields.IsExpedia &&
  159. (selectedSchedule.fields.IsExpedia[0] === 'true' ||
  160. selectedSchedule.fields.Carrier_Name ===
  161. 'Copper Valley Air Service')
  162. "
  163. class="form-row"
  164. >
  165. <label for="">How Many Adults?</label>
  166. <v-select
  167. v-model="numAdult"
  168. :options="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
  169. />
  170. </div>
  171. <div
  172. v-if="
  173. selectedSchedule &&
  174. selectedSchedule.fields &&
  175. selectedSchedule.fields.IsExpedia &&
  176. (selectedSchedule.fields.IsExpedia[0] === 'true' ||
  177. selectedSchedule.fields.Carrier_Name ===
  178. 'Copper Valley Air Service')
  179. "
  180. class="form-row"
  181. >
  182. <label for="">How Many Children?</label>
  183. <v-select
  184. v-model="numChild"
  185. :options="[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
  186. />
  187. </div>
  188. <div
  189. v-if="
  190. selectedSchedule &&
  191. selectedSchedule.fields &&
  192. selectedSchedule.fields.Carrier_Name ===
  193. 'Copper Valley Air Service'
  194. "
  195. class="alert--flights"
  196. >
  197. <h3>
  198. You'll be flying with<br />
  199. <strong>Copper Valley Air Service</strong>,<br />a great local
  200. airline!
  201. </h3>
  202. <!-- <p>
  203. Copper Valley is a small, family run airline that not only
  204. provides bi-weekly air-taxi flights, but also mail delivery,
  205. sight-seeing, and adventure flights to remote areas of Alaska.
  206. </p> -->
  207. <p>
  208. To book with Copper Valley, enter your contact info below and they
  209. will contact you directly to finish your booking.
  210. </p>
  211. </div>
  212. <div
  213. v-if="
  214. selectedSchedule &&
  215. selectedSchedule.fields &&
  216. selectedSchedule.fields.Carrier_Name ===
  217. 'Copper Valley Air Service'
  218. "
  219. class="form-row form-row--two-columns"
  220. >
  221. <input
  222. type="text"
  223. name="firstName"
  224. placeholder="first name"
  225. v-model="firstName"
  226. :class="[{ 'input--error': firstNameTouched && !firstName }]"
  227. @focus="firstNameTouched = true"
  228. />
  229. <input
  230. type="text"
  231. name="lastName"
  232. placeholder="last name"
  233. v-model="lastName"
  234. :class="[{ 'input--error': lastNameTouched && !lastName }]"
  235. @focus="lastNameTouched = true"
  236. />
  237. </div>
  238. <div
  239. v-if="
  240. selectedSchedule &&
  241. selectedSchedule.fields &&
  242. selectedSchedule.fields.Carrier_Name ===
  243. 'Copper Valley Air Service'
  244. "
  245. class="form-row"
  246. >
  247. <input
  248. type="email"
  249. name="email"
  250. placeholder="email address"
  251. v-model="email"
  252. required
  253. @input="validateEmail"
  254. :class="[{ 'input--error': emailError }]"
  255. />
  256. <span class="alert--error">
  257. {{ emailError }}
  258. </span>
  259. </div>
  260. <div
  261. v-if="
  262. selectedSchedule &&
  263. selectedSchedule.fields &&
  264. selectedSchedule.fields.Carrier_Name ===
  265. 'Copper Valley Air Service'
  266. "
  267. class="form-row"
  268. >
  269. <VuePhoneNumberInput
  270. v-model="phone"
  271. required
  272. @update="validatePhone"
  273. show-code-on-list
  274. @phone-number-focused="phoneTouched = true"
  275. />
  276. <!-- <input
  277. type="tel"
  278. name="tel"
  279. placeholder="phone number"
  280. v-model="phone"
  281. required
  282. @blur="validatePhone"
  283. /> -->
  284. <span v-if="phoneTouched" class="alert--error">
  285. {{ phoneError }}
  286. </span>
  287. </div>
  288. <button
  289. v-if="
  290. selectedSchedule &&
  291. selectedSchedule.fields &&
  292. selectedSchedule.fields.IsExpedia &&
  293. selectedSchedule.fields.IsExpedia[0] === 'true'
  294. "
  295. class="btn btn--primary"
  296. @click="book"
  297. >
  298. book now!
  299. </button>
  300. <button
  301. v-if="
  302. selectedSchedule &&
  303. selectedSchedule.fields &&
  304. selectedSchedule.fields.Carrier_Name ===
  305. 'Copper Valley Air Service'
  306. "
  307. :disabled="
  308. (emailError + phoneError).length > 0 || !firstName || !lastName
  309. "
  310. class="btn btn--primary"
  311. @click="contactCarrier"
  312. >
  313. contact carrier
  314. <svg
  315. xmlns="http://www.w3.org/2000/svg"
  316. class="icon icon-tabler icon-tabler-send"
  317. width="24"
  318. height="24"
  319. viewBox="0 0 24 24"
  320. stroke-width="3"
  321. stroke="#000000"
  322. fill="none"
  323. stroke-linecap="round"
  324. stroke-linejoin="round"
  325. >
  326. <path stroke="none" d="M0 0h24v24H0z" fill="none" />
  327. <line x1="10" y1="14" x2="21" y2="3" />
  328. <path
  329. d="M21 3l-6.5 18a0.55 .55 0 0 1 -1 0l-3.5 -7l-7 -3.5a0.55 .55 0 0 1 0 -1l18 -6.5"
  330. />
  331. </svg>
  332. </button>
  333. <div v-if="mailSent" class="alert--sent">
  334. Thanks! You will be contacted shortly.
  335. </div>
  336. </div>
  337. </div>
  338. </main>
  339. </div>
  340. </template>
  341. <script>
  342. export default {
  343. data() {
  344. return {
  345. schedules: [],
  346. startingDate: "",
  347. selectedSchedule: {},
  348. selectedDays: [],
  349. selectedOrig: {},
  350. selectedDest: {},
  351. numAdult: 1,
  352. numChild: 0,
  353. firstName: "",
  354. lastName: "",
  355. email: "",
  356. phone: "",
  357. firstNameTouched: false,
  358. lastNameTouched: false,
  359. emailTouched: false,
  360. phoneTouched: false,
  361. emailError: "",
  362. phoneError: "",
  363. mailSent: false,
  364. };
  365. },
  366. async fetch() {
  367. // console.log(this.$route.params.o)
  368. const today = new Date().toLocaleDateString("en-CA");
  369. const scheduleFetchFilterFormula = `AND(Is_Current="Yes",Origin_IATA="${
  370. this.$route.params.o
  371. }",Destination_IATA="${
  372. this.$route.params.d
  373. }",Effective_End>"${today}",SEARCH("${new Date(
  374. this.$route.params.departure
  375. ).getUTCDay()}",Frequency)>0)`;
  376. // console.log(scheduleFetchFilterFormula)
  377. const scheduleFetchSort =
  378. "&sort[0][field]=Departure_TimeL&sort[0][direction]=asc";
  379. const response = await fetch(
  380. `https://api.airtable.com/v0/appiQwfVZixRgRICe/Schedule?filterByFormula=${scheduleFetchFilterFormula}${scheduleFetchSort}`,
  381. {
  382. method: "GET",
  383. headers: {
  384. "Content-Type": "application/x-www-form-urlencoded",
  385. Authorization: "Bearer keyJ2ht64ZSN57AG1",
  386. },
  387. }
  388. );
  389. const data = await response.json();
  390. // console.log(data)
  391. this.schedules = [...data.records];
  392. const emailCookie = this.$cookies.get("email");
  393. if (emailCookie) {
  394. const newUser = {
  395. fields: {
  396. Email: emailCookie,
  397. Date: new Date(),
  398. Type: "portier-login",
  399. Site: "https://iflylocal.com/",
  400. },
  401. };
  402. const res = await fetch(
  403. `https://api.airtable.com/v0/appiQwfVZixRgRICe/User/`,
  404. {
  405. method: "POST",
  406. headers: {
  407. "Content-Type": "application/json",
  408. Authorization: "Bearer keyJ2ht64ZSN57AG1",
  409. },
  410. body: JSON.stringify(newUser),
  411. }
  412. );
  413. await res.json()
  414. }
  415. },
  416. mounted() {
  417. console.log("asdf");
  418. //this.$segment.page('flights');
  419. this.$segment.track("flights__mount-page", {
  420. origin: this.$route.params.o,
  421. destination: this.$route.params.d,
  422. departureDate: this.$route.params.departure,
  423. });
  424. const emailCookie = this.$cookies.get("email");
  425. if (emailCookie) {
  426. this.$segment.identify(emailCookie, {
  427. email: emailCookie,
  428. });
  429. }
  430. },
  431. created() {
  432. // console.log(this.$route.path)
  433. if (this.$route && this.$route.params && this.$route.params.o) {
  434. this.fetchSingleAirport(this.$route.params.o, true);
  435. }
  436. if (this.$route && this.$route.params && this.$route.params.d) {
  437. this.fetchSingleAirport(this.$route.params.d, false);
  438. }
  439. },
  440. methods: {
  441. formattedDate(date) {
  442. const thisDate = new Date(date);
  443. const month = thisDate.toLocaleString("en-us", { month: "short" });
  444. const year =
  445. thisDate.getUTCFullYear() !== new Date().getUTCFullYear()
  446. ? ", " + thisDate.getUTCFullYear()
  447. : "";
  448. return month + " " + thisDate.getUTCDate() + year;
  449. },
  450. daysList(days) {
  451. const list = days.split(", ").map((day) => {
  452. const name = day.toLowerCase().slice(0, -1);
  453. let num;
  454. switch (name) {
  455. case "su":
  456. num = 0;
  457. break;
  458. case "mo":
  459. num = 1;
  460. break;
  461. case "tu":
  462. num = 2;
  463. break;
  464. case "we":
  465. num = 3;
  466. break;
  467. case "th":
  468. num = 4;
  469. break;
  470. case "fr":
  471. num = 5;
  472. break;
  473. case "sa":
  474. num = 6;
  475. break;
  476. default:
  477. break;
  478. }
  479. return {
  480. name,
  481. num,
  482. };
  483. });
  484. return list.sort(function (a, b) {
  485. return a.num - b.num;
  486. });
  487. },
  488. goBack() {
  489. this.$segment.track("flights__go_back", {
  490. origin: this.$route.params.o,
  491. destination: this.$route.params.d,
  492. departureDate: this.$route.params.departure,
  493. });
  494. //this.$router.go(-1)
  495. this.$router.push({
  496. path: "/dates/" + this.$route.params.o + "/" + this.$route.params.d,
  497. });
  498. },
  499. // book2 (schedule) {
  500. // if (schedule.fields.IsExpedia[0] === 'true') {
  501. // this.startingDate = new Date()
  502. // this.selectedSchedule = schedule.fields
  503. // this.selectedDays = this.daysList(schedule.fields.Frequency_Convert)
  504. // } else {
  505. // window.open(schedule.fields['BookingLink (from Carriers_Alaska)'][0])
  506. // }
  507. // },
  508. book() {
  509. this.$segment.track("flights__book", {
  510. origin: this.selectedSchedule.fields.Origin_IATA[0],
  511. destination: this.selectedSchedule.fields.Destination_IATA[0],
  512. departureDate: this.$route.params.departure,
  513. departureTime: this.selectedSchedule.fields.Departure_TimeL,
  514. arrivalTime: this.selectedSchedule.fields.Arrival_TimeL,
  515. numAdult: this.numAdult,
  516. numChild: this.numChild,
  517. carrier: this.selectedSchedule.fields.Carrier_Name,
  518. });
  519. window.open(
  520. `https://www.anrdoezrs.net/links/100449149/type/am/https://www.expedia.com/go/flight/search/oneway/${this.$route.params.departure}/${this.$route.params.departure}?langid=1033&FromAirport=${this.selectedSchedule.fields.Origin_IATA}&FromTime=${this.selectedSchedule.fields.Departure_TimeL}&ToTime=${this.selectedSchedule.fields.Arrival_TimeL}&ToAirport=${this.selectedSchedule.fields.Destination_IATA}&Class=3&NumAdult=${this.numAdult}&NumChild=${this.numChild}`
  521. );
  522. },
  523. goToCarrier() {
  524. this.$segment.track("flights__open_carrier", {
  525. origin: this.selectedSchedule.fields.Origin_IATA[0],
  526. destination: this.selectedSchedule.fields.Destination_IATA[0],
  527. departureDate: this.$route.params.departure,
  528. departureTime: this.selectedSchedule.fields.Departure_TimeL,
  529. arrivalTime: this.selectedSchedule.fields.Arrival_TimeL,
  530. numAdult: this.numAdult,
  531. numChild: this.numChild,
  532. carrier: this.selectedSchedule.fields.Carrier_Name,
  533. });
  534. window.open(
  535. this.selectedSchedule.fields["BookingLink (from Carriers_Alaska)"][0]
  536. );
  537. },
  538. async contactCarrier() {
  539. const newBooking = {
  540. fields: {
  541. ArrivalTime: this.selectedSchedule.fields.Arrival_TimeL,
  542. Carrier: this.selectedSchedule.fields.Carrier_Name,
  543. DepartureTime: this.selectedSchedule.fields.Departure_TimeL,
  544. Destination: this.selectedSchedule.fields.Destination_IATA[0],
  545. Origin: this.selectedSchedule.fields.Origin_IATA[0],
  546. CreatedDate: new Date(),
  547. FirstName: this.firstName,
  548. LastName: this.lastName,
  549. Phone: this.phone,
  550. Email: this.email,
  551. DepartureDate: this.$route.params.departure,
  552. NumAdult: this.numAdult,
  553. NumChild: this.numChild,
  554. },
  555. };
  556. const res = await fetch(
  557. `https://api.airtable.com/v0/appiQwfVZixRgRICe/CVAS_Booking/`,
  558. {
  559. method: "POST",
  560. headers: {
  561. "Content-Type": "application/json",
  562. Authorization: "Bearer key8ZVBxVZJL2Wp7y",
  563. },
  564. body: JSON.stringify(newBooking),
  565. }
  566. );
  567. const json = await res.json();
  568. //this.guests = { ...await json }
  569. if (await json) {
  570. this.mailSent = true;
  571. this.firstName = "";
  572. this.lastName = "";
  573. this.email = "";
  574. this.phone = "";
  575. this.firstNameTouched = false;
  576. this.lastNameTouched = false;
  577. this.emailTouched = false;
  578. this.phoneTouched = false;
  579. }
  580. },
  581. selectSchedule(schedule) {
  582. this.selectedSchedule = { ...schedule };
  583. this.$segment.track("flights__select_schedule", {
  584. origin: this.selectedSchedule.fields.Origin_IATA[0],
  585. destination: this.selectedSchedule.fields.Destination_IATA[0],
  586. departureDate: this.$route.params.departure,
  587. departureTime: this.selectedSchedule.fields.Departure_TimeL,
  588. arrivalTime: this.selectedSchedule.fields.Arrival_TimeL,
  589. carrier: this.selectedSchedule.fields.Carrier_Name,
  590. });
  591. },
  592. clearSelectedSchedule() {
  593. // console.log('clear')
  594. this.startingDate = "";
  595. this.selectedSchedule = {};
  596. },
  597. async fetchSingleAirport(iata, isOrig) {
  598. const airportFetchFilterFormula = isOrig
  599. ? `AND(Is_Origin=1,{IsCurrent-AsOrigin}="Yes",Airport_IATA="${iata}")`
  600. : `AND(Is_Destination=1,{IsCurrent-AsDest}="Yes",Airport_IATA="${iata}")`;
  601. const response = await fetch(
  602. `https://api.airtable.com/v0/appiQwfVZixRgRICe/Airports_IATA?filterByFormula=${airportFetchFilterFormula}`,
  603. {
  604. method: "GET",
  605. headers: {
  606. "Content-Type": "application/x-www-form-urlencoded",
  607. Authorization: "Bearer keyJ2ht64ZSN57AG1",
  608. },
  609. }
  610. );
  611. const mapData = await response.json();
  612. const thisAirport = {
  613. iata: mapData.records[0].fields.Airport_IATA,
  614. lat: mapData.records[0].fields.Latitude_Deg,
  615. long: mapData.records[0].fields.Longitude_Deg,
  616. icon: mapData.records[0].fields.Icon_Url,
  617. name: mapData.records[0].fields.Airport_Name,
  618. municipality: mapData.records[0].fields.Municipality,
  619. type: mapData.records[0].fields.Type,
  620. search: mapData.records[0].fields.Search_Field,
  621. };
  622. switch (isOrig) {
  623. case true:
  624. this.selectedOrig = { ...thisAirport };
  625. break;
  626. case false:
  627. this.selectedDest = { ...thisAirport };
  628. break;
  629. default:
  630. break;
  631. }
  632. },
  633. validateEmail() {
  634. if (
  635. /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/.test(
  636. this.email
  637. )
  638. ) {
  639. const emailArray = this.email.split("@");
  640. const name = emailArray[0];
  641. const fullDomain = emailArray[1];
  642. const domainArray = fullDomain.split(".");
  643. const domain = domainArray.slice(-2)[0];
  644. const tld = domainArray.slice(-1)[0];
  645. if (tld.length < 2 || domain.length < 2) {
  646. this.emailError = "your email isn't valid";
  647. } else {
  648. this.emailError = "";
  649. }
  650. } else {
  651. this.emailError = "your email isn't valid";
  652. }
  653. },
  654. validatePhone(data) {
  655. this.phoneIsValid = data.isValid;
  656. if (data.isValid) {
  657. this.phoneError = "";
  658. } else {
  659. this.phoneError = "your phone number isn't valid";
  660. }
  661. },
  662. },
  663. };
  664. </script>
  665. <style lang="scss" scoped>
  666. .main-with-header {
  667. margin-top: clamp(20px + 1.75rem, 4rem + 1.75rem, 50px + 1.75rem);
  668. }
  669. .schedule-wrapper {
  670. display: flex;
  671. flex-direction: column;
  672. align-items: center;
  673. }
  674. .schedule-grid {
  675. display: flex;
  676. flex-direction: column;
  677. gap: 1rem;
  678. margin: 2rem 2rem 2rem 2rem;
  679. }
  680. .grid__row {
  681. display: flex;
  682. flex-direction: row;
  683. align-items: center;
  684. gap: 1rem;
  685. padding: 1rem 1rem 1rem 4rem;
  686. flex-wrap: wrap;
  687. border-radius: 2rem;
  688. }
  689. .grid__row:nth-child(odd) {
  690. background: #eee;
  691. }
  692. label {
  693. font-size: 0.75rem;
  694. font-weight: 700;
  695. color: white;
  696. display: block;
  697. }
  698. .field__content {
  699. font-size: 1.5rem;
  700. }
  701. .dow-grid {
  702. width: 200px;
  703. display: grid;
  704. grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
  705. gap: 0.5rem;
  706. }
  707. .dow-grid__day {
  708. font-size: 0.8rem;
  709. border: 1px solid #ccc !important;
  710. display: flex;
  711. align-items: center;
  712. justify-content: center;
  713. border-radius: 3px !important;
  714. text-transform: capitalize;
  715. }
  716. .day--su {
  717. grid-column: 1 / span 1;
  718. }
  719. .day--mo {
  720. grid-column: 2 / span 1;
  721. }
  722. .day--tu {
  723. grid-column: 3 / span 1;
  724. }
  725. .day--we {
  726. grid-column: 4 / span 1;
  727. }
  728. .day--th {
  729. grid-column: 5 / span 1;
  730. }
  731. .day--fr {
  732. grid-column: 6 / span 1;
  733. }
  734. .day--sa {
  735. grid-column: 7 / span 1;
  736. }
  737. .row__trip {
  738. display: flex;
  739. align-items: center;
  740. }
  741. .row__trip svg {
  742. margin-bottom: -0.5rem;
  743. width: 3.5rem;
  744. height: 3.5rem;
  745. margin-left: -1rem;
  746. margin-right: -1rem;
  747. }
  748. .shape--trip {
  749. height: 0.4rem;
  750. width: 2rem;
  751. background: #007fff;
  752. display: block;
  753. margin-top: 1rem;
  754. position: relative;
  755. }
  756. .shape--trip::before {
  757. height: 0.8rem;
  758. width: 0.8rem;
  759. background: #fff;
  760. border: 0.02rem solid #007fff;
  761. border-radius: 999px;
  762. content: "";
  763. position: absolute;
  764. top: -0.15rem;
  765. left: -0.5rem;
  766. }
  767. .shape--trip::after {
  768. height: 0.8rem;
  769. width: 0.8rem;
  770. background: #007fff;
  771. border: 0.02rem solid #007fff;
  772. border-radius: 999px;
  773. content: "";
  774. position: absolute;
  775. top: -0.15rem;
  776. right: -0.5rem;
  777. }
  778. h1 {
  779. font-size: 3rem;
  780. line-height: 0.85;
  781. }
  782. h2 {
  783. font-size: 2rem;
  784. line-height: 1;
  785. }
  786. label.text--muni {
  787. color: #007fff;
  788. line-height: 1;
  789. padding-left: 0.3rem;
  790. }
  791. span.text--flight-num {
  792. font-weight: 700;
  793. margin-right: 0.5rem;
  794. }
  795. span.text--dow {
  796. font-weight: 300;
  797. font-size: 0.8rem;
  798. color: #007fff;
  799. }
  800. span.badge--avail {
  801. border-radius: 999px;
  802. border: 1px solid gray;
  803. color: gray;
  804. padding: 0.2em 0.8em;
  805. line-height: 1;
  806. font-size: 0.8rem;
  807. display: block;
  808. width: fit-content;
  809. }
  810. .radio span {
  811. border-radius: 999px;
  812. border: 3px solid #007fff;
  813. background-color: white;
  814. height: 2rem;
  815. width: 2rem;
  816. display: flex;
  817. position: relative;
  818. transition: all 0.3s;
  819. align-items: center;
  820. justify-content: center;
  821. }
  822. .radio span svg {
  823. transition: all 0.3s;
  824. stroke: #eee;
  825. }
  826. .radio span:hover {
  827. background: #007fff;
  828. }
  829. .radio input:checked + span {
  830. background: #007fff;
  831. }
  832. .radio input:checked + span svg {
  833. stroke: white;
  834. }
  835. .radio input {
  836. display: none;
  837. }
  838. .row__info {
  839. min-width: fit-content;
  840. }
  841. .row__check {
  842. margin-left: -3rem;
  843. }
  844. .form-row {
  845. display: flex;
  846. flex-direction: column;
  847. justify-content: stretch;
  848. }
  849. .form-row label {
  850. text-transform: uppercase;
  851. font-size: 1rem;
  852. font-weight: 300;
  853. }
  854. .form-row input {
  855. background: #eee;
  856. border-radius: 0.5rem 3rem 3rem 3rem;
  857. transition: 0.3s all;
  858. padding: 0.5em 0.8em;
  859. font-size: min(1rem, 18px);
  860. line-height: 1;
  861. }
  862. .form-row--two-columns {
  863. display: flex;
  864. flex-direction: row;
  865. flex-wrap: nowrap;
  866. column-gap: 1rem;
  867. }
  868. .form-row--two-columns input {
  869. width: 50%;
  870. }
  871. .form-row input:focus {
  872. border: 0;
  873. }
  874. .flyout {
  875. background: #007fff;
  876. border-radius: 3rem 3rem 0 0;
  877. padding: 3rem;
  878. pointer-events: all;
  879. width: clamp(450px, 50vw, 400px);
  880. display: flex;
  881. flex-direction: column;
  882. gap: 1rem;
  883. }
  884. .flyout .btn.btn--primary {
  885. background: white;
  886. color: black;
  887. }
  888. .flyout .btn.btn--primary:hover {
  889. background: #00ca00;
  890. color: #fff;
  891. }
  892. .flyout p {
  893. font-size: 1rem;
  894. line-height: 1.2;
  895. font-weight: 400;
  896. color: #fff;
  897. margin-bottom: 1rem;
  898. margin-left: 0.5rem;
  899. margin-right: 0.5rem;
  900. }
  901. .flyout p:last-child {
  902. margin-bottom: 0;
  903. }
  904. .flyout h3 {
  905. font-size: 1.5rem;
  906. line-height: 1;
  907. font-weight: 200;
  908. margin-bottom: 1rem;
  909. color: #fff;
  910. margin-left: 0.5rem;
  911. margin-right: 0.5rem;
  912. }
  913. .flyout h3 strong {
  914. font-weight: 600;
  915. }
  916. .btn:disabled {
  917. opacity: 0.2;
  918. background-color: #ddd;
  919. color: #222;
  920. cursor: not-allowed;
  921. pointer-events: none;
  922. }
  923. .form-row input.input--error {
  924. --stripe1: #ff69b433;
  925. --stripe2: #eee;
  926. // box-shadow: 0 0 0 0.2rem red;
  927. background:
  928. linear-gradient(
  929. 135deg,
  930. var(--stripe1) 25%,
  931. var(--stripe2) 25%,
  932. var(--stripe2) 50%,
  933. var(--stripe1) 50%,
  934. var(--stripe1) 75%,
  935. var(--stripe2) 75%,
  936. var(--stripe2) 100%
  937. ),
  938. var(--stripe2);
  939. background-size: 56.57px 56.57px;
  940. }
  941. .alert--error {
  942. text-align: center;
  943. color: white;
  944. font-size: 0.75rem;
  945. margin-top: 0.15rem;
  946. }
  947. .alert--sent {
  948. text-align: center;
  949. background-color: #00ca00;
  950. margin: 0 -3rem -3rem -3rem;
  951. text-transform: uppercase;
  952. font-size: 0.9rem;
  953. letter-spacing: 0.1rem;
  954. padding: 1rem;
  955. }
  956. </style>