Skip to content

refactor: polish marketing UI fonts, carousels, and add gradient button - #145

Merged
Smartlify07 merged 2 commits into
mainfrom
fix/ui-polish
Aug 11, 2026
Merged

refactor: polish marketing UI fonts, carousels, and add gradient button#145
Smartlify07 merged 2 commits into
mainfrom
fix/ui-polish

Conversation

@Smartlify07

@Smartlify07 Smartlify07 commented Aug 11, 2026

Copy link
Copy Markdown
Owner

What

  • Added reusable GradientButton (src/shared/ui/gradient-button.tsx) encapsulating the navbar's blue-to-violet gradient hover-overlay effect; supports both button and href/link usage.
  • Converted community stories to a GSAP track-based carousel (container slides, not blocks); prev/next disabled at first/last.
  • Testimonials rewritten as a motion page-style carousel (AnimatePresence mode="wait", directional slide variants).
  • Decoupled donation-form custom amount; dropdown reduced to one-time/monthly only.
  • Registered Inter via next/font and applied .marketing-header/.marketing-headline utilities across marketing headings.
  • Added qualities: [100] to image optimizer config.

Verify

  • npx tsc --noEmit: clean
  • npm run build: passes

Summary by CodeRabbit

  • New Features

    • Added custom donation amounts and simplified one-time/monthly donation options.
    • Introduced refreshed gradient call-to-action buttons with animated arrow interactions.
    • Improved story and testimonial carousel navigation with animated transitions and boundary controls.
  • Improvements

    • Updated marketing typography, spacing, headings, and responsive layouts across pages.
    • Renamed “Courses” to “Trainings” and reordered navigation links.
    • Updated the dashboard logo and refined course-player loading visuals.
    • Improved image quality and loading priority for featured marketing imagery.

@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
modern-advocates Building Building Preview Aug 11, 2026 10:27pm

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR standardizes marketing typography and layout utilities, introduces a reusable gradient button, adds custom donation amounts, updates navigation and imagery, revises marketing carousels, and applies course-player presentation changes.

Changes

Marketing typography foundation

Layer / File(s) Summary
Inter font and shared heading utilities
src/app/layout.tsx, src/app/globals.css, src/app/(marketing)/layout.tsx
Loads Inter, exposes its CSS variable, removes the global font-sans rule, and adds shared marketing heading utilities.
Marketing heading adoption
src/features/marketing/components/*.tsx
Applies marketing-header and marketing-headline across marketing headings and updates selected responsive typography and layout classes.

Shared CTAs and donation flow

Layer / File(s) Summary
Reusable gradient button
src/shared/ui/gradient-button.tsx
Adds GradientButton with shared button props, optional href, gradient hover styling, and icon rotation.
Marketing CTA integration
src/features/marketing/components/about-support-mission-section.tsx, src/features/marketing/components/cta-section.tsx, src/features/marketing/components/footer.tsx
Replaces selected standard buttons or links with GradientButton and updates related CTA styling.
Custom donation amounts
src/features/marketing/components/donation-form.tsx
Supports custom amounts, validates positive finite values, updates fee and total calculations, removes tier frequency, and submits the effective amount.

Layout, navigation, and assets

Layer / File(s) Summary
Container and page layout updates
next.config.ts, src/app/(marketing)/contact/page.tsx, src/app/(user)/account/layout.tsx, src/app/(user)/my-learning/page.tsx
Restricts image quality to 100, removes the contact page minimum height, and adopts marketing-container in user layouts.
Navigation and image presentation
src/features/marketing/components/navbar.tsx, src/features/marketing/components/footer.tsx, src/features/marketing/components/about-impact-carousel-section.tsx, src/features/user-dashboard/components/dashboard-navbar.tsx
Renames and reorders navigation items, prioritizes carousel images, and replaces the dashboard logo asset.

Carousel interactions

Layer / File(s) Summary
Community story track
src/features/marketing/components/stories-from-our-community-section.tsx
Replaces Motion-based story transitions with a GSAP horizontal track and bounded navigation.
Testimonial pagination
src/features/marketing/components/testimonials.tsx
Adds animated pagination, responsive visible-review slices, and previous/next state handlers.

Course-player presentation

Layer / File(s) Summary
Player and dashboard polish
src/features/user-dashboard/components/course-player-content.tsx, src/features/user-dashboard/components/course-player-shell.tsx
Removes rounded corners from video placeholders and skeletons and applies formatting-only cleanup.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Poem

A rabbit hops through headings bright,
Inter letters line the light.
Gradient buttons bloom and glow,
Custom gifts now calculate and flow.
Stories slide, reviews take flight—
UI carrots dressed just right.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes to marketing fonts, carousels, and the reusable gradient button.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch fix/ui-polish
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ui-polish

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

🧹 Nitpick comments (3)
src/features/marketing/components/stories-from-our-community-section.tsx (1)

140-140: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Replace the arbitrary hex color with a design token.

Both navigation buttons use disabled:text-[#D9D9D9]. Use a repository token, for example a muted foreground token, so the disabled state follows the theme.

As per coding guidelines: "Do not use arbitrary hex colors or spacing; use the repository's design tokens."

Also applies to: 149-149

🤖 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 `@src/features/marketing/components/stories-from-our-community-section.tsx` at
line 140, Replace the disabled:text-[`#D9D9D9`] utility on both navigation buttons
in the stories section with the repository’s muted foreground design-token
utility, preserving the existing disabled styling and button behavior.

Source: Coding guidelines

src/features/marketing/components/testimonials.tsx (2)

113-113: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer design tokens over arbitrary sizing values.

These lines add arbitrary spacing and type sizes, for example mt-21.5, max-w-[1050px], h-[500px], w-[330px], px-[15px], pb-[30px], text-[100px], and mt-[-14px]. Map the recurring values to the repository's design tokens where a token exists.

As per coding guidelines: "Do not use arbitrary hex colors or spacing; use the repository's design tokens."

Also applies to: 129-129, 144-147

🤖 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 `@src/features/marketing/components/testimonials.tsx` at line 113, Replace the
arbitrary Tailwind sizing values in the testimonials component, including
mt-21.5, max-w-[1050px], h-[500px], w-[330px], px-[15px], pb-[30px],
text-[100px], and mt-[-14px], with the closest existing repository design
tokens. Preserve the current layout and responsive behavior while applying
tokens consistently to the affected elements.

Source: Coding guidelines


46-49: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the unused pagination state and DOM ref.

activeIndex is never read. scrollRef is only assigned and never read. Remove both, including ref={scrollRef}, and keep pageIndex as the single source of truth.

🤖 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 `@src/features/marketing/components/testimonials.tsx` around lines 46 - 49,
Remove the unused activeIndex state and scrollRef declaration from the
testimonials component, then remove the corresponding ref={scrollRef} usage.
Preserve pageIndex and direction state as the single pagination source of truth.
🤖 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 `@next.config.ts`:
- Line 28: Update the image quality allowlist in the Next.js image configuration
to include the default quality value 75 alongside the existing 100 entry,
preserving support for the background Image in about-impact-carousel-section.tsx
while retaining explicit quality 100 support.

In `@src/app/`(user)/my-learning/page.tsx:
- Line 90: Update the loading and empty-state wrappers in MyLearningPage to use
the same marketing-container class as the loaded state, while preserving only
their state-specific vertical spacing classes.

In `@src/features/marketing/components/about-impact-carousel-section.tsx`:
- Around line 107-108: Update the mapped impact images in the about impact
carousel to remove the priority prop and explicitly use lazy loading, ensuring
all seven below-hero photos are not preloaded.

In `@src/features/marketing/components/contact-hero-section.tsx`:
- Line 49: Update the grid breakpoint on the marketing-container in the contact
hero section so the 562px column and 88px gap are applied only at xl, not lg;
preserve the narrower responsive layout at lg so the form fields do not become
squeezed or overflow.

In `@src/features/marketing/components/course-information-card.tsx`:
- Line 64: Update the h2 className in the course information card to replace the
malformed leading/[100%]-normal utility with the intended valid Tailwind
utility, using either leading-normal or leading-[100%] while preserving the
other classes.

In `@src/features/marketing/components/cta-section.tsx`:
- Around line 21-24: Update the CTA GradientButton in the marketing section so
activating it navigates to the existing /contact destination. Use the project’s
established link/navigation pattern rather than leaving it as a button without
an href or onClick handler, while preserving the current label and styling.

In `@src/features/marketing/components/donation-form.tsx`:
- Around line 71-76: Validate parsedCustomAmount for finiteness before passing
it to computeAdminFee and computeDonationTotal in the donation form. When the
custom amount is invalid, avoid calculating or displaying Infinity by using the
existing neutral-value or inline-validation behavior, while preserving normal
calculations for valid custom amounts and watchedAmount.
- Around line 271-277: Update the GradientButton in the donation CTA to set
type="submit" and form="donation-form" so it submits the existing donation-form
handler; also restore disabled={submitting || !isConfirmed} to prevent duplicate
or unconfirmed checkout requests.

In `@src/features/marketing/components/footer.tsx`:
- Around line 83-86: Replace the arbitrary p-[15px] padding on the
GradientButton with the matching repository spacing token, using p-4 if it
preserves the current layout. Keep the button’s other classes and content
unchanged.
- Around line 64-65: Update the newsletter heading text in the marketing
footer’s h2 element from “Join our News letter” to “Join our Newsletter,”
preserving the existing styling and structure.

In `@src/features/marketing/components/stories-from-our-community-section.tsx`:
- Around line 59-78: In
src/features/marketing/components/stories-from-our-community-section.tsx lines
59-78, update the carousel effect around transitionTo and trackRef to recompute
the GSAP x offset from the current track.clientWidth whenever the viewport
resizes or orientation changes, and kill any active tween during unmount
cleanup. In src/features/marketing/components/testimonials.tsx lines 51-52,
clamp pageIndex to the recomputed maxIndex whenever visibleCount changes,
including the transition from one to three visible items.
- Around line 104-158: Update the stories map in the track so navigation
controls are rendered only once outside the per-story slides, or ensure every
inactive slide is marked inert and non-focusable. Preserve the existing
showPreviousStory/showNextStory behavior and active-slide controls while
preventing off-screen buttons from entering the accessibility tree or being
announced repeatedly.

In `@src/features/marketing/components/testimonials.tsx`:
- Around line 51-52: Clamp pageIndex to maxIndex whenever visibleCount or
maxIndex changes, and use the clamped value as the starting index for
reviews.slice. Preserve existing pagination behavior while ensuring viewport
changes cannot leave pageIndex beyond the new maximum.
- Around line 143-153: Update the testimonial data and rendering in the
testimonials component so each review entry includes its intended quote and
renders that entry’s quote instead of shared hardcoded text. Replace duplicated
“Michael R.” values with the distinct reviewer names associated with each
testimonial, while preserving the existing card layout and styling.

In `@src/features/user-dashboard/components/course-player-content.tsx`:
- Line 171: Update the Skeleton in the course player content loading state to
include the rounded-none class, preserving square corners consistently with the
unavailable-video placeholders and the corresponding loading skeleton in
CoursePlayerShell.

---

Nitpick comments:
In `@src/features/marketing/components/stories-from-our-community-section.tsx`:
- Line 140: Replace the disabled:text-[`#D9D9D9`] utility on both navigation
buttons in the stories section with the repository’s muted foreground
design-token utility, preserving the existing disabled styling and button
behavior.

In `@src/features/marketing/components/testimonials.tsx`:
- Line 113: Replace the arbitrary Tailwind sizing values in the testimonials
component, including mt-21.5, max-w-[1050px], h-[500px], w-[330px], px-[15px],
pb-[30px], text-[100px], and mt-[-14px], with the closest existing repository
design tokens. Preserve the current layout and responsive behavior while
applying tokens consistently to the affected elements.
- Around line 46-49: Remove the unused activeIndex state and scrollRef
declaration from the testimonials component, then remove the corresponding
ref={scrollRef} usage. Preserve pageIndex and direction state as the single
pagination source of truth.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ef15b6d5-dd0d-45ae-91da-5861ce398073

📥 Commits

Reviewing files that changed from the base of the PR and between cbba5e3 and 857e550.

⛔ Files ignored due to path filters (1)
  • public/figma-home/community-story-6.png is excluded by !**/*.png
📒 Files selected for processing (48)
  • next.config.ts
  • src/app/(marketing)/contact/page.tsx
  • src/app/(marketing)/layout.tsx
  • src/app/(user)/account/layout.tsx
  • src/app/(user)/my-learning/page.tsx
  • src/app/globals.css
  • src/app/layout.tsx
  • src/features/marketing/components/about-ai-healthcare-section.tsx
  • src/features/marketing/components/about-hero-section.tsx
  • src/features/marketing/components/about-impact-carousel-section.tsx
  • src/features/marketing/components/about-our-values-section.tsx
  • src/features/marketing/components/about-support-mission-section.tsx
  • src/features/marketing/components/community-section.tsx
  • src/features/marketing/components/contact-hero-section.tsx
  • src/features/marketing/components/course-detail-content-section.tsx
  • src/features/marketing/components/course-detail-hero-section.tsx
  • src/features/marketing/components/course-information-card.tsx
  • src/features/marketing/components/course-module.tsx
  • src/features/marketing/components/courses-hero-section.tsx
  • src/features/marketing/components/cta-section.tsx
  • src/features/marketing/components/donation-cta-section.tsx
  • src/features/marketing/components/donation-form.tsx
  • src/features/marketing/components/donation-hero-section.tsx
  • src/features/marketing/components/donation-support-section.tsx
  • src/features/marketing/components/empowerment-section.tsx
  • src/features/marketing/components/faq.tsx
  • src/features/marketing/components/featured-courses.tsx
  • src/features/marketing/components/footer.tsx
  • src/features/marketing/components/hero-content.tsx
  • src/features/marketing/components/hero-section.tsx
  • src/features/marketing/components/how-can-we-help.tsx
  • src/features/marketing/components/how-can-we-support.tsx
  • src/features/marketing/components/how-it-works.tsx
  • src/features/marketing/components/invest-in-hope-section.tsx
  • src/features/marketing/components/mission-sections.tsx
  • src/features/marketing/components/navbar.tsx
  • src/features/marketing/components/our-vision-section.tsx
  • src/features/marketing/components/practical-pathways-section.tsx
  • src/features/marketing/components/review-card.tsx
  • src/features/marketing/components/social-proof.tsx
  • src/features/marketing/components/stories-from-our-community-section.tsx
  • src/features/marketing/components/testimonials.tsx
  • src/features/marketing/components/tutor-card.tsx
  • src/features/marketing/components/why-choose-us.tsx
  • src/features/user-dashboard/components/course-player-content.tsx
  • src/features/user-dashboard/components/course-player-shell.tsx
  • src/features/user-dashboard/components/dashboard-navbar.tsx
  • src/shared/ui/gradient-button.tsx

Comment thread next.config.ts
pathname: "/**",
},
],
qualities: [100],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Expect: all Image usages and explicit quality values are reviewed against the global allowlist.
rg -n -C 3 '(<Image|quality\s*=|qualities\s*:)' next.config.ts src --glob '*.{ts,tsx}'

Repository: Smartlify07/Modern-Advocates

Length of output: 28079


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- package versions ---'
rg -n '"next"\s*:|next@' package.json package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null || true

printf '%s\n' '--- next.config.ts ---'
cat -n next.config.ts

printf '%s\n' '--- affected component ---'
cat -n src/features/marketing/components/about-impact-carousel-section.tsx

printf '%s\n' '--- Image quality props in source ---'
rg -n -C 2 'quality\s*=|qualities\s*:' src next.config.ts --glob '*.{ts,tsx}'

Repository: Smartlify07/Modern-Advocates

Length of output: 13358


🌐 Web query:

Next.js 16 images.qualities default quality omitted closest allowed value 75 100 official documentation

💡 Result:

In Next.js 16, the handling of image quality has changed significantly to improve security and prevent abuse [1][2]. The images.qualities configuration now defaults to [75] [1][3], and this field is effectively required because unrestricted access to all quality levels is no longer allowed [1]. If you pass a quality prop to the Image component that does not match one of the values explicitly defined in your images.qualities array, the system will automatically coerce your requested value to the closest allowed value from that list [1][2]. Key details include: * Default Behavior: By default, only 75 is allowed [1][2]. If you do not configure images.qualities, any quality prop you provide will be coerced to 75 [1]. * Configuration: To support other quality levels (such as 100), you must explicitly define them in your next.config.js file: js filename="next.config.js" module.exports = { images: { qualities: [75, 100], }, } * Coercion Logic: The Image Optimization API selects the closest value from your defined qualities list based on the absolute mathematical difference between your requested quality and the allowed entries [1][4]. For example, if your configuration is qualities: [75, 100] and you request quality={85}, the system will select 75 as it is closer to 85 than 100 is [1][4]. If you request quality={90}, it will select 100 [1][4].

Citations:


Keep the default image quality in the allowlist.

The background Image in about-impact-carousel-section.tsx omits quality. Next.js 16 defaults it to 75, then coerces it to 100 because images.qualities contains only 100. Add 75, for example [75, 100], unless this higher payload is intentional.

🤖 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 `@next.config.ts` at line 28, Update the image quality allowlist in the Next.js
image configuration to include the default quality value 75 alongside the
existing 100 entry, preserving support for the background Image in
about-impact-carousel-section.tsx while retaining explicit quality 100 support.


return (
<div className="mx-auto px-4 py-12.5 lg:max-w-7xl lg:px-25 lg:py-19.25 2xl:max-w-360 2xl:px-50">
<div className="marketing-container">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use the same container contract for every render state.

MyLearningPage now uses marketing-container only for the loaded state. The loading and empty states still use different wrappers. If the shared class changes max-width or horizontal padding, the page shifts when the query state changes. Update those wrappers to use marketing-container and retain only state-specific vertical spacing.

Proposed loading-state alignment
-      <div className="mx-auto px-4 py-8 lg:max-w-7xl lg:px-25 2xl:max-w-360 2xl:px-50">
+      <div className="marketing-container py-8">
🤖 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 `@src/app/`(user)/my-learning/page.tsx at line 90, Update the loading and
empty-state wrappers in MyLearningPage to use the same marketing-container class
as the loaded state, while preserving only their state-specific vertical spacing
classes.

Comment on lines +107 to +108
priority
quality={100}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Expect: only the actual LCP image uses priority, preload, or high fetch priority.
rg -n -C 3 '\b(priority|preload|fetchPriority)\b' src --glob '*.{ts,tsx}'

Repository: Smartlify07/Modern-Advocates

Length of output: 7847


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- component outline ---'
ast-grep outline src/features/marketing/components/about-impact-carousel-section.tsx --view expanded

printf '%s\n' '--- component source ---'
sed -n '1,180p' src/features/marketing/components/about-impact-carousel-section.tsx

printf '%s\n' '--- Next.js version and image configuration ---'
rg -n '"next"|"version"|"images"|"qualities"' package.json package-lock.json pnpm-lock.yaml yarn.lock next.config.* 2>/dev/null || true

printf '%s\n' '--- impact photo definitions and usages ---'
rg -n -C 4 'impactPhotos|AboutImpactCarouselSection' src/features/marketing src/app

Repository: Smartlify07/Modern-Advocates

Length of output: 8116


🌐 Web query:

Next.js 16.2.6 Image component priority prop deprecated preload documentation

💡 Result:

In Next.js 16 and later, the priority prop on the Image component has been deprecated in favor of the new preload prop [1][2][3]. This change was introduced in version 16.0.0 to improve clarity regarding the behavior of the component [1][4]. While the priority prop is deprecated, it has not been removed and will continue to function, though it will trigger a warning in development [5]. Key details regarding this update: - Use preload instead of priority: For images that serve as the Largest Contentful Paint (LCP) element or are otherwise critical to above-the-fold content, you should now use the preload prop [5][3]. - Code Transition: // Deprecated in Next.js 16 Hero // Recommended for Next.js 16+ Hero - Alternatives for specific use cases: If you have images that may be the LCP element depending on the viewport (e.g., different hero images for mobile vs. desktop), documentation suggests using loading="eager" or fetchPriority="high" instead of preloading every possible image [5][3]. Official documentation for the Image component provides further details on these props and best practices for image optimization [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

component = Path("src/features/marketing/components/about-impact-carousel-section.tsx").read_text()
photos = re.findall(r'src:\s*"([^"]+)"', component)
map_block = component[component.index("{impactPhotos.map"):]

print(f"impact photo count: {len(photos)}")
print(f"priority props in mapped block: {len(re.findall(r'\\bpriority\\b', map_block))}")
print(f"preload props in mapped block: {len(re.findall(r'\\bpreload\\b', map_block))}")
print(f"loading props in mapped block: {len(re.findall(r'\\bloading\\s*=', map_block))}")

for src in photos:
    path = Path("public") / src.lstrip("/")
    print(f"{src}: {path.stat().st_size if path.exists() else 'missing'} bytes")

page = Path("src/app/(marketing)/about/page.tsx").read_text()
print("impact section follows hero:", page.index("AboutHeroSection") < page.index("AboutImpactCarouselSection"))
PY

Repository: Smartlify07/Modern-Advocates

Length of output: 444


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

component = Path("src/features/marketing/components/about-impact-carousel-section.tsx").read_text()
photos = re.findall(r'src:\s*"([^"]+)"', component)
map_block = component[component.index("{impactPhotos.map"):]

priority_count = len(re.findall(r"\bpriority\b", map_block))
preload_count = len(re.findall(r"\bpreload\b", map_block))
loading_count = len(re.findall(r"\bloading\s*=", map_block))

print(f"impact photo count: {len(photos)}")
print(f"priority props in mapped block: {priority_count}")
print(f"preload props in mapped block: {preload_count}")
print(f"loading props in mapped block: {loading_count}")

for src in photos:
    path = Path("public") / src.lstrip("/")
    print(f"{src}: {path.stat().st_size if path.exists() else 'missing'} bytes")

page = Path("src/app/(marketing)/about/page.tsx").read_text()
print("impact section follows hero:", page.index("AboutHeroSection") < page.index("AboutImpactCarouselSection"))
PY

Repository: Smartlify07/Modern-Advocates

Length of output: 644


Do not preload the impact photos.

All seven mapped images use priority, preloading about 2.9 MB of below-hero content. Remove priority from these images and use lazy loading.

🤖 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 `@src/features/marketing/components/about-impact-carousel-section.tsx` around
lines 107 - 108, Update the mapped impact images in the about impact carousel to
remove the priority prop and explicitly use lazy loading, ensuring all seven
below-hero photos are not preloaded.

return (
<section id="contact" className="bg-white text-ma-text">
<div className="marketing-container grid items-start gap-14 lg:grid-cols-[444px_1fr] lg:gap-[88px]">
<div className="marketing-container grid items-start gap-14 lg:grid-cols-[562px_1fr] lg:gap-[88px]">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep the 562px column out of the lg breakpoint.

At a 1024px viewport, marketing-container leaves 824px of content width after lg:px-25. The 562px column and 88px gap leave only 174px for the form. The form’s responsive two-column fields will be squeezed or overflow at these widths.

Keep the narrower column at lg and apply the 562px layout at xl, or stack the form until enough width is available.

Suggested breakpoint adjustment
-      <div className="marketing-container grid items-start gap-14 lg:grid-cols-[562px_1fr] lg:gap-[88px]">
+      <div className="marketing-container grid items-start gap-14 lg:grid-cols-[444px_minmax(0,1fr)] xl:grid-cols-[562px_minmax(0,1fr)] lg:gap-[88px]">
📝 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.

Suggested change
<div className="marketing-container grid items-start gap-14 lg:grid-cols-[562px_1fr] lg:gap-[88px]">
<div className="marketing-container grid items-start gap-14 lg:grid-cols-[444px_minmax(0,1fr)] xl:grid-cols-[562px_minmax(0,1fr)] lg:gap-[88px]">
🤖 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 `@src/features/marketing/components/contact-hero-section.tsx` at line 49,
Update the grid breakpoint on the marketing-container in the contact hero
section so the 562px column and 88px gap are applied only at xl, not lg;
preserve the narrower responsive layout at lg so the form fields do not become
squeezed or overflow.

return (
<aside className="w-full rounded-2xl border border-ma-border-light bg-white px-4 pt-4 pr-3.5 pb-[25px] lg:top-8">
<h2 className="leading/[100%]-normal text-xl font-extrabold text-ma-text sm:text-2xl">
<h2 className="marketing-header leading/[100%]-normal text-xl font-extrabold text-ma-text sm:text-2xl">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

! rg -n 'leading/\[100%\]-normal' src/features/marketing/components/course-information-card.tsx
npm run lint
npx tsc --noEmit

Repository: Smartlify07/Modern-Advocates

Length of output: 22229


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- package metadata ---'
rg -n '"(tailwindcss|`@tailwindcss/postcss`|next)"|tailwind' package.json package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null || true

printf '%s\n' '--- target component ---'
sed -n '52,72p' src/features/marketing/components/course-information-card.tsx

printf '%s\n' '--- related class definitions and usage ---'
rg -n 'marketing-header|leading/\[100%\]-normal|leading-\[100%\]|leading-normal' . \
  -g '!node_modules' -g '!dist' -g '!build' -g '!*lock*' || true

printf '%s\n' '--- token-shape probe ---'
python3 - <<'PY'
from pathlib import Path
import re

path = Path("src/features/marketing/components/course-information-card.tsx")
text = path.read_text()
classes = re.findall(r'className="([^"]*)"', text)
for value in classes:
    for token in value.split():
        if "leading" in token:
            print(f"{token!r}: {'valid named leading utility' if token == 'leading-normal' else 'valid arbitrary leading utility' if re.fullmatch(r'leading-\\[[^]]+\\]', token) else 'not a Tailwind leading utility token'}")
PY

Repository: Smartlify07/Modern-Advocates

Length of output: 25632


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- marketing-header definition ---'
sed -n '184,202p' src/app/globals.css

printf '%s\n' '--- exact class-token classification ---'
python3 - <<'PY'
from pathlib import Path
import re

line = next(
    line for line in Path("src/features/marketing/components/course-information-card.tsx").read_text().splitlines()
    if "Course information" in line
)
class_value = re.search(r'className="([^"]*)"', line).group(1)
for token in class_value.split():
    if token.startswith("leading"):
        if token == "leading-normal":
            result = "valid named leading utility"
        elif re.fullmatch(r"leading-\[[^]]+\]", token):
            result = "valid arbitrary leading utility"
        else:
            result = "not a valid leading utility token"
        print(f"{token!r}: {result}")
PY

printf '%s\n' '--- nearby valid patterns ---'
sed -n '48,70p' src/features/marketing/components/course-detail-content-section.tsx

Repository: Smartlify07/Modern-Advocates

Length of output: 826


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

line = next(
    line for line in Path("src/features/marketing/components/course-information-card.tsx").read_text().splitlines()
    if '<h2 className=' in line
)
match = re.search(r'className="([^"]*)"', line)
if not match:
    raise SystemExit(f"Could not parse className from: {line}")
for token in match.group(1).split():
    if token.startswith("leading"):
        if token == "leading-normal":
            result = "valid named leading utility"
        elif re.fullmatch(r"leading-\[[^]]+\]", token):
            result = "valid arbitrary leading utility"
        else:
            result = "not a valid leading utility token"
        print(f"{token!r}: {result}")
PY

Repository: Smartlify07/Modern-Advocates

Length of output: 225


Replace the malformed Tailwind utility.

leading/[100%]-normal is not a valid Tailwind CSS v4 utility. Replace it with leading-normal or leading-[100%].

🤖 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 `@src/features/marketing/components/course-information-card.tsx` at line 64,
Update the h2 className in the course information card to replace the malformed
leading/[100%]-normal utility with the intended valid Tailwind utility, using
either leading-normal or leading-[100%] while preserving the other classes.

Source: Coding guidelines

Comment on lines +59 to 78
const [activeIndex, setActiveIndex] = useState(0)
const trackRef = useRef<HTMLDivElement>(null)
const isTransitioningRef = useRef(false)

const slideVariants = {
enter: (dir: number) => ({ x: dir * 48 }),
center: { x: 0 },
exit: (dir: number) => ({ x: dir * -48 }),
function transitionTo(nextIndex: number) {
const track = trackRef.current
if (!track || nextIndex === activeIndex || isTransitioningRef.current)
return

isTransitioningRef.current = true
gsap.to(track, {
x: -nextIndex * track.clientWidth,
duration: 0.45,
ease: "power2.inOut",
onComplete: () => {
setActiveIndex(nextIndex)
isTransitioningRef.current = false
},
})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Carousel state is not resynchronized when the viewport changes. Both carousels derive layout state from viewport-dependent values and never recompute that state after a resize or an orientation change, so each carousel can render a partial or misaligned slide.

  • src/features/marketing/components/stories-from-our-community-section.tsx#L59-L78: recompute the GSAP x offset from the current track.clientWidth on resize, and kill the tween on unmount.
  • src/features/marketing/components/testimonials.tsx#L51-L52: clamp pageIndex to the recomputed maxIndex when visibleCount changes from 1 to 3.
📍 Affects 2 files
  • src/features/marketing/components/stories-from-our-community-section.tsx#L59-L78 (this comment)
  • src/features/marketing/components/testimonials.tsx#L51-L52
🤖 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 `@src/features/marketing/components/stories-from-our-community-section.tsx`
around lines 59 - 78, In
src/features/marketing/components/stories-from-our-community-section.tsx lines
59-78, update the carousel effect around transitionTo and trackRef to recompute
the GSAP x offset from the current track.clientWidth whenever the viewport
resizes or orientation changes, and kill any active tween during unmount
cleanup. In src/features/marketing/components/testimonials.tsx lines 51-52,
clamp pageIndex to the recomputed maxIndex whenever visibleCount changes,
including the transition from one to three visible items.

Comment on lines +104 to +158
<div ref={trackRef} className="flex w-full will-change-transform">
{stories.map((story) => (
<div
key={story.name}
className="grid w-full min-w-full shrink-0 gap-8 lg:grid-cols-[minmax(0,600px)_minmax(0,605px)] lg:items-start lg:gap-[50px]"
>
<Image
src={activeStory.image}
alt={activeStory.imageAlt}
fill
sizes="(min-width: 1024px) 600px, calc(100vw - 32px)"
className={cn("object-cover", activeStory.imageClassName)}
/>
</motion.div>
</AnimatePresence>
<div className="relative min-h-[340px] overflow-hidden rounded-3xl bg-ma-bg sm:min-h-[460px] lg:h-[560px]">
<Image
src={story.image}
alt={story.imageAlt}
fill
sizes="(min-width: 1024px) 600px, calc(100vw - 32px)"
className={cn("object-cover", story.imageClassName)}
/>
</div>

<div className="flex min-h-[420px] flex-col justify-between lg:min-h-[560px]">
<AnimatePresence mode="wait" custom={direction} initial={false}>
<motion.div
key={activeIndex}
custom={direction}
variants={slideVariants}
initial="enter"
animate="center"
exit="exit"
transition={{ duration: 0.3, ease: "easeInOut" }}
className="flex flex-col justify-between"
>
<p className="text-lg font-medium text-primary sm:text-3xl">
{activeStory.quote}
<div className="flex min-h-[420px] flex-col justify-between lg:min-h-[560px]">
<p className="text-xl font-medium text-primary sm:text-3xl">
{story.quote}
</p>

<div className="mt-4 space-y-3 sm:mt-8 sm:space-y-4">
<h3 className="text-base font-semibold text-primary sm:text-2xl">
{activeStory.name}
</h3>
<p className="text-base text-ma-muted-text sm:text-xl">
{activeStory.role}
</p>
<div className="flex items-center justify-between">
<div className="space-y-3 sm:space-y-4">
<h3 className="marketing-header text-base font-semibold text-primary sm:text-2xl">
{story.name}
</h3>
<p className="text-base text-ma-muted-text sm:text-xl">
{story.role}
</p>
</div>
<div className="flex justify-center gap-[18px]">
<button
type="button"
aria-label="Previous community story"
onClick={showPreviousStory}
disabled={activeIndex === 0}
className="flex size-[50px] items-center justify-center rounded-full border border-border text-primary transition-colors hover:border-ma-text/20 hover:bg-white disabled:cursor-not-allowed disabled:bg-ma-bg disabled:text-[#D9D9D9] disabled:hover:border-border disabled:hover:bg-ma-bg sm:size-[60px]"
>
<ArrowLeft className="size-6" aria-hidden="true" />
</button>
<button
type="button"
aria-label="Next community story"
onClick={showNextStory}
disabled={activeIndex === stories.length - 1}
className="flex size-[50px] items-center justify-center rounded-full border border-border text-primary transition-colors hover:border-ma-text/20 hover:bg-white disabled:cursor-not-allowed disabled:bg-ma-bg disabled:text-[#D9D9D9] disabled:hover:border-border disabled:hover:bg-ma-bg sm:size-[60px]"
>
<ArrowRight className="size-6" aria-hidden="true" />
</button>
</div>
</div>
</motion.div>
</AnimatePresence>

<div className="mt-8 flex justify-center gap-[18px] sm:justify-end">
<button
type="button"
aria-label="Previous community story"
onClick={showPreviousStory}
className="flex size-[50px] items-center justify-center rounded-full border border-border bg-ma-bg text-primary transition-colors hover:border-ma-text/20 hover:bg-white sm:size-[60px]"
>
<ArrowLeft className="size-6" aria-hidden="true" />
</button>
<button
type="button"
aria-label="Next community story"
onClick={showNextStory}
className="flex size-[50px] items-center justify-center rounded-full border border-border bg-ma-bg text-primary transition-colors hover:border-ma-text/20 hover:bg-white sm:size-[60px]"
>
<ArrowRight className="size-6" aria-hidden="true" />
</button>
</div>
</div>
</div>
</article>
))}
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remove the duplicate navigation buttons from off-screen slides.

The track renders every story, and each slide contains its own previous and next buttons. The container uses overflow-hidden, not display: none. The off-screen buttons stay in the accessibility tree and stay focusable. Keyboard users tab into controls they cannot see, and screen readers announce Previous community story and Next community story once per story.

Render the navigation controls once outside the track, or mark non-active slides with inert.

🔧 Minimal mitigation if the controls stay inside each slide
               <div
                 key={story.name}
+                inert={story.name !== stories[activeIndex]?.name ? true : undefined}
                 className="grid w-full min-w-full shrink-0 gap-8 lg:grid-cols-[minmax(0,600px)_minmax(0,605px)] lg:items-start lg:gap-[50px]"
               >
📝 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.

Suggested change
<div ref={trackRef} className="flex w-full will-change-transform">
{stories.map((story) => (
<div
key={story.name}
className="grid w-full min-w-full shrink-0 gap-8 lg:grid-cols-[minmax(0,600px)_minmax(0,605px)] lg:items-start lg:gap-[50px]"
>
<Image
src={activeStory.image}
alt={activeStory.imageAlt}
fill
sizes="(min-width: 1024px) 600px, calc(100vw - 32px)"
className={cn("object-cover", activeStory.imageClassName)}
/>
</motion.div>
</AnimatePresence>
<div className="relative min-h-[340px] overflow-hidden rounded-3xl bg-ma-bg sm:min-h-[460px] lg:h-[560px]">
<Image
src={story.image}
alt={story.imageAlt}
fill
sizes="(min-width: 1024px) 600px, calc(100vw - 32px)"
className={cn("object-cover", story.imageClassName)}
/>
</div>
<div className="flex min-h-[420px] flex-col justify-between lg:min-h-[560px]">
<AnimatePresence mode="wait" custom={direction} initial={false}>
<motion.div
key={activeIndex}
custom={direction}
variants={slideVariants}
initial="enter"
animate="center"
exit="exit"
transition={{ duration: 0.3, ease: "easeInOut" }}
className="flex flex-col justify-between"
>
<p className="text-lg font-medium text-primary sm:text-3xl">
{activeStory.quote}
<div className="flex min-h-[420px] flex-col justify-between lg:min-h-[560px]">
<p className="text-xl font-medium text-primary sm:text-3xl">
{story.quote}
</p>
<div className="mt-4 space-y-3 sm:mt-8 sm:space-y-4">
<h3 className="text-base font-semibold text-primary sm:text-2xl">
{activeStory.name}
</h3>
<p className="text-base text-ma-muted-text sm:text-xl">
{activeStory.role}
</p>
<div className="flex items-center justify-between">
<div className="space-y-3 sm:space-y-4">
<h3 className="marketing-header text-base font-semibold text-primary sm:text-2xl">
{story.name}
</h3>
<p className="text-base text-ma-muted-text sm:text-xl">
{story.role}
</p>
</div>
<div className="flex justify-center gap-[18px]">
<button
type="button"
aria-label="Previous community story"
onClick={showPreviousStory}
disabled={activeIndex === 0}
className="flex size-[50px] items-center justify-center rounded-full border border-border text-primary transition-colors hover:border-ma-text/20 hover:bg-white disabled:cursor-not-allowed disabled:bg-ma-bg disabled:text-[#D9D9D9] disabled:hover:border-border disabled:hover:bg-ma-bg sm:size-[60px]"
>
<ArrowLeft className="size-6" aria-hidden="true" />
</button>
<button
type="button"
aria-label="Next community story"
onClick={showNextStory}
disabled={activeIndex === stories.length - 1}
className="flex size-[50px] items-center justify-center rounded-full border border-border text-primary transition-colors hover:border-ma-text/20 hover:bg-white disabled:cursor-not-allowed disabled:bg-ma-bg disabled:text-[#D9D9D9] disabled:hover:border-border disabled:hover:bg-ma-bg sm:size-[60px]"
>
<ArrowRight className="size-6" aria-hidden="true" />
</button>
</div>
</div>
</motion.div>
</AnimatePresence>
<div className="mt-8 flex justify-center gap-[18px] sm:justify-end">
<button
type="button"
aria-label="Previous community story"
onClick={showPreviousStory}
className="flex size-[50px] items-center justify-center rounded-full border border-border bg-ma-bg text-primary transition-colors hover:border-ma-text/20 hover:bg-white sm:size-[60px]"
>
<ArrowLeft className="size-6" aria-hidden="true" />
</button>
<button
type="button"
aria-label="Next community story"
onClick={showNextStory}
className="flex size-[50px] items-center justify-center rounded-full border border-border bg-ma-bg text-primary transition-colors hover:border-ma-text/20 hover:bg-white sm:size-[60px]"
>
<ArrowRight className="size-6" aria-hidden="true" />
</button>
</div>
</div>
</div>
</article>
))}
</div>
<div ref={trackRef} className="flex w-full will-change-transform">
{stories.map((story) => (
<div
key={story.name}
inert={story.name !== stories[activeIndex]?.name ? true : undefined}
className="grid w-full min-w-full shrink-0 gap-8 lg:grid-cols-[minmax(0,600px)_minmax(0,605px)] lg:items-start lg:gap-[50px]"
>
<div className="relative min-h-[340px] overflow-hidden rounded-3xl bg-ma-bg sm:min-h-[460px] lg:h-[560px]">
<Image
src={story.image}
alt={story.imageAlt}
fill
sizes="(min-width: 1024px) 600px, calc(100vw - 32px)"
className={cn("object-cover", story.imageClassName)}
/>
</div>
<div className="flex min-h-[420px] flex-col justify-between lg:min-h-[560px]">
<p className="text-xl font-medium text-primary sm:text-3xl">
{story.quote}
</p>
<div className="flex items-center justify-between">
<div className="space-y-3 sm:space-y-4">
<h3 className="marketing-header text-base font-semibold text-primary sm:text-2xl">
{story.name}
</h3>
<p className="text-base text-ma-muted-text sm:text-xl">
{story.role}
</p>
</div>
<div className="flex justify-center gap-[18px]">
<button
type="button"
aria-label="Previous community story"
onClick={showPreviousStory}
disabled={activeIndex === 0}
className="flex size-[50px] items-center justify-center rounded-full border border-border text-primary transition-colors hover:border-ma-text/20 hover:bg-white disabled:cursor-not-allowed disabled:bg-ma-bg disabled:text-[`#D9D9D9`] disabled:hover:border-border disabled:hover:bg-ma-bg sm:size-[60px]"
>
<ArrowLeft className="size-6" aria-hidden="true" />
</button>
<button
type="button"
aria-label="Next community story"
onClick={showNextStory}
disabled={activeIndex === stories.length - 1}
className="flex size-[50px] items-center justify-center rounded-full border border-border text-primary transition-colors hover:border-ma-text/20 hover:bg-white disabled:cursor-not-allowed disabled:bg-ma-bg disabled:text-[`#D9D9D9`] disabled:hover:border-border disabled:hover:bg-ma-bg sm:size-[60px]"
>
<ArrowRight className="size-6" aria-hidden="true" />
</button>
</div>
</div>
</div>
</div>
))}
</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 `@src/features/marketing/components/stories-from-our-community-section.tsx`
around lines 104 - 158, Update the stories map in the track so navigation
controls are rendered only once outside the per-story slides, or ensure every
inactive slide is marked inert and non-focusable. Preserve the existing
showPreviousStory/showNextStory behavior and active-slide controls while
preventing off-screen buttons from entering the accessibility tree or being
announced repeatedly.

Comment on lines +51 to +52
const visibleCount = isMobile ? 1 : 3
const maxIndex = Math.max(0, reviews.length - visibleCount)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clamp pageIndex when visibleCount changes.

maxIndex depends on visibleCount, and visibleCount depends on isMobile. If the viewport changes from mobile to desktop, visibleCount becomes 3 and maxIndex drops by 2. A stored pageIndex above the new maxIndex is not corrected, so reviews.slice(pageIndex, pageIndex + visibleCount) returns fewer than three cards and the row renders partially empty.

Clamp the value that feeds the slice.

🐛 Proposed fix
   const visibleCount = isMobile ? 1 : 3
   const maxIndex = Math.max(0, reviews.length - visibleCount)
+  const safePageIndex = Math.min(pageIndex, maxIndex)
-  const visibleReviews = reviews.slice(pageIndex, pageIndex + visibleCount)
+  const visibleReviews = reviews.slice(
+    safePageIndex,
+    safePageIndex + visibleCount
+  )

Also applies to: 74-74

🤖 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 `@src/features/marketing/components/testimonials.tsx` around lines 51 - 52,
Clamp pageIndex to maxIndex whenever visibleCount or maxIndex changes, and use
the clamped value as the starting index for reviews.slice. Preserve existing
pagination behavior while ensuring viewport changes cannot leave pageIndex
beyond the new maximum.

Comment on lines +143 to +153
<div className="relative z-10 h-[166px] w-[300px] text-white">
<p className="font-heading text-[100px] leading-[60px] font-extrabold">
&ldquo;
</p>
<p className="mt-[-14px] text-[18px] leading-normal font-semibold">
ModernAdvocates helped me see a clear path forward. The
guidance and training resources gave
</p>
<p className="mt-5 text-[18px] leading-normal font-semibold">
-{review.name}
</p>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect the reviews data shape used by the testimonials component.
fd -t f 'testimonials.tsx' | xargs rg -n -A 8 'const reviews'

Repository: Smartlify07/Modern-Advocates

Length of output: 369


🏁 Script executed:

#!/bin/bash
set -eu
file="$(fd -t f 'testimonials.tsx' | head -n 1)"
printf '%s\n' "FILE: $file"
cat -n "$file" | sed -n '1,230p'
printf '\nREVIEWS REFERENCES:\n'
rg -n -C 3 '\breviews\b|review\.' "$file"

Repository: Smartlify07/Modern-Advocates

Length of output: 8557


🏁 Script executed:

#!/bin/bash
set -eu
file="src/features/marketing/components/testimonials.tsx"
python3 - <<'PY'
from pathlib import Path
import re

text = Path("src/features/marketing/components/testimonials.tsx").read_text()
entries = re.findall(r'\{\s*image:\s*"([^"]+)",\s*name:\s*"([^"]+)",\s*\}', text)
quote_literals = re.findall(r'<p className="mt-\[-14px\][^>]*>\s*(.*?)\s*</p>', text, re.S)
print(f"review_entries={len(entries)}")
print(f"review_names={[name for _, name in entries]}")
print(f"review_images={[image for image, _ in entries]}")
print(f"quote_fields_present={bool(re.search(r'\\bquote\\s*:', text))}')
print(f"hardcoded_quote_render_count={len(quote_literals)}")
print("rendered_quote_text=" + " ".join(q.strip() for q in quote_literals))
PY
printf '\nOTHER TESTIMONIAL DATA REFERENCES:\n'
rg -n -i 'ModernAdvocates helped me see a clear path forward|testimonial-[1-4]|quote' src public README.md 2>/dev/null || true

Repository: Smartlify07/Modern-Advocates

Length of output: 394


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
import re

text = Path("src/features/marketing/components/testimonials.tsx").read_text()
entries = re.findall(r'\{\s*image:\s*"([^"]+)",\s*name:\s*"([^"]+)",\s*\}', text)
quote_literals = re.findall(r'<p className="mt-\[-14px\][^>]*>\s*(.*?)\s*</p>', text, re.S)
quote_field_pattern = re.compile(r'\bquote\s*:')
print(f"review_entries={len(entries)}")
print(f"review_names={[name for _, name in entries]}")
print(f"review_images={[image for image, _ in entries]}")
print(f"quote_fields_present={bool(quote_field_pattern.search(text))}")
print(f"hardcoded_quote_render_count={len(quote_literals)}")
print("rendered_quote_text=" + " ".join(q.strip() for q in quote_literals))
PY
printf '\nOTHER TESTIMONIAL DATA REFERENCES:\n'
rg -n -i 'ModernAdvocates helped me see a clear path forward|testimonial-[1-4]|quote' src public README.md 2>/dev/null || true

Repository: Smartlify07/Modern-Advocates

Length of output: 2729


Bind each testimonial to its review data.

The reviews entries contain no quote field, and all four cards render the same hardcoded quote. Add and render the intended quote for each review. All entries also use "Michael R."; provide distinct names when applicable.

🤖 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 `@src/features/marketing/components/testimonials.tsx` around lines 143 - 153,
Update the testimonial data and rendering in the testimonials component so each
review entry includes its intended quote and renders that entry’s quote instead
of shared hardcoded text. Replace duplicated “Michael R.” values with the
distinct reviewer names associated with each testimonial, while preserving the
existing card layout and styling.

</div>
) : videoPending ? (
<Skeleton className="aspect-video w-full rounded-xl" />
<Skeleton className="aspect-video w-full" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve square corners on the video loading skeleton.

Skeleton adds rounded-md by default in src/shared/ui/skeleton.tsx. Removing the previous local rounded class therefore leaves rounded corners. Add rounded-none here to match the square unavailable-video placeholders and the loading skeleton in src/features/user-dashboard/components/course-player-shell.tsx:129.

Proposed fix
-          <Skeleton className="aspect-video w-full" />
+          <Skeleton className="aspect-video w-full rounded-none" />
📝 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.

Suggested change
<Skeleton className="aspect-video w-full" />
<Skeleton className="aspect-video w-full rounded-none" />
🤖 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 `@src/features/user-dashboard/components/course-player-content.tsx` at line
171, Update the Skeleton in the course player content loading state to include
the rounded-none class, preserving square corners consistently with the
unavailable-video placeholders and the corresponding loading skeleton in
CoursePlayerShell.

@Smartlify07
Smartlify07 merged commit 6806add into main Aug 11, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant