From 9df53f1971a2f51d4b7a29071bc386719bd81d19 Mon Sep 17 00:00:00 2001
From: James Southern
Date: Sat, 23 Mar 2024 18:04:03 +0000
Subject: [PATCH] update the home page & constituency lookup ui (#83)
* update the navbar, footer, home page & constituency page ui in line with Josh's designs
* Update the css to match
* add back in form-check class which seems to be sometimes added by bootstrap-react and sometimes not?
* fix css for footer svgs
* update tactical voting advice to handle tbc, vote with heart
* remove flex from svg div container for safari
* rename non voters to didn't vote
* bring updates back into the refactored front page form
---
app/constituencies/[slug]/page.tsx | 161 ++++-
app/globals.scss | 601 +++++++++++++++---
app/page.tsx | 15 +-
.../ConstituencyLookup.tsx | 350 +++++-----
components/footer/Footer.tsx | 129 ++--
components/footer/FooterLink.tsx | 5 +-
.../forms/ConstituencyFormWithSignup.tsx | 245 ++++---
components/forms/constituencyLookup.tsx | 123 ++--
components/info_box/ImpliedChart.tsx | 17 +-
components/info_box/MRPChart.tsx | 20 +-
components/info_box/PlanToVoteBox.tsx | 108 ++--
components/info_box/TacticalReasoningBox.tsx | 23 +-
.../info_box/ToryCantWinReasoningBox.tsx | 92 +++
components/navigation/Navigation.tsx | 87 +--
components/sections/MovementSection.tsx | 114 ++--
utils/Echarts.ts | 12 +-
utils/Party.ts | 14 +-
17 files changed, 1367 insertions(+), 749 deletions(-)
create mode 100644 components/info_box/ToryCantWinReasoningBox.tsx
diff --git a/app/constituencies/[slug]/page.tsx b/app/constituencies/[slug]/page.tsx
index d5f683e..a608f64 100644
--- a/app/constituencies/[slug]/page.tsx
+++ b/app/constituencies/[slug]/page.tsx
@@ -1,4 +1,5 @@
-import { Col, Container, Row } from "react-bootstrap";
+import { Col, Container, Row, ButtonGroup, Button } from "react-bootstrap";
+import Link from "next/link";
import Header from "@/components/Header";
import ActionBox from "@/components/info_box/ActionBox";
import ImpliedChart from "@/components/info_box/ImpliedChart";
@@ -11,6 +12,13 @@ import {
getConstituencySlugs,
} from "@/utils/constituencyData";
import { notFound } from "next/navigation";
+import {
+ FaShare,
+ FaPuzzlePiece,
+ FaCopy,
+ FaHandHoldingHeart,
+} from "react-icons/fa6";
+import PostcodeLookup from "@/components/constituency_lookup/ConstituencyLookup";
export const dynamicParams = false; // Don't allow params not in generateStaticParams
@@ -54,6 +62,30 @@ export default async function ConstituencyPage({
(a, b) => b.votePercent - a.votePercent,
);
+ let tacticalVoteHeader = "";
+ let tacticalVoteAdvice = "";
+ let tacticalVoteClass = "";
+
+ if (constituencyData.otherVoteData.conservativeWinUnlikely) {
+ tacticalVoteHeader = "Tories unlikely to win here";
+ tacticalVoteAdvice = "Vote with your heart";
+ tacticalVoteClass = "party-your-heart";
+ } else {
+ tacticalVoteHeader = "The Tactical Vote is";
+
+ if (constituencyData.recommendation.partySlug) {
+ tacticalVoteAdvice = partyNameFromSlug(
+ constituencyData.recommendation.partySlug,
+ );
+ tacticalVoteClass = partyCssClassFromSlug(
+ constituencyData.recommendation.partySlug,
+ );
+ } else {
+ tacticalVoteClass = "party-too-soon";
+ tacticalVoteAdvice = "Too Soon to call";
+ }
+ }
+
if (constituencyData.recommendation.partySlug === "None") {
return (
<>
@@ -92,50 +124,139 @@ export default async function ConstituencyPage({
{constituencyData.constituencyIdentifiers.name}
Bookmark this page and check back before the election for updated
- info.
+ info. Not your constituency?{" "}
+
+ Search here.
+
-
+
- The tactical vote is
+ {tacticalVoteHeader}
+
+ {tacticalVoteAdvice}
+
+
+ Why?
+
+
+
+
+
-
-
- {partyNameFromSlug(constituencyData.recommendation.partySlug)}
-
+
+ be counted, stick together!
-
-
-
+
+
+
Grow this movement
+
You're in! Now let's build our numbers
+
+ {/* TODO share link and clipboard copy */}
+
+ Share with friends & family
+
+
+
+ Copy link to this page
+
+
+ Volunteer
+
+
+
+ Support our crowdfunder
+
+
+
-
-
+
+
+ Reasons to be counted
+
+
+ 1. Show how many of us are voting tactically and not just for
+ the party we're lending our vote to, and that we want our
+ votes to count next time.
+
+
+ 2. Our large numbers show that the country is rejecting the
+ narrative the right wing media and think tanks spin.
+
+
+ 3. Together we can be a huge independent influence on the next
+ government, for Proportional Representation, and other
+ crucial, common sense, policies.
+
-
+
+
+
+
+
+
+
+ {constituencyData.otherVoteData.conservativeWinUnlikely ? (
+ <>
+ Why Tories Won't Win Here
+ ({constituencyData.constituencyIdentifiers.name})
+ >
+ ) : constituencyData.recommendation.partySlug ? (
+ <>
+
+ Why vote{" "}
+
+ {partyNameFromSlug(
+ constituencyData.recommendation.partySlug,
+ )}
+ {" "}
+ here?
+
+ ({constituencyData.constituencyIdentifiers.name})
+ >
+ ) : (
+ <>
+ Why it's too soon to call here
+ ({constituencyData.constituencyIdentifiers.name})
+ >
+ )}
+
+
+
-
+
+
+
+
+ Read more about our process
+
+
+
+
-
diff --git a/app/globals.scss b/app/globals.scss
index e984707..328b9cc 100644
--- a/app/globals.scss
+++ b/app/globals.scss
@@ -91,7 +91,21 @@ $bs-link-hover-color-rgb: 238, 102, 238;
/***** GLOBAL CSS VARIABLES *****/
:root {
- --mvtfwd-pink-strong: #ff66ff;
+ --mf-pink: #ff99ff;
+ --mf-pink-light: #ffccff;
+ --mf-pink-strong: #ff66ff;
+ --mf-pink-dark: #ee66ee;
+ --bs-link-color: var(--bs-black);
+ --bs-link-color-rgb: 0, 0, 0;
+ --bs-link-decoration: underline;
+ --bs-link-hover-color: var(--mf-pink-dark);
+ --bs-link-hover-color-rgb: 238, 102, 238;
+ --bs-red-rgb: 220, 53, 69;
+ --bs-orange-rgb: 253, 126, 20;
+ --bs-green-rgb: 25, 135, 84;
+ --bs-blue-rgb: 13, 110, 253;
+ --bs-gray-150: #f1f3f5;
+
--con-party-color: #0087dc;
--lab-party-color: #e4003b;
--ld-party-color: #faa61a;
@@ -103,6 +117,40 @@ $bs-link-hover-color-rgb: 238, 102, 238;
--other-party-color: var(--bs-gray-600);
}
+.btn-primary {
+ --bs-btn-color: #fff;
+ --bs-btn-bg: var(--mf-pink-strong);
+ --bs-btn-border-color: var(--mf-pink-strong);
+ --bs-btn-hover-color: #fff;
+ --bs-btn-hover-bg: var(--mf-pink-dark);
+ --bs-btn-hover-border-color: var(--mf-pink-dark);
+ --bs-btn-focus-shadow-rgb: 49, 132, 253;
+ --bs-btn-active-color: #fff;
+ --bs-btn-active-bg: var(--mf-pink-dark);
+ --bs-btn-active-border-color: var(--mf-pink-dark);
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #fff;
+ --bs-btn-disabled-bg: var(--mf-pink-strong);
+ --bs-btn-disabled-border-color: var(--mf-pink-strong);
+}
+
+.btn-link {
+ --bs-btn-font-weight: 400;
+ --bs-btn-color: var(--bs-link-color);
+ --bs-btn-bg: transparent;
+ --bs-btn-border-color: transparent;
+ --bs-btn-hover-color: var(--bs-link-hover-color);
+ --bs-btn-hover-bg: #fff;
+ --bs-btn-hover-border-color: transparent;
+ --bs-btn-active-color: var(--bs-link-hover-color);
+ --bs-btn-active-border-color: transparent;
+ --bs-btn-disabled-color: #6c757d;
+ --bs-btn-disabled-border-color: transparent;
+ --bs-btn-box-shadow: 0 0 0 #000;
+ --bs-btn-focus-shadow-rgb: 49, 132, 253;
+ text-decoration: underline;
+}
+
/***** GLOBAL DEFAULT STYLES *****/
// TODO: Many of these could be Next.js CSS Modules as they only affect a single component
@@ -121,6 +169,7 @@ $bs-link-hover-color-rgb: 238, 102, 238;
/* TYPOGRAPHY */
body {
font-size: 18px;
+ line-height: 1.3;
}
html,
@@ -128,8 +177,122 @@ body {
background-color: var(--bs-gray-300);
}
+H1,
+H2,
+H3,
+H4,
+H5,
+H6,
+navbar,
+nav,
+.btn {
+ font-family: var(--font-rubik);
+ line-height: 1;
+}
+
+H1,
+H2,
+H3,
+H4,
+H5,
+H6 {
+ font-weight: bold;
+ text-transform: uppercase;
+}
+
+.btn {
+ line-height: 1.3;
+ font-weight: bold;
+}
+
+header h6 {
+ color: var(--bs-gray-900);
+ background-color: var(--bs-white);
+ display: inline-block;
+ padding: 0.2rem 0.4rem;
+}
+
+.small,
+small {
+ font-size: 0.75em;
+ line-height: 1.2;
+}
+
+/* LAYOUT */
+
+.alignwide {
+ margin: 0px calc(25% - 25vw);
+ max-width: 100vw;
+ /*width: 100vw;*/
+}
+
+.alignfull {
+ margin: 0px calc(50% - 50vw);
+ max-width: 100vw;
+ width: 100vw;
+}
+
+.py-6 {
+ padding-top: 6rem !important;
+ padding-bottom: 6rem !important;
+}
+
+@include media-breakpoint-up(md) {
+ .row-cols-md-6 > * {
+ flex: 0 0 auto;
+ width: 16.6666%;
+ }
+}
+
+/* LINKS */
+
+p a:link,
+p a:visited,
+p a:hover,
+p a:active {
+ font-weight: bold;
+}
+
+a.dropdown-item:link,
+a.dropdown-item:visited,
+a.dropdown-item:hover,
+a.dropdown-item:active,
+a.dropdown-item i {
+ color: bs var(--bs-dark);
+}
+
+main .section-light p a:link,
+main .section-light p a:visited,
+main .section-light p a:hover,
+main .section-light p a:active {
+ color: var(--bs-black);
+}
+
+main .section-dark p a:link,
+main .section-dark p a:visited,
+main .section-dark p a:hover,
+main .section-dark p a:active {
+ color: var(--bs-white);
+}
+
+main .section-darker p a:link,
+main .section-darker p a:visited,
+main .section-darker p a:hover,
+main .section-darker p a:active {
+ color: var(--bs-white);
+}
+
+footer ul svg {
+ color: var(--bs-black);
+}
+
+footer ul svg:hover {
+ color: var(--mf-pink-strong);
+}
+
/* NAVBAR */
/* Below 360px, the Navigation component won't naturally fit, so start to scale down the text */
+/* Not In Josh CSS
.brand-text {
@media (max-width: 359.98px) {
font-size: 5.25vw;
@@ -137,16 +300,44 @@ body {
}
/* Below 385px, the NavWithHamburger component won't naturally fit, so start to scale down the text */
+/*
.hamburger-brand-text {
@media (max-width: 384.98px) {
font-size: 4vw;
}
}
+*/
+
+/* FORMS - CUSTOM CHECKBOX */
+
+.custom-checkbox .form-check-input {
+ width: 22px;
+ height: 22px;
+ position: relative;
+ top: -2px;
+}
+
+.custom-checkbox .form-check-input:checked {
+ background-color: var(--bs-gray-900);
+ border-color: var(--bs-gray-900);
+}
+// Set validation error style on all inputs
+input[type="text"].is-invalid,
+input[type="email"].is-invalid {
+ box-shadow: 0 0 0px 2px rgba(var(--bs-black-rgb), 0.5) !important;
+ border-color: var(--bs-gray-900);
+}
+
+// Custom validation for postcode & email text inputs
+.invalid-text-greyed:invalid {
+ color: var(--bs-gray-600);
+}
/* HEADER */
header {
background-color: var(--bs-black);
color: var(--bs-white);
+ font-size: 18px;
}
header h1 {
@@ -159,123 +350,250 @@ header h1 {
}
}
-/* TEXT HEADINGS */
-h1,
-h2,
-h3,
-h4,
-h5,
-h6 {
- font-family: var(--font-rubik);
- font-weight: bold;
- text-transform: uppercase;
- line-height: 1;
+header p {
+ color: var(--bs-white);
+ font-size: 18px;
}
-/* FORM INPUTS */
-// Set checkbox size & style
-input[type="checkbox"] {
- width: 22px;
- height: 22px;
+/* SECTIONS */
+section {
+ padding-top: 2rem;
+ padding-bottom: 1rem;
}
-input[type="checkbox"]:checked {
+
+@include media-breakpoint-up(lg) {
+ section {
+ padding-top: 3rem;
+ padding-bottom: 2rem;
+ }
+}
+
+section h2 {
+ font-size: 28px;
+}
+
+@include media-breakpoint-up(md) {
+ section h2 {
+ font-size: 42px;
+ }
+}
+
+section h3 {
+ font-size: 20px;
+}
+
+section h4 {
+ font-size: 32px;
+}
+
+section h3.position-sticky {
background-color: var(--bs-gray-900);
- border-color: var(--bs-gray-900);
+ color: var(--bs-white);
+ top: 74px;
}
-// Set validation error style on all inputs
-input[type="text"]:invalid,
-input[type="email"]:invalid,
-select:invalid,
-select option:invalid {
- box-shadow: 0 0 0px 4px rgba(196, 0, 0, 0.5) !important;
+section h2.position-sticky {
+ top: 200px;
}
-// Set focus style on all inputs
-input[type="text"]:focus,
-input[type="email"]:focus,
-input[type="checkbox"]:focus,
-select:focus,
-select option:focus,
-button:focus {
- box-shadow: 0 0 0px 4px rgba(255, 255, 255, 0.5) !important;
- border-color: var(--bs-gray-600) !important;
+.section-light {
+ background-color: var(--bs-gray-100);
+ /*box-shadow: inset 0px -1px 0px 0px var(--bs-gray-300);*/
+ border-top: 2px solid var(--bs-gray-100);
+ border-bottom: 2px solid var(--bs-gray-400);
}
-// Custom validation for postcode & email text inputs
-.invalid-text-greyed:invalid {
- color: var(--bs-gray-600);
+.section-light h2,
+.section-light h3,
+.section-light h4,
+.section-light p {
+ color: var(--bs-body-color);
}
-/* PARTY STYLES */
-h3.party {
- font-size: 8vmax;
- font-weight: 800;
- text-transform: uppercase;
+.section-light h3 {
+ /*color: var(--mf-pink-strong);*/
}
-h3.party-labour,
-svg.party-labour,
-i.party-labour {
- color: var(--bs-red);
+/* SECTION DARK */
+
+.section-dark {
+ background-color: var(--bs-gray-900);
+ /*box-shadow: inset 0px -2px 0px 0px var(--bs-gray-900);*/
+ /*border-top: 2px solid var(--bs-gray-700);*/
+ /*border-bottom: 2px solid var(--bs-gray-900);*/
}
-div.party-labour {
- background-color: rgba(var(--bs-red-rgb), 1);
- background: linear-gradient(
- rgba(var(--bs-red-rgb), 1),
- rgba(var(--bs-red-rgb), 0.85)
- );
- box-shadow: 0px 5px 10px 0px rgba(var(--bs-black-rgb), 0.075);
- /*border: solid 1px var(--bs-gray-300);*/
+.section-dark h2,
+.section-dark h3,
+.section-dark h4,
+.section-dark p {
+ color: var(--bs-body-bg);
}
-h3.party-libdem,
-svg.party-libdem,
-i.party-libdem {
- color: var(--bs-orange);
+.section-dark h3 {
+ /*color: var(--mf-pink-strong);*/
}
-div.party-libdem {
- background-color: rgba(var(--bs-orange-rgb), 1);
+/* SECTION DARKER */
+
+.section-darker {
+ background-color: var(--bs-black);
+ color: var(--bs-white);
+}
+
+.section-darker h2,
+.section-darker h3,
+.section-darker h4,
+.section-darker p {
+ color: var(--bs-body-bg);
+}
+
+/* SEARCH */
+
+.form-search {
+ max-width: 475px;
+ background: var(--mf-pink-strong);
+ border-radius: 12px;
+ padding: 18px;
+ margin-bottom: 24px;
+ box-shadow: 0px 0px 24px rgba(0, 0, 0, 0.25);
+}
+
+div.alert {
+ margin-left: 12px;
+ margin-right: 12px;
+}
+
+/* SECTION INFO AREAS */
+
+.rounded-box {
+ padding: 1rem 1rem 0.1rem 1rem;
+ margin-bottom: 1rem;
+ border-radius: 1rem;
+}
+
+.rounded-box h3,
+.rounded-box h4,
+.rounded-box p {
+ color: white;
+}
+
+.info-area a:link,
+.info-area a:visited,
+.info-area a:hover,
+.info-area a:active,
+.action-area a:link,
+.action-area a:visited,
+.action-area a:hover,
+.action-area a:active {
+ color: var(--bs-black);
+}
+
+.info-area p,
+.action-area p {
+ color: var(--bs-black);
+}
+
+.info-area h3,
+.action-area h3 {
+ color: var(--bs-black);
+}
+
+.section-light .action-area {
+ /*background-color: var(--bs-gray-200);*/
+ background: linear-gradient(var(--bs-gray-100), var(--bs-gray-150));
+ box-shadow: 0px 5px 10px 0px rgba(var(--bs-black-rgb), 0.075);
+ /*border: solid 1px var(--bs-gray-300);*/
+}
+
+.section-light .info-area {
+ /*background-color: var(--bs-gray-200);*/
+ background: linear-gradient(var(--bs-gray-100), var(--bs-gray-150));
background: linear-gradient(
- rgba(var(--bs-orange-rgb), 1),
- rgba(var(--bs-orange-rgb), 0.85)
+ var(--bs-warning-bg-subtle),
+ var(--bs-warning-border-subtle)
);
box-shadow: 0px 5px 10px 0px rgba(var(--bs-black-rgb), 0.075);
/*border: solid 1px var(--bs-gray-300);*/
}
-h3.party-green,
-svg.party-green,
-i.party-green {
- color: var(--bs-green);
+.section-dark .action-area {
+ background-color: var(--bs-gray-400);
+ background: linear-gradient(var(--bs-gray-200), var(--bs-gray-400));
+ box-shadow: 0px 5px 10px 0px rgba(var(--bs-black-rgb), 0.5);
+ /*border: solid 1px var(--bs-gray-300);*/
}
-div.party-green {
- background-color: rgba(var(--bs-green-rgb), 1);
+.section-dark .info-area {
+ background-color: var(--bs-gray-400);
+ background: linear-gradient(var(--bs-gray-200), var(--bs-gray-400));
background: linear-gradient(
- rgba(var(--bs-green-rgb), 1),
- rgba(var(--bs-green-rgb), 0.85)
+ var(--bs-warning-bg-subtle),
+ var(--bs-warning-border-subtle)
);
- box-shadow: 0px 5px 10px 0px rgba(var(--bs-black-rgb), 0.075);
+ box-shadow: 0px 5px 10px 0px rgba(var(--bs-black-rgb), 0.5);
/*border: solid 1px var(--bs-gray-300);*/
}
-h3.party-conservative,
-svg.party-conservative,
-i.party-conservative {
- color: var(--bs-blue);
+/* FOOTER */
+
+footer {
+ background: var(--bs-gray-300);
+ padding-top: 2rem;
+ padding-bottom: 4rem;
+ border-top: 2px solid var(--bs-gray-100);
}
-div.party-conservative {
- background-color: rgba(var(--bs-blue-rgb), 1);
+@media (min-width: 992px) {
+ footer {
+ background: var(--bs-gray-300);
+ padding-top: 6rem;
+ padding-bottom: 4rem;
+ border-top: 2px solid var(--bs-gray-100);
+ }
}
-/* EXTRA PADDING CLASSES (py-6 / py-sm-6 / etc) */
-.py-6 {
- padding-top: 6rem !important;
- padding-bottom: 6rem !important;
+.brand-tag {
+ position: fixed;
+ bottom: 0px;
+ right: 0px;
+ color: var(--mf-pink-dark) !important;
+ background-color: rgba(255, 255, 255, 0.9) !important;
+ border: 0;
+ border-radius: 0;
+}
+
+/* BUTTON GROUPS */
+
+.btn-group-vertical {
+ margin-bottom: 24px;
+}
+
+.btn-group-vertical .btn {
+ margin-bottom: 4px;
+ text-align: left;
+}
+
+.fa,
+.fas,
+.far,
+.btn svg,
+.btn img {
+ margin-right: 0.5rem;
+ width: 1.2rem;
+ display: inline-flex;
+ justify-content: center;
+}
+
+.dropdown-toggle::after {
+ margin-top: 0.4555em;
+ float: right;
+}
+
+/* RESPONSIVE */
+
+[id] {
+ scroll-margin-top: 48px;
}
@include media-breakpoint-up(sm) {
@@ -313,34 +631,111 @@ div.party-conservative {
}
}
-/* SECTIONS */
-section {
- padding-top: 3rem;
- padding-bottom: 2rem;
+@include media-breakpoint-up(lg) {
+ div.two-columns {
+ column-count: 2;
+ }
}
-.section-light {
- background-color: var(--bs-gray-200);
- /*box-shadow: inset 0px -1px 0px 0px var(--bs-gray-300);*/
- border-top: 2px solid var(--bs-gray-100);
- border-bottom: 2px solid var(--bs-gray-400);
+@include media-breakpoint-up(xxl) {
+ div.three-columns {
+ column-count: 3;
+ }
}
-.section-dark {
- background-color: var(--bs-gray-800);
- /*box-shadow: inset 0px -2px 0px 0px var(--bs-gray-900);*/
- border-top: 2px solid var(--bs-gray-700);
- border-bottom: 2px solid var(--bs-gray-900);
+/* PARTIES */
+
+section h3.party {
+ font-size: 6vmax;
+ font-weight: 800;
+ text-transform: uppercase;
}
-/* FOOTER */
-.footer {
- background-color: var(--bs-gray-300);
- border-top: 2px solid var(--bs-gray-100);
- border-bottom: 2px solid var(--bs-gray-400);
+span.party-too-soon,
+h3.party-too-soon,
+svg.party-too-soon,
+i.party-too-soon {
+ color: var(--bs-gray-600);
}
-.footer-dark {
- background-color: var(--bs-gray-400);
- border-top: 2px solid var(--bs-gray-300);
+span.party-your-heart,
+h3.party-your-heart,
+svg.party-your-heart,
+i.party-your-heart {
+ color: var(--mf-pink);
+}
+
+span.party-labour,
+h3.party-labour,
+svg.party-labour,
+i.party-labour {
+ color: var(--bs-red);
+}
+
+div.party-labour {
+ background-color: rgba(var(--bs-red-rgb), 1);
+ background: linear-gradient(
+ rgba(var(--bs-red-rgb), 1),
+ rgba(var(--bs-red-rgb), 0.85)
+ );
+ box-shadow: 0px 5px 10px 0px rgba(var(--bs-black-rgb), 0.075);
+ /*border: solid 1px var(--bs-gray-300);*/
+}
+
+span.party-libdem,
+h3.party-libdem,
+svg.party-libdem,
+i.party-libdem {
+ color: var(--bs-orange);
+}
+
+div.party-libdem {
+ background-color: rgba(var(--bs-orange-rgb), 1);
+ background: linear-gradient(
+ rgba(var(--bs-orange-rgb), 1),
+ rgba(var(--bs-orange-rgb), 0.85)
+ );
+ box-shadow: 0px 5px 10px 0px rgba(var(--bs-black-rgb), 0.075);
+ /*border: solid 1px var(--bs-gray-300);*/
+}
+
+span.party-plaid,
+h3.party-plaid,
+svg.party-plaid,
+i.party-plaid {
+ color: var(--pc-party-color);
+}
+
+span.party-snp,
+h3.party-snp,
+svg.party-snp,
+i.party-snp {
+ color: var(--snp-party-color);
+}
+
+span.party-green,
+h3.party-green,
+svg.party-green,
+i.party-green {
+ color: var(--bs-green);
+}
+
+div.party-green {
+ background-color: rgba(var(--bs-green-rgb), 1);
+ background: linear-gradient(
+ rgba(var(--bs-green-rgb), 1),
+ rgba(var(--bs-green-rgb), 0.85)
+ );
+ box-shadow: 0px 5px 10px 0px rgba(var(--bs-black-rgb), 0.075);
+ /*border: solid 1px var(--bs-gray-300);*/
+}
+
+h3.party-conservative,
+svg.party-conservative,
+i.party-conservative {
+ color: var(--bs-blue);
+}
+
+div.party-conservative {
+ background-color: rgba(var(--bs-blue-rgb), 1);
}
diff --git a/app/page.tsx b/app/page.tsx
index 1a2ba20..ace4196 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -10,15 +10,24 @@ export default async function Index() {
-
+
Your vote IS
your power
- Join the movement that sticks together using it's votes
- collectively for change.
+ 1. Show up
+
+ {" "}
+ to get the Tories out.
+
+
+
+ 2. Join up{" "}
+
+ to lobby the next gov for PR.
+
diff --git a/components/constituency_lookup/ConstituencyLookup.tsx b/components/constituency_lookup/ConstituencyLookup.tsx
index 66adcc1..a390f7a 100644
--- a/components/constituency_lookup/ConstituencyLookup.tsx
+++ b/components/constituency_lookup/ConstituencyLookup.tsx
@@ -171,6 +171,7 @@ const PostcodeLookup = () => {
const [subscribed, setSubscribed] = useState(false);
const [formState, setFormState] = useState(initialFormState);
+ const [formPostcode, setFormPostcode] = useState("");
const [apiResponse, setApiResponse] = useState<
ConstituencyLookupResponse | false | null
>(null);
@@ -247,6 +248,8 @@ const PostcodeLookup = () => {
};
const postcodeChanged = async (userPostcode: string) => {
+ setFormPostcode(userPostcode);
+ setPostError(null);
const normalizedPostcode = normalizePostcode(userPostcode);
if (
@@ -330,81 +333,82 @@ const PostcodeLookup = () => {
};
return (
-
-
-
- {/* FOOTER LINKS */}
-
+
+ {/* FOOTER LINKS */}
{blok.links.map((link) => (
-
-
-
+
))}
-
-
-
-
-
-
-
-
-
-
- {/* POSTCODE LOOKUP ATTRIBUTION */}
-
- Postcode lookup contains data from{" "}
-
- DemocracyClub
-
- , the{" "}
-
- ONS
- {" "}
- &{" "}
-
- MySociety
-
-
-
- Contains OS data © Crown copyright and database right 2024
-
-
- Contains Royal Mail data © Royal Mail copyright and Database
- right 2024
-
-
- Contains GeoPlace data © Local Government Information House
- Limited copyright and database right 2024
-
-
- Source: Office for National Statistics licensed under the Open
- Government Licence v.3.0
-
-
+
+ We don't use cookies on this website.
+
-
-
+
+
+
+
+
+ {/* POSTCODE LOOKUP ATTRIBUTION */}
+
+ Postcode lookup contains data from{" "}
+
+ DemocracyClub
+
+ , the{" "}
+
+ ONS
+ {" "}
+ &{" "}
+
+ MySociety
+
+
+
+
+ Contains OS data © Crown copyright and database right 2024
+
+
+ Contains Royal Mail data © Royal Mail copyright and Database
+ right 2024
+
+
+ Contains GeoPlace data © Local Government Information House
+ Limited copyright and database right 2024
+
+
+ Source: Office for National Statistics licensed under the Open
+ Government Licence v.3.0
+
+
+
+
+
+
+
+
+ @MVTFWD
+
+ >
);
export default Footer;
diff --git a/components/footer/FooterLink.tsx b/components/footer/FooterLink.tsx
index 34b5b09..da3aa68 100644
--- a/components/footer/FooterLink.tsx
+++ b/components/footer/FooterLink.tsx
@@ -5,13 +5,12 @@ import {
} from "@/storyblok/types/storyblok-types";
import Link from "next/link";
-import Col from "react-bootstrap/Col";
-
+// TODO we only want links to Donate About Data Privacy
const FooterLink = ({
blok,
}: {
blok: FooterInternalLinkStoryblok | FooterExternalLinkStoryblok;
-}) => {link(blok)};
+}) => <>{link(blok)}>;
const link = (
blok: FooterInternalLinkStoryblok | FooterExternalLinkStoryblok,
diff --git a/components/forms/ConstituencyFormWithSignup.tsx b/components/forms/ConstituencyFormWithSignup.tsx
index 2fe606c..b9e6433 100644
--- a/components/forms/ConstituencyFormWithSignup.tsx
+++ b/components/forms/ConstituencyFormWithSignup.tsx
@@ -2,27 +2,13 @@
import { useRouter } from "next/navigation";
-import {
- Container,
- Form,
- Button,
- FormCheck,
- Row,
- Col,
- Spinner,
- InputGroup,
-} from "react-bootstrap";
+import { Form, Button, FormCheck, Spinner, InputGroup } from "react-bootstrap";
-import {
- normalizePostcode,
- postcodeInputPattern,
- validatePostcode,
-} from "@/utils/Postcodes";
import { submitANForm } from "@/utils/AnApiSubmission";
import { rubik } from "@/utils/Fonts";
import FormCheckInput from "react-bootstrap/esm/FormCheckInput";
import FormCheckLabel from "react-bootstrap/esm/FormCheckLabel";
-import { useMemo, useRef, useState, useEffect } from "react";
+import { useRef, useState, useEffect } from "react";
import ConstituencyLookup from "./constituencyLookup";
const emailErrorMessage = (code: EmailErrorCode) => {
@@ -133,129 +119,114 @@ const ConstituencyFormWithSignup = () => {
};
return (
-
-
- Vote the Tories out
-
- Vote tactically at the General Election
-
-
- {/* Renders the postcode box, makes API calls, and if necessary shows an address/constituency picker */}
-
-
- {subscribed ? (
-
- ) : (
-
-
-
-
- setFormState({
- ...formState,
- emailOptIn: !formState.emailOptIn,
- })
- }
- className="me-2"
+
+ How to vote your Tory out
+ {/* Renders the postcode box, makes API calls, and if necessary shows an address/constituency picker */}
+
+
+ {subscribed ? (
+
+ ) : (
+
+
+
+ setFormState({
+ ...formState,
+ emailOptIn: !formState.emailOptIn,
+ })
+ }
+ />
+
+ setFormState({
+ ...formState,
+ emailOptIn: !formState.emailOptIn,
+ })
+ }
+ >
+ Join up, be counted, stick together
+
+
+
+ {formState.emailOptIn && (
+ <>
+
+ {
+ setFormState({ ...formState, email: e.target.value });
+ if (!e.target.validity.typeMismatch) {
+ setEmailError(null);
+ }
+ }}
+ className="invalid-text-greyed"
/>
-
- setFormState({
- ...formState,
- emailOptIn: !formState.emailOptIn,
- })
- }
+
- Join with your email to stick together
-
-
-
-
- {formState.emailOptIn && (
- <>
-
- {
- setFormState({ ...formState, email: e.target.value });
- if (!e.target.validity.typeMismatch) {
- setEmailError(null);
- }
- }}
- className="my-2 invalid-text-greyed"
- />
-
- {emailError ? emailErrorMessage(emailError) : ""}
-
-
-
- We store your email address, postcode, and constituency, so we
- can send you exactly the information you need, and the actions
- to take.
-
- >
- )}
-
- )}
-
-
-
-
- {constituencyApiLoading && (
- <>
-
- Loading... {" "}
- >
- )}
- Go
-
-
-
-
-
- Privacy Policy
-
-
-
-
-
-
+ {emailError ? emailErrorMessage(emailError) : ""}
+
+
+
+ You're opting in to receive emails. We store your email
+ address, postcode, and constituency, so we can send you exactly
+ the information you need.
+
+ >
+ )}
+
+ )}
+
+
+
+ {constituencyApiLoading && (
+ <>
+
+ Loading... {" "}
+ >
+ )}
+
+ {formState.emailOptIn ? "Go + Join" : "Go"}
+
+
+
+ Privacy Policy
+
+
+
);
};
diff --git a/components/forms/constituencyLookup.tsx b/components/forms/constituencyLookup.tsx
index 753a8d2..0cce09d 100644
--- a/components/forms/constituencyLookup.tsx
+++ b/components/forms/constituencyLookup.tsx
@@ -1,6 +1,6 @@
"use client";
-import { Form, InputGroup } from "react-bootstrap";
+import { Button, Form, InputGroup } from "react-bootstrap";
import {
normalizePostcode,
@@ -159,6 +159,7 @@ const ConstituencyLookup = ({
null,
);
+ const [formPostcode, setFormPostcode] = useState("");
useEffect(() => {
if (
apiResponse &&
@@ -225,6 +226,7 @@ const ConstituencyLookup = ({
};
const postcodeChanged = async (userPostcode: string) => {
+ setFormPostcode(userPostcode);
const normalizedPostcode = normalizePostcode(userPostcode);
if (
@@ -251,6 +253,7 @@ const ConstituencyLookup = ({
<>
postcodeChanged(e.target.value)}
className="invalid-text-greyed"
+ onBlur={(e) => {
+ if (!validatePostcode.test(normalizePostcode(e.target.value)))
+ setPostcodeError("POSTCODE_INVALID");
+ }}
/>
- {constituency && (
-
- {!constituency.name
- ? ""
- : constituency.name.length < 31
- ? constituency.name
- : constituency.name.substring(0, 27) + "..."}
-
- )}
{postcodeError ? postcodeErrorMessage(postcodeError) : ""}
+ {constituency && (
+
+
+ {
+ setApiResponse(null);
+ setFormPostcode("");
+ }}
+ >
+ CLEAR
+
+
+ )}
+
{apiResponse && apiResponse.constituencies.length > 1 && (
<>
{apiResponse.addresses ? (
-
- We can't work out exactly which constituency you're in
- - please select your address:
-
+
Select your exact address
Select Address
- {apiResponse.addresses.map((c) => (
-
- {c.name}
-
- ))}
+
+ {apiResponse.addresses.map((c) => (
+
+ {c.name.toLowerCase()}
+
+ ))}
+
) : (
-
-
- We can't work out exactly which constituency you're in
- - please select one of the {apiResponse.constituencies.length}{" "}
- options:
-
-
{
- if (e.target.value.length > 2) {
- lookupConstituency(validPostcode.current, e.target.value);
- } else {
- setFormState({
- ...formState,
- constituencyIndex: parseInt(e.target.value),
- });
- }
- }}
- >
-
- Select Constituency
-
- {apiResponse.constituencies.map((c, idx) => (
-
- {c.name}
+ !constituency && (
+
+
Select your constituency
+
{
+ if (e.target.value.length > 2) {
+ lookupConstituency(validPostcode.current, e.target.value);
+ } else {
+ setFormState({
+ ...formState,
+ constituencyIndex: parseInt(e.target.value),
+ });
+ }
+ }}
+ >
+
+ Select Constituency
- ))}
-
-
+
+ {apiResponse.constituencies.map((c, idx) => (
+
+ {c.name}
+
+ ))}
+
+
+
+ )
)}
>
)}
diff --git a/components/info_box/ImpliedChart.tsx b/components/info_box/ImpliedChart.tsx
index 19bbc29..78327dd 100644
--- a/components/info_box/ImpliedChart.tsx
+++ b/components/info_box/ImpliedChart.tsx
@@ -1,5 +1,6 @@
-import InfoBox from "./InfoBox";
import { svgChart } from "@/utils/Echarts";
+import metadata from "@/data/metadata.json";
+import Link from "next/link";
const ImpliedChart = ({
constituencyData,
@@ -12,16 +13,20 @@ const ImpliedChart = ({
);
return (
-
+
<>
-
2019 Election Results (Implied)
-
The 2019 implied results from your constituency:
+
2019 Results
+
+ Calculated by{" "}
+ BBC & Sky using
+ new boundaries.
+
>
-
+
);
};
diff --git a/components/info_box/MRPChart.tsx b/components/info_box/MRPChart.tsx
index c5996d3..efe8358 100644
--- a/components/info_box/MRPChart.tsx
+++ b/components/info_box/MRPChart.tsx
@@ -1,5 +1,6 @@
-import InfoBox from "./InfoBox";
import { svgChart } from "@/utils/Echarts";
+import metadata from "@/data/metadata.json";
+import Link from "next/link";
const MRPChart = ({
constituencyData,
@@ -9,16 +10,23 @@ const MRPChart = ({
const svgStr = svgChart(constituencyData.pollingResults.partyVoteResults);
return (
-
+
<>
-
Constituency Regression Polls
-
Average of last 6 months MRP models:
+
Local MRP polling
+
+ Aggregate average of:{" "}
+ {metadata.mrp_poll.map((datum, idx) => (
+ <>
+ Poll {idx + 1}{" "}
+ >
+ ))}{" "}
+
>
-
+
);
};
diff --git a/components/info_box/PlanToVoteBox.tsx b/components/info_box/PlanToVoteBox.tsx
index 7d1c728..0358427 100644
--- a/components/info_box/PlanToVoteBox.tsx
+++ b/components/info_box/PlanToVoteBox.tsx
@@ -1,59 +1,71 @@
-import InfoBox from "./InfoBox";
-
import {
FaPenClip,
- FaRegIdCard,
+ FaIdCard,
FaEnvelopeOpenText,
FaFileImage,
+ FaHand,
+ FaCheckToSlot,
+ FaTriangleExclamation,
} from "react-icons/fa6";
const PlanToVoteBox = () => {
return (
-
- <>
- Your Plan
-
-
-
- Register to vote, it takes 5 minutes
-
-
-
-
-
- Don't forget your photo Voter ID
-
-
-
-
-
- Vote in advance, privately, by post from home
-
-
-
-
- Download and put up some posters
-
- >
-
+ <>
+
+
+ Join up and we'll take you through this plan.
+
+ >
);
};
diff --git a/components/info_box/TacticalReasoningBox.tsx b/components/info_box/TacticalReasoningBox.tsx
index 8415964..8d2bf2a 100644
--- a/components/info_box/TacticalReasoningBox.tsx
+++ b/components/info_box/TacticalReasoningBox.tsx
@@ -1,4 +1,3 @@
-import InfoBox from "./InfoBox";
import { partyCssClassFromSlug, partyNameFromSlug } from "@/utils/Party";
import {
@@ -34,14 +33,12 @@ const TacticalReasoningBox = ({
const closeSeat = !constituencyData.otherVoteData.conservativeWinUnlikely;
return (
-
+
<>
-
Why?
-
{/* Always show previous general election winner */}
- {partyNameFromSlug(previousWinner)} won here in 2019
+ {partyNameFromSlug(previousWinner)} won in 2019
{/* Show previous biggest progressive if it matches our recommendation AND they weren't the winner */}
@@ -52,8 +49,7 @@ const TacticalReasoningBox = ({
className="me-2"
style={{ color: "var(--bs-green)" }}
/>
- {recommendedPartyName} received the most progressive votes here in
- 2019
+
{recommendedPartyName} were closest in 2019
)}
@@ -64,8 +60,7 @@ const TacticalReasoningBox = ({
className="me-2"
style={{ color: "var(--bs-green)" }}
/>
- Polling indicates that {recommendedPartyName} have the best chance
- of beating the Tory party here at the next election
+ Polls favour
{recommendedPartyName} here
)}
@@ -73,11 +68,13 @@ const TacticalReasoningBox = ({
{recommendedPartyTargetSeat && (
- This is a likely target seat for {recommendedPartyName}
+ Likely a {recommendedPartyName} target seat
)}
- {/* Check if this is a close seat */}
+ {/*
+ TODO motivational text based on whats required to get tories out
+ Check if this is a close seat
{closeSeat && (
This is a close seat, tactical voting can work very well here.
- )}
+ )}*/}
>
-
+
);
};
diff --git a/components/info_box/ToryCantWinReasoningBox.tsx b/components/info_box/ToryCantWinReasoningBox.tsx
new file mode 100644
index 0000000..aa902fc
--- /dev/null
+++ b/components/info_box/ToryCantWinReasoningBox.tsx
@@ -0,0 +1,92 @@
+import { partyCssClassFromSlug, partyNameFromSlug } from "@/utils/Party";
+
+import {
+ FaUser,
+ FaChartSimple,
+ FaChartLine,
+ FaBullseye,
+ FaTriangleExclamation,
+} from "react-icons/fa6";
+
+const ToryCantWinReasoningBox = ({
+ constituencyData,
+}: {
+ constituencyData: ConstituencyData;
+}) => {
+ const recommendedParty = constituencyData.recommendation.partySlug;
+ const recommendedPartyName = partyNameFromSlug(recommendedParty);
+
+ const previousWinner = constituencyData.impliedPreviousResult.winningParty;
+ const previousBiggestProgressive =
+ constituencyData.impliedPreviousResult.biggestProgressiveParty;
+
+ const pollingWinner = constituencyData.pollingResults.winningParty;
+ const pollingBiggestProgressive =
+ constituencyData.pollingResults.biggestProgressiveParty;
+
+ const recommendedPartyTargetSeat =
+ constituencyData.otherVoteData.targetSeatData.some(
+ (target) =>
+ target.partySlug == recommendedParty && target.likelyTarget == "YES",
+ );
+
+ const closeSeat = !constituencyData.otherVoteData.conservativeWinUnlikely;
+
+ return (
+
+ <>
+ {/* Always show previous general election winner */}
+
+
+ {partyNameFromSlug(previousWinner)} won in 2019
+
+
+ {/* Show previous biggest progressive if it matches our recommendation AND they weren't the winner */}
+ {recommendedParty == previousBiggestProgressive &&
+ recommendedParty != previousWinner && (
+
+
+ {recommendedPartyName} were closest in 2019
+
+ )}
+
+ {/* Show polling biggest progressive if it matches our recommendation */}
+ {recommendedParty == pollingBiggestProgressive && (
+
+
+ Polls favour {recommendedPartyName} here
+
+ )}
+
+ {/* If this is a target seat for our recommended party, show this */}
+ {recommendedPartyTargetSeat && (
+
+
+ Likely a {recommendedPartyName} target seat
+
+ )}
+
+ {/*
+ TODO motivational text based on whats required to get tories out
+ Check if this is a close seat
+ {closeSeat && (
+
+
+ This is a close seat, tactical voting can work very well here.
+
+ )}*/}
+ >
+
+ );
+};
+
+export default ToryCantWinReasoningBox;
diff --git a/components/navigation/Navigation.tsx b/components/navigation/Navigation.tsx
index 08fa34a..26c6838 100644
--- a/components/navigation/Navigation.tsx
+++ b/components/navigation/Navigation.tsx
@@ -3,71 +3,52 @@
// (even tho this navbar doesn't use a collapse, it still fails without use client)
import Image from "next/image";
-import Link from "next/link";
-import { Container, Navbar, Row, Col } from "react-bootstrap";
+import { Container, Button, Nav, Navbar } from "react-bootstrap";
import logo from "@/assets/stop-the-tories-logo-transparent.png";
import { rubik } from "@/utils/Fonts";
import { FaBoltLightning, FaMagnifyingGlass } from "react-icons/fa6";
-import styles from "./styles.module.css";
-
// Navbar which just includes our main call to actions, which are (currently) the search
// & join buttons. These are always displayed on the navbar, and there is no hamburger
// menu. Users need to scroll to the footer to see links to non-primary pages.
const Navigation = () => {
return (
-
-
- {/* Branding section - left-aligned */}
-
-
- StopTheTories
- .Vote
-
-
- {/* Right-aligned section - always shown links */}
-
-
-
-
-
-
- Search
-
-
-
-
-
- Join
-
-
-
-
-
-
-
+
+
+
+ {/* Branding section - left-aligned */}
+
+
+
+
+ StopTheTories
+ .Vote
+
+
+ {/* Right-aligned section - always shown links */}
+
+ {/* pushes the buttons to the right */}
+
+
+
+ Search
+
+
+
+ Join
+
+
+
+
+
);
};
diff --git a/components/sections/MovementSection.tsx b/components/sections/MovementSection.tsx
index c6bec19..cd54e95 100644
--- a/components/sections/MovementSection.tsx
+++ b/components/sections/MovementSection.tsx
@@ -33,66 +33,66 @@ const MovementSection = () => {
};
return (
-
-
- {/* PEOPLE */}
-
-
-
- Join A Movement building voter power, beyond this election.
-
-
-
-
+
+
+
+ {/* PEOPLE */}
+
+
+
+ Join A Movement building voter power, beyond this election.
+
+
+
-
-
- {Object.keys(people).map((key) => {
- // TS isn't smart enough to work out `key` is just the keys from the people object,
- // so thinks they might not be a valid key for the object...
- // @ts-expect-error
- const name: string = people[key]["name"];
- // @ts-expect-error
- const image: StaticImageData = people[key]["image"];
+
+ {Object.keys(people).map((key) => {
+ // TS isn't smart enough to work out `key` is just the keys from the people object,
+ // so thinks they might not be a valid key for the object...
+ // @ts-expect-error
+ const name: string = people[key]["name"];
+ // @ts-expect-error
+ const image: StaticImageData = people[key]["image"];
- return (
-
-
-
- );
- })}
-
-
+ return (
+
+
+
+ );
+ })}
+
- {/* PLAN */}
-
-
-
- We show up
- To get the tories out
-
- In many places around the country, if we vote together for the
- most likely candidate to beat the Tory, out votes demolish them
- into tiny numbers.
-
-
-
- We stick together
- To influence the next government
-
- A bigger influence on the next government than the media that
- currently shape the narrative. We stick together offering our
- votes for the changes we want.
-
-
-
-
-
+ {/* PLAN */}
+
+
+ We show up
+ To get the tories out
+
+ In many places around the country, if we vote together for the
+ most likely candidate to beat the Tory, out votes demolish them
+ into tiny numbers.
+
+
+
+ We stick together
+
+ To influence the next government
+
+
+ A bigger influence on the next government than the media that
+ currently shape the narrative. We stick together offering our
+ votes for the changes we want.
+
+
+
+
+
+
);
};
diff --git a/utils/Echarts.ts b/utils/Echarts.ts
index 335ed29..245a625 100644
--- a/utils/Echarts.ts
+++ b/utils/Echarts.ts
@@ -34,16 +34,16 @@ const svgChart = (partyData: PartyVoteResult[], rawVote: boolean = false) => {
const chart = echarts.init(null, null, {
renderer: "svg", // must use SVG rendering mode
ssr: true, // enable SSR
- width: 350, // need to specify height and width
+ width: 416, // need to specify height and width
height: 250,
});
chart.setOption({
grid: {
- left: 40,
- right: 10,
- top: 10,
- bottom: 100,
+ left: 35,
+ right: 2,
+ top: 5,
+ bottom: 57,
},
xAxis: {
data: axisArray,
@@ -76,7 +76,7 @@ const svgChart = (partyData: PartyVoteResult[], rawVote: boolean = false) => {
});
}
- return chart.renderToSVGString();
+ return chart.renderToSVGString().replace('width="416" height="250" ', "");
};
export { svgChart };
diff --git a/utils/Party.ts b/utils/Party.ts
index 3f0d84e..e6945dc 100644
--- a/utils/Party.ts
+++ b/utils/Party.ts
@@ -7,15 +7,15 @@ const partyNameFromSlug = (slug: PartySlug): string => {
case "LD":
return "Liberal Democrat";
case "Green":
- return "Green";
+ return "Green Party";
case "SNP":
- return "SNP";
+ return "The SNP";
case "PC":
return "Plaid Cymru";
case "Reform":
return "Reform UK";
case "NonVoter":
- return "Non Voters";
+ return "Didn't Vote";
default:
return "Other";
}
@@ -38,7 +38,7 @@ const shortPartyNameFromSlug = (slug: PartySlug): string => {
case "Reform":
return "Reform";
case "NonVoter":
- return "Non Voters";
+ return "Didn't Vote";
default:
return "Other";
}
@@ -61,7 +61,7 @@ const partyColorFromSlug = (slug: PartySlug) => {
case "Reform":
return "var(--reform-party-color)";
case "NonVoter":
- return "var(--mvtfwd-pink-strong)";
+ return "var(--mf-pink-strong)";
case "Other":
return "var(--other-party-color)";
default:
@@ -81,9 +81,9 @@ const partyCssClassFromSlug = (slug: PartySlug) => {
case "Green":
return "party-green";
case "SNP":
- return "";
+ return "party-snp";
case "PC":
- return "";
+ return "party-plaid";
case "Reform":
return "";
default: