Skip to content

Commit 06c5c96

Browse files
authored
Merge pull request #383 from SpiritFour/feat/use-case-landing-pages
feat: use-case landing pages (court evidence, relationship proof)
2 parents 63442b7 + 63373ff commit 06c5c96

18 files changed

Lines changed: 1974 additions & 1 deletion

README.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,47 @@ Please report bugs in the github issues.
2929
## Build Setup
3030
We use Node.js 22 and pnpm 8.15.8. The project also needs Python 3.11 because some legacy build dependencies compile native modules.
3131

32+
### With Nix (recommended)
33+
34+
A Nix flake is provided with all dependencies pre-configured:
35+
36+
```bash
37+
# Enter development shell
38+
$ nix develop
39+
40+
# Install dependencies
41+
$ pnpm install --frozen-lockfile
42+
```
43+
44+
### Standard setup
45+
3246
```bash
3347
# install dependencies
3448
$ pnpm install
3549

3650
# serve with hot reload at localhost:3000
3751
$ pnpm dev
3852

39-
# build for production and launch server
53+
# build for production and launch server (recommended for low-memory environments)
4054
$ pnpm build
4155
$ pnpm start
4256

4357
# generate static project
4458
$ pnpm generate
4559
```
4660

61+
### Exposing via Tailscale
62+
63+
If running on a remote server or VM, expose the instance to your tailnet:
64+
65+
```bash
66+
# For production preview (HTTP on port 3000):
67+
$ tailscale serve --bg 3000
68+
69+
# For dev server (HTTPS on port 3000):
70+
$ tailscale serve --bg https+insecure://localhost:3000
71+
```
72+
4773
Search for prettier and eslint in pycharm to set it up on saving a file.
4874
You can also add .vue there for running stuff on Vue files as well.
4975

components/GlobalFooter.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ export default {
5757
// name: "whatsapp-to-pdf",
5858
// text: "Create a PDF from your WhatsApp chat",
5959
// },
60+
{
61+
name: "court-evidence",
62+
text: "pageNameCourt",
63+
},
64+
{
65+
name: "proof-of-relationship",
66+
text: "pageNameRelationship",
67+
},
6068
{
6169
name: "switch-from-whatsapp-to-signal",
6270
text: "pageNameSignal",
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<template>
2+
<NuxtLink :to="to" class="landing-btn" @click="$emit('click')">
3+
<slot />
4+
</NuxtLink>
5+
</template>
6+
7+
<script>
8+
export default {
9+
name: "LandingButton",
10+
props: {
11+
to: { type: [String, Object], required: true },
12+
},
13+
emits: ["click"],
14+
};
15+
</script>
16+
17+
<style lang="scss" scoped>
18+
.landing-btn {
19+
display: inline-block;
20+
padding: 0.9em 2.2em;
21+
border-radius: 999px;
22+
background: $c-blue-accent;
23+
color: #ffffff !important;
24+
font-size: clamp(1rem, 1.5vw, 1.2rem);
25+
font-weight: 600;
26+
text-decoration: none;
27+
transition: transform 0.2s ease, background 0.2s ease, box-shadow 0.2s ease;
28+
box-shadow: 0 10px 30px rgba(33, 166, 141, 0.35);
29+
30+
&:hover {
31+
background: $c-blue-accent-light;
32+
transform: translateY(-1px);
33+
box-shadow: 0 14px 34px rgba(33, 166, 141, 0.45);
34+
}
35+
36+
&:active {
37+
transform: translateY(0);
38+
}
39+
}
40+
</style>
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<template>
2+
<div class="landing-cards">
3+
<component
4+
:is="item.to ? 'NuxtLink' : 'div'"
5+
v-for="item in items"
6+
:key="item.title"
7+
:to="item.to"
8+
class="landing-card"
9+
:class="{ 'landing-card--link': item.to }"
10+
>
11+
<v-icon v-if="item.icon" class="landing-card__icon" size="32">
12+
{{ item.icon }}
13+
</v-icon>
14+
<h3 class="landing-card__title">{{ item.title }}</h3>
15+
<p class="landing-card__text">{{ item.text }}</p>
16+
<span v-if="item.to" class="landing-card__more">
17+
{{ item.linkText }} <v-icon size="16">mdi-arrow-right</v-icon>
18+
</span>
19+
</component>
20+
</div>
21+
</template>
22+
23+
<script>
24+
export default {
25+
name: "LandingCards",
26+
props: {
27+
// [{ icon, title, text, to?, linkText? }]
28+
items: { type: Array, required: true },
29+
},
30+
};
31+
</script>
32+
33+
<style lang="scss" scoped>
34+
.landing-cards {
35+
display: grid;
36+
grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
37+
gap: 1.2rem;
38+
margin-top: clamp(2.2rem, 5vw, 3.6rem);
39+
text-align: left;
40+
}
41+
42+
.landing-card {
43+
background: var(--landing-card-bg, #ffffff);
44+
color: var(--landing-card-fg, #1d1d1f);
45+
border-radius: 20px;
46+
padding: 1.8rem 1.6rem;
47+
box-shadow: var(--landing-card-shadow, none);
48+
text-decoration: none;
49+
display: flex;
50+
flex-direction: column;
51+
}
52+
53+
.landing-card--link {
54+
transition: transform 0.25s ease;
55+
56+
&:hover {
57+
transform: translateY(-4px);
58+
}
59+
}
60+
61+
.landing-card__icon {
62+
color: $c-blue-accent;
63+
margin-bottom: 1rem;
64+
}
65+
66+
.landing-card__title {
67+
font-size: 1.15rem;
68+
font-weight: 700;
69+
line-height: 1.3;
70+
margin: 0 0 0.6rem;
71+
}
72+
73+
.landing-card__text {
74+
font-size: 1rem;
75+
line-height: 1.55;
76+
color: var(--landing-card-muted, rgba(29, 29, 31, 0.68));
77+
margin: 0;
78+
}
79+
80+
.landing-card__more {
81+
margin-top: auto;
82+
padding-top: 1rem;
83+
font-weight: 600;
84+
color: $c-blue-accent;
85+
display: inline-flex;
86+
align-items: center;
87+
gap: 0.3em;
88+
}
89+
</style>

components/landing/LandingCta.vue

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<template>
2+
<LandingSection theme="dark">
3+
<h2 class="landing-cta__title">{{ title }}</h2>
4+
<div class="landing-cta__actions">
5+
<LandingButton :to="ctaTo">{{ ctaText }}</LandingButton>
6+
</div>
7+
<p v-if="note" class="landing-cta__note">{{ note }}</p>
8+
<p v-if="disclaimer" class="landing-cta__disclaimer">{{ disclaimer }}</p>
9+
</LandingSection>
10+
</template>
11+
12+
<script>
13+
export default {
14+
name: "LandingCta",
15+
props: {
16+
title: { type: String, required: true },
17+
ctaText: { type: String, required: true },
18+
ctaTo: { type: [String, Object], default: "/" },
19+
note: { type: String, default: "" },
20+
disclaimer: { type: String, default: "" },
21+
},
22+
};
23+
</script>
24+
25+
<style lang="scss" scoped>
26+
.landing-cta__title {
27+
font-size: clamp(2.2rem, 6vw, 4rem);
28+
font-weight: 700;
29+
line-height: 1.08;
30+
letter-spacing: -0.02em;
31+
max-width: 20ch;
32+
margin: 0 auto;
33+
}
34+
35+
.landing-cta__actions {
36+
margin-top: 2.2rem;
37+
}
38+
39+
.landing-cta__note {
40+
margin-top: 1.2rem;
41+
font-size: 0.95rem;
42+
color: rgba(245, 245, 247, 0.55);
43+
}
44+
45+
.landing-cta__disclaimer {
46+
margin: clamp(2.5rem, 6vw, 4rem) auto 0;
47+
max-width: 40rem;
48+
font-size: 0.8rem;
49+
line-height: 1.5;
50+
color: rgba(245, 245, 247, 0.4);
51+
}
52+
</style>

0 commit comments

Comments
 (0)