Add TinyPaws three-entry landing pages#1
Conversation
📝 WalkthroughWalkthroughThe PR adds Care and Share landing pages, refreshes the homepage to a private-hub layout, updates routing and crawl access, and rewires the shared signup flow so footer email capture works across the new pages. ChangesPrivate Hub Launch with Care and Share Landing Pages
Estimated code review effort: 4 (Complex) | ~45 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…kens/type, privacy-first positioning (no false EU-hosting claim), 'Start free' CTAs wired to app.tinypaws.com/start?door= attribution, honest no-fabricated-testimonials placeholder; waitlist handler kept as email-capture fallback. Not pushed — review before deploy.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
functions/api/join-waitlist.js (1)
76-95: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winPreserve existing KV fields on re-submission
Email-only submissions still overwrite the whole record for an address, so a later footer signup can clear previously stored
name/petName/petType/interest/updates. Merge with the existing KV value and only replace fields that are actually present.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@functions/api/join-waitlist.js` around lines 76 - 95, The join-waitlist handler is overwriting the full KV record on repeated submissions, which can erase previously stored subscriber fields. Update join-waitlist.js in the join-waitlist function to read the existing value from env[KV_NAMESPACE_BINDING] before writing, merge it with the new subscriberData, and only replace fields that are actually present so email-only signups preserve prior name, petName, petType, interest, and updates values.
🧹 Nitpick comments (1)
index.html (1)
236-291: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider marking plan names as headings for accessibility.
<span class="plan-name">(Free/Plus/Premium) are the primary identifiers for each pricing card but aren't exposed as headings, so screen-reader users navigating by heading can't jump directly between plans.♻️ Proposed fix
- <span class="plan-name">Free</span> + <h3 class="plan-name">Free</h3>(repeat for Plus and Premium plan cards)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@index.html` around lines 236 - 291, Mark the plan labels in the pricing cards as real headings so screen-reader users can navigate between plans more easily; update the Free, Plus, and Premium identifiers currently rendered via `span.plan-name` in the pricing section to use heading semantics while keeping their visual styling intact. Make the change consistently across all three plan cards in the pricing markup so the cards have accessible structure without changing the surrounding layout.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@care.html`:
- Around line 42-49: The mobile menu duplicate link for the current page is
missing the same active-page indicator used in the desktop nav. Update the Care
link inside the mobile-menu block in care.html so it matches the existing
desktop Care navigation by including aria-current="page" on the care.html
anchor, keeping the mobile and desktop navigation semantics consistent.
In `@index.html`:
- Around line 353-392: The footer email form in the `email-form` block currently
defaults to a GET navigation when JavaScript is unavailable, which can expose
the submitted email in the URL. Update the `form` markup to use an explicit
POST-based fallback or otherwise prevent a GET submission from the
`email-form`/`email-input` flow. Keep the fix localized to the footer form in
`index.html` so the no-script path is safe without relying on `script.js`.
In `@script.js`:
- Around line 46-50: The footer waitlist submission is missing the updates
opt-in flag, so subscribers are always saved as not opted in. Update the payload
in the join-waitlist request within the footer signup flow to include the
updates value alongside email, source, and page, and make sure the footer’s
opt-in handler passes the correct truthy updates state into that request so the
API’s updates field is persisted correctly.
In `@share.html`:
- Around line 42-49: The mobile menu in share.html is missing the current-page
accessibility state that the desktop nav already has. Update the Memories link
inside the mobile-menu navigation duplicate so it uses aria-current="page"
consistently with the corresponding desktop navigation item, and keep the rest
of the mobile nav links unchanged.
---
Outside diff comments:
In `@functions/api/join-waitlist.js`:
- Around line 76-95: The join-waitlist handler is overwriting the full KV record
on repeated submissions, which can erase previously stored subscriber fields.
Update join-waitlist.js in the join-waitlist function to read the existing value
from env[KV_NAMESPACE_BINDING] before writing, merge it with the new
subscriberData, and only replace fields that are actually present so email-only
signups preserve prior name, petName, petType, interest, and updates values.
---
Nitpick comments:
In `@index.html`:
- Around line 236-291: Mark the plan labels in the pricing cards as real
headings so screen-reader users can navigate between plans more easily; update
the Free, Plus, and Premium identifiers currently rendered via `span.plan-name`
in the pricing section to use heading semantics while keeping their visual
styling intact. Make the change consistently across all three plan cards in the
pricing markup so the cards have accessible structure without changing the
surrounding layout.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1506024d-e960-4a19-8a1b-5b31ffc4dceb
📒 Files selected for processing (7)
care.htmlfunctions/api/join-waitlist.jsindex.htmlrobots.txtscript.jsshare.htmlstyles.css
✅ Files skipped from review due to trivial changes (1)
- robots.txt
| <div class="mobile-menu" id="mobile-menu"> | ||
| <ul> | ||
| <li><a href="care.html">Care pages</a></li> | ||
| <li><a href="share.html">Memories</a></li> | ||
| <li><a href="index.html#privacy">Privacy</a></li> | ||
| <li><a href="index.html#pricing">Pricing</a></li> | ||
| </ul> | ||
| </div> |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Mobile nav duplicate is missing aria-current="page".
Desktop nav marks the active page (<a href="care.html" aria-current="page">, line 31), but the mobile menu's duplicate link (line 44) omits it, so assistive tech using the mobile menu won't get the same "current page" signal.
♿ Proposed fix
<ul>
- <li><a href="care.html">Care pages</a></li>
+ <li><a href="care.html" aria-current="page">Care pages</a></li>
<li><a href="share.html">Memories</a></li>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div class="mobile-menu" id="mobile-menu"> | |
| <ul> | |
| <li><a href="care.html">Care pages</a></li> | |
| <li><a href="share.html">Memories</a></li> | |
| <li><a href="index.html#privacy">Privacy</a></li> | |
| <li><a href="index.html#pricing">Pricing</a></li> | |
| </ul> | |
| </div> | |
| <div class="mobile-menu" id="mobile-menu"> | |
| <ul> | |
| <li><a href="care.html" aria-current="page">Care pages</a></li> | |
| <li><a href="share.html">Memories</a></li> | |
| <li><a href="index.html#privacy">Privacy</a></li> | |
| <li><a href="index.html#pricing">Pricing</a></li> | |
| </ul> | |
| </div> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@care.html` around lines 42 - 49, The mobile menu duplicate link for the
current page is missing the same active-page indicator used in the desktop nav.
Update the Care link inside the mobile-menu block in care.html so it matches the
existing desktop Care navigation by including aria-current="page" on the
care.html anchor, keeping the mobile and desktop navigation semantics
consistent.
| <footer class="footer"> | ||
| <div class="container"> | ||
| <div class="footer-top"> | ||
| <div class="footer-logo"> | ||
| <img src="images/logo.svg" alt="TinyPaws Logo"> | ||
| <p class="mt-4 text-gray-600">Preserving precious pet memories that last a lifetime.</p> | ||
| <div class="social-links mt-6"> | ||
| <a href="https://www.facebook.com/tinypawsapp" aria-label="Facebook"> | ||
| <i data-feather="facebook"></i> | ||
| </a> | ||
| <a href="https://www.instagram.com/tinypaws.app" aria-label="Instagram"> | ||
| <i data-feather="instagram"></i> | ||
| </a> | ||
| <a href="https://www.twitter.com/tinypawsapp" aria-label="Twitter"> | ||
| <i data-feather="twitter"></i> | ||
| </a> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div class="footer-links"> | ||
| <h3>Company</h3> | ||
| <ul> | ||
| <li><a href="coming-soon">About Us</a></li> | ||
| <li><a href="coming-soon">Blog</a></li> | ||
| <li><a href="mailto:press@tinypaws.com">Press</a></li> | ||
| </ul> | ||
| <div class="footer-grid"> | ||
| <div> | ||
| <h2>Not ready yet?</h2> | ||
| <p>Leave your email and we'll write when there's something genuinely worth telling you — big features, launch news. No drip campaigns.</p> | ||
| <form class="email-form" id="email-form" novalidate> | ||
| <label for="email-input">Email address</label> | ||
| <input type="email" id="email-input" name="email" autocomplete="email" placeholder="you@example.com" required> | ||
| <button type="submit" class="btn btn-primary on-dark-btn">Keep me posted</button> | ||
| </form> | ||
| <p class="form-status" id="form-status" role="status" aria-live="polite"></p> | ||
| </div> | ||
|
|
||
| <div class="footer-links"> | ||
| <h3>Resources</h3> | ||
| <ul> | ||
| <li><a href="https://www.nutrified.com?ref=tinypaws-footer" target="_blank" rel="dofollow">Pet Nutrition</a></li> | ||
| <li><a href="https://www.ampawssadors.com?ref=tinypaws-footer" target="_blank" rel="dofollow">Ampawssadors</a></li> | ||
| <li><a href="mailto:web@tinypaws.com">Contact Support</a></li> | ||
| </ul> | ||
| </div> | ||
|
|
||
| <div class="footer-links"> | ||
| <h3>Legal</h3> | ||
| <ul> | ||
| <li><a href="coming-soon">Privacy Policy</a></li> | ||
| <li><a href="coming-soon">Terms of Service</a></li> | ||
| <li><a href="coming-soon">Cookie Policy</a></li> | ||
| </ul> | ||
| <div> | ||
| <h3>Product</h3> | ||
| <ul> | ||
| <li><a href="care.html">Care pages</a></li> | ||
| <li><a href="share.html">Memories</a></li> | ||
| <li><a href="index.html#pricing">Pricing</a></li> | ||
| <li><a href="index.html#privacy">Privacy promises</a></li> | ||
| </ul> | ||
| </div> | ||
| <div> | ||
| <h3>Company</h3> | ||
| <ul> | ||
| <li><a href="https://www.nutrified.com?ref=tinypaws">Nutrified</a></li> | ||
| <li><a href="https://ampawssadors.com?ref=tinypaws">Ampawssadors</a></li> | ||
| <li><a href="mailto:support@tinypaws.com">support@tinypaws.com</a></li> | ||
| </ul> | ||
| </div> | ||
| </div> | ||
|
|
||
|
|
||
| </div> | ||
|
|
||
| <div class="footer-bottom"> | ||
| <p class="copyright">© 2025 TinyPaws. All rights reserved.</p> | ||
| <div class="nutrified-partnership"> | ||
| <p>Brought to you by <a href="https://www.nutrified.com?ref=tinypaws-footer" target="_blank" rel="dofollow">Nutrified</a></p> | ||
| </div> | ||
| <span>© 2026 Nutrified Media. TinyPaws is a private place — it stays that way.</span> | ||
| <span>Made for the people your pet loves.</span> | ||
| </div> | ||
| </div> | ||
| </footer> | ||
|
|
||
| <!-- Waitlist Popup Overlay --> | ||
| <div id="waitlist-overlay" class="overlay"> | ||
| <div class="popup"> | ||
| <div class="popup-header"> | ||
| <h3>Join Our Pack</h3> | ||
| <button id="close-popup" class="close-popup">×</button> | ||
| </div> | ||
| <div class="popup-content"> | ||
| <p>Be among the first to preserve your pet's legacy when TinyPaws launches.</p> | ||
| <form id="waitlist-form" class="popup-form"> | ||
| <div class="form-group"> | ||
| <label for="popup-email">Email Address*</label> | ||
| <input type="email" id="popup-email" name="email" required> | ||
| </div> | ||
|
|
||
| <div class="form-group"> | ||
| <label for="popup-name">Your Name*</label> | ||
| <input type="text" id="popup-name" name="name" required> | ||
| </div> | ||
|
|
||
| <div class="form-group"> | ||
| <label for="popup-pet-name">Pet's Name*</label> | ||
| <input type="text" id="popup-pet-name" name="pet-name" required> | ||
| </div> | ||
|
|
||
| <div class="form-group"> | ||
| <label for="popup-pet-type">Pet Type*</label> | ||
| <select id="popup-pet-type" name="pet-type" required> | ||
| <option value="">Select pet type</option> | ||
| <option value="dog">Dog</option> | ||
| <option value="cat">Cat</option> | ||
| <option value="rabbit">Rabbit</option> | ||
| <option value="other">Other</option> | ||
| </select> | ||
| </div> | ||
|
|
||
| <div class="form-group checkbox"> | ||
| <input type="checkbox" id="popup-updates" name="updates" checked> | ||
| <label for="popup-updates">I'd like to receive updates about TinyPaws and pet wellness tips</label> | ||
| </div> | ||
|
|
||
| <div id="form-error" class="error-message"></div> | ||
| <div id="form-success" class="success-message"></div> | ||
|
|
||
| <button type="submit" class="btn btn-primary btn-large plausible-event-name=join_waitlist_popup_form">JOIN THE WAITLIST</button> | ||
| </form> | ||
|
|
||
| <div class="waitlist-perks"> | ||
| <h4>Waitlist Perks:</h4> | ||
| <ul> | ||
| <li>Priority access when we launch</li> | ||
| <li>3 months of Premium features free</li> | ||
| <li>Exclusive founding member badge</li> | ||
| <li>Input on future features through our VIP feedback community</li> | ||
| </ul> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <script src="script.js"></script> | ||
| <script> | ||
| document.addEventListener('DOMContentLoaded', function() { | ||
| feather.replace(); | ||
| }); | ||
| </script> | ||
| </body> |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the relevant files and inspect the form + handler.
git ls-files | rg '(^|/)(index\.html|script\.js)$'
printf '\n--- index.html (form area) ---\n'
rg -n -C 4 '<form class="email-form"|id="email-form"|form-status|novalidate' index.html
printf '\n--- script.js (email form handling) ---\n'
rg -n -C 6 'email-form|form-status|preventDefault|submit|DOMContentLoaded' script.jsRepository: nutrified/tinypaws.com
Length of output: 3769
Add a POST fallback for the footer email form (index.html:359-364) The form still falls back to a GET navigation to the current page when script.js doesn’t run, so the email can end up in the URL/history/logs. Give it an explicit POST action or block submission at the markup level.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@index.html` around lines 353 - 392, The footer email form in the `email-form`
block currently defaults to a GET navigation when JavaScript is unavailable,
which can expose the submitted email in the URL. Update the `form` markup to use
an explicit POST-based fallback or otherwise prevent a GET submission from the
`email-form`/`email-input` flow. Keep the fix localized to the footer form in
`index.html` so the no-script path is safe without relying on `script.js`.
| fetch("/api/join-waitlist", { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ email: email, source: "footer_updates", page: location.pathname }) | ||
| }) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Footer signup never records the updates opt-in.
The footer form is an updates opt-in (source: "footer_updates", plausible("email_updates_signup")), but the payload omits updates. The API stores updates: Boolean(updates), so every footer subscriber is persisted with updates: false, contradicting the stated goal of persisting opt-in.
🛠️ Proposed fix
- body: JSON.stringify({ email: email, source: "footer_updates", page: location.pathname })
+ body: JSON.stringify({ email: email, updates: true, source: "footer_updates", page: location.pathname })📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fetch("/api/join-waitlist", { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ email: email, source: "footer_updates", page: location.pathname }) | |
| }) | |
| fetch("/api/join-waitlist", { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ email: email, updates: true, source: "footer_updates", page: location.pathname }) | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@script.js` around lines 46 - 50, The footer waitlist submission is missing
the updates opt-in flag, so subscribers are always saved as not opted in. Update
the payload in the join-waitlist request within the footer signup flow to
include the updates value alongside email, source, and page, and make sure the
footer’s opt-in handler passes the correct truthy updates state into that
request so the API’s updates field is persisted correctly.
| <div class="mobile-menu" id="mobile-menu"> | ||
| <ul> | ||
| <li><a href="care.html">Care pages</a></li> | ||
| <li><a href="share.html">Memories</a></li> | ||
| <li><a href="index.html#privacy">Privacy</a></li> | ||
| <li><a href="index.html#pricing">Pricing</a></li> | ||
| </ul> | ||
| </div> |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Mobile nav duplicate is missing aria-current="page".
Same gap as care.html: desktop nav marks the current page (line 32), but the mobile menu duplicate (line 45) doesn't.
♿ Proposed fix
<ul>
<li><a href="care.html">Care pages</a></li>
- <li><a href="share.html">Memories</a></li>
+ <li><a href="share.html" aria-current="page">Memories</a></li>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div class="mobile-menu" id="mobile-menu"> | |
| <ul> | |
| <li><a href="care.html">Care pages</a></li> | |
| <li><a href="share.html">Memories</a></li> | |
| <li><a href="index.html#privacy">Privacy</a></li> | |
| <li><a href="index.html#pricing">Pricing</a></li> | |
| </ul> | |
| </div> | |
| <div class="mobile-menu" id="mobile-menu"> | |
| <ul> | |
| <li><a href="care.html">Care pages</a></li> | |
| <li><a href="share.html" aria-current="page">Memories</a></li> | |
| <li><a href="index.html#privacy">Privacy</a></li> | |
| <li><a href="index.html#pricing">Pricing</a></li> | |
| </ul> | |
| </div> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@share.html` around lines 42 - 49, The mobile menu in share.html is missing
the current-page accessibility state that the desktop nav already has. Update
the Memories link inside the mobile-menu navigation duplicate so it uses
aria-current="page" consistently with the corresponding desktop navigation item,
and keep the rest of the mobile nav links unchanged.
Summary
Verification
Notes
Direct push to nutrified/tinypaws.com was denied for the local simplebytes-agent token, so this PR is opened from a fork branch.
Summary by CodeRabbit
/careand/sharecorrectly open their respective pages.