commit 74e52e9dcda4184378ebda6843e2092caa8fe443 Author: Goutam Date: Fri Jul 17 14:51:13 2026 +0530 Initial commit: LynkedUp Fusion site Co-Authored-By: Claude Opus 4.8 (1M context) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a12c35 --- /dev/null +++ b/.gitignore @@ -0,0 +1,51 @@ +# Dependencies +node_modules/ + +# Build output +dist/ +dist-ssr/ + +# Vite cache +.vite/ + +# Logs +logs/ +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Environment variables +.env +.env.* +!.env.example + +# Editor / OS +.DS_Store +.idea/ +.vscode/* +!.vscode/extensions.json +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Package manager lockfiles state +.npm +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* +.vercel +.env* + +# Playwright +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ + +.vercel diff --git a/blog/48-hour-storm-checklist/index.html b/blog/48-hour-storm-checklist/index.html new file mode 100644 index 0000000..24aa526 --- /dev/null +++ b/blog/48-hour-storm-checklist/index.html @@ -0,0 +1,335 @@ + + + + + +The First 48 Hours After Hail: A Field Checklist That Protects the Claim - LynkedUp Pro Blog + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+
+ +
+ Storm response +

The First 48 Hours After Hail: A Field Checklist That Protects the Claim

+ +
+
+ Close up of hail damaged shingles +
+
+
+ +
+
+ +

The storm is the easy part. It shows up, does the damage, and moves on. What happens in the 48 hours after it passes decides whether every claim in its path gets approved on the first pass, or drags into a second visit and a supplemental fight.

+ +

Crews that treat those two days as a documentation sprint — not just a canvassing sprint — close faster and get paid more of what they bid. Here's the order that works.

+ +
+

Hour 0–6: Confirm and Document the Event Itself

+

Before a single roof gets inspected, lock down the storm data you'll need for every claim in the territory: verified hail size, wind speed, and the exact swath the cell tracked over. Adjusters trust radar-verified data far more than a homeowner's memory of "it was big."

+
    +
  • Pull the radar-verified hail swath and impact size for your zone.
  • +
  • Screenshot or export the report with a timestamp — you'll attach this to every claim filed from this event.
  • +
  • Flag every property already in your pipeline that falls inside the swath.
  • +
+
+ +
+

Hour 6–24: Get Eyes on Every Roof in the Path

+

This is the window where speed compounds. The first crew to reach a property with a phone-based scan usually walks away with the signature, because nobody else has shown up with a measured bid yet.

+
+
+ 1.75 in +

Hail size, verified

+

Radar-confirmed impact diameter for the April 22 supercell used in our reference claim.

+ ✕ HAIL VERIFIED +
+
+ 41 +

Impact points, facet F2

+

Individually mapped and photographed — the density that turns "some wear" into an approved claim.

+ ⚠ MODERATE–HIGH +
+
+ 0.6s +

Scan-to-report time

+

From phone capture to a measured, damage-flagged estimate, ready to hand to the homeowner.

+ ✓ ON SITE +
+
+
+ +
+ A claim doesn't get denied because the damage wasn't real. It gets denied because the damage wasn't proven. +
+ +
+

Hour 24–48: Build the Evidence Pack Before the Adjuster Calls

+

By the time an adjuster schedules a visit, you want the packet already assembled: dated photos of every impact, a facet-by-facet damage map, and the shingle class and age on record. Adjusters move fast through files that are already organized in the format carriers expect — and slow through everything else.

+
    +
  • Group photos by facet, not by folder date.
  • +
  • Include a wide shot of each slope alongside the close-up impact photos.
  • +
  • Note shingle class, roof age, and any prior repair history up front.
  • +
  • Timestamp and geo-tag everything automatically — don't rely on manually renaming files at 11pm.
  • +
+
+ +
+

What Adjusters Actually Look For

+

Adjusters aren't looking to deny a legitimate claim — they're looking for a reason to approve it quickly and move to the next file on their desk. Give them a complete, dated, facet-mapped packet and most claims in a verified hail path close without a second roof visit.

+
+ +
+ +
+ ⚠ STORM WATCH ACTIVE +

Get hail alerts for your territory.

+

Radar-verified swaths land in your inbox the morning after the storm, so your crews are the first ones knocking.

+ +
+ +
+ ← All field notes + Next: Why Adjusters Reject 40% of Claims → +
+
+ +
+ + + + + diff --git a/blog/cost-of-paperwork/index.html b/blog/cost-of-paperwork/index.html new file mode 100644 index 0000000..0f9886d --- /dev/null +++ b/blog/cost-of-paperwork/index.html @@ -0,0 +1,336 @@ + + + + + +The Real Cost of Paperwork: Why Contractors Lose Margin Before the Job Even Starts - LynkedUp Pro Blog + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+
+ +
+ Operations +

The Real Cost of Paperwork: Why Contractors Lose Margin Before the Job Even Starts

+ +
+
+ Weathered roof shingles after storm exposure +
+
+
+ +
+
+ +

Ask any owner what their margin is and they'll give you a number from the estimate: 34%, 30%, whatever the spreadsheet says. Ask them what they actually collected on the last ten jobs, and the number is usually lower — sometimes a lot lower. The gap isn't bad pricing. It's paperwork.

+ +

Every manual follow-up, every missing project detail, every proposal re-typed because a price changed, every job photo that never made it off someone's phone — each one is small. Stacked across a season, they're the difference between a good year and a break-even one.

+ +
+

The Hidden Tax on Every Job

+

We pulled the timeline on a real hail claim — same lead, same roof, run two ways: once through a manual file (voicemail, ladder measurement, typed proposal, mailed revisions), and once through a system that captures, measures, and files automatically.

+
+
+ 47 → 15 +

Days to close

+

The manual file sat on a second site visit and a re-typed revision. The automated file never left the queue.

+ ✕ 32 DAYS LOST +
+
+ 12.5 → 0.8 +

Admin hours

+

Nearly a full day of phone tag, retyping, and chasing signatures — on one job.

+ ⚠ PER FILE +
+
+ $4,120 +

Margin at risk

+

What the manual file was on pace to lose before the job even reached production.

+ ✓ HELD, AUTOMATED +
+
+
+ +
+

Where the Margin Actually Leaks

+

It rarely leaks in one dramatic place. It leaks in the same three spots, on almost every job:

+
    +
  • The second site visit. A measurement taken with a tape and a ladder isn't wrong, but it isn't documented well enough to survive a dispute. When the insurer asks for evidence, someone drives back out.
  • +
  • The re-typed proposal. A homeowner disputes one line item, and the whole document gets retyped from scratch because there's no live source of truth — just a Word file on someone's desktop.
  • +
  • The follow-up that never happens. Leads go cold not because reps are lazy, but because there's no system nudging the next action. "I'll call them back" is not a workflow.
  • +
+
+ +
+ The roof doesn't cost you the margin. The gap between the site visit and the signed proposal does. +
+ +
+

Why "We'll Follow Up" Doesn't Scale

+

Every sales team believes it follows up consistently. Almost none of them actually do, because follow-up that lives in someone's memory competes with a truck full of other jobs, a crew that needs scheduling, and a phone that won't stop ringing.

+

The fix isn't a more disciplined team. It's removing the step where a human has to remember anything. A missed call that auto-triggers a text. A scan that becomes a proposal without anyone opening a word processor. A signed job that files its own claim paperwork. None of that requires better people — it requires a system that does the remembering.

+
+ +
+

What Actually Fixes It

+

You don't need to overhaul how your crews work. You need to close the three leaks above:

+
    +
  1. Capture the scan and the measurement in one visit, on the phone that's already in the truck.
  2. +
  3. Generate the proposal from that same data, so a disputed line item is an edit, not a re-type.
  4. +
  5. Let the system trigger the next follow-up automatically, on a schedule a human doesn't have to hold in their head.
  6. +
+

None of it is exotic. It's just the difference between a job file that works for you and one you have to work for.

+
+ +
+ +
+ ✓ SEE IT ON YOUR OWN NUMBERS +

Run the same comparison on your last job.

+

Book a 30-minute walkthrough and we'll build the before/after timeline using one of your own recent files.

+ +
+ +
+ ← All field notes + Next: The First 48 Hours After Hail → +
+
+ +
+ + + + + diff --git a/blog/index.html b/blog/index.html new file mode 100644 index 0000000..1b08f55 --- /dev/null +++ b/blog/index.html @@ -0,0 +1,284 @@ + + + + + +Blog - LynkedUp Pro | Field notes for contractors + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+
+ + +
+ Field notes +

Straight talk for people who build things.

+

Notes from the field on estimating, storm claims, crew ops, and the paperwork that quietly eats your margin. No fluff — just what we've seen work on real jobs.

+
+
+
+ + +
+ +
+ +
+ + + + + diff --git a/blog/why-claims-get-denied/index.html b/blog/why-claims-get-denied/index.html new file mode 100644 index 0000000..8d8b4dd --- /dev/null +++ b/blog/why-claims-get-denied/index.html @@ -0,0 +1,334 @@ + + + + + +Why Adjusters Reject 40% of Roof Claims (And How to Fix It) - LynkedUp Pro Blog + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+
+ +
+ Claims +

Why Adjusters Reject 40% of Roof Claims (And How to Fix It)

+ +
+
+ Damaged roof membrane surface +
+
+
+ +
+
+ +

"Lack of maintenance" shows up on more denial letters than any other reason in the industry. It's frustrating for homeowners and worse for the contractor who now has to either re-fight the claim or eat the cost of a job that was never in question.

+ +

Here's the part that should change how you run every job going forward: it's almost never actually about maintenance. It's about proof.

+ +
+

The #1 Denial Reason: "Lack of Maintenance"

+

When an insurer can't find a record of regular inspection, "lack of maintenance" becomes the default explanation for wear they'd otherwise attribute to a storm. It's not that the roof wasn't maintained — it's that nobody can show that it was.

+
+
+ 40% +

Insurance denials

+

Of claims are denied for "lack of maintenance" when proof of regular inspection is missing from the file.

+ ✕ HIGH RISK +
+
+ 5 yrs +

Life reduced

+

A minor leak left undocumented for six months can shorten a roof's serviceable life by up to five years.

+ ⚠ CAUTION +
+
+ 15 min +

To build the record

+

The time it actually takes to capture a dated, facet-mapped inspection that removes the ambiguity entirely.

+ ✓ VERIFIED +
+
+
+ +
+

What a Denial Actually Costs You

+

A denied claim rarely just disappears. It turns into a second inspection, an appeal letter, weeks of delay while the homeowner grows frustrated with everyone involved, and in some cases a job that gets pulled entirely once trust in the process erodes. None of that shows up on the original bid, but all of it hits your calendar and your reputation.

+
+ +
+ Insurers don't deny roofs. They deny files that can't prove what happened to them. +
+ +
+

The Fix: Photograph Everything, Timestamp Everything

+

The single biggest lever here isn't a better argument with the adjuster after the fact — it's a better file before the claim is even filed. A dated, geo-tagged, facet-by-facet inspection record does more to prevent a denial than any appeal letter written after one.

+
    +
  • Capture a full inspection at first contact, not just at claim time.
  • +
  • Store records in a format that survives a change of ownership or a change of contractor.
  • +
  • Make the record easy to hand over — carriers move faster through formats they already recognize.
  • +
+
+ +
+

A Quick Pre-Claim Checklist

+
    +
  1. Full-roof scan with facet-level measurements, not just a visual walkthrough.
  2. +
  3. Photos of every existing wear pattern, dated before the storm event if possible.
  4. +
  5. Shingle class, install date, and any prior repair history on file.
  6. +
  7. One clean export, ready to attach to the claim on day one.
  8. +
+
+ +
+ +
+ ✓ CLAIM-READY BY DEFAULT +

Stop building the file after the denial.

+

See how a phone-based scan builds a claim-ready record on day one, before there's ever a dispute to fight.

+ +
+ +
+ ← All field notes + Next: The Real Cost of Paperwork → +
+
+ +
+ + + + + diff --git a/faq.html b/faq.html new file mode 100644 index 0000000..4e52b85 --- /dev/null +++ b/faq.html @@ -0,0 +1,643 @@ + + + + + +FAQ - LynkedUp Pro | The construction CRM that does the paperwork + + + + + + + + + + + + + + + + + +
+ +
+ + + + + +
+ + +
+
+ + +
+ Frequently asked questions +

Answers, not guesswork.

+

Straight answers about the platform, ProCanvas, Owners Box, AI estimating, and the Founders License — pulled from real conversations with contractors. Claims marked * tie back to our internal & field-testing disclaimer.

+
+ + +
+
+ + +
+
+ + +
+ Overview & platform +

What LynkedUp Pro actually is.

+
+
+
+ What is LynkedUp Pro? +

LynkedUp Pro is an AI-powered contractor CRM and construction operations platform built to help roofing, restoration, and construction companies manage leads, sales, projects, crews, documents, follow-ups, invoices, and job updates from one connected system.

+
+
+ Who is LynkedUp Pro built for? +

Contractors, roofers, restoration companies, field sales teams, project managers, owners, admins, canvassers, subcontractors, and growing construction businesses that need better visibility across sales and production.

+
+
+ Is LynkedUp Pro just a CRM? +

No. LynkedUp Pro includes CRM tools, but it is designed to operate more like a full contractor business command center. It connects sales, project management, canvassing, automation, customer communication, document storage, owner reporting, and upcoming AI-powered estimating features.

+
+
+ What makes LynkedUp Pro different from other contractor CRMs? +

LynkedUp Pro was built from real contractor workflows, not just generic software templates. The platform is designed to help contractors move faster, reduce missed follow-ups, improve job visibility, and keep sales, production, and ownership aligned from first lead to final payment.*

+
+
+ What can I manage inside LynkedUp Pro? +

Leads, contacts, customers, jobs, inspections, estimates, project stages, documents, photos, invoices, payments, follow-ups, team members, subcontractors, and business health reporting, depending on your plan and enabled features.

+
+
+
+
+ + +
+
+ + +
+ Sales, follow-ups & messaging +

Never let a lead go quiet.

+
+
+
+ Does LynkedUp Pro help with follow-ups? +

Yes. LynkedUp Pro is designed to automate follow-ups by text and email for leads, appointments, estimates, active jobs, completed projects, review requests, referrals, and re-engagement campaigns.*

+
+
+ Can LynkedUp Pro send SMS and email messages? +

Yes. LynkedUp Pro supports customer communication through SMS and email workflows. SMS, email, usage, sending numbers, data, and related messaging services may require additional monthly charges depending on volume and configuration.

+
+
+ Does each company need its own phone number for texting? +

Most companies should use a dedicated sending number or approved messaging setup for better deliverability, branding, tracking, and compliance. LynkedUp Pro can support tenant-level communication setup so each company can manage its own customer messaging environment.

+
+
+ Does LynkedUp Pro support automated appointment reminders? +

Yes. LynkedUp Pro can help automate appointment confirmations, prep reminders, on-the-way messages, estimate follow-ups, job updates, and post-project review requests.*

+
+
+ Can LynkedUp Pro help reduce missed follow-ups? +

Yes. LynkedUp Pro is designed to reduce missed follow-ups by automating reminders, customer messages, lead nurture sequences, and estimate follow-up workflows.*

+
+
+ Can LynkedUp Pro help my reps sell more? +

LynkedUp Pro is designed to help reps stay organized, respond faster, follow up consistently, and manage opportunities more clearly, which may help improve sales performance when used correctly.*

+
+
+
+
+ + +
+
+ + +
+ ProCanvas · field canvassing +

Door knocking, mapped and measured.

+
+
+
+ What is ProCanvas? +

ProCanvas is LynkedUp Pro's field canvassing system. It is designed to turn door knocking and field prospecting into a mobile-first, map-based workflow with property intelligence, lead tracking, rep activity, check-ins, leaderboards, and CRM syncing.*

+
+
+ How does ProCanvas help sales reps? +

ProCanvas helps reps see where they are working, log activity, track knocks, set inspections, capture notes, and push qualified opportunities into the CRM. It is designed to make canvassing feel more organized, competitive, and easier to manage.*

+
+
+ Can owners see what the field team is doing? +

Yes. Owners and managers can view activity, pipeline movement, job status, follow-ups, and team performance from inside the platform. This helps leadership see the health of the business without chasing updates across multiple tools.*

+
+
+
+
+ + +
+
+ + +
+ Projects, customer portal & documents +

One job file, every stakeholder.

+
+
+
+ What is the Project Command Center? +

The Project Command Center is the central view for a job. It is designed to show customer details, project status, estimate information, documents, photos, payments, production updates, activity history, and risk indicators in one place.

+
+
+ Does LynkedUp Pro include a customer portal? +

Yes. LynkedUp Pro includes customer portal functionality designed to keep homeowners updated on project status, technician ETA, next steps, approvals, change order requests, messages, and project notifications.

+
+
+ Can customers approve estimates or change orders online? +

Yes. LynkedUp Pro is designed to support customer approvals, estimate reviews, change order requests, status updates, and documented communication from the customer portal.

+
+
+ Can subcontractors upload job photos? +

Yes. LynkedUp Pro is designed to allow subcontractors or field users to upload photos, document completed work, and confirm that a job is ready for the next stage of production or payment review.

+
+
+ Can owners approve subcontractor payment releases? +

Yes. LynkedUp Pro is designed to help owners or authorized managers review job completion evidence before releasing subcontractor payments or commission-related approvals.

+
+
+ Can LynkedUp Pro help project managers run jobs better? +

Yes. LynkedUp Pro is designed to help project managers track project stages, job photos, customer updates, crew activity, documents, schedules, production notes, and next steps in one place.*

+
+
+
+
+ + +
+
+ + +
+ Owners Box, team & roles +

The back office, run from one screen.

+
+
+
+ What is Owners Box? +

Owners Box is LynkedUp Pro's back-office command center. It is designed to help manage HR, onboarding, team roles, vendor documents, compliance alerts, company files, accounting visibility, action alerts, and owner-level business health.

+
+
+ Can LynkedUp Pro help with onboarding new team members? +

Yes. LynkedUp Pro is designed to make onboarding easier by allowing admins to invite new users, collect required information, classify team roles, manage documents, and track onboarding status.*

+
+
+ What roles can be assigned inside LynkedUp Pro? +

Common roles include Owner, Admin, Sales Rep, Project Manager, Site Supervisor, Canvasser, Subcontractor, Sales Manager, Team Lead, and Other. Smaller companies can also assign multiple responsibilities to the same person.

+
+
+ Can LynkedUp Pro help owners understand business health? +

Yes. LynkedUp Pro is designed to give owners better visibility into active jobs, follow-ups, payments, production status, team activity, compliance items, and business alerts.*

+
+
+
+
+ + +
+
+ + +
+ AI estimating +

AI-assisted, human-reviewed.

+
+
+
+ Can LynkedUp Pro help with roofing estimates? +

Yes. LynkedUp Pro includes current and upcoming estimating workflows designed for retail roofing, storm restoration, insurance-style scopes, Good / Better / Best options, proposal generation, and homeowner-friendly explanations.*

+
+
+ Does LynkedUp Pro replace Xactimate? +

LynkedUp Pro is not positioned as a direct replacement for every Xactimate use case. It is designed to help contractors create structured, evidence-supported estimating workflows and proposal outputs. Insurance-grade and courtroom-ready estimating features may depend on enabled modules, data quality, and workflow setup.

+
+
+ Does LynkedUp Pro use AI? +

Yes. LynkedUp Pro uses AI to support automation, task guidance, project insights, proposal assistance, follow-up workflows, and upcoming evidence-based estimating and field capture tools.

+
+
+ Is the AI always accurate? +

No. AI should always be reviewed by a qualified human. LynkedUp Pro is designed to assist contractors, not replace professional judgment, code compliance review, carrier requirements, or final business decisions.

+
+
+
+
+ + +
+
+ + +
+ Reporting, customization & integrations +

Fits the way you already run jobs.

+
+
+
+ Does LynkedUp Pro include reporting? +

Yes. LynkedUp Pro is designed to include reporting and dashboard views for leads, sales activity, project status, follow-ups, production, payments, and owner-level business insights.

+
+
+ Can LynkedUp Pro be customized? +

Yes. LynkedUp Pro is built to be customizable. Contractors can use the platform out of the box or discuss custom workflows, automations, dashboards, forms, integrations, and business-specific modifications.

+
+
+ Can LynkedUp Pro be customized for my company's process? +

Yes. LynkedUp Pro can be configured around your sales process, production stages, team structure, lead sources, automation sequences, customer communication style, and reporting needs.

+
+
+ Does LynkedUp Pro integrate with other tools? +

LynkedUp Pro is designed to support integrations with common business systems such as calendars, communication tools, accounting platforms, payment systems, proposal workflows, and data providers depending on the configuration and development roadmap.

+
+
+ Does LynkedUp Pro support payments and invoices? +

Yes. LynkedUp Pro is designed to support invoice and payment workflows. Payment processor fees, third-party service fees, and advanced payment automations may be billed separately.

+
+
+
+
+ + +
+
+ + +
+ Who it's for & mobile +

From one truck to a whole crew.

+
+
+
+ Is LynkedUp Pro mobile-friendly? +

Yes. LynkedUp Pro is designed for contractors who work from the field, office, truck, jobsite, and home. Key workflows are designed to be mobile-first or mobile-friendly.

+
+
+ Does LynkedUp Pro work for companies with small teams? +

Yes. LynkedUp Pro is designed for both smaller contractors where people wear multiple hats and growing companies that need more structure across sales, production, admin, and ownership.

+
+
+ Does LynkedUp Pro work for larger construction teams? +

Yes. LynkedUp Pro is designed to support teams with multiple reps, project managers, admins, subcontractors, managers, and owners who need role-based visibility and workflow control.

+
+
+ Is LynkedUp Pro only for roofing companies? +

No. Roofing is a major focus, but LynkedUp Pro is designed for construction, restoration, exterior remodeling, field sales, trade contractors, and service-based construction businesses.

+
+
+
+
+ + +
+
+ + +
+ Roadmap & availability +

What's shipped, what's next.

+
+
+
+ What upcoming features are planned? +

Upcoming and advanced features may include enhanced AI estimating, retail proposal tools, insurance-grade estimate support, LiDAR and digital twin workflows, advanced canvassing intelligence, expanded automation, customer portal improvements, owner reporting, and deeper back-office controls.

+
+
+ Are all features available today? +

Not every feature may be available for every user, plan, or stage of rollout. Some features are active, some are in beta, and some are part of the upcoming roadmap. Availability may vary by subscription, license, configuration, and development phase.

+
+
+
+
+ + +
+
+ + +
+ Founders License & getting started +

Reserve access before it's public.

+
+
+
+ What is the Founders License? +

The Founders License is a limited early-access offer for select companies that want to reserve access before standard pricing and public availability. Per-seat Starter and Pro plans are currently on hold while this offer is live. Founders License terms, limits, included features, and usage costs should be reviewed before purchase.

+
+
+ How much does the Founders Lifetime License cost? +

$2,000, one-time. It reserves lifetime access to the core platform under the Founders License terms while per-seat pricing is on hold. SMS, email, data usage, GPU usage, advanced AI features, Campaign-X, LiDAR scans, digital twin technology, custom development, and third-party services may still incur additional monthly or usage-based charges.

+
+
+ Does the Founders License include everything forever? +

No. The Founders License includes access to core platform features based on the license terms. SMS, email, data usage, GPU usage, advanced AI features, Campaign-X, LiDAR scans, digital twin technology, custom development, and third-party services may incur additional monthly or usage-based charges.

+
+
+ Why should I book a demo? +

A demo gives you a direct look at the LynkedUp Pro platform, current features, upcoming roadmap, and how the system can fit your company's sales, production, and owner workflows.

+
+
+ How long is the demo? +

The standard demo is 30 minutes and gives you a walkthrough of the LynkedUp Pro platform and upcoming features. A 1-hour custom buildout meeting is available for companies that want to discuss deeper customization or a business-specific solution.

+
+
+ How do I reserve my spot? +

You can reserve your spot by booking a demo or requesting access before the Founders License offer is gone. Availability may be limited by company, license count, rollout phase, or onboarding capacity.

+
+
+
+
+ + +
+
+ + +
+ About LynkedUp Pro +

Built by contractors, tested in the field.

+
+
+
+ Is LynkedUp Pro a new company? +

Yes. LynkedUp Pro is a new and growing company. Some claims, results, projections, workflows, and performance statements are based on internal testing and field testing with founding users.*

+
+
+ Why are some claims marked with an asterisk? +

Claims marked with an asterisk are based on internal testing, field testing, early workflows, founding-user feedback, projected use cases, or controlled product testing. Results may vary by company, team size, setup, data quality, adoption, market, and usage.

+
+
+ Is LynkedUp Pro built by contractors? +

Yes. LynkedUp Pro was built from real contractor experience and is designed around the daily problems contractors face in sales, production, communication, follow-up, documentation, and owner visibility.

+
+
+
+ Website disclaimer for asterisk claims* +

Claims marked with an asterisk are based on a mix of internal testing, field testing, early product workflows, founding-user feedback, and projected use cases. LynkedUp Pro is a new and growing company curated by a group of construction industry veterans and some of the brightest minds in technology and AI. Results may vary by business size, team adoption, data quality, configuration, market conditions, usage volume, and enabled features. Some user names, company names, or examples may be changed to protect customer privacy and trade secrets.

+
+
+
+ + +
+
+ + +
+ ✓ STILL HAVE QUESTIONS? +

Bring your real questions.

+

Get a 30-minute walkthrough of the platform, or a 1-hour buildout call if your workflow needs custom work.

+ +
+
+
+ +
+ + + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..b7c5019 --- /dev/null +++ b/index.html @@ -0,0 +1,1315 @@ + + + + + +LynkedUp Pro - The construction CRM that does the paperwork + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + +
+ + +
+ + +
+
+ Field-grade software · built for contractors +

The only construction CRM built to free up your team, kill the paperwork, and make you more money.

+

+ LynkedUp Pro connects your sales, projects, estimates, follow-ups, marketing, content + creation, documents, crews, payments, and customer updates in one AI-powered platform + built to keep every job moving and every dollar accounted for. +

+ +
+
0lead to contract
+
0insurance claims
+
0homes verified
+
+
+ + +
+
+ Instant estimate + AI EST v4.2 +
+
+
+ + +
+
+
+ + + +
+ +
+ +

3.1 seconds. Pick an address above to run the live demo.

+
+
+ + + + + + +
+ + + + + +
+
+
0properties scanned
+
0contractors onboarded
+
0estimate accuracy
+
0avg report time
+
+
+ + +
+
+ + +
+ Field proof +

The old way vs the LynkedUp way.

+

One roof, two inspections. Drag the seam and compare the ladder to the drone.

+
+
+
+ Drone-led inspection with tablet-guided AI scan +
+ Our way +
15-minute drone scan
+
Zero safety risk
+
AI + thermal imaging
+
Evidence-driven damage detection
+
~30% lower inspection cost
+
60% faster lead conversion
+
Claim-ready evidence pack
+
Signature-ready proposal, same day
+
+
+
+ Manual ladder inspection +
+ The old way +
Dangerous ladders & falls
+
Guesswork estimates
+
Slow turnaround times
+
Hidden material costs
+
Second visits for missed photos
+
Paper files lost in trucks
+
Claims stall on missing proof
+
+
+
+ +
+ DRAG TO COMPARE +
+
+
+ + +
+
+ + +
+ Hyper-local sales intelligence +

Know your Client before you Knock.

+

Storm paths, roof age, and owner intelligence mapped street by street. Your reps walk into every conversation already briefed.

+
+
+ + + + +
+
+
+ Aerial twilight view of the DFW metro +
+ Active region + DFW Zone 2 +
+
+ +
+ Severe hail watch + Approaching Tarrant County + ETA 15 MIN · WIND 24 MPH +
+
+
+
+ + Schedule conflict + +
+

Heavy rainfall expected in Plano at 2:00 PM. This conflicts with the inspection at Legacy West.

+
+ + +
+
+ + + +
+ +
+
+
+ Total pipeline + $0M + ◆ ACTIVE +
+
+ Active alerts + 0 + ▲ MONITORING +
+
+ Urgent + 0 + ✕ CRITICAL +
+
+ Avg roof age + 0 + ✓ ASSESSED +
+
+

LIVE FEED · 5,847 PROPERTIES TRACKED · REFRESHED 90 SEC AGO

+
+
+ + +
+
+ + +
+ Cost of chaos +

Paperwork is eating your 34% margin before the job is even finished.

+

Every manual follow-up, missing project detail, delayed estimate, lost document, unverified job photo, and incomplete subcontractor onboarding packet slows the job down and chips away at profit. LynkedUp Pro automates the busy work, keeps details from falling through the cracks, and gives your team more time to sell, build, manage, and get paid.

+
+
+ + + + + +
+
+
+
Manual fileCLOSED DAY 47 · 12.5 HRS ADMIN · -$4,120
+
+ +
D1Voicemail lead
+
D4First callback
+
D9Manual measure · 2 hrs
+
D16Proposal typed
+
D24Revision re-sent
+
D31Evidence missing
+
D47Approved · crew slipped
+
+
+ +
+
+ + TIME RECOVERED · 32 DAYS +
D1Lead captured · 6 min
+
Scan · 47 sec
+
D2E-signed 9:41 PM
+
D5Claim filed
+
D15Approved · closed
+
+
LynkedUp fileCLOSED DAY 15 · 0.8 HRS ADMIN · MARGIN HELD
+
+
+
+
First contact
DAY 046 MIN
+
Measurement
2 VISITS< 10 MIN
+
Claim approved
DAY 47DAY 15
+
Gross margin
$1,028$4,120
+
+
+
+ + +
+
+ + +
+ Built on native AI +

Not bolted-on ChatGPT. Purpose-built AI.

+

Five tools, one job file, no mockups. The reels below are captured from the shipping product; pick a figure to play its chapter.

+
+
+
+
+
+ +
+

AI Estimation

Address in, measured bid out. Line items priced from the mesh.

+
+
+ +
+

Storm Intelligence

Hail swaths land on the map; every roof under them gets scored.

+
+
+ +
+

Owners Box

Profit, crew, and cash flow per job, on one screen.

+
+
+ +
+

Smart Automation

Crews routed and rescheduled around the weather, automatically.

+
+
+ +
+

Pro Canvas

Your progress, gamified: see what's unlocked and exactly what to close next.

+
+
+
+ +
+ + LIVE CAPTURE · REC 01 / 04 +
+
+
+ +
+
+
+ AI Estimation demo preview + + FIG 01 · PLAN VIEW +
+
+

AI Estimation

+

Instant material lists and pricing for every job. Type an address, get a bid.

+ Play reel 01 +
+
+
+
+ Mobile Capture demo preview + + FIG 02 · MOBILE CAPTURE +
+
+

Mobile Capture

+

Property scans from your phone or drone.

+ Play reel 02 +
+
+
+
+ Owners Box demo preview + + FIG 03 · OWNERS LEDGER +
+
+

Owners Box

+

Profits, crew, and cash flow on one screen. Know where every job stands.

+ Play reel 03 +
+
+
+
+ Smart Automation demo preview + + FIG 04 · DISPATCH LOOP +
+
+

Smart Automation

+

Follow-ups and reminders that never miss a lead, drafted while you sleep.

+ Play reel 04 +
+
+
+
+ Works with + QuickBooksXeroCompanyCamEagleViewGoogle CalendarStripe +
+
+
+ + +
+
+ + +
+ What it does for your business +

More booked. Paid faster. Less time wasted.

+

Built for the three outcomes every contractor wants: more appointments, faster cash flow, and less admin drag.

+
+
+
+
+ +

Get more homeowner appointments

+
+
    +
  • Storm maps surface the homes ready to buy
  • +
  • Pitch scripts written per address, not per template
  • +
  • Live rep tracking books 34% more jobs
  • +
+
0lead accuracy
+
+
+
+ +

Win jobs with 3D proof

+
+
    +
  • Homeowners see their exact house, not a brochure
  • +
  • Mesh-measured numbers end pricing arguments
  • +
  • Claims approved 3x faster with evidence packs
  • +
+
0properties inspected
+
+
+
+ +

Run the business on autopilot

+
+
    +
  • AI drafts the schedules, contracts, and follow-ups
  • +
  • Job-level profit visible the moment costs change
  • +
  • Invoices collect themselves on milestones
  • +
+
0admin per closed file
+
+
+ +
+
+ + +
+
+ + +
+ Who runs on it +

Three seats at the same table.

+

Contractors, asset managers, and homeowners read from one factual record, so nobody argues what the damages really are with verifiable proof.

+
+
+
+
+ +

Contractors

+
+

Contractors live and die by speed, workmanship, and trust. That is exactly what the file delivers.

+
    +
  • Faster insurance approvals through time-stamped, geo-verified capture
  • +
  • Fewer chargebacks and denials with irrefutable evidence
  • +
  • Shorter sales cycles: homeowners trust what they can see
  • +
  • More profitable jobs with accurate scopes
  • +
+
ResultOne system replaces 5–7 tools.
+
+
+
+ +

Asset Managers

+
+

Portfolio owners care about risk reduction, documentation, and long-term asset value, not sales hype.

+
    +
  • Verified condition records for every property
  • +
  • Audit-ready documentation for lenders and investors
  • +
  • Reduced fraud exposure: no undocumented claims
  • +
  • Remote visibility across whole portfolios
  • +
+
Net effectA digital twin that reduces capital risk.
+
+
+
+ +

Homeowners

+
+

Homeowners want protection and clarity, not pressure. The record gives them exactly that.

+
    +
  • Clear, visual proof instead of confusing explanations
  • +
  • Transparency on what is submitted to insurance
  • +
  • Faster claim resolution with fewer surprises
  • +
  • A permanent property record for resale value
  • +
+
FeelingInformed, protected, confident.
+
+
+
+
+ + +
+
+ + +
+ Critical data +

Why risk the wait?

+

Delaying roof repairs compounds the damage. The numbers do the arguing.

+
+
+
+ 0* +

Insurance denials

+

Of claims are denied for "lack of maintenance" when proof of regular inspection is missing from the file.

+ ✕ HIGH RISK +
+
+ 0* +

Life reduced

+

A minor leak left for six months can shorten a roof's serviceable life by up to five years.

+ ⚠ CAUTION +
+
+ 0* +

Our estimate time

+

While others schedule a second visit, the bid is measured, priced, and filed before the drone lands.

+ ✓ VERIFIED +
+
+
+
+ + +
+
+ + +
+ One job, end to end +

Case file LP-1987.

+

A real hail claim run through the system, from missed call to crew on the roof. Names on file; published with permission.

+
+
+ + + + + + +
+
+
+
+ File log · every entry time-stamped +
TUE 09:12Missed call auto-captured. Follow-up text out in 6 minutes; drive-by scan booked for the same afternoon.
+
TUE 14:4047-second drone orbit from the driveway. 28 sq measured off the mesh; 41 impact points mapped on facets F2 and F3.
+
TUE 15:05Claim CLM-88-0417 filed with the carrier: scope of loss, 202 geo-tagged photos, 3 marked test squares.
+
WED 10:20Adjuster opens the evidence pack for desk review. No site visit requested, no supplemental documents.
+
FRI 11:05Approved as filed. ACV check issued for $15,960; build scheduled while the check clears.
+
MON 07:00Crew of 5 on the roof. Tear-off, deck repair, and Class-3 laminated install complete by end of day.
+
DAY 21Completion certificate filed from the app. Recoverable depreciation of $6,410 released to the homeowner.
+
+
+ Scope of loss · RCV +
+
Tear-off + disposal · 1 layer$2,184
+
Class-3 laminated shingles · 31 sq$7,130
+
Synthetic underlayment · 8 rolls$704
+
Ridge cap, drip edge, starter, valley shield$1,265
+
Decking replacement · 6 sheets$780
+
Gutters + downspouts · 132 lf$1,584
+
Labor · crew of 5 · 2 days$7,600
+
Permits + final inspection$850
+
Overhead & profit$2,773
+
REPLACEMENT COST VALUE$24,870
+
+ Settlement +
+
Deductible · homeowner−$2,500
+
Recoverable depreciation · withheld−$6,410
+
ACV check · day 5$15,960
+
Depreciation released · day 21+$6,410
+
+ Evidence pack +
+ 202 geo-tagged photos + 41 impacts mapped + 3 test squares · 10–13 hits + 47-sec orbit · hash-sealed +
+
+
+
+ “Filed the claim Tuesday with the evidence pack. The adjuster approved it Friday + from his desk. We never met on the roof, and the crew was up Monday morning.” +
+
+ R. Calloway, production manager at Calloway Exteriors +
+
R. Calloway
+
Production Manager · Calloway Exteriors, McKinney TX
+
+ +
+
+
+
+ + +
+
+ + +
+
+ Founders vision +

Software should support the people who build.

+
+ +
+ “Technology should reduce complexity, not create it. Most construction software + lacks real jobsite experience. We built the CRM we needed on the roof.” +
+
+ Justin Johnson, founder and CEO of LynkedUp Pro +
+
Justin Johnson
+
Founder & CEO · LynkedUp Pro
+
+
+
+
+
+
Saved per weekPer field sales rep, on admin work
0
+
Deal close rateAverage lift across all users
0
+
Homes verifiedDigital twins created and secured
0
+
Built by a contractor, not a consultant
+
+
+
+
+ + +
+
+ + +
+ Pricing +

Per-seat pricing. No setup fees.

+

Every plan ships the full field kit. Per-seat plans are on hold while the Founders Lifetime License is live — see the offer below.

+
+
+
+ + Field Intelligence +
+
+ + Sales CRM +
+
+ + AI Estimation +
+
+ + 3D Job Proof +
+
+ + Crew Scheduling +
+
+
+ + +
+
+
+ ✕ ON HOLD +

Starter

+
$129 /user/mo
+
    +
  • Core CRM and ERP pipeline
  • +
  • Basic automations
  • +
  • Up to 5 users, 1,000 contacts
  • +
  • Mobile app and email support
  • +
+ See Founders offer +
+ +
+

Enterprise

+
Custom
+
    +
  • Everything in Pro, unlimited users
  • +
  • Full Digital Twin access and APIs
  • +
  • Dedicated account manager
  • +
  • Security reviews and SLAs
  • +
+ Talk to sales +
+
+
+ ✓ FOUNDERS LIFETIME LICENSE +
+
+

Skip the per-seat plans. Own it once.

+

A limited one-time license that reserves lifetime access to the core platform before per-seat pricing and public availability return. Reserved for founding contractors while the offer lasts.

+
    +
  • Everything in Pro, for your whole team, for life
  • +
  • Locked in before standard per-seat pricing returns
  • +
  • Priority access to new AI estimating & ProCanvas features
  • +
  • SMS, email, GPU, and advanced AI usage billed separately*
  • +
+
+
+ One-time +
$2,000
+ Reserve Founders access + Limited licenses · terms apply +
+
+
+
+
+ + +
+
+ + +
+ Key terms, explained simply +

No jargon. Just what it means for your business.

+
+
+
+
+
+
+ +
+ TERM 01 · SECURITY +

Zero-Trust

+

Verify everything, trust nothing. Every action is checked, even from logged-in users, so one stolen password never owns the file.

+
+
+
+ +
+ TERM 02 · EVIDENCE +

Provenance

+

The origin story of every file: who captured it, when, where, on what device. An unbreakable chain of custody adjusters accept.

+
+
+
+ +
+ TERM 03 · CAPTURE +

Digital Twin

+

A 3D scan of one property with eighth-inch accuracy. Like Google Earth for a single home, and provable in court.

+
+
+
+ +
+ TERM 04 · WEATHER +

Storm Path

+

Radar-verified hail tracks overlaid on your territory. Every roof under the swath is scored within minutes of the cell passing.

+
+
+
+ +
+ TERM 05 · CLAIMS +

Scope of Loss

+

The line-itemed damage inventory an insurer pays from. Ours is generated from the mesh, so the numbers match the roof.

+
+
+
+
+ + + + 05 TERMS · DRAG OR SCROLL +
+
+ + +
+ +
+ +
+
+ Do I need a drone? +

No. Phone capture works from day one and holds up with adjusters. Drone and aerial integrations (EagleView, CompanyCam) plug in when you want them.

+
+
+ What is the Founders Lifetime License? +

A one-time $2,000 license that reserves lifetime access to the core platform while per-seat plans are on hold. Full Pro features; if you leave, your files and exports go with you.

+
+
+ Does it work offline in the field? +

Yes. Capture, estimates, and signatures work without signal and sync when the truck gets back in coverage.

+
+
+ Who owns the data? +

You do. Every scan, photo, and record exports in standard formats, any time. We never sell or share job records.

+
+
+ Will adjusters accept the evidence pack? +

The pack is time-stamped, geo-verified, and delivered in the formats carriers already use. Most claims in the system close without a second roof visit.

+
+
+

Read the full FAQ →

+
+ + + +
+ + ✓ PROJECT READY +

Ready to build?
Bring an address.

+

Whether you protect one home or manage a thousand roofs. We run your address live: + mesh, measurements, and a signature-ready proposal before the call ends.

+
+ + +
+ +

Founders Lifetime License available · no credit card · works offline in the field

+
+
+
+
+
+ + + + + + + + + + + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..cbe215a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1083 @@ +{ + "name": "lynkedup-fusion", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "lynkedup-fusion", + "version": "1.0.0", + "dependencies": { + "@fontsource-variable/inter": "^5.0.18", + "@fontsource/jetbrains-mono": "^5.0.20", + "gsap": "^3.12.5", + "three": "^0.165.0" + }, + "devDependencies": { + "@playwright/test": "^1.61.1", + "vite": "^5.3.1" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@fontsource-variable/inter": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource-variable/inter/-/inter-5.2.8.tgz", + "integrity": "sha512-kOfP2D+ykbcX/P3IFnokOhVRNoTozo5/JxhAIVYLpea/UBmCQ/YWPBfWIDuBImXX/15KH+eKh4xpEUyS2sQQGQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/jetbrains-mono": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.2.8.tgz", + "integrity": "sha512-6w8/SG4kqvIMu7xd7wt6x3idn1Qux3p9N62s6G3rfldOUYHpWcc2FKrqf+Vo44jRvqWj2oAtTHrZXEP23oSKwQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@playwright/test": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz", + "integrity": "sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==", + "dev": true, + "dependencies": { + "playwright": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gsap": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.15.0.tgz", + "integrity": "sha512-dMW4CWBTUK1AEEDeZc1g4xpPGIrSf9fJF960qbTZmN/QwZIWY5wgliS6JWl9/25fpTGJrMRtSjGtOmPnfjZB+A==", + "license": "Standard 'no charge' license: https://gsap.com/standard-license." + }, + "node_modules/nanoid": { + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/playwright": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", + "dev": true, + "dependencies": { + "playwright-core": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/postcss": { + "version": "8.5.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", + "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/three": { + "version": "0.165.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.165.0.tgz", + "integrity": "sha512-cc96IlVYGydeceu0e5xq70H8/yoVT/tXBxV/W8A/U6uOq7DXc4/s1Mkmnu6SqoYGhSRWWYFOhVwvq6V0VtbplA==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..8ff6207 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "lynkedup-fusion", + "private": true, + "version": "1.0.0", + "author": "Sarthak", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@fontsource-variable/inter": "^5.0.18", + "@fontsource/jetbrains-mono": "^5.0.20", + "gsap": "^3.12.5", + "three": "^0.165.0" + }, + "devDependencies": { + "@playwright/test": "^1.61.1", + "vite": "^5.3.1" + } +} diff --git a/public/clips/ai-estimation-demo.mp4 b/public/clips/ai-estimation-demo.mp4 new file mode 100644 index 0000000..b9573fd Binary files /dev/null and b/public/clips/ai-estimation-demo.mp4 differ diff --git a/public/clips/dispatch.mp4 b/public/clips/dispatch.mp4 new file mode 100644 index 0000000..2582ffc Binary files /dev/null and b/public/clips/dispatch.mp4 differ diff --git a/public/clips/estimates.mp4 b/public/clips/estimates.mp4 new file mode 100644 index 0000000..8252ebb Binary files /dev/null and b/public/clips/estimates.mp4 differ diff --git a/public/clips/mobile-capture-demo.mp4 b/public/clips/mobile-capture-demo.mp4 new file mode 100644 index 0000000..a3f3882 Binary files /dev/null and b/public/clips/mobile-capture-demo.mp4 differ diff --git a/public/clips/owner-snapshot.mp4 b/public/clips/owner-snapshot.mp4 new file mode 100644 index 0000000..388f415 Binary files /dev/null and b/public/clips/owner-snapshot.mp4 differ diff --git a/public/clips/owners-box-demo.mp4 b/public/clips/owners-box-demo.mp4 new file mode 100644 index 0000000..128c165 Binary files /dev/null and b/public/clips/owners-box-demo.mp4 differ diff --git a/public/clips/pro-canvas.mp4 b/public/clips/pro-canvas.mp4 new file mode 100644 index 0000000..be26de5 Binary files /dev/null and b/public/clips/pro-canvas.mp4 differ diff --git a/public/clips/smart-automation-demo.mp4 b/public/clips/smart-automation-demo.mp4 new file mode 100644 index 0000000..4a53374 Binary files /dev/null and b/public/clips/smart-automation-demo.mp4 differ diff --git a/public/clips/storm-intel.mp4 b/public/clips/storm-intel.mp4 new file mode 100644 index 0000000..368e19d Binary files /dev/null and b/public/clips/storm-intel.mp4 differ diff --git a/public/img/compare-new.webp b/public/img/compare-new.webp new file mode 100644 index 0000000..f8a0aec Binary files /dev/null and b/public/img/compare-new.webp differ diff --git a/public/img/compare-old.webp b/public/img/compare-old.webp new file mode 100644 index 0000000..25f0112 Binary files /dev/null and b/public/img/compare-old.webp differ diff --git a/public/img/feat-ai-estimation.jpg b/public/img/feat-ai-estimation.jpg new file mode 100644 index 0000000..ce5276c Binary files /dev/null and b/public/img/feat-ai-estimation.jpg differ diff --git a/public/img/feat-mobile-capture.jpg b/public/img/feat-mobile-capture.jpg new file mode 100644 index 0000000..fa6bbd0 Binary files /dev/null and b/public/img/feat-mobile-capture.jpg differ diff --git a/public/img/feat-owners-box.jpg b/public/img/feat-owners-box.jpg new file mode 100644 index 0000000..8aa8be1 Binary files /dev/null and b/public/img/feat-owners-box.jpg differ diff --git a/public/img/feat-smart-automation.jpg b/public/img/feat-smart-automation.jpg new file mode 100644 index 0000000..3df54d3 Binary files /dev/null and b/public/img/feat-smart-automation.jpg differ diff --git a/public/img/intel_map_dfw_twilight.webp b/public/img/intel_map_dfw_twilight.webp new file mode 100644 index 0000000..b8fb61d Binary files /dev/null and b/public/img/intel_map_dfw_twilight.webp differ diff --git a/public/img/justin-johnson.jpg b/public/img/justin-johnson.jpg new file mode 100644 index 0000000..31eb5cc Binary files /dev/null and b/public/img/justin-johnson.jpg differ diff --git a/public/img/props/Apartment_Complex.jpg b/public/img/props/Apartment_Complex.jpg new file mode 100644 index 0000000..8f19f27 Binary files /dev/null and b/public/img/props/Apartment_Complex.jpg differ diff --git a/public/img/props/Beige_Two_Story_House.jpg b/public/img/props/Beige_Two_Story_House.jpg new file mode 100644 index 0000000..e1d9ecf Binary files /dev/null and b/public/img/props/Beige_Two_Story_House.jpg differ diff --git a/public/img/props/Brick_Front_Porch_Home.jpg b/public/img/props/Brick_Front_Porch_Home.jpg new file mode 100644 index 0000000..afa050b Binary files /dev/null and b/public/img/props/Brick_Front_Porch_Home.jpg differ diff --git a/public/img/props/Broken_Roof_Surface.jpg b/public/img/props/Broken_Roof_Surface.jpg new file mode 100644 index 0000000..cde7142 Binary files /dev/null and b/public/img/props/Broken_Roof_Surface.jpg differ diff --git a/public/img/props/Cracked_Asphalt_Shingles.jpg b/public/img/props/Cracked_Asphalt_Shingles.jpg new file mode 100644 index 0000000..934928f Binary files /dev/null and b/public/img/props/Cracked_Asphalt_Shingles.jpg differ diff --git a/public/img/props/Curled_Aging_Shingles.jpg b/public/img/props/Curled_Aging_Shingles.jpg new file mode 100644 index 0000000..5f042fc Binary files /dev/null and b/public/img/props/Curled_Aging_Shingles.jpg differ diff --git a/public/img/props/Hail_Damaged_Shingles.jpg b/public/img/props/Hail_Damaged_Shingles.jpg new file mode 100644 index 0000000..8e5e13b Binary files /dev/null and b/public/img/props/Hail_Damaged_Shingles.jpg differ diff --git a/public/img/props/Mall_Rooftop_HVAC_Units.jpg b/public/img/props/Mall_Rooftop_HVAC_Units.jpg new file mode 100644 index 0000000..de57d53 Binary files /dev/null and b/public/img/props/Mall_Rooftop_HVAC_Units.jpg differ diff --git a/public/img/props/Modern_White_Suburban_Home.jpg b/public/img/props/Modern_White_Suburban_Home.jpg new file mode 100644 index 0000000..e1ddd8e Binary files /dev/null and b/public/img/props/Modern_White_Suburban_Home.jpg differ diff --git a/public/img/props/Office_Roof_Top_View.jpg b/public/img/props/Office_Roof_Top_View.jpg new file mode 100644 index 0000000..8f19f27 Binary files /dev/null and b/public/img/props/Office_Roof_Top_View.jpg differ diff --git a/public/img/props/Residential_Roof_Texture.jpg b/public/img/props/Residential_Roof_Texture.jpg new file mode 100644 index 0000000..65adff4 Binary files /dev/null and b/public/img/props/Residential_Roof_Texture.jpg differ diff --git a/public/img/props/Roof_Inspection_Angle.jpg b/public/img/props/Roof_Inspection_Angle.jpg new file mode 100644 index 0000000..3a35f78 Binary files /dev/null and b/public/img/props/Roof_Inspection_Angle.jpg differ diff --git a/public/img/props/Storm_Worn_Roof.jpg b/public/img/props/Storm_Worn_Roof.jpg new file mode 100644 index 0000000..1775b2d Binary files /dev/null and b/public/img/props/Storm_Worn_Roof.jpg differ diff --git a/public/img/props/Sunny_Suburban_House.jpg b/public/img/props/Sunny_Suburban_House.jpg new file mode 100644 index 0000000..5716015 Binary files /dev/null and b/public/img/props/Sunny_Suburban_House.jpg differ diff --git a/public/img/props/Warehouse_Roof_Aerial.jpg b/public/img/props/Warehouse_Roof_Aerial.jpg new file mode 100644 index 0000000..47bb254 Binary files /dev/null and b/public/img/props/Warehouse_Roof_Aerial.jpg differ diff --git a/public/img/r-calloway.jpg b/public/img/r-calloway.jpg new file mode 100644 index 0000000..f591182 Binary files /dev/null and b/public/img/r-calloway.jpg differ diff --git a/public/og.jpg b/public/og.jpg new file mode 100644 index 0000000..a7e81d3 Binary files /dev/null and b/public/og.jpg differ diff --git a/src/blog.js b/src/blog.js new file mode 100644 index 0000000..13e4037 --- /dev/null +++ b/src/blog.js @@ -0,0 +1,172 @@ +import "@fontsource-variable/inter"; +import "@fontsource/jetbrains-mono/400.css"; +import "@fontsource/jetbrains-mono/500.css"; +import "@fontsource/jetbrains-mono/600.css"; +import "./styles.css"; + +console.log("%cCreated by Sarthak", "font-size:14px;font-weight:700;color:#F2A83B;"); + +(function(){ + var reduced = matchMedia("(prefers-reduced-motion: reduce)").matches; + var finePointer = matchMedia("(pointer: fine)").matches; + + /* ---------- scrollbar materialises only while scrolling ---------- */ + (function(){ + var to = null; + addEventListener("scroll", function(){ + document.documentElement.classList.add("scrolling"); + clearTimeout(to); + to = setTimeout(function(){ document.documentElement.classList.remove("scrolling"); }, 900); + }, { passive: true, capture: true }); + })(); + + /* ---------- mobile scroll progress bar ---------- */ + (function(){ + var bar = document.getElementById("mprogBar"); + if(!bar) return; + var raf = null; + function upd(){ + raf = null; + var doc = document.scrollingElement || document.documentElement; + var max = Math.max(1, doc.scrollHeight - innerHeight); + bar.style.transform = "scaleX(" + Math.min(1, window.scrollY / max).toFixed(4) + ")"; + } + addEventListener("scroll", function(){ if(raf == null) raf = requestAnimationFrame(upd); }, { passive: true }); + upd(); + })(); + + /* ---------- mobile nav ---------- */ + (function(){ + var mb = document.getElementById("menuBtn"); + if(!mb) return; + mb.addEventListener("click", function(){ + var open = document.body.classList.toggle("nav-open"); + mb.setAttribute("aria-expanded", open ? "true" : "false"); + }); + [].slice.call(document.querySelectorAll(".nav-links a")).forEach(function(a){ + a.addEventListener("click", function(){ + document.body.classList.remove("nav-open"); + mb.setAttribute("aria-expanded", "false"); + }); + }); + })(); + + /* ---------- footer: hail alerts by ZIP ---------- */ + (function(){ + var form = document.getElementById("zipForm"); + if(!form) return; + var zin = document.getElementById("zipInput"); + form.addEventListener("submit", function(e){ + e.preventDefault(); + var v = zin.value.trim(); + if(!/^\d{5}$/.test(v)){ zin.focus(); zin.select(); return; } + form.innerHTML = "
⚑ WATCHING " + v + "" + + "First alert lands after the next radar-verified swath. No spam, only storms.
"; + }); + })(); + + /* ---------- station rail: generic, built from any [data-rail] section on the page ---------- */ + (function(){ + var rail = document.getElementById("rail"), track = document.getElementById("railTrack"); + if(!rail || !track) return; + var car = document.getElementById("railCar"), read = document.getElementById("railRead"); + var secs = [].slice.call(document.querySelectorAll("[data-rail]")).map(function(el){ + return { el: el, label: el.getAttribute("data-rail") }; + }); + if(!secs.length) return; + var nodes = secs.map(function(s){ + var b = document.createElement("button"); + b.className = "rail-node"; b.type = "button"; b.title = s.label; + b.setAttribute("aria-label", s.label); + b.addEventListener("click", function(){ + scrollTo({top:s.el.offsetTop - 52, behavior:reduced ? "auto" : "smooth"}); + }); + track.appendChild(b); + return b; + }); + var tops = [], maxScroll = 1; + function layout(){ + var doc = document.scrollingElement || document.documentElement; + maxScroll = Math.max(1, doc.scrollHeight - innerHeight); + tops = secs.map(function(s){ return Math.max(0, Math.min(s.el.offsetTop - 52, maxScroll)); }); + tops.forEach(function(tp, i){ nodes[i].style.top = (tp / maxScroll * 100) + "%"; }); + } + var carP = 0, target = 0, actLabel = "", raf = null, lastT = 0; + function frame(now){ + var rawDt = lastT ? now - lastT : 16; + var dt = Math.min(rawDt, 50); + lastT = now; + var k = rawDt > 400 ? 1 : 1 - Math.exp(-dt / 110); + carP += (target - carP) * k; + if(Math.abs(target - carP) < .0005) carP = target; + car.style.top = (carP * 100) + "%"; + read.textContent = actLabel + " · " + Math.round(carP * 100) + "%"; + if(carP === target){ raf = null; lastT = 0; return; } + raf = requestAnimationFrame(frame); + } + function upd(){ + var doc = document.scrollingElement || document.documentElement; + if(Math.abs(doc.scrollHeight - innerHeight - maxScroll) > 2) layout(); + var y = window.scrollY; + target = Math.max(0, Math.min(1, y / maxScroll)); + var act = 0; + tops.forEach(function(tp, i){ if(y >= tp - innerHeight * .35) act = i; }); + if(maxScroll - y < 4) act = secs.length - 1; + nodes.forEach(function(n, i){ n.classList.toggle("on", i === act); }); + actLabel = secs[act].label; + if(raf == null){ lastT = 0; raf = requestAnimationFrame(frame); } + } + addEventListener("scroll", upd, {passive:true}); + addEventListener("resize", function(){ layout(); upd(); }); + addEventListener("load", function(){ layout(); upd(); }); + layout(); upd(); + })(); + + /* ---------- scroll reveals: 150ms, 60ms stagger ---------- */ + var revealEls = [].slice.call(document.querySelectorAll(".reveal")); + if(!reduced && "IntersectionObserver" in window){ + var groups = new Map(); + var io = new IntersectionObserver(function(entries){ + entries.forEach(function(e){ + if(!e.isIntersecting && e.boundingClientRect.top >= 0) return; + io.unobserve(e.target); + var parent = e.target.parentElement; + var idx = groups.get(parent) || 0; + groups.set(parent, idx + 1); + setTimeout(function(){ e.target.classList.add("in"); }, idx * 60); + setTimeout(function(){ groups.set(parent, Math.max(0,(groups.get(parent)||1)-1)); }, 400); + }); + }, {threshold:.15}); + revealEls.forEach(function(el){ io.observe(el); }); + } else { + revealEls.forEach(function(el){ el.classList.add("in"); }); + } + + /* ---------- card tilt physics (spring, with glare tracking) ---------- */ + if(finePointer && !reduced){ + [].slice.call(document.querySelectorAll(".risk-card,.founder-card,.feat,.terms-card")).forEach(function(el){ + var rx=0,ry=0,tx=0,ty=0,vx=0,vy=0,raf=null,over=false; + function frame(){ + vx+=(tx-rx)*.16; vy+=(ty-ry)*.16; vx*=.72; vy*=.72; + rx+=vx; ry+=vy; + if(!over && Math.abs(rx)<.02 && Math.abs(ry)<.02 && Math.abs(vx)<.02 && Math.abs(vy)<.02){ + el.style.transform=""; raf=null; return; + } + el.style.transform="perspective(900px) rotateX("+rx.toFixed(2)+"deg) rotateY("+ry.toFixed(2)+"deg) translateY(-2px)"; + raf=requestAnimationFrame(frame); + } + el.addEventListener("pointerenter",function(){ + over=true; el.classList.add("tilting"); + if(!raf) raf=requestAnimationFrame(frame); + }); + el.addEventListener("pointermove",function(e){ + var r=el.getBoundingClientRect(); + var px=(e.clientX-r.left)/r.width, py=(e.clientY-r.top)/r.height; + ty=(px-.5)*5; tx=(.5-py)*5; + el.style.setProperty("--gx",(px*100)+"%"); + el.style.setProperty("--gy",(py*100)+"%"); + }); + el.addEventListener("pointerleave",function(){ over=false; tx=0; ty=0; }); + }); + } +})(); diff --git a/src/faq.js b/src/faq.js new file mode 100644 index 0000000..53f9be7 --- /dev/null +++ b/src/faq.js @@ -0,0 +1,180 @@ +import "@fontsource-variable/inter"; +import "@fontsource/jetbrains-mono/400.css"; +import "@fontsource/jetbrains-mono/500.css"; +import "@fontsource/jetbrains-mono/600.css"; +import "./styles.css"; + +console.log("%cCreated by Sarthak", "font-size:14px;font-weight:700;color:#F2A83B;"); + +(function(){ + var reduced = matchMedia("(prefers-reduced-motion: reduce)").matches; + + /* ---------- scrollbar materialises only while scrolling ---------- */ + (function(){ + var to = null; + addEventListener("scroll", function(){ + document.documentElement.classList.add("scrolling"); + clearTimeout(to); + to = setTimeout(function(){ document.documentElement.classList.remove("scrolling"); }, 900); + }, { passive: true, capture: true }); + })(); + + /* ---------- mobile scroll progress bar ---------- */ + (function(){ + var bar = document.getElementById("mprogBar"); + if(!bar) return; + var raf = null; + function upd(){ + raf = null; + var doc = document.scrollingElement || document.documentElement; + var max = Math.max(1, doc.scrollHeight - innerHeight); + bar.style.transform = "scaleX(" + Math.min(1, window.scrollY / max).toFixed(4) + ")"; + } + addEventListener("scroll", function(){ if(raf == null) raf = requestAnimationFrame(upd); }, { passive: true }); + upd(); + })(); + + /* ---------- mobile nav ---------- */ + (function(){ + var mb = document.getElementById("menuBtn"); + if(!mb) return; + mb.addEventListener("click", function(){ + var open = document.body.classList.toggle("nav-open"); + mb.setAttribute("aria-expanded", open ? "true" : "false"); + }); + [].slice.call(document.querySelectorAll(".nav-links a")).forEach(function(a){ + a.addEventListener("click", function(){ + document.body.classList.remove("nav-open"); + mb.setAttribute("aria-expanded", "false"); + }); + }); + })(); + + /* ---------- footer: hail alerts by ZIP ---------- */ + (function(){ + var form = document.getElementById("zipForm"); + if(!form) return; + var zin = document.getElementById("zipInput"); + form.addEventListener("submit", function(e){ + e.preventDefault(); + var v = zin.value.trim(); + if(!/^\d{5}$/.test(v)){ zin.focus(); zin.select(); return; } + form.innerHTML = "
⚑ WATCHING " + v + "" + + "First alert lands after the next radar-verified swath. No spam, only storms.
"; + }); + })(); + + /* ---------- station rail: section index ---------- */ + (function(){ + var rail = document.getElementById("rail"), track = document.getElementById("railTrack"); + if(!rail || !track) return; + var car = document.getElementById("railCar"), read = document.getElementById("railRead"); + var defs = [ + ["intro","01 · FAQ"], ["ov","02 · OVERVIEW"], ["sales","03 · SALES"], ["canvas","04 · PROCANVAS"], + ["projects","05 · PROJECTS"], ["ownersbox","06 · OWNERS BOX"], ["ai","07 · AI"], + ["reporting","08 · REPORTING"], ["fit","09 · FIT"], ["roadmap","10 · ROADMAP"], + ["founders","11 · FOUNDERS"], ["about","12 · ABOUT"], ["cta","13 · BOOK"] + ]; + var secs = defs.map(function(d){ return {el:document.getElementById(d[0]), label:d[1]}; }) + .filter(function(s){ return s.el; }); + var nodes = secs.map(function(s){ + var b = document.createElement("button"); + b.className = "rail-node"; b.type = "button"; b.title = s.label; + b.setAttribute("aria-label", s.label); + b.addEventListener("click", function(){ + scrollTo({top:s.el.offsetTop - 52, behavior:reduced ? "auto" : "smooth"}); + }); + track.appendChild(b); + return b; + }); + var tops = [], maxScroll = 1; + function layout(){ + var doc = document.scrollingElement || document.documentElement; + maxScroll = Math.max(1, doc.scrollHeight - innerHeight); + /* nodes live in the SAME space as the carriage: clamped scroll position / max scroll */ + tops = secs.map(function(s){ return Math.max(0, Math.min(s.el.offsetTop - 52, maxScroll)); }); + tops.forEach(function(tp, i){ nodes[i].style.top = (tp / maxScroll * 100) + "%"; }); + } + var carP = 0, target = 0, actLabel = "", raf = null, lastT = 0; + function frame(now){ + var rawDt = lastT ? now - lastT : 16; + var dt = Math.min(rawDt, 50); + lastT = now; + var k = rawDt > 400 ? 1 : 1 - Math.exp(-dt / 110); + carP += (target - carP) * k; + if(Math.abs(target - carP) < .0005) carP = target; + car.style.top = (carP * 100) + "%"; + read.textContent = actLabel + " · " + Math.round(carP * 100) + "%"; + if(carP === target){ raf = null; lastT = 0; return; } + raf = requestAnimationFrame(frame); + } + function upd(){ + var doc = document.scrollingElement || document.documentElement; + if(Math.abs(doc.scrollHeight - innerHeight - maxScroll) > 2) layout(); + var y = window.scrollY; + target = Math.max(0, Math.min(1, y / maxScroll)); + var act = 0; + tops.forEach(function(tp, i){ if(y >= tp - innerHeight * .35) act = i; }); + if(maxScroll - y < 4) act = secs.length - 1; + nodes.forEach(function(n, i){ n.classList.toggle("on", i === act); }); + actLabel = secs[act].label; + if(raf == null){ lastT = 0; raf = requestAnimationFrame(frame); } + } + addEventListener("scroll", upd, {passive:true}); + addEventListener("resize", function(){ layout(); upd(); }); + addEventListener("load", function(){ layout(); upd(); }); + layout(); upd(); + })(); + + /* ---------- scroll reveals: 150ms, 60ms stagger ---------- */ + var revealEls = [].slice.call(document.querySelectorAll(".reveal")); + if(!reduced && "IntersectionObserver" in window){ + var groups = new Map(); + var io = new IntersectionObserver(function(entries){ + entries.forEach(function(e){ + if(!e.isIntersecting && e.boundingClientRect.top >= 0) return; + io.unobserve(e.target); + var parent = e.target.parentElement; + var idx = groups.get(parent) || 0; + groups.set(parent, idx + 1); + setTimeout(function(){ e.target.classList.add("in"); }, idx * 60); + setTimeout(function(){ groups.set(parent, Math.max(0,(groups.get(parent)||1)-1)); }, 400); + }); + }, {threshold:.15}); + revealEls.forEach(function(el){ io.observe(el); }); + } else { + revealEls.forEach(function(el){ el.classList.add("in"); }); + } + + /* ---------- FAQ search filter ---------- */ + (function(){ + var input = document.getElementById("faqSearch"); + if(!input) return; + var count = document.getElementById("faqCount"); + var groups = [].slice.call(document.querySelectorAll(".faq-cat")).map(function(cat){ + return { + cat: cat, + rows: [].slice.call(cat.querySelectorAll(".faq details")).map(function(d){ + return { el: d, text: d.textContent.toLowerCase() }; + }) + }; + }); + function run(){ + var q = input.value.trim().toLowerCase(); + var shown = 0; + groups.forEach(function(group){ + var visible = 0; + group.rows.forEach(function(row){ + var match = !q || row.text.indexOf(q) !== -1; + row.el.classList.toggle("hide", !match); + if(match) visible++; + }); + group.cat.classList.toggle("hide", visible === 0); + shown += visible; + }); + if(count) count.textContent = q ? shown + " match" + (shown === 1 ? "" : "es") + " for “" + input.value.trim() + "”" : ""; + dispatchEvent(new Event("resize")); + } + input.addEventListener("input", run); + })(); +})(); diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..ac0ef77 --- /dev/null +++ b/src/main.js @@ -0,0 +1,1459 @@ +import "@fontsource-variable/inter"; +import "@fontsource/jetbrains-mono/400.css"; +import "@fontsource/jetbrains-mono/500.css"; +import "@fontsource/jetbrains-mono/600.css"; +import "./styles.css"; + +console.log("%cCreated by Sarthak", "font-size:14px;font-weight:700;color:#F2A83B;"); + +(function(){ + var reduced = matchMedia("(prefers-reduced-motion: reduce)").matches; + var finePointer = matchMedia("(pointer: fine)").matches; + var fmt = function(n){ return n.toLocaleString("en-US"); }; + + /* ---------- scrollbar materialises only while scrolling ---------- */ + (function(){ + var to = null; + addEventListener("scroll", function(){ + document.documentElement.classList.add("scrolling"); + clearTimeout(to); + to = setTimeout(function(){ document.documentElement.classList.remove("scrolling"); }, 900); + }, { passive: true, capture: true }); + })(); + + /* ---------- mobile scroll progress bar ---------- */ + (function(){ + var bar = document.getElementById("mprogBar"); + if(!bar) return; + var raf = null; + function upd(){ + raf = null; + var doc = document.scrollingElement || document.documentElement; + var max = Math.max(1, doc.scrollHeight - innerHeight); + bar.style.transform = "scaleX(" + Math.min(1, window.scrollY / max).toFixed(4) + ")"; + } + addEventListener("scroll", function(){ if(raf == null) raf = requestAnimationFrame(upd); }, { passive: true }); + upd(); + })(); + + /* ---------- footer: hail alerts by ZIP ---------- */ + (function(){ + var form = document.getElementById("zipForm"); + if(!form) return; + var zin = document.getElementById("zipInput"); + form.addEventListener("submit", function(e){ + e.preventDefault(); + var v = zin.value.trim(); + if(!/^\d{5}$/.test(v)){ zin.focus(); zin.select(); return; } + form.innerHTML = "
⚑ WATCHING " + v + "" + + "First alert lands after the next radar-verified swath. No spam, only storms.
"; + }); + })(); + + /* ---------- scroll reveals: 150ms, 60ms stagger ---------- */ + var revealEls = [].slice.call(document.querySelectorAll(".reveal")); + if(!reduced && "IntersectionObserver" in window){ + var groups = new Map(); + var io = new IntersectionObserver(function(entries){ + entries.forEach(function(e){ + if(!e.isIntersecting && e.boundingClientRect.top >= 0) return; + io.unobserve(e.target); + var parent = e.target.parentElement; + var idx = groups.get(parent) || 0; + groups.set(parent, idx + 1); + setTimeout(function(){ e.target.classList.add("in"); }, idx * 60); + setTimeout(function(){ groups.set(parent, Math.max(0,(groups.get(parent)||1)-1)); }, 400); + }); + }, {threshold:.15}); + revealEls.forEach(function(el){ io.observe(el); }); + } else { + revealEls.forEach(function(el){ el.classList.add("in"); }); + } + + /* ---------- count-ups: one-shot, comma-formatted ---------- */ + var counters = [].slice.call(document.querySelectorAll("[data-count]")); + function runCounter(el){ + var target = +el.dataset.count; + var suffix = el.dataset.suffix || "", prefix = el.dataset.prefix || ""; + var dec = +(el.dataset.decimals || 0); + function print(v){ + el.textContent = prefix + (dec ? v.toFixed(dec) : fmt(Math.round(v))) + suffix; + } + if(reduced){ print(target); return; } + var t0 = null, dur = 2400; + function step(ts){ + if(!t0) t0 = ts; + var p = Math.min((ts - t0)/dur, 1); + p = 1 - Math.pow(1-p, 3); + print(target * p); + if(p < 1) requestAnimationFrame(step); + } + requestAnimationFrame(step); + } + if("IntersectionObserver" in window){ + var cio = new IntersectionObserver(function(entries){ + entries.forEach(function(e){ + if(e.isIntersecting){ cio.unobserve(e.target); runCounter(e.target); } + }); + }, {threshold:.6}); + counters.forEach(function(el){ cio.observe(el); }); + } else counters.forEach(runCounter); + + /* ---------- hero contour-line shader (survey isolines, cyan) ---------- */ + (function(){ + var cv = document.getElementById("heroFx"); + if(!cv) return; + var gl = cv.getContext("webgl", {alpha:true, antialias:false}); + if(!gl){ cv.remove(); return; } + var vsrc = "attribute vec2 p;void main(){gl_Position=vec4(p,0.,1.);}"; + var fsrc = + "precision mediump float;" + + "uniform vec2 u_res;uniform float u_t;uniform vec2 u_m;" + + "void main(){" + + " vec2 uv=gl_FragCoord.xy/u_res;" + + " vec2 p=uv*vec2(u_res.x/u_res.y,1.);" + + " p+=(u_m-.5)*.10;" + + " float v=sin(p.x*4.2+u_t*.25)+sin(p.y*5.1-u_t*.2)+sin((p.x+p.y)*3.1+u_t*.12)+sin(length(p-vec2(1.2,.2))*6.-u_t*.3);" + + " float f=fract(v*1.5);" + + " float line=smoothstep(.90,.975,f)*(1.-smoothstep(.975,1.,f));" + + " float mask=smoothstep(1.3,.1,distance(uv,vec2(.78,.82)));" + + " gl_FragColor=vec4(.27,.69,.79,line*mask*.085);" + + "}"; + function sh(t,s){var o=gl.createShader(t);gl.shaderSource(o,s);gl.compileShader(o);return o;} + var pr=gl.createProgram(); + gl.attachShader(pr,sh(gl.VERTEX_SHADER,vsrc)); + gl.attachShader(pr,sh(gl.FRAGMENT_SHADER,fsrc)); + gl.linkProgram(pr);gl.useProgram(pr); + var buf=gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER,buf); + gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,3,-1,-1,3]),gl.STATIC_DRAW); + var loc=gl.getAttribLocation(pr,"p"); + gl.enableVertexAttribArray(loc);gl.vertexAttribPointer(loc,2,gl.FLOAT,false,0,0); + var uRes=gl.getUniformLocation(pr,"u_res"),uT=gl.getUniformLocation(pr,"u_t"),uM=gl.getUniformLocation(pr,"u_m"); + var hero=document.querySelector(".hero"),mx=.5,my=.5; + if(finePointer) hero.addEventListener("pointermove",function(e){ + var r=hero.getBoundingClientRect(); + mx=(e.clientX-r.left)/r.width; my=(e.clientY-r.top)/r.height; + }); + var dpr=Math.min(devicePixelRatio||1,1.5); + function rs(){var r=hero.getBoundingClientRect();cv.width=r.width*dpr;cv.height=r.height*dpr;gl.viewport(0,0,cv.width,cv.height);} + rs();addEventListener("resize",rs); + gl.enable(gl.BLEND);gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA); + function paint(t){ + gl.uniform2f(uRes,cv.width,cv.height); + gl.uniform1f(uT,t); + gl.uniform2f(uM,mx,1-my); + gl.drawArrays(gl.TRIANGLES,0,3); + } + paint(0); /* warm the compile at load */ + if(reduced){ paint(8); return; } + var vis=true,t0=performance.now(); + if("IntersectionObserver" in window) new IntersectionObserver(function(en){vis=en[0].isIntersecting;}).observe(hero); + (function frame(now){ + if(vis) paint((now-t0)/1000); + requestAnimationFrame(frame); + })(t0); + })(); + + /* ---------- cursor-following hero glow ---------- */ + (function(){ + var g=document.getElementById("heroGlow"); + if(!g) return; + if(!finePointer || reduced){ g.remove(); return; } + var hero=document.querySelector(".hero"); + hero.addEventListener("pointermove",function(e){ + var r=hero.getBoundingClientRect(); + g.style.opacity="1"; + g.style.transform="translate("+(e.clientX-r.left-260)+"px,"+(e.clientY-r.top-260)+"px)"; + }); + hero.addEventListener("pointerleave",function(){ g.style.opacity="0"; }); + })(); + + /* ---------- card tilt physics (spring, with glare tracking) ---------- */ + if(finePointer && !reduced){ + [].slice.call(document.querySelectorAll(".ind,.risk-card,.plan,.founder-card,.feat,.oc")).forEach(function(el){ + var rx=0,ry=0,tx=0,ty=0,vx=0,vy=0,raf=null,over=false; + function frame(){ + vx+=(tx-rx)*.16; vy+=(ty-ry)*.16; vx*=.72; vy*=.72; + rx+=vx; ry+=vy; + if(!over && Math.abs(rx)<.02 && Math.abs(ry)<.02 && Math.abs(vx)<.02 && Math.abs(vy)<.02){ + el.style.transform=""; raf=null; return; + } + el.style.transform="perspective(900px) rotateX("+rx.toFixed(2)+"deg) rotateY("+ry.toFixed(2)+"deg) translateY(-2px)"; + raf=requestAnimationFrame(frame); + } + el.addEventListener("pointerenter",function(){ + over=true; el.classList.add("tilting"); + if(!raf) raf=requestAnimationFrame(frame); + }); + el.addEventListener("pointermove",function(e){ + var r=el.getBoundingClientRect(); + var px=(e.clientX-r.left)/r.width, py=(e.clientY-r.top)/r.height; + ty=(px-.5)*5; tx=(.5-py)*5; + el.style.setProperty("--gx",(px*100)+"%"); + el.style.setProperty("--gy",(py*100)+"%"); + }); + el.addEventListener("pointerleave",function(){ over=false; tx=0; ty=0; }); + }); + } + + /* ---------- mobile nav ---------- */ + (function(){ + var mb = document.getElementById("menuBtn"); + if(!mb) return; + mb.addEventListener("click", function(){ + var open = document.body.classList.toggle("nav-open"); + mb.setAttribute("aria-expanded", open ? "true" : "false"); + }); + [].slice.call(document.querySelectorAll(".nav-links a")).forEach(function(a){ + a.addEventListener("click", function(){ + document.body.classList.remove("nav-open"); + mb.setAttribute("aria-expanded", "false"); + }); + }); + })(); + + /* The 3D scene (three.js + gsap, ~540KB) loads after first paint; + everything that needs the model lives inside initEstimator. */ + function initEstimator(createScene){ + /* ---------- 3D blueprint model: source of truth for the estimator ---------- */ + var modelWrap = document.getElementById("modelWrap"); + var roof3d = createScene(modelWrap, { reduced: reduced }); + roof3d.stage(1); /* no scan phase on this page: model arrives fully inked */ + roof3d.onDragStart(function(){ + var hint = document.getElementById("dragHint"); + if(hint) hint.style.opacity = "0"; + }); + (function(){ + var slot = document.getElementById("estSlot"); + function size(){ + var r = slot.getBoundingClientRect(); + if(r.width > 2 && r.height > 2) roof3d.resize(r.width, r.height); + } + if("ResizeObserver" in window) new ResizeObserver(size).observe(slot); + else addEventListener("resize", size); + size(); + })(); + + /* ---------- estimator: quantities derived from the 3D model ---------- + rates below are pulled from a real Certainteed Landmark AR tear-off/install + invoice (roofing material + labor line items on a 12/12, 64.89 sq job): + shingles $203.19/sq, synthetic underlayment ~$44.53/sq (10 sq/roll @ $445.30), + ridge/hip cap ~$4.00/lf, accessories (drip edge, vents, flashing, nails, + starter, sealant) ~$53/sq, plus a steep-pitch labor surcharge that kicks in + at 8/12 and climbs with pitch — the old flat/guessed numbers didn't scale + with job size or pitch at all. */ + function makeEstimate(o){ + var CANON_W = {gable:42, hip:46, storm:58, estate:42, mustang:48}; + var CANON_P = {gable:6, hip:6, storm:5, estate:6, mustang:12}; + o.wf = CANON_W[o.style] || o.wf; + o.pn = CANON_P[o.style] || o.pn; + var mm = roof3d.measure({style:o.style, wf:o.wf, pn:o.pn}); + var layers = o.layers || 1; + var rate = o.shingleRate || 203; + /* hips and complex multi-facet roofs honestly carry more cut waste than plain gables */ + var waste = (o.style === "hip" || o.style === "mustang") ? 1.12 : 1.10; + var squares = Math.ceil(mm.roofSqFt * waste / 100); + var shingles = squares * rate; + var rolls = Math.ceil(squares / 10), under = rolls * 445; + var ridge = Math.round(mm.ridgeLf * 4.0); + var miscRate = o.miscRate || 53; + var misc = Math.round(squares * miscRate); + var tear = squares * 78 * layers; + var labor = Math.round(o.crew * o.days * 760); + var gutters = o.gutters ? Math.round(o.gutters * 12) : 0; + var extras = o.extras || []; + var extrasSum = extras.reduce(function(a,x){ return a + x[1]; }, 0); + /* real steep-fee tiering: 8-9/12, 10-11/12, 12/12+ */ + var steepRate = o.pn >= 12 ? 26 : o.pn >= 10 ? 20 : o.pn >= 8 ? 14.29 : 0; + var steep = steepRate ? Math.round(squares * steepRate) : 0; + var total = shingles + under + ridge + misc + tear + labor + gutters + extrasSum + steep; + var lines = [ + ["SCAN   " + o.scanLabel, "OK " + o.scanTime + "", null], + ["MEASURE  roof " + fmt(mm.roofSqFt) + " sq ft · " + o.pn + "/12 · " + mm.facets + " facets", "OK", "roof"], + ["" + (o.shingleLabel || "Architectural shingles") + " · " + squares + " sq", "$" + fmt(shingles), "roof"], + ["Synthetic underlayment · " + rolls + " rolls", "$" + fmt(under), "roof"], + ["Ridge" + (o.style === "hip" || o.style === "mustang" ? " + hip" : "") + " cap · " + mm.ridgeLf + " lf", "$" + fmt(ridge), "ridge"], + ["Drip edge, vents, fasteners", "$" + fmt(misc), "eave"], + ["Tear-off + disposal · " + layers + " layer" + (layers > 1 ? "s" : ""), "$" + fmt(tear), "roof"] + ]; + if(steep) lines.push(["Steep-pitch labor · " + o.pn + "/12 · " + squares + " sq", "$" + fmt(steep), "roof"]); + if(o.gutters) lines.push(["Gutters · " + o.gutters + " lf aluminum", "$" + fmt(gutters), "eave"]); + extras.forEach(function(x){ lines.push(["" + x[0] + "", "$" + fmt(x[1]), null]); }); + lines.push(["Labor · crew of " + o.crew + " · " + o.days + " day" + (o.days > 1 ? "s" : ""), "$" + fmt(labor), null]); + if(o.damage){ + var sev = o.damage > 38 ? "HIGH" : o.damage > 26 ? "MODERATE" : "LOW"; + lines.splice(2, 0, ["DAMAGE  " + o.damage + " impacts · facet F2 · severity " + sev, "FLAG", "damage"]); + lines.splice(3, 0, ["Digital Twin evidence pack · " + (o.damage * 4 + 38) + " photos", "included", "damage"]); + } + return { + roof:{ style:o.style, wf:o.wf, pn:o.pn }, + lines:lines, total:"$" + fmt(total), time:o.time + }; + } + + var jobs = [ + makeEstimate({style:"gable", wf:42, pn:6, scanLabel:"aerial imagery + street view", scanTime:"0.4s", + layers:2, crew:5, days:2, gutters:110, + extras:[["Chimney reflash + cricket",680],["Skylight curb + reflash · 2 units",940]], + time:"3.1 seconds."}), + makeEstimate({style:"hip", wf:46, pn:8, scanLabel:"aerial imagery + street view", scanTime:"0.5s", + shingleRate:203, crew:5, days:1, time:"3.4 seconds."}), + makeEstimate({style:"storm", wf:58, pn:5, scanLabel:"hail impact detection", scanTime:"0.6s", + shingleRate:255, shingleLabel:"Class-4 impact shingles", + crew:6, days:2, damage:41, gutters:148, + extras:[["Decking replacement · 12 sheets",1560],["Drip edge + valley shield · code items",1100],["Permits + inspection",850]], + time:"2.8 seconds. Claim-ready."}) + ]; + + var body = document.getElementById("estBody"); + var timer = []; + function clearTimers(){ timer.forEach(clearTimeout); timer = []; } + function renderJob(i, animate){ renderEstimate(jobs[i], animate); } + + function renderEstimate(j, animate){ + clearTimers(); + roof3d.set(j.roof); + document.getElementById("estTime").textContent = j.time; + + body.innerHTML = ""; + var frag = document.createDocumentFragment(); + j.lines.forEach(function(l){ + var d = document.createElement("div"); + d.className = "est-line"; + d.innerHTML = ""+l[0]+""+l[1]+""; + if(l[2]){ + d.setAttribute("data-hl", l[2]); + d.addEventListener("mouseenter", function(){ roof3d.highlight(l[2]); }); + d.addEventListener("mouseleave", function(){ roof3d.highlight(null); }); + } + frag.appendChild(d); + }); + var tot = document.createElement("div"); + tot.className = "est-total"; + tot.innerHTML = "ESTIMATE TOTAL"+j.total+""; + frag.appendChild(tot); + body.appendChild(frag); + + var rows = [].slice.call(body.querySelectorAll(".est-line")); + if(!animate || reduced){ + rows.forEach(function(r){ r.classList.add("on"); }); + tot.classList.add("on"); + return; + } + var roofBox = document.querySelector(".est-roof"); + roofBox.classList.remove("scanning"); + void roofBox.offsetWidth; + roofBox.classList.add("scanning"); + timer.push(setTimeout(function(){ roofBox.classList.remove("scanning"); }, 1300)); + rows.forEach(function(r, idx){ + timer.push(setTimeout(function(){ r.classList.add("on"); }, 220 + idx * 260)); + }); + timer.push(setTimeout(function(){ tot.classList.add("on"); }, 220 + rows.length * 260 + 180)); + } + + /* typed address: deterministic scripted estimate */ + function hashStr(s){ + var h = 2166136261; + for(var i = 0; i < s.length; i++){ h ^= s.charCodeAt(i); h = Math.imul(h, 16777619); } + return h >>> 0; + } + function rng(seed){ + return function(){ + seed = Math.imul(seed ^ (seed >>> 15), 2246822519); + seed = Math.imul(seed ^ (seed >>> 13), 3266489917); + return ((seed ^= seed >>> 16) >>> 0) / 4294967296; + }; + } + function jobFromAddress(addr){ + var clean = addr.replace(/[<>&"]/g, "").replace(/\s+/g, " ").trim(); + var r = rng(hashStr(clean.toLowerCase())); + var pitch = 4 + Math.floor(r() * 7); + var hipStyle = r() < .45; + var damaged = r() < .3; + var impacts = damaged ? 18 + Math.floor(r() * 36) : 0; + var widthFt = 30 + Math.round(r() * 30); + var layers = r() < .3 ? 2 : 1; + var crew = 4 + Math.floor(r() * 3); + var days = 1 + Math.round(r() * 3) / 2; + var short = clean.length > 30 ? clean.slice(0, 28) + "..." : clean; + return makeEstimate({ + style: hipStyle ? "hip" : "gable", wf: widthFt, pn: pitch, + scanLabel: short, scanTime: "0.5s", + layers: layers, crew: crew, days: days, + damage: impacts, misc: 600 + Math.round(r() * 350), + time: (2.4 + r() * 1.2).toFixed(1) + " seconds." + (damaged ? " Claim-ready." : "") + }); + } + + /* the addresses we have a client-supplied aerial survey for get their own modelled house */ + var BENT_OAK = "5705 Bent Oak Place, Dallas, TX 75248"; + function estateJob(){ + return makeEstimate({ + style: "estate", wf: 42, pn: 6, + scanLabel: BENT_OAK, scanTime: "0.6s", + layers: 1, crew: 6, days: 3, gutters: 165, + extras: [["Chimney reflash + cricket", 680], ["Pool deck coping reseal", 420]], + time: "3.3 seconds." + }); + } + var MUSTANG = "12552 Mustang Circle, Forney, TX 75126"; + function mustangJob(){ + return makeEstimate({ + style: "mustang", wf: 48, pn: 12, + scanLabel: MUSTANG, scanTime: "0.7s", + layers: 1, crew: 7, days: 3, gutters: 240, miscRate: 58, + extras: [["Ice & water shield · valleys + eaves", 1850], ["Steep-pitch safety rigging", 620]], + time: "3.6 seconds." + }); + } + + var chips = [].slice.call(document.querySelectorAll(".est-chips .chip")); + + /* the two addresses with a client-supplied aerial survey get their modelled house; + anything else falls back to the deterministic hashed estimate */ + function jobForAddress(addr){ + if(addr === MUSTANG) return mustangJob(); + if(addr === BENT_OAK) return estateJob(); + return jobFromAddress(addr); + } + + /* custom address dropdown: native popups can't be themed) ---------- */ + .addr-select{position:relative;flex:1;min-width:0} + .addr-select-btn{ + width:100%;display:flex;align-items:center;justify-content:space-between;gap:10px; + background:var(--canvas);border:1px solid var(--border);border-radius:4px; + color:var(--text);font-family:var(--mono);font-size:12.5px;letter-spacing:.02em; + padding:10px 12px;cursor:pointer;text-align:left;transition:border-color .12s ease,box-shadow .12s ease; + } + .addr-select-btn span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap} + .addr-select-btn .addr-chev{flex:none;color:var(--faint);transition:transform .15s ease} + .addr-select-btn:hover{border-color:var(--border-strong)} + .addr-select-btn:focus-visible{outline:none;border-color:var(--sky);box-shadow:0 0 0 3px rgba(69,175,201,.13)} + .addr-select.open .addr-select-btn{border-color:var(--sky)} + .addr-select.open .addr-chev{transform:rotate(180deg);color:var(--sky)} + .addr-select-list{ + position:absolute;left:0;right:0;top:calc(100% + 6px);z-index:20; + list-style:none;background:var(--surface-2);border:1px solid var(--border-strong);border-radius:8px; + padding:5px;box-shadow:0 20px 40px -16px rgba(0,0,0,.75); + opacity:0;transform:translateY(-4px);pointer-events:none;transition:opacity .12s ease,transform .12s ease; + } + .addr-select.open .addr-select-list{opacity:1;transform:translateY(0);pointer-events:auto} + .addr-select-list li{ + padding:9px 10px;border-radius:5px;cursor:pointer; + font-family:var(--mono);font-size:12px;letter-spacing:.01em;color:var(--dim); + display:flex;align-items:center;gap:8px;transition:background .1s ease,color .1s ease; + } + .addr-select-list li::before{content:"";width:13px;flex:none} + .addr-select-list li.on{color:var(--text)} + .addr-select-list li.on::before{content:"\2713";color:var(--sky);width:13px;font-size:11px} + .addr-select-list li:hover,.addr-select-list li.focus{background:rgba(69,175,201,.12);color:var(--text)} + .est-chips{display:flex;gap:8px;padding:14px 16px 4px;flex-wrap:wrap} + .chip{ + font-family:var(--mono);font-size:11.5px;letter-spacing:.04em; + padding:7px 12px;border:1px solid var(--border);border-radius:4px; + background:transparent;color:var(--dim);cursor:pointer; + transition:border-color .12s ease,color .12s ease,background .12s ease,box-shadow .15s ease; + } + .chip:hover{border-color:var(--border-strong);color:var(--text)} + .chip[aria-pressed="true"],.chip.on{ + border-color:var(--sky);color:var(--sky-bright);background:rgba(69,175,201,.07); + box-shadow:0 0 14px -4px rgba(69,175,201,.4); + } + .est-roof{padding:18px 16px 10px;position:relative} + .est-roof::before{ + content:"";position:absolute;inset:12px 10px 6px;pointer-events:none;opacity:.55;z-index:2; + background-image: + linear-gradient(var(--faint),var(--faint)),linear-gradient(var(--faint),var(--faint)), + linear-gradient(var(--faint),var(--faint)),linear-gradient(var(--faint),var(--faint)), + linear-gradient(var(--faint),var(--faint)),linear-gradient(var(--faint),var(--faint)), + linear-gradient(var(--faint),var(--faint)),linear-gradient(var(--faint),var(--faint)); + background-repeat:no-repeat; + background-size:12px 1.5px,1.5px 12px,12px 1.5px,1.5px 12px,12px 1.5px,1.5px 12px,12px 1.5px,1.5px 12px; + background-position:0 0,0 0,100% 0,100% 0,0 100%,0 100%,100% 100%,100% 100%; + } + .est-roof::after{ + content:"";position:absolute;top:10px;bottom:4px;left:16px;width:2px; + background:linear-gradient(180deg,transparent,var(--sky),transparent); + opacity:0;pointer-events:none; + } + .est-roof.scanning::after{opacity:.9;animation:scan 1.2s cubic-bezier(.2,0,0,1) 1 forwards} + @keyframes scan{from{left:16px}to{left:calc(100% - 18px);opacity:0}} + .est-slot{position:relative;width:100%;aspect-ratio:2.2} + .model-wrap{ + position:absolute;inset:0; + -webkit-mask-image:radial-gradient(115% 108% at 50% 45%, #000 58%, transparent 97%); + mask-image:radial-gradient(115% 108% at 50% 45%, #000 58%, transparent 97%); + } + .est-roof canvas{cursor:grab;touch-action:pan-y} + .drag-hint{ + position:absolute;right:20px;bottom:12px; + font-family:var(--mono);font-size:9px;letter-spacing:.12em; + color:var(--faint);text-transform:uppercase; + pointer-events:none;transition:opacity .4s ease; + } + .est-body{padding:8px 16px 16px;font-family:var(--mono);font-size:12.5px;min-height:238px} + .est-line{display:flex;justify-content:space-between;gap:12px;padding:4.5px 0;border-bottom:1px dashed rgba(31,41,51,.7);opacity:0} + .est-line.on{opacity:1} + .est-line .k{color:var(--dim);letter-spacing:.03em;white-space:nowrap} + .est-line .k b{color:var(--text);font-weight:600} + .est-line .v{color:var(--text);font-variant-numeric:tabular-nums;text-align:right} + .est-line .ok{color:var(--sky)} + .est-line .flag{color:var(--red);letter-spacing:.08em} + .est-line[data-hl]{cursor:crosshair;transition:background .12s ease} + .est-line[data-hl]:hover{background:rgba(242,168,59,.05)} + .est-total{ + display:flex;justify-content:space-between;align-items:center; + margin-top:14px;padding:13px 14px;border:1px solid var(--amber-dim); + background:rgba(242,168,59,.05);border-radius:4px;opacity:0; + } + .est-total.on{opacity:1} + .est-total .k{font-size:11px;letter-spacing:.14em;color:var(--amber)} + .est-total .v{font-size:22px;font-weight:700;color:var(--amber);font-variant-numeric:tabular-nums} + .est-hint{ + padding:0 16px 8px;font-family:var(--mono);font-size:9px;letter-spacing:.12em; + text-transform:uppercase;color:var(--faint); + } + .est-hint b{color:var(--amber);font-weight:500;margin-right:6px} + @media(hover:none){.est-hint{display:none}} + .est-foot{padding:0 16px 16px;font-family:var(--mono);font-size:11px;color:var(--faint);letter-spacing:.04em} + .est-foot b{color:var(--dim);font-weight:500} + + /* ---------- ticker ---------- */ + .ticker{border-top:1px solid var(--border);background:var(--canvas);overflow:hidden;opacity:.62} + .ticker-track{display:flex;white-space:nowrap;width:max-content;animation:tick 48s linear infinite;padding:11px 0} + .ticker:hover .ticker-track{animation-play-state:paused} + .ticker span{font-family:var(--mono);font-size:10.5px;letter-spacing:.14em;color:var(--faint);text-transform:uppercase;padding:0 30px;border-left:1px solid var(--border)} + .ticker b{color:var(--dim);font-weight:500} + @keyframes tick{to{transform:translateX(-50%)}} + + /* ---------- telemetry strip ---------- */ + .telemetry{border-top:1px solid var(--border);border-bottom:1px solid var(--border);background:var(--surface)} + .telemetry .wrap{display:grid;grid-template-columns:repeat(4,1fr);gap:0} + .tele{padding:28px 24px;border-left:1px solid var(--border)} + .tele:first-child{border-left:0} + .tele .num{font-size:clamp(1.7rem,3vw,2.4rem);font-weight:640;color:var(--text);line-height:1.1} + .tele .tag{margin-top:6px;display:block} + + /* ---------- sections ---------- */ + section{padding:88px 0} + section > .wrap{position:relative} + /* drafting-sheet numerals: ghost index behind each section head */ + .sheet{ + position:absolute;right:24px;top:44px;z-index:0;pointer-events:none; + font-size:clamp(72px,10vw,132px);font-weight:680;line-height:1; + letter-spacing:-.05em;color:rgba(237,239,242,.033);user-select:none; + } + /* the dusk arc: afternoon up top, lights-on by the footer */ + #industries{background:radial-gradient(900px 420px at 84% -8%, rgba(242,168,59,.014), transparent 62%)} + #risk{background:radial-gradient(900px 420px at 10% 108%, rgba(242,168,59,.02), transparent 62%)} + /* case file runs full-bleed as a banded sheet with faint plan grid */ + #casefile{ + border-top:1px solid var(--border); + background: + radial-gradient(1000px 480px at 88% 110%, rgba(242,168,59,.035), transparent 62%), + linear-gradient(rgba(69,175,201,.022) 1px, transparent 1px), + linear-gradient(90deg, rgba(69,175,201,.022) 1px, transparent 1px), + var(--surface); + background-size:auto,56px 56px,56px 56px,auto; + } + #pricing{background:radial-gradient(1000px 480px at 12% -6%, rgba(242,168,59,.033), transparent 62%)} + #terms{background:radial-gradient(1000px 500px at 90% 106%, rgba(242,168,59,.04), transparent 62%)} + .dim-rule{ + display:flex;align-items:center;gap:14px;margin-bottom:34px;grid-column:1/-1; + font-family:var(--mono);font-size:9.5px;letter-spacing:.16em;color:var(--faint); + text-transform:uppercase;white-space:nowrap; + } + .dim-rule i{display:block;flex:1;height:1px;background:var(--border);position:relative} + .dim-rule i::before{content:"";position:absolute;top:-4.5px;width:1px;height:10px;background:var(--border-strong)} + .dim-rule i::after{content:"";position:absolute;top:-2px;width:0;height:0;border-top:2.5px solid transparent;border-bottom:2.5px solid transparent} + .dim-rule i.l::before{left:0} + .dim-rule i.l::after{left:2px;border-right:6px solid var(--border-strong)} + .dim-rule i.r::before{right:0} + .dim-rule i.r::after{right:2px;border-left:6px solid var(--border-strong)} + .sec-head{margin-bottom:44px;max-width:62ch} + .sec-head .tag{display:block;margin-bottom:14px} + h2{font-size:clamp(1.9rem,3.3vw,2.7rem);letter-spacing:-.028em;line-height:1.1;font-weight:620;text-wrap:balance} + h2 em{font-style:normal;color:var(--amber)} + .sec-head p{color:var(--dim);margin-top:14px;max-width:54ch} + + /* ---------- glass card baseline ---------- */ + .vs-chip,.ind,.risk-card,.plan,.founder-card,.quote-card,.feat,.car-card,.incl-item,.oc,.terms-card{ + background:linear-gradient(180deg, rgba(255,255,255,.035), rgba(255,255,255,0) 44%), rgba(18,21,26,.55); + border:1px solid; + border-color:rgba(255,255,255,.085) rgba(255,255,255,.05) rgba(0,0,0,.55); + -webkit-backdrop-filter:blur(14px) saturate(1.15); + backdrop-filter:blur(14px) saturate(1.15); + border-radius:8px; + } + /* amber viewfinder brackets on hover */ + .ind,.risk-card,.plan,.founder-card,.feat,.oc,.terms-card{position:relative} + .ind::before,.risk-card::before,.plan::before,.founder-card::before,.feat::before,.oc::before,.terms-card::before{ + content:"";position:absolute;inset:7px;pointer-events:none;opacity:0;z-index:2; + background-image: + linear-gradient(var(--amber),var(--amber)),linear-gradient(var(--amber),var(--amber)), + linear-gradient(var(--amber),var(--amber)),linear-gradient(var(--amber),var(--amber)), + linear-gradient(var(--amber),var(--amber)),linear-gradient(var(--amber),var(--amber)), + linear-gradient(var(--amber),var(--amber)),linear-gradient(var(--amber),var(--amber)); + background-repeat:no-repeat; + background-size:12px 1.5px,1.5px 12px,12px 1.5px,1.5px 12px,12px 1.5px,1.5px 12px,12px 1.5px,1.5px 12px; + background-position:0 0,0 0,100% 0,100% 0,0 100%,0 100%,100% 100%,100% 100%; + transform:scale(1.04); + transition:opacity .15s ease,transform .15s cubic-bezier(.2,0,0,1),inset .15s cubic-bezier(.2,0,0,1); + } + .ind:hover::before,.risk-card:hover::before,.plan:hover::before,.founder-card:hover::before,.feat:hover::before,.oc:hover::before,.terms-card:hover::before{opacity:.85;transform:scale(1);inset:4px} + /* cursor glare */ + .ind::after,.risk-card::after,.plan::after,.founder-card::after,.feat::after,.oc::after,.terms-card::after{ + content:"";position:absolute;inset:0;border-radius:8px; + background:radial-gradient(300px 220px at var(--gx,50%) var(--gy,50%),rgba(120,220,240,.07),transparent 60%); + opacity:0;transition:opacity .25s ease;pointer-events:none; + } + .ind:hover::after,.risk-card:hover::after,.plan:hover::after,.founder-card:hover::after,.feat:hover::after,.oc:hover::after,.terms-card:hover::after{opacity:1} + .tilting{transition:box-shadow .12s ease,border-color .12s ease!important} + + /* ---------- reveal motion ---------- */ + .reveal{opacity:0;transform:translateY(12px);transition:opacity .15s ease,transform .15s cubic-bezier(.2,0,0,1)} + .reveal.in{opacity:1;transform:none} + + /* ---------- job plate (drafting title block) ---------- */ + .job-plate{ + position:relative;display:flex;flex-wrap:wrap;margin-bottom:20px; + border:1px solid rgba(255,255,255,.09);border-radius:6px;overflow:hidden; + background:rgba(11,13,16,.5); + -webkit-backdrop-filter:blur(10px) saturate(1.1); + backdrop-filter:blur(10px) saturate(1.1); + } + .job-plate::after{ + content:"";position:absolute;inset:3px;border:1px solid rgba(223,231,236,.09); + border-radius:3px;pointer-events:none; + } + .jp-cell{ + flex:1 1 auto;display:flex;flex-direction:column;gap:3px; + padding:11px 18px 10px;border-left:1px solid rgba(255,255,255,.07); + } + .jp-cell:first-child{border-left:0} + .jp-wide{flex:2 1 auto} + .jp-k{font-family:var(--mono);font-size:8.5px;letter-spacing:.18em;text-transform:uppercase;color:var(--faint)} + .jp-v{font-size:13.5px;font-weight:560;color:var(--text);white-space:nowrap} + .jp-v.hi{color:var(--amber);font-size:13px;letter-spacing:.06em} + .jp-v i{font-style:normal;color:var(--faint);font-size:11px;padding:0 2px} + .jp-a{color:var(--red);font-weight:600;font-size:12.5px} + .jp-b{color:var(--amber);font-weight:600;font-size:12.5px} + @media(max-width:860px){ + .jp-cell{flex:1 1 46%;border-top:1px solid rgba(255,255,255,.07)} + .jp-cell:nth-child(-n+2){border-top:0} + .jp-cell:nth-child(odd){border-left:0} + .jp-v{white-space:normal} + } + + /* ---------- dual-clock timeline ---------- */ + .tl{margin-top:6px} + .tl-lane{position:relative} + .tl-head{display:flex;justify-content:space-between;align-items:baseline;gap:12px;flex-wrap:wrap;margin:0 2px 10px} + .tl-lane.good .tl-head{margin:14px 2px 0} + .tl-head span:first-child{font-size:14px;font-weight:620;color:var(--text)} + .tl-close{font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;color:var(--red)} + .tl-close.good{color:var(--amber)} + .tl-track{position:relative} + .tl-lane.bad .tl-track{height:150px} + .tl-lane.good .tl-track{height:176px} + .tl-line{ + position:absolute;left:0;height:2px;width:var(--len,100%);background:var(--border-strong); + transform:scaleX(0);transform-origin:left; + transition:transform 1.3s cubic-bezier(.2,0,0,1) .2s; + } + .tl-lane.bad .tl-line{bottom:0} + .tl-lane.good .tl-line{top:0;transition-duration:.55s} + .tl.in .tl-line{transform:scaleX(1)} + .tl-node{position:absolute;left:calc(var(--d)/47*100%);width:15px;height:15px;opacity:0;transform:translateX(-50%) translateY(3px);transition:opacity .15s ease,transform .15s cubic-bezier(.2,0,0,1)} + .tl-lane.bad .tl-node{bottom:-7px;transition-delay:calc(.2s + var(--d)/47*1.3s)} + .tl-lane.good .tl-node{top:-7px;transition-delay:calc(.2s + var(--d)/15*.55s)} + .tl.in .tl-node{opacity:1;transform:translateX(-50%) translateY(0)} + .tl-node::after{ + content:"";position:absolute;inset:0;background:var(--surface-2);border:1.5px solid var(--dim); + transform:rotate(45deg);transition:border-color .12s ease,background .12s ease; + } + .tl-node.end::after{background:var(--surface-2);border-color:var(--text)} + .tl-node i{position:absolute;left:50%;width:1px;background:var(--border-strong)} + .tl-lane.bad .tl-node i{bottom:100%;height:calc(18px + var(--row)*30px)} + .tl-lane.good .tl-node i{top:100%;height:calc(28px + var(--row)*30px)} + .tl-tag{ + position:absolute;left:50%;transform:translateX(-50%); + font-family:var(--mono);font-size:10.5px;font-weight:600;letter-spacing:.08em;color:var(--text); + background:var(--canvas);padding:0 3px; + } + .tl-lane.bad .tl-tag{top:calc(100% + 7px)} + .tl-lane.good .tl-tag{top:calc(100% + 5px)} + .tl-lbl{ + position:absolute;left:50%;transform:translateX(-50%);white-space:nowrap; + font-size:13.5px;font-weight:560;color:var(--text);background:var(--surface-2); + padding:4px 10px;border:1px solid var(--border-strong);border-radius:4px; + box-shadow:0 4px 14px -6px rgba(0,0,0,.7); + transition:color .12s ease,border-color .12s ease,background .12s ease; + } + .tl-lane.bad .tl-lbl{bottom:calc(100% + 13px + var(--row)*30px)} + .tl-lane.good .tl-lbl{top:calc(100% + 24px + var(--row)*30px)} + .tl-node.el .tl-lbl{left:-4px;transform:none} + .tl-node.end .tl-lbl{left:auto;right:-6px;transform:none} + .tl-lane.bad .tl-node.end .tl-tag{left:auto;right:0;transform:none} + .tl-ruler{position:relative;height:26px;margin:32px 0 10px;border-top:1px solid var(--border-strong)} + .tl-ruler span{ + position:absolute;top:9px;left:calc(var(--d)/47*100%);transform:translateX(-50%); + font-family:var(--mono);font-size:10.5px;color:var(--dim);letter-spacing:.08em; + } + .tl-ruler span::before{content:"";position:absolute;left:50%;bottom:100%;margin-bottom:2px;width:1px;height:5px;background:var(--border-strong)} + .tl-ruler span.cap{transform:none;left:auto;right:0} + .tl-ruler span.cap::before{display:none} + .tl-rec{ + position:absolute;left:calc(15/47*100% + 16px);right:0;top:14px;height:26px; + display:flex;align-items:center;justify-content:center; + font-family:var(--mono);font-size:11px;font-weight:600;letter-spacing:.16em;color:var(--green); + border:1px dashed var(--green-dim);border-radius:3px; + background:repeating-linear-gradient(-45deg,rgba(76,195,138,.08) 0 8px,transparent 8px 16px); + opacity:0;transition:opacity .3s ease .85s; + } + .tl.in .tl-rec{opacity:1} + #chaos .tl-node.hot::after{border-color:var(--amber);background:rgba(242,168,59,.3)} + #chaos .tl-lane.bad .tl-node.hot::after{border-color:var(--red);background:rgba(242,109,109,.25)} + #chaos .tl-node.hot .tl-lbl{border-color:var(--amber);background:var(--surface)} + @media(max-width:860px){ + .tl-lbl{display:none} + .tl-lane.bad .tl-track{height:58px} + .tl-lane.good .tl-track{height:70px} + .tl-node i{display:none} + } + + /* ---------- vs chips ---------- */ + .vs-totals{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-top:20px} + .vs-chip{border-radius:6px;padding:14px 16px;transition:border-color .15s ease,background .15s ease} + .vs-chip .lbl{font-family:var(--mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint);margin-bottom:8px} + .vs-chip .vals{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;font-family:var(--mono);font-size:13px;font-variant-numeric:tabular-nums} + .vs-chip .was{color:rgba(242,109,109,.8);text-decoration:line-through;text-decoration-color:rgba(242,109,109,.45);text-decoration-thickness:1px} + .vs-chip .arr{color:var(--faint)} + .vs-chip .now{color:var(--amber);font-weight:600} + .vs-chip.hot{border-color:var(--amber-dim);background:var(--surface-2)} + .vs-chip.margin .now{color:var(--green);font-size:15px;font-weight:700} + + /* ---------- comparison slider ---------- */ + .cmp{ + position:relative;border-radius:12px;overflow:hidden;isolation:isolate; + max-width:1024px;height:600px;margin-inline:auto; + user-select:none;touch-action:pan-y;cursor:ew-resize; + border:1px solid;border-color:rgba(255,255,255,.1) rgba(255,255,255,.06) rgba(0,0,0,.55); + box-shadow:0 40px 90px -40px rgba(0,0,0,.9),0 0 120px -40px rgba(69,175,201,.15); + } + .cmp img{width:100%;height:100%;object-fit:cover;pointer-events:none} + .cmp-new{position:absolute;inset:0} + .cmp-old{position:absolute;inset:0;width:var(--pos,50%);overflow:hidden;border-right:2px solid rgba(233,238,243,.9)} + .cmp-old img{width:calc(100% / var(--posf,.5));max-width:none;filter:grayscale(1) sepia(.22) brightness(.9)} + .cmp-list{position:absolute;top:20px;display:flex;flex-direction:column;gap:9px;transition:opacity .15s ease} + .cmp-list-old{left:20px;align-items:flex-start} + .cmp-list-new{right:20px;align-items:flex-end} + .cmp-badge{ + font-weight:700;font-size:19px;color:var(--text);padding:9px 17px;border-radius:9px; + background:rgba(8,11,15,.62);border:1px solid rgba(242,109,109,.4); + -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);margin-bottom:5px; + } + .cmp-badge.new{border-color:rgba(69,175,201,.5)} + .cmp-item{ + display:flex;align-items:center;gap:9px;padding:6px 12px;border-radius:7px; + background:rgba(8,11,15,.55);border:1px solid rgba(255,255,255,.08); + -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px); + font-size:14.5px;font-weight:600;white-space:nowrap; + } + .cmp-item i{ + font-style:normal;width:19px;height:19px;border-radius:50%;flex:none; + display:grid;place-items:center;font-size:11px; + } + .cmp-item .ok{background:var(--sky);color:#03222B} + .cmp-item .no{background:rgba(242,109,109,.85);color:#fff} + .cmp-handle{ + position:absolute;top:0;bottom:0;left:var(--pos,50%);width:0;z-index:3;cursor:ew-resize; + display:flex;align-items:center;justify-content:center; + } + .cmp-grip{ + width:42px;height:42px;flex:none;border-radius:50%;display:grid;place-items:center; + background:rgba(233,238,243,.95);color:#0A0E13; + box-shadow:0 0 0 4px rgba(69,175,201,.25),0 8px 30px rgba(0,0,0,.6); + transition:transform .12s ease,box-shadow .15s ease; + } + .cmp:hover .cmp-grip{transform:scale(1.06);box-shadow:0 0 0 5px rgba(69,175,201,.4),0 8px 30px rgba(0,0,0,.6)} + .cmp-cue{ + position:absolute;left:50%;bottom:16px;transform:translateX(-50%);z-index:3; + padding:7px 14px;border-radius:20px;background:rgba(8,11,15,.6); + border:1px solid rgba(255,255,255,.1);pointer-events:none; + -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px); + transition:opacity .3s ease; + } + @media(max-width:860px){ + .cmp{height:auto;aspect-ratio:4/4.4;cursor:default} + .cmp-item{font-size:11px;padding:3px 8px} + .cmp-item.wide{display:none} + .cmp-badge{font-size:13px} + } + + /* ---------- intelligence map ---------- */ + .intel-tabs{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:18px} + .intel{ + position:relative;border-radius:12px;overflow:hidden; + border:1px solid;border-color:rgba(255,255,255,.1) rgba(255,255,255,.06) rgba(0,0,0,.55); + background:var(--surface); + box-shadow:0 40px 90px -40px rgba(0,0,0,.9),0 0 120px -40px rgba(69,175,201,.12); + } + .intel-map{position:relative;height:560px;overflow:hidden} + .intel-map img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;filter:saturate(.45) brightness(.62) contrast(1.04)} + /* dark intel overlay: the photo recedes, the pins own the frame */ + .intel-map::after{ + content:"";position:absolute;inset:0;pointer-events:none;z-index:1; + background: + linear-gradient(rgba(237,239,242,.03) 1px, transparent 1px), + linear-gradient(90deg, rgba(237,239,242,.03) 1px, transparent 1px), + linear-gradient(180deg, rgba(9,12,16,.5), rgba(9,12,16,.18) 30%, rgba(9,12,16,.2) 65%, rgba(9,12,16,.62)), + radial-gradient(120% 90% at 50% 50%, rgba(13,20,28,.14) 40%, rgba(6,8,11,.66)); + background-size:44px 44px,44px 44px,auto,auto; + } + /* radar-verified hail corridor, revealed by the storm-path filter */ + .swath{ + position:absolute;inset:0;width:100%;height:100%;z-index:2;pointer-events:none; + opacity:0;transition:opacity .4s ease;overflow:visible; + } + .intel-map.swath-on .swath{opacity:1} + .swath polygon{fill:url(#swathGrad);stroke:rgba(242,109,109,.85);stroke-width:2;stroke-dasharray:8 6} + .swath line{stroke:rgba(242,109,109,.5);stroke-width:1;stroke-dasharray:2 7} + .swath text{fill:var(--red);font:600 10px var(--mono);letter-spacing:.14em} + .intel-scanline{ + position:absolute;left:-30%;top:0;bottom:0;width:26%;pointer-events:none;z-index:2;opacity:.4; + background:linear-gradient(90deg,transparent,rgba(69,175,201,.06) 45%,rgba(69,175,201,.14) 50%,rgba(69,175,201,.06) 55%,transparent); + animation:sweep 9s linear infinite; + } + @keyframes sweep{to{left:104%}} + .intel-region{ + position:absolute;left:16px;top:16px;z-index:4;padding:9px 14px;border-radius:8px; + background:rgba(8,11,15,.6);border:1px solid rgba(69,175,201,.35);border-left-width:3px; + -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px); + } + .intel-region .tag{font-size:9px;color:var(--sky)} + .intel-region b{display:block;font-size:15px;font-weight:700} + .mk{ + position:absolute;z-index:4;width:16px;height:16px;margin:-8px 0 0 -8px; + border-radius:50%;padding:0;cursor:pointer;color:var(--sky-bright); + background:currentColor; + border:2px solid rgba(255,255,255,.92); + box-shadow:0 0 0 3px rgba(9,12,16,.65),0 0 14px currentColor,0 2px 8px rgba(0,0,0,.7); + transition:transform .12s ease,box-shadow .15s ease,opacity .2s ease; + } + .mk::after{ + content:"";position:absolute;inset:-8px;border-radius:50%; + border:1.5px solid currentColor;opacity:.8; + animation:ping 2.6s cubic-bezier(.2,0,.4,1) infinite; + } + @keyframes ping{0%{transform:scale(.5);opacity:.9}80%,100%{transform:scale(1.9);opacity:0}} + .mk.commercial{color:#C9B2FF} + .mk.storm{color:var(--red)} + .mk:hover{transform:scale(1.3)} + .mk.sel{ + transform:scale(1.35); + box-shadow:0 0 0 3px rgba(9,12,16,.65),0 0 0 5.5px rgba(255,255,255,.4),0 0 22px currentColor; + } + .mk.hid,.mk.crop{opacity:0;pointer-events:none;visibility:hidden} + /* 40px+ touch target behind each 16px pin */ + .mk::before{content:"";position:absolute;inset:-13px;border-radius:50%} + .ip-tabs{display:flex;gap:20px;border-bottom:1px solid var(--border);margin-bottom:14px} + .ip-tabs button{ + background:none;border:0;cursor:pointer;padding:2px 2px 9px;position:relative; + font-family:var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase; + color:var(--faint);transition:color .12s ease; + } + .ip-tabs button::after{ + content:"";position:absolute;left:0;right:0;bottom:-1px;height:2px; + background:var(--sky);transform:scaleX(0);transition:transform .15s cubic-bezier(.2,0,0,1); + } + .ip-tabs button:hover{color:var(--dim)} + .ip-tabs button.on{color:var(--sky-bright)} + .ip-tabs button.on::after{transform:scaleX(1)} + .ip-photos{display:flex;flex-direction:column;gap:10px} + .ip-ph{border:1px solid var(--border);border-radius:8px;overflow:hidden;margin:0;background:var(--surface-2)} + .ip-ph img{width:100%;height:132px;object-fit:cover;display:block;filter:saturate(.9)} + .ip-ph figcaption{padding:7px 10px;font-size:9px;border-top:1px solid var(--border)} + /* the intel feed floats over the map and only appears once a pin is selected */ + .intel-panel{ + position:absolute;top:14px;right:14px;bottom:14px;width:306px;z-index:6; + border:1px solid;border-color:rgba(255,255,255,.12) rgba(255,255,255,.07) rgba(0,0,0,.55); + border-radius:12px;padding:16px; + background:linear-gradient(180deg, rgba(255,255,255,.04), rgba(255,255,255,0) 30%), rgba(11,15,20,.82); + -webkit-backdrop-filter:blur(16px) saturate(1.1);backdrop-filter:blur(16px) saturate(1.1); + box-shadow:0 30px 70px -30px rgba(0,0,0,.9); + display:flex;flex-direction:column;font-size:13px;overflow-y:auto; + opacity:0;transform:translateX(14px);pointer-events:none; + transition:opacity .18s ease,transform .2s cubic-bezier(.2,0,0,1); + } + .intel-panel.open{opacity:1;transform:none;pointer-events:auto} + .ip-tabs[hidden],.ip-photos[hidden]{display:none} + .ip-head{display:flex;justify-content:space-between;align-items:center;padding-bottom:12px;border-bottom:1px solid var(--border);margin-bottom:14px} + .ip-head .tag{color:var(--sky);letter-spacing:.18em} + .ip-close{ + width:26px;height:26px;border-radius:50%;border:1px solid var(--border-strong); + background:transparent;color:var(--dim);cursor:pointer;font-size:10px; + transition:border-color .12s ease,color .12s ease; + } + .ip-close:hover{border-color:var(--sky);color:var(--sky)} + .ip-chips{display:flex;gap:8px;margin-bottom:14px} + .pill{ + display:inline-block;font-family:var(--mono);font-size:9.5px;font-style:normal; + letter-spacing:.1em;text-transform:uppercase;padding:4px 10px;border-radius:20px; + border:1px solid rgba(76,195,138,.4);color:#4CC38A; + } + .pill.dim{border-color:var(--border-strong);color:var(--dim)} + .pill.hot{border-color:rgba(242,109,109,.55);color:var(--red)} + .pill.warn{border-color:var(--amber-dim);color:var(--amber)} + .ip-value{ + border:1px solid rgba(69,175,201,.35);border-radius:8px;padding:12px 14px;margin-bottom:14px; + background:linear-gradient(180deg,rgba(69,175,201,.09),rgba(69,175,201,.02)); + } + .ip-value b{display:block;font-size:30px;font-weight:700;color:var(--sky-bright);margin-top:2px} + .ip-row{margin-bottom:13px} + .ip-row .tag{display:block;font-size:9px;margin-bottom:4px} + .ip-row b{font-size:14.5px;font-weight:650} + .ip-row b.warn{color:var(--amber);font-size:13.5px} + .ip-row p{color:var(--faint);font-size:11.5px;margin-top:2px} + .ip-cols{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:14px} + .ip-cols .tag{display:block;font-size:9px;margin-bottom:4px} + .ip-cols b{font-size:13.5px} + .ip-owner{ + border:1px dashed var(--border-strong);border-radius:8px;padding:11px 12px;margin-bottom:16px; + } + .ip-owner > .tag{display:block;font-size:9px;margin-bottom:8px;color:#4CC38A} + .ip-fico{display:flex;align-items:baseline;gap:10px;padding:4px 0;font-size:12.5px;color:var(--dim)} + .ip-fico b{margin-left:auto} + .ip-fico .blur{filter:blur(4px);user-select:none} + .ip-fico a{font-family:var(--mono);font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--sky);border-bottom:1px dashed var(--sky-dim)} + .ip-cta{justify-content:center;margin-top:auto} + /* hail watch alert card */ + .hail-watch{ + position:absolute;top:16px;right:16px;z-index:4; + display:flex;gap:11px;align-items:flex-start;max-width:250px; + padding:11px 14px;border-radius:10px; + background:rgba(8,11,15,.72);border:1px solid rgba(242,168,59,.3); + -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px); + transition:opacity .18s ease; + } + .intel.panel-open .hail-watch{opacity:0;pointer-events:none} + /* .hail-watch span (0,1,1) outranks a lone class, so match its specificity here */ + .hail-watch span.hw-ico{ + width:34px;height:34px;flex:none;border-radius:50%; + display:flex;align-items:center;justify-content:center; + color:var(--amber);background:rgba(242,168,59,.1);border:1px solid rgba(242,168,59,.3); + } + .hw-ico svg{display:block;flex:none} + .hail-watch b{display:block;font-size:13px;font-weight:650} + .hail-watch span{display:block;font-size:11.5px;color:var(--sky-bright)} + .hail-watch i{display:block;font-style:normal;font-size:8.5px;margin-top:4px} + /* schedule conflict toast: an OS-notification contrast piece */ + .conflict{ + position:absolute;left:16px;bottom:16px;z-index:5;width:min(320px,calc(100% - 32px)); + border-radius:10px;overflow:hidden;padding:13px 15px 13px 18px; + background:rgba(237,239,242,.95);color:#181C22; + border-left:3px solid #E5484D; + box-shadow:0 20px 50px -18px rgba(0,0,0,.8); + transition:opacity .25s ease,transform .25s cubic-bezier(.2,0,0,1); + } + .conflict.gone{opacity:0;transform:translateY(10px);pointer-events:none} + .cf-head{display:flex;align-items:center;gap:9px;margin-bottom:7px} + .cf-dot{ + width:19px;height:19px;flex:none;border-radius:50%;display:grid;place-items:center; + background:rgba(229,72,77,.14);color:#E5484D;font-size:11px;font-weight:800; + } + .cf-head b{font-size:12.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase} + .cf-x{ + margin-left:auto;border:0;background:none;color:#8A9099;cursor:pointer;font-size:11px; + padding:3px 5px;border-radius:4px; + } + .cf-x:hover{color:#181C22} + .conflict p{font-size:13px;line-height:1.5;color:#3A414B;margin-bottom:11px} + .conflict p b{color:#181C22} + .cf-actions{display:flex;gap:8px} + .cf-btn{ + font-family:var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase; + padding:8px 14px;border-radius:6px;cursor:pointer; + border:1px solid rgba(24,28,34,.25);background:transparent;color:#3A414B; + transition:background .12s ease,color .12s ease; + } + .cf-btn:hover{background:rgba(24,28,34,.06)} + .cf-btn.solid{background:#181C22;border-color:#181C22;color:#EDEFF2} + .cf-btn.solid:hover{background:#2A313B} + /* pipeline stats band */ + .intel-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-top:14px} + .istat{ + position:relative;padding:20px 18px 14px;border-radius:8px; + border:1px solid;border-color:rgba(255,255,255,.085) rgba(255,255,255,.05) rgba(0,0,0,.55); + background: + repeating-linear-gradient(90deg, rgba(237,239,242,.16) 0 1.5px, transparent 1.5px 22px) 14px 100% / calc(100% - 28px) 7px no-repeat, + repeating-linear-gradient(90deg, rgba(237,239,242,.16) 0 1.5px, transparent 1.5px 22px) 14px 0 / calc(100% - 28px) 7px no-repeat, + linear-gradient(180deg, rgba(255,255,255,.03), rgba(255,255,255,0) 44%), rgba(18,21,26,.55); + display:flex;flex-direction:column;gap:7px; + } + .istat .tag{font-size:9px} + .istat > .num{font-size:30px;font-weight:700;line-height:1.1} + .istat .num.sky{color:var(--sky-bright)} + .istat .num.amber{color:var(--amber)} + .istat .num.red{color:var(--red)} + .istat .num.ok{color:#4CC38A} + .istat .stamp-tag{margin-top:4px} + .intel-foot{margin-top:14px;text-align:center;font-size:9.5px} + @media(max-width:1020px){ + .intel-map{height:420px} + .intel-panel{width:min(306px,calc(100% - 28px))} + .intel-stats{grid-template-columns:repeat(2,1fr)} + .hail-watch{max-width:210px} + } + + /* ---------- product features: blueprint figures that ink in on hover ---------- */ + .feats{display:grid;grid-template-columns:repeat(4,1fr);gap:16px} + .feat{overflow:hidden;display:flex;flex-direction:column;transition:transform .08s ease,border-color .12s ease,box-shadow .15s ease} + .feat:hover{border-color:rgba(255,255,255,.16) rgba(255,255,255,.09) rgba(0,0,0,.55);box-shadow:0 24px 60px -28px rgba(0,0,0,.9)} + .feat-media{ + aspect-ratio:16/10;position:relative;display:grid;place-items:center; + background: + repeating-linear-gradient(45deg,transparent 0 6px,rgba(32,36,43,.5) 6px 7px), + var(--surface-2); + border-bottom:1px solid var(--border); + } + .feat-thumb{ + position:absolute;inset:0;width:100%;height:100%;object-fit:cover; + filter:saturate(.65) brightness(.72); + transition:filter .2s ease,transform .3s cubic-bezier(.2,0,0,1); + } + .feat:hover .feat-thumb{filter:saturate(.9) brightness(.88);transform:scale(1.015)} + .feat.on .feat-thumb{filter:none} + .feat-play{ + position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);z-index:2; + width:44px;height:44px;border-radius:50%;display:grid;place-items:center;padding-left:3px; + border:1px solid rgba(124,207,226,.5);background:rgba(11,15,20,.6);color:var(--sky-bright); + -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px); + box-shadow:0 0 0 5px rgba(69,175,201,.1),0 14px 34px -14px rgba(0,0,0,.9); + transition:transform .15s cubic-bezier(.2,0,0,1),background .12s ease; + } + .feat:hover .feat-play{transform:translate(-50%,-50%) scale(1.08);background:rgba(11,15,20,.78)} + .feat-media .cap{position:absolute;left:10px;bottom:8px;z-index:2;font-family:var(--mono);font-size:9.5px;letter-spacing:.1em;color:var(--text);text-shadow:0 1px 4px rgba(0,0,0,.8);text-transform:uppercase} + .feat-body{padding:18px 18px 20px;display:flex;flex-direction:column;gap:8px;flex:1} + .feat-body h3{font-size:16px;font-weight:650} + .feat-body p{color:var(--dim);font-size:13.5px;flex:1} + .feat-link{ + font-family:var(--mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase; + color:var(--faint);display:inline-flex;gap:7px;align-items:center;transition:color .12s ease; + } + .feat-link span{transition:transform .12s ease} + /* figure cards double as reel selectors */ + .deck-sel{ + display:flex;align-items:center;gap:14px;margin:58px 0 24px; + font-family:var(--mono);font-size:9px;letter-spacing:.18em;color:var(--faint); + text-transform:uppercase;white-space:nowrap; + } + .deck-sel i{flex:1;height:1px;background:linear-gradient(90deg,transparent,var(--border) 30% 70%,transparent)} + .feats-select{margin-top:0} + .feats-select .feat{cursor:pointer} + .feat:hover .feat-link{color:var(--dim)} + .feat.on{border-color:rgba(242,168,59,.4) rgba(242,168,59,.2) rgba(0,0,0,.55)} + .feat.on::before{opacity:.85;transform:scale(1);inset:4px} + .feat.on .feat-link{color:var(--amber)} + .feat.on .feat-link span{transform:none;animation:pulse 1.6s ease-in-out infinite} + .integ{ + display:flex;align-items:center;gap:26px;flex-wrap:wrap; + margin-top:30px;padding:16px 4px 0;border-top:1px solid var(--border); + } + .integ span{font-size:13.5px;color:var(--faint);font-weight:560} + .integ .tag{font-size:10px;color:var(--faint)} + + /* ---------- live product reel: center-stage deck ---------- */ + .deck-stage{position:relative;height:560px;touch-action:pan-y} + .deck-card{ + position:absolute;left:50%;top:0;width:min(760px,80vw); + border-radius:12px;overflow:hidden; + border:1px solid;border-color:rgba(255,255,255,.1) rgba(255,255,255,.06) rgba(0,0,0,.55); + background:var(--surface); + box-shadow:0 40px 90px -40px rgba(0,0,0,.9); + transform:translateX(-50%) scale(.7);opacity:0;pointer-events:none; + transition:transform .5s cubic-bezier(.2,0,0,1),opacity .35s ease,filter .35s ease; + will-change:transform; + } + .deck-card.is-active{transform:translateX(-50%) scale(1);opacity:1;pointer-events:auto;z-index:3;filter:none} + .deck-card.is-prev,.deck-card.is-next{ + opacity:.45;pointer-events:auto;z-index:2;cursor:pointer; + filter:brightness(.45) saturate(.7); + } + .deck-card.is-prev{transform:translateX(calc(-50% - 56%)) scale(.78)} + .deck-card.is-next{transform:translateX(calc(-50% + 56%)) scale(.78)} + .deck-card.is-prev:hover,.deck-card.is-next:hover{opacity:.7;filter:brightness(.6) saturate(.85)} + .deck-chrome{ + display:flex;align-items:center;gap:7px;padding:11px 14px; + background:var(--surface-2);border-bottom:1px solid var(--border); + } + .deck-chrome i{width:9px;height:9px;border-radius:50%;background:var(--border-strong)} + .deck-chrome span{margin-left:12px;font-family:var(--mono);font-size:10.5px;color:var(--faint);letter-spacing:.04em} + .deck-chrome .tag{margin-left:auto;font-size:9px;color:var(--amber)} + .deck-card video{width:100%;aspect-ratio:16/9;object-fit:cover;object-position:top;display:block;background:#0d1116} + .deck-video-wrap{position:relative} + .deck-card.is-active .deck-video-wrap{cursor:pointer} + /* centered play/pause pulse: brief feedback when the video itself is tapped */ + .deck-pulse{ + position:absolute;left:50%;top:50%;transform:translate(-50%,-50%) scale(.75); + width:60px;height:60px;border-radius:50%;z-index:5;display:grid;place-items:center;padding-left:2px; + background:rgba(11,15,20,.6);border:1px solid rgba(124,207,226,.4);color:#fff; + opacity:0;pointer-events:none; + -webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px); + } + .deck-pulse.show{animation:deckPulse .65s cubic-bezier(.2,0,0,1) forwards} + @keyframes deckPulse{ + 0%{opacity:0;transform:translate(-50%,-50%) scale(.7)} + 22%{opacity:1;transform:translate(-50%,-50%) scale(1)} + 100%{opacity:0;transform:translate(-50%,-50%) scale(1.18)} + } + /* subtle playback overlay: play/pause + seekbar + cycling speed chip, shown only on the active reel */ + .deck-controls{ + display:none;position:absolute;left:10px;right:10px;bottom:10px;z-index:4; + align-items:center;gap:9px;padding:4px 5px 4px 4px;border-radius:20px; + background:rgba(11,13,16,.62);border:1px solid rgba(255,255,255,.12); + -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px); + opacity:.65;transition:opacity .15s ease; + } + .deck-card.is-active .deck-controls{display:flex} + .deck-video-wrap:hover .deck-controls,.deck-controls:focus-within{opacity:1} + .deck-ctl-btn{ + flex:none;width:24px;height:24px;border-radius:50%;display:grid;place-items:center;padding-left:1px; + background:transparent;border:none;color:var(--sky-bright);cursor:pointer; + transition:background .12s ease; + } + .deck-ctl-btn:hover{background:rgba(124,207,226,.14)} + .deck-ctl-seek{flex:1;min-width:0;display:flex;align-items:center;height:24px;cursor:pointer;touch-action:none} + .deck-ctl-seek-track{position:relative;width:100%;height:3px;border-radius:2px;background:rgba(255,255,255,.2)} + .deck-ctl-seek-fill{position:absolute;left:0;top:0;height:100%;width:0%;border-radius:2px;background:var(--sky-bright)} + .deck-ctl-seek-thumb{ + position:absolute;top:50%;left:0%;width:10px;height:10px;border-radius:50%; + background:var(--sky-bright);transform:translate(-50%,-50%); + box-shadow:0 0 0 2px rgba(11,13,16,.85);opacity:0;transition:opacity .12s ease; + } + .deck-ctl-seek:hover .deck-ctl-seek-thumb,.deck-ctl-seek:focus-visible .deck-ctl-seek-thumb{opacity:1} + .deck-ctl-speed{ + flex:none;font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.03em; + color:var(--amber);padding:0 8px;height:24px;border-radius:14px; + background:rgba(242,168,59,.1);border:1px solid rgba(242,168,59,.28); + cursor:pointer;transition:background .12s ease; + } + .deck-ctl-speed:hover{background:rgba(242,168,59,.2)} + .deck-info{ + display:flex;align-items:baseline;gap:14px;flex-wrap:wrap; + padding:14px 18px;border-top:1px solid var(--border); + } + .deck-info h3{font-size:15.5px;font-weight:650} + .deck-info p{color:var(--dim);font-size:13px} + /* tap-to-play fallback when the platform denies muted autoplay */ + .deck-play{ + position:absolute;left:50%;top:44%;transform:translate(-50%,-50%);z-index:5; + width:64px;height:64px;border-radius:50%;display:none;place-items:center; + border:1px solid rgba(124,207,226,.5);background:rgba(11,15,20,.72);color:var(--sky-bright); + cursor:pointer;padding-left:4px; + -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px); + box-shadow:0 0 0 6px rgba(69,175,201,.12),0 18px 44px -16px rgba(0,0,0,.9); + } + .deck-stage.blocked .deck-play{display:grid;animation:pulse 2.4s ease-in-out infinite} + .deck-play:active{transform:translate(-50%,-50%) scale(.94)} + .deck-ui{display:flex;align-items:center;gap:12px;margin-top:22px} + .deck-dots{display:flex;gap:8px} + .deck-dots button{ + width:22px;height:10px;padding:0;border:1px solid var(--border-strong);border-radius:2px; + background:transparent;cursor:pointer;transform:skewX(-14deg); + transition:background .15s ease,border-color .15s ease; + } + .deck-dots button:hover{border-color:var(--dim)} + .deck-dots button.on{background:rgba(242,168,59,.5);border-color:var(--amber)} + #deckCap{margin-left:auto;font-size:9px} + @media(max-width:860px){ + .deck-card{width:min(560px,88vw)} + .deck-card.is-prev{transform:translateX(calc(-50% - 48%)) scale(.74)} + .deck-card.is-next{transform:translateX(calc(-50% + 48%)) scale(.74)} + .deck-info p{font-size:12px} + } + + /* ---------- outcomes ---------- */ + .ocs{display:grid;grid-template-columns:repeat(3,1fr);gap:16px} + .oc{padding:24px 24px 20px;display:flex;flex-direction:column;gap:16px;transition:transform .08s ease,border-color .12s ease,box-shadow .15s ease} + .oc:hover{border-color:rgba(255,255,255,.16) rgba(255,255,255,.09) rgba(0,0,0,.55);box-shadow:0 24px 60px -28px rgba(0,0,0,.9)} + .oc-head{display:flex;align-items:center;gap:14px} + .oc-ico{ + width:52px;height:52px;flex:none;border-radius:12px;display:grid;place-items:center; + border:1px solid var(--sky-dim);background:rgba(69,175,201,.07); + box-shadow:inset 0 1px 0 rgba(255,255,255,.08),0 0 24px -10px rgba(69,175,201,.5); + } + .oc-ico svg{ + width:30px;height:30px;fill:none;stroke:var(--sky);stroke-width:1.5; + stroke-linecap:round;stroke-linejoin:round; + } + .oc-ico svg .dim{stroke:var(--sky-dim)} + .oc-ico svg .hot{stroke:var(--amber)} + .oc-head h3{font-size:16.5px;font-weight:650;line-height:1.3} + .oc ul{list-style:none;display:flex;flex-direction:column;gap:10px} + .oc li{ + font-size:13.5px;color:var(--dim);padding-left:24px;position:relative;line-height:1.5; + } + .oc li::before{ + content:"\2713";position:absolute;left:0;top:0; + font-family:var(--mono);font-size:12px;font-weight:700;color:var(--amber); + } + .oc-stat{ + display:flex;align-items:baseline;gap:10px;margin-top:4px; + padding-top:14px;border-top:1px dashed rgba(46,59,72,.8); + } + .oc-stat .num{font-size:24px;font-weight:700} + .svc-signals{ + display:flex;gap:26px;flex-wrap:wrap;justify-content:center;margin-top:26px; + font-family:var(--mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint); + } + @media(max-width:1020px){.ocs{grid-template-columns:1fr}} + + /* ---------- industries ---------- */ + .inds{display:grid;grid-template-columns:repeat(3,1fr);gap:16px} + .ind{padding:24px 24px 18px;display:flex;flex-direction:column;gap:12px;transition:transform .08s ease,box-shadow .15s ease} + .ind:hover{border-color:rgba(255,255,255,.16) rgba(255,255,255,.09) rgba(0,0,0,.55);box-shadow:0 24px 60px -28px rgba(0,0,0,.9)} + .ind-head{display:flex;align-items:center;gap:12px} + .ind-head svg{width:34px;height:34px;flex:none;color:var(--sky);padding:6px;border:1px solid var(--sky-dim);border-radius:8px;background:rgba(69,175,201,.07)} + .ind-head h3{font-size:17px;font-weight:650} + .ind > p{color:var(--dim);font-size:13.5px} + .ind ul{list-style:none;display:flex;flex-direction:column;gap:8px;flex:1} + .ind li{font-size:12.5px;color:var(--dim);padding-left:18px;position:relative;line-height:1.5} + .ind li::before{content:"";position:absolute;left:0;top:8px;width:9px;height:2px;background:var(--sky-dim)} + .ind-foot{border-top:1px solid var(--border);padding-top:12px;display:flex;flex-direction:column;gap:3px} + .ind-foot b{font-size:13.5px;font-weight:620} + + /* ---------- risk cards ---------- */ + .risks{display:grid;grid-template-columns:repeat(3,1fr);gap:16px} + .risk-card{ + padding:26px 24px 18px;display:flex;flex-direction:column;gap:8px;overflow:hidden; + transition:transform .08s ease,box-shadow .15s ease; + } + /* measurement tick strips */ + .risk-card{ + background-image: + repeating-linear-gradient(90deg, rgba(233,238,243,.2) 0 1.5px, transparent 1.5px 24px), + repeating-linear-gradient(90deg, rgba(233,238,243,.09) 0 1px, transparent 1px 6px), + linear-gradient(180deg, rgba(255,255,255,.035), rgba(255,255,255,0) 44%); + background-size:calc(100% - 32px) 8px,calc(100% - 32px) 5px,100% 100%; + background-position:16px 0,16px 0,0 0; + background-repeat:no-repeat; + } + .risk-card:hover{transform:translateY(-2px)} + .num.big{font-size:44px;font-weight:700;line-height:1.15;margin-top:8px} + .num.big.red{color:var(--red)} + .num.big.amber{color:var(--amber)} + .num.big.sky{color:var(--sky-bright)} + .num-wrap{display:inline-flex;align-items:flex-start;margin-top:8px} + .num-wrap .num.big{margin-top:0} + .tnc-star{font-size:20px;font-weight:700;color:var(--faint);margin-left:1px} + .tnc-star.red{color:var(--red)} + .tnc-star.amber{color:var(--amber)} + .tnc-star.sky{color:var(--sky-bright)} + .risk-card h3{font-size:16.5px;font-weight:650} + .risk-card p{color:var(--dim);font-size:13px;flex:1} + .risk-card .stamp-tag{margin-top:12px} + + + /* ---------- case file ---------- */ + .case{ + margin-top:8px;padding:30px 34px 26px;border-radius:10px; + border:1px solid;border-color:rgba(255,255,255,.085) rgba(255,255,255,.05) rgba(0,0,0,.55); + background:linear-gradient(180deg, rgba(255,255,255,.03), rgba(255,255,255,0) 40%), rgba(18,21,26,.55); + -webkit-backdrop-filter:blur(14px) saturate(1.1);backdrop-filter:blur(14px) saturate(1.1); + border-left:2px solid var(--amber); + } + .case-quote{ + font-size:clamp(1.15rem,2vw,1.45rem);line-height:1.45;letter-spacing:-.01em; + font-weight:500;text-wrap:balance;max-width:52ch; + } + .case-quote em{font-style:normal;color:var(--amber)} + .case-id{margin-top:20px;display:flex;align-items:center;gap:14px} + .case-id .avatar{ + width:48px;height:48px;flex:none;border-radius:6px;border:1px solid var(--border-strong); + object-fit:cover;object-position:center 25%;background:var(--surface-2); + } + .case-id .n{font-weight:650;font-size:15px} + .case-id .r{font-family:var(--mono);font-size:11px;color:var(--dim);letter-spacing:.08em;text-transform:uppercase} + .case-cols{ + display:grid;grid-template-columns:1.05fr .95fr;gap:38px; + padding-bottom:26px;margin-bottom:24px;border-bottom:1px dashed rgba(46,59,72,.8); + } + .case-cols > div > .tag{display:block;font-size:9px;margin-bottom:14px} + .case-fin > .tag{margin-top:20px} + .case-fin > .tag:first-child{margin-top:0} + /* file log: dated entries down a survey line */ + .case-log .cl{ + position:relative;margin-left:5px;padding:0 0 16px 22px; + border-left:1px dashed var(--border-strong); + } + .case-log .cl:last-child{padding-bottom:2px;border-left-color:transparent} + .case-log .cl::before{ + content:"";position:absolute;left:-4.5px;top:3px;width:8px;height:8px; + background:var(--canvas);border:1px solid var(--dim);transform:rotate(45deg); + } + .case-log .cl.end::before{background:rgba(242,168,59,.3);border-color:var(--amber)} + .case-log .cl b{ + display:block;font-family:var(--mono);font-size:10px;font-weight:600; + letter-spacing:.14em;color:var(--amber);margin-bottom:3px; + } + .case-log .cl span{font-size:13px;color:var(--dim);line-height:1.55;display:block;max-width:46ch} + /* scope of loss + settlement */ + .sol,.stl{font-family:var(--mono);font-size:12px;margin-bottom:4px} + .sol-line,.stl-line{ + display:flex;justify-content:space-between;gap:12px;padding:4.5px 0; + border-bottom:1px dashed rgba(32,36,43,.9); + } + .sol-line span,.stl-line span{color:var(--dim);letter-spacing:.02em} + .sol-line b,.stl-line b{font-weight:600;color:var(--text);font-variant-numeric:tabular-nums;white-space:nowrap} + .sol-total{ + display:flex;justify-content:space-between;align-items:center;gap:12px; + margin-top:10px;padding:9px 12px;border:1px solid var(--amber-dim); + background:rgba(242,168,59,.05);border-radius:4px; + } + .sol-total span{font-size:9.5px;letter-spacing:.14em;color:var(--amber)} + .sol-total b{font-size:16px;font-weight:700;color:var(--amber)} + .stl-line b.neg{color:var(--red);opacity:.85} + .stl-line b.sky{color:var(--sky-bright)} + .stl-line b.amb{color:var(--amber)} + .ev{display:flex;flex-wrap:wrap;gap:7px} + .ev span{ + font-family:var(--mono);font-size:9.5px;letter-spacing:.08em;text-transform:uppercase; + color:var(--dim);padding:5px 10px;border:1px solid var(--border-strong);border-radius:20px; + } + .case-id{flex-wrap:wrap} + .case-stamps{display:flex;gap:14px;flex-wrap:wrap;margin-left:auto} + @media(max-width:860px){ + .case{padding:22px 20px 20px} + .case-cols{grid-template-columns:1fr;gap:28px} + .case-stamps{margin-left:0;width:100%;margin-top:6px} + } + + /* ---------- faq ---------- */ + .faq{max-width:720px;margin:0 auto 58px} + .faq details{ + border:1px solid;border-color:rgba(255,255,255,.085) rgba(255,255,255,.05) rgba(0,0,0,.55); + border-radius:8px;margin-bottom:10px;overflow:hidden; + background:linear-gradient(180deg, rgba(255,255,255,.03), rgba(255,255,255,0) 60%), rgba(18,21,26,.5); + -webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px); + } + .faq summary{ + list-style:none;cursor:pointer;display:flex;align-items:center;gap:14px; + padding:15px 18px;font-size:14.5px;font-weight:600; + transition:color .12s ease,background .12s ease; + } + .faq summary::-webkit-details-marker{display:none} + .faq summary:hover{background:rgba(255,255,255,.02)} + .faq summary i{ + margin-left:auto;width:22px;height:22px;flex:none;position:relative; + border:1px solid var(--border-strong);border-radius:50%; + transition:border-color .15s ease,transform .2s cubic-bezier(.2,0,0,1); + } + .faq summary i::before,.faq summary i::after{ + content:"";position:absolute;left:50%;top:50%;background:var(--dim); + transition:background .12s ease; + } + .faq summary i::before{width:9px;height:1.5px;transform:translate(-50%,-50%)} + .faq summary i::after{width:1.5px;height:9px;transform:translate(-50%,-50%)} + .faq details[open] summary i{transform:rotate(45deg);border-color:var(--amber)} + .faq details[open] summary i::before,.faq details[open] summary i::after{background:var(--amber)} + .faq details p{padding:0 18px 16px;color:var(--dim);font-size:13.5px;max-width:60ch} + .faq details p sup,.sec-head p sup,.tag sup{color:var(--amber);margin-left:1px} + .faq-more{text-align:center} + .faq-more a{color:var(--sky);transition:color .12s ease} + .faq-more a:hover{color:var(--sky-bright)} + + /* ---------- FAQ page: intro, search, quick-nav, per-category list, disclaimer note ---------- */ + .page-hero,.faq-hero{ + padding:64px 0 8px; + background:radial-gradient(1000px 480px at 80% -10%, rgba(69,175,201,.05), transparent 62%), var(--canvas); + } + .faq-search{position:relative;max-width:440px;margin:8px 0 22px} + .faq-search input{ + width:100%;background:rgba(11,13,16,.75); + border:1px solid var(--border-strong);border-radius:6px; + color:var(--text);font-family:var(--mono);font-size:13px; + padding:12px 14px;letter-spacing:.02em; + } + .faq-search input::placeholder{color:var(--faint)} + .faq-search input:focus{outline:none;border-color:var(--sky);box-shadow:0 0 0 3px rgba(69,175,201,.13)} + .faq-count{display:block;margin-top:9px;min-height:11px} + .faq-quicknav{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px} + .faq-cat{transition:opacity .15s ease} + .faq-cat.hide{display:none} + .faq-cat .faq{margin:0;max-width:720px} + .faq-cat .faq details.hide{display:none} + .faq-note{max-width:720px;padding:22px 24px;border-left:2px solid var(--sky);margin-top:26px} + .faq-note p{color:var(--dim);font-size:13px;line-height:1.7;margin-top:10px} + .faq-cta{max-width:560px;margin:0 auto;text-align:center;padding:40px 36px} + .faq-cta h2{margin:14px 0 10px} + .faq-cta p{color:var(--dim);margin-bottom:22px;max-width:44ch;margin-left:auto;margin-right:auto} + .faq-cta .cta-alt{margin-bottom:0} + @media(max-width:720px){ + .faq-hero{padding:48px 0 4px} + .faq-cta{padding:30px 22px} + } + + /* ---------- founders vision ---------- */ + .founder{ + background:radial-gradient(900px 380px at 16% -12%, rgba(242,168,59,.03), transparent 60%), var(--surface); + border-top:1px solid var(--border);border-bottom:1px solid var(--border); + } + .founder-grid{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(0,.75fr);gap:56px;align-items:center} + .quote-card{padding:26px 28px;border-left:2px solid var(--amber)} + .stars{color:var(--amber);letter-spacing:4px;font-size:15px;margin-bottom:12px} + blockquote{font-size:clamp(1.15rem,2vw,1.5rem);line-height:1.45;letter-spacing:-.01em;font-weight:500;text-wrap:balance} + blockquote em{font-style:normal;color:var(--amber)} + .founder-id{margin-top:22px;display:flex;align-items:center;gap:14px} + .founder-id .avatar{ + width:48px;height:48px;flex:none;border-radius:6px;border:1px solid var(--border-strong); + object-fit:cover;object-position:center 18%;background:var(--surface-2); + } + .founder-id .n{font-weight:650;font-size:15px} + .founder-id .r{font-family:var(--mono);font-size:11px;color:var(--dim);letter-spacing:.08em;text-transform:uppercase} + .founder-card{padding:10px 22px 16px;max-width:400px;justify-self:end;width:100%} + .fv-row{display:flex;justify-content:space-between;align-items:center;gap:14px;padding:15px 0;border-bottom:1px dashed rgba(46,59,72,.8)} + .fv-row b{display:block;font-size:14.5px;font-weight:640} + .fv-row div span{font-size:11.5px;color:var(--faint)} + .fv-row > .num{font-size:26px;font-weight:700;color:var(--amber);white-space:nowrap} + .fc-foot{padding-top:13px} + + /* ---------- shared tooltip ---------- */ + .tip{ + position:fixed;z-index:150;max-width:272px;padding:10px 13px;border-radius:8px; + background:rgba(11,13,16,.94);border:1px solid var(--border-strong); + color:var(--dim);font-size:12.5px;line-height:1.5;pointer-events:none; + opacity:0;transform:translateY(4px); + transition:opacity .12s ease,transform .15s cubic-bezier(.2,0,0,1); + box-shadow:0 18px 40px -18px rgba(0,0,0,.9); + -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px); + } + .tip.on{opacity:1;transform:none} + + /* ---------- pricing ---------- */ + .bill{display:flex;gap:8px;margin:-8px 0 24px} + .bill .chip b{color:var(--amber);font-weight:600;margin-left:5px} + .plan .price .a{display:inline-block} + .plan .price .a.pop{animation:pricePop .3s cubic-bezier(.2,0,0,1)} + @keyframes pricePop{0%{transform:scale(.92);opacity:.4}100%{transform:scale(1);opacity:1}} + .incl{display:grid;grid-template-columns:repeat(5,1fr);gap:12px;margin-bottom:34px} + .incl-item{ + display:flex;flex-direction:column;align-items:center;gap:10px;text-align:center; + padding:18px 10px 14px;transition:border-color .15s ease,box-shadow .15s ease,transform .12s ease; + } + .incl-item svg{width:36px;height:36px;stroke:var(--sky);stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;opacity:.9} + .incl-item b{font-size:12px;font-weight:620;color:var(--dim)} + .incl-item:hover{transform:translateY(-2px);border-color:rgba(255,255,255,.18);box-shadow:0 14px 34px -20px rgba(0,0,0,.8)} + .incl-item:hover b{color:var(--text)} + .plans{display:grid;grid-template-columns:repeat(3,1fr);gap:16px} + .plan{padding:28px;display:flex;flex-direction:column;gap:18px;box-shadow:inset 0 1px 0 rgba(255,255,255,.04),0 14px 40px -24px rgba(0,0,0,.8)} + .plan.featured{ + border-color:rgba(242,168,59,.4) rgba(242,168,59,.2) rgba(0,0,0,.55); + background:linear-gradient(180deg,rgba(242,168,59,.05),rgba(255,255,255,0) 40%),rgba(18,21,26,.6); + box-shadow:inset 0 1px 0 rgba(255,255,255,.05),0 18px 48px -22px rgba(0,0,0,.9),0 0 60px -16px rgba(242,168,59,.3); + } + .plan h3{font-size:17px;font-weight:650;display:flex;justify-content:space-between;align-items:baseline} + .plan h3 .tag{font-size:9.5px} + .plan .price{font-family:var(--mono);font-variant-numeric:tabular-nums} + .plan .price .a{font-size:34px;font-weight:700} + .plan .price .b{color:var(--dim);font-size:12.5px;letter-spacing:.04em} + .featured .price .a{color:var(--amber)} + .plan ul{list-style:none;display:flex;flex-direction:column;gap:9px;flex:1} + .plan li{font-size:13.5px;color:var(--dim);padding-left:20px;position:relative} + .plan li::before{content:"";position:absolute;left:0;top:7px;width:9px;height:2px;background:var(--amber-dim)} + .plan .btn{justify-content:center} + .stamp{ + position:absolute;top:56px;right:18px;width:80px;height:80px;z-index:3; + transform:rotate(-12deg);opacity:0;pointer-events:none; + } + .plan.featured.in .stamp{animation:stampIn .18s cubic-bezier(.2,0,0,1) .5s forwards} + @keyframes stampIn{ + 0%{opacity:0;transform:rotate(-12deg) scale(1.5)} + 70%{opacity:.6;transform:rotate(-12deg) scale(.96)} + 100%{opacity:.5;transform:rotate(-12deg) scale(1)} + } + .stamp text{font-family:var(--mono);font-size:9px;font-weight:600;letter-spacing:.18em;fill:var(--amber)} + @media(max-width:1020px){.incl{grid-template-columns:repeat(3,1fr)}} + + /* ---------- per-seat plans on hold ---------- */ + .plan-paused .stamp{display:none} + .plan-paused h3,.plan-paused ul,.plan-paused .btn{opacity:.5} + .plan-paused .price .b{opacity:.55} + .plan-paused .price .a{position:relative;color:var(--dim);text-decoration:none} + .plan-paused .price .a::after{ + content:"";position:absolute;left:-7%;right:-7%;top:50%;height:4px;margin-top:-2px; + background:var(--red);border-radius:3px;transform:rotate(-8deg); + box-shadow:0 0 10px rgba(242,109,109,.6); + } + .plan-paused .btn.solid{background:transparent;box-shadow:none;color:var(--text)} + .plan-hold{align-self:flex-start} + + /* ---------- Founders Lifetime License ---------- */ + .founders-deal{ + margin-top:20px;padding:34px 36px;border-radius:10px; + border:1px solid rgba(242,168,59,.35); + background:linear-gradient(180deg,rgba(242,168,59,.06),rgba(255,255,255,0) 45%),rgba(18,21,26,.65); + box-shadow:0 20px 50px -26px rgba(0,0,0,.85),0 0 70px -20px rgba(242,168,59,.28),inset 0 1px 0 rgba(255,255,255,.05); + -webkit-backdrop-filter:blur(14px) saturate(1.15);backdrop-filter:blur(14px) saturate(1.15); + } + .founders-deal > .stamp-tag{margin-bottom:20px} + .fd-grid{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(0,.9fr);gap:36px;align-items:center} + .fd-copy h3{font-size:22px;font-weight:640;margin-bottom:10px} + .fd-copy h3 em{font-style:normal;color:var(--amber)} + .fd-copy p{color:var(--dim);font-size:14px;max-width:52ch;margin-bottom:16px} + .fd-copy ul{list-style:none;display:flex;flex-direction:column;gap:8px} + .fd-copy li{font-size:13.5px;color:var(--dim);padding-left:20px;position:relative} + .fd-copy li::before{content:"";position:absolute;left:0;top:7px;width:9px;height:2px;background:var(--amber)} + .fd-copy li sup a{color:var(--amber)} + .fd-price{text-align:center;padding:22px;border-left:1px dashed rgba(255,255,255,.12)} + .fd-price .price{font-family:var(--mono);margin:6px 0 18px} + .fd-price .price .a{font-size:38px;font-weight:700;color:var(--amber)} + .fd-price .btn{width:100%;justify-content:center;margin-bottom:10px} + .fd-fine{display:block;font-size:9.5px} + @media(max-width:860px){ + .fd-grid{grid-template-columns:1fr} + .fd-price{border-left:0;border-top:1px dashed rgba(255,255,255,.12);padding-top:20px;margin-top:6px} + } + @media(max-width:720px){.founders-deal{padding:26px 22px}} + + /* ---------- glossary rail: drafting index cards ---------- */ + .car{ + position:relative;overflow:hidden;cursor:grab;padding:8px 0 0; + touch-action:pan-y; + -webkit-mask-image:linear-gradient(90deg,transparent,#000 4%,#000 96%,transparent); + mask-image:linear-gradient(90deg,transparent,#000 4%,#000 96%,transparent); + } + .car.dragging{cursor:grabbing} + .car-track{ + display:flex;gap:16px;width:max-content;padding:6px max(24px,calc(50vw - 590px)) 22px; + will-change:transform; + } + .car-card{ + width:312px;flex:none;padding:16px 18px 20px; + display:flex;flex-direction:column;gap:8px; + transition:border-color .15s ease,box-shadow .15s ease,transform .15s cubic-bezier(.2,0,0,1); + } + .car-card:hover{ + border-color:rgba(255,255,255,.16) rgba(255,255,255,.09) rgba(0,0,0,.55); + box-shadow:0 18px 50px -24px rgba(0,0,0,.85); + transform:translateY(-3px); + } + .car-fig{ + border:1px solid var(--border);border-radius:6px;padding:12px 16px;margin-bottom:6px; + background: + repeating-linear-gradient(45deg,transparent 0 6px,rgba(32,36,43,.45) 6px 7px), + var(--surface-2); + } + .car-fig svg{ + width:100%;height:auto;display:block; + fill:none;stroke:var(--faint);stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round; + transition:stroke .18s ease,filter .18s ease; + } + .car-fig svg .dash{stroke-dasharray:.05 .05;opacity:.6} + /* the figure lights up: ink turns amber and redraws itself */ + .car-card:hover .car-fig svg{ + stroke:var(--amber); + filter:drop-shadow(0 0 5px rgba(242,168,59,.35)); + } + .car-card:hover .car-fig svg *{animation:carDraw .55s cubic-bezier(.2,0,0,1) 1} + @keyframes carDraw{from{stroke-dasharray:1;stroke-dashoffset:1}to{stroke-dasharray:1;stroke-dashoffset:0}} + .car-card .tag{font-size:9px;color:var(--faint);letter-spacing:.18em} + .car-card h3{font-size:17px;font-weight:650} + .car-card p{color:var(--dim);font-size:13px;line-height:1.55} + .car-ui{display:flex;align-items:center;gap:14px;padding-top:6px;padding-bottom:4px} + .car-btn{ + width:34px;height:34px;flex:none;border-radius:50%;display:grid;place-items:center; + border:1px solid var(--border-strong);background:transparent;color:var(--dim);cursor:pointer; + transition:color .12s ease,border-color .12s ease,background .12s ease; + } + .car-btn:hover{color:var(--text);border-color:rgba(255,255,255,.3);background:rgba(255,255,255,.04)} + .car-btn:active{transform:translateY(1px)} + .car-prog{ + position:relative;flex:1;height:9px;margin-left:6px; + background: + repeating-linear-gradient(90deg, rgba(237,239,242,.18) 0 1px, transparent 1px 14px) bottom / 100% 6px no-repeat, + linear-gradient(var(--border-strong),var(--border-strong)) bottom / 100% 1px no-repeat; + } + .car-prog i{ + position:absolute;left:0;bottom:-1px;height:3px;width:18%;min-width:26px;border-radius:2px; + background:var(--amber);box-shadow:0 0 10px rgba(242,168,59,.5); + } + #carHint{font-size:9px;white-space:nowrap} + + /* ---------- crane CTA ---------- */ + .final{ + position:relative;overflow:hidden;padding-top:64px; + background: + radial-gradient(800px 420px at 50% 108%, rgba(69,175,201,.05), transparent 62%), + radial-gradient(900px 460px at 16% 22%, rgba(242,168,59,.038), transparent 62%), + var(--canvas); + } + .cta-fx{position:absolute;inset:0;width:100%;height:100%;pointer-events:none} + .final .wrap{position:relative;z-index:1} + .crane-scene{display:flex;flex-direction:column;align-items:center} + .crane{width:min(760px,100%);margin-bottom:-6px} + .crane .c0{stroke:rgba(69,175,201,.14);stroke-width:.75} + .crane .c1{stroke:rgba(223,231,236,.14);stroke-width:1} + .crane .c2{stroke:rgba(223,231,236,.22);stroke-width:2} + .crane .cf{fill:rgba(223,231,236,.03)} + .crane .cab{fill:rgba(69,175,201,.07);stroke:rgba(69,175,201,.3);stroke-width:1} + .crane .cab2{fill:rgba(69,175,201,.1);stroke:rgba(69,175,201,.24);stroke-width:.7} + .crane .hoist{stroke:rgba(69,175,201,.35);stroke-width:1.5;stroke-dasharray:6 4} + @keyframes craneSwing{0%,100%{transform:rotate(1.3deg)}50%{transform:rotate(-1.3deg)}} + .crane-swing{animation:craneSwing 5s ease-in-out infinite;transform-origin:top center} + .crane-swing:hover{animation-play-state:paused} + .cta{ + position:relative;max-width:640px;width:100%;margin-top:-10px; + padding:40px 44px 30px;text-align:center;border-radius:14px; + border:1px solid;border-color:rgba(255,255,255,.1) rgba(255,255,255,.06) rgba(0,0,0,.55); + background: + radial-gradient(480px 220px at 50% -40%, rgba(69,175,201,.1), transparent 70%), + rgba(13,18,24,.6); + -webkit-backdrop-filter:blur(16px) saturate(1.15); + backdrop-filter:blur(16px) saturate(1.15); + box-shadow:0 40px 90px -44px rgba(0,0,0,.9); + } + .cta::after{ + content:"";position:absolute;inset:0;border-radius:14px;pointer-events:none; + background:radial-gradient(360px 260px at var(--gx,50%) var(--gy,40%),rgba(140,225,245,.07),transparent 62%); + opacity:0;transition:opacity .25s ease; + } + .cta:hover::after{opacity:1} + .cta-hook{ + position:absolute;top:-13px;left:50%;transform:translateX(-50%); + width:26px;height:26px;border-radius:50%;border:1px solid rgba(69,175,201,.35); + background:var(--canvas);display:grid;place-items:center; + } + .cta-hook i{width:8px;height:8px;border-radius:50%;background:rgba(69,175,201,.5)} + .cta h2{margin-top:16px} + .cta > p{color:var(--dim);max-width:46ch;margin:14px auto 24px;font-size:14.5px} + .cta-form{display:flex;gap:8px;max-width:520px;margin:0 auto 14px} + .cta-form input{ + flex:1;min-width:0;background:rgba(11,13,16,.75); + border:1px solid var(--border-strong);border-radius:6px; + color:var(--text);font-family:var(--mono);font-size:12.5px; + padding:12px 14px;letter-spacing:.02em; + } + .cta-form input::placeholder{color:var(--faint)} + .cta-form input:focus{outline:none;border-color:var(--sky);box-shadow:0 0 0 3px rgba(69,175,201,.13)} + .cta-alt{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-bottom:16px} + .cta-fine{font-size:9.5px} + + /* ---------- footer: dusk street ---------- */ + footer{ + position:relative;padding:78px 0 190px;border-top:1px solid var(--border); + background: + linear-gradient(rgba(237,239,242,.02) 1px, transparent 1px), + linear-gradient(90deg, rgba(237,239,242,.02) 1px, transparent 1px), + linear-gradient(180deg, var(--canvas), #07080B); + background-size:56px 56px,56px 56px,auto; + } + .skyline2{position:absolute;left:0;right:0;bottom:0;width:100%;height:150px;pointer-events:none} + .sk-ln rect,.sk-ln polygon,.sk-ln line,.sk-ln circle{ + fill:none;stroke:rgba(223,231,236,.3);stroke-width:1.1; + } + .sk-ln .w-on{fill:rgba(242,168,59,.75);stroke:rgba(242,168,59,.4)} + .sk-ln .w-off{fill:none;stroke:rgba(223,231,236,.28)} + .sk-ln .lamp{fill:rgba(255,214,90,.9);stroke:none} + .sk-ln .lamp-halo{fill:rgba(242,168,59,.1);stroke:none} + .w-tw{animation:twinkle 9s steps(1,end) 5} + .w-tw2{animation:twinkle 13s steps(1,end) 4 2.5s} + @keyframes twinkle{0%,100%{opacity:1}42%{opacity:.12}58%{opacity:1}} + .foot-glass{ + position:relative;z-index:2;padding:34px 36px 20px;border-radius:14px; + border:1px solid;border-color:rgba(255,255,255,.09) rgba(255,255,255,.05) rgba(0,0,0,.5); + background:rgba(11,13,16,.55); + -webkit-backdrop-filter:blur(14px) saturate(1.15); + backdrop-filter:blur(14px) saturate(1.15); + box-shadow:0 34px 80px -42px rgba(0,0,0,.85); + } + .foot-grid{display:grid;grid-template-columns:1.4fr 1fr 1fr 1.1fr;gap:30px;padding-bottom:26px} + .foot-col{display:flex;flex-direction:column;gap:9px} + .foot-col b{font-size:13px;font-weight:620;margin-bottom:2px} + .foot-col a{font-size:13.5px;color:var(--dim);transition:color .12s ease;width:max-content} + .foot-col a:hover{color:var(--sky-bright)} + .foot-col.brand .logo{margin-bottom:4px} + .foot-tag{font-size:13.5px;color:var(--dim);max-width:24ch;margin-bottom:6px} + .foot-col.brand .tag{line-height:1.9;text-transform:none;letter-spacing:.06em} + .foot-social{display:flex;gap:10px;margin-top:10px} + .foot-social a{ + width:34px;height:34px;border-radius:50%;display:grid;place-items:center; + border:1px solid var(--border-strong);color:var(--dim); + transition:color .12s ease,border-color .12s ease,box-shadow .15s ease; + } + .foot-social a svg{width:16px;height:16px} + .foot-social a:hover{color:var(--sky-bright);border-color:var(--sky-dim);box-shadow:0 0 18px -6px rgba(69,175,201,.5)} + .foot-alerts{ + display:flex;align-items:center;justify-content:space-between;gap:18px;flex-wrap:wrap; + padding:18px 0;border-top:1px dashed rgba(255,255,255,.09); + } + .fa-copy b{display:block;font-size:14px;font-weight:640;margin-bottom:3px} + .fa-copy .tag{font-size:9.5px;text-transform:none;letter-spacing:.06em} + .fa-row{display:flex;gap:8px} + .fa-row input{ + width:110px;background:rgba(11,13,16,.75);border:1px solid var(--border-strong); + border-radius:6px;color:var(--text);font-family:var(--mono);font-size:13px; + padding:10px 12px;letter-spacing:.08em;text-align:center; + } + .fa-row input:focus{outline:none;border-color:var(--sky);box-shadow:0 0 0 3px rgba(69,175,201,.13)} + .fa-row .btn{padding:9px 16px} + .foot-alerts .fa-done{display:flex;align-items:center;gap:10px;font-size:13.5px;font-weight:600} + .foot-alerts .fa-done .tag{color:var(--amber)} + .foot-base{display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap;padding-top:16px;border-top:1px solid rgba(255,255,255,.06)} + .foot-base .tag{font-size:10px;line-height:1.8} + + /* ---------- photo lightbox ---------- */ + .lb{ + position:fixed;inset:0;z-index:200; + display:flex;align-items:center;justify-content:center;padding:34px 84px; + background:rgba(5,7,10,.88); + -webkit-backdrop-filter:blur(10px) saturate(1.05); + backdrop-filter:blur(10px) saturate(1.05); + opacity:0;transition:opacity .2s ease; + } + .lb.in{opacity:1} + .lb[hidden]{display:none} + body.lb-open{overflow:hidden} + .lb figure{margin:0;max-width:min(1080px,100%)} + .lb-img{ + display:block;margin:0 auto;max-width:100%;max-height:74vh;width:auto; + border-radius:10px;border:1px solid rgba(255,255,255,.14); + box-shadow:0 60px 140px -40px rgba(0,0,0,.95); + transform:scale(.98);transition:transform .2s cubic-bezier(.2,0,0,1); + } + .lb.in .lb-img{transform:none} + #vlb figure{max-width:min(1100px,100%);width:100%} + .vlb-video{ + display:block;margin:0 auto;max-width:100%;max-height:80vh;width:100%;background:#000; + border-radius:10px;border:1px solid rgba(255,255,255,.14); + box-shadow:0 60px 140px -40px rgba(0,0,0,.95); + transform:scale(.98);transition:transform .2s cubic-bezier(.2,0,0,1); + } + .lb.in .vlb-video{transform:none} + .lb-cap{ + display:flex;align-items:baseline;gap:12px;justify-content:center;flex-wrap:wrap; + padding-top:16px;font-size:13.5px; + } + .lb-cap b{font-weight:650} + .lb-cap span{color:var(--dim)} + .lb-count{font-family:var(--mono);font-style:normal;font-size:10px;letter-spacing:.14em;color:var(--amber)} + .lb-x{ + position:absolute;top:20px;right:24px;width:38px;height:38px;border-radius:50%; + border:1px solid var(--border-strong);background:rgba(11,13,16,.6);color:var(--dim); + cursor:pointer;font-size:13px;transition:color .12s ease,border-color .12s ease; + } + .lb-x:hover{color:var(--text);border-color:rgba(255,255,255,.35)} + .lb-prev,.lb-next{position:absolute;top:50%;transform:translateY(-50%);width:40px;height:40px;background:rgba(11,13,16,.6)} + .lb-prev{left:22px} + .lb-next{right:22px} + .lb-dots{position:absolute;bottom:22px;left:50%;transform:translateX(-50%);display:flex;gap:8px} + .lb-dots button{ + width:22px;height:9px;padding:0;border:1px solid var(--border-strong);border-radius:2px; + background:transparent;cursor:pointer;transform:skewX(-14deg); + transition:background .15s ease,border-color .15s ease; + } + .lb-dots button.on{background:rgba(242,168,59,.5);border-color:var(--amber)} + .ip-ph{cursor:zoom-in;transition:border-color .12s ease} + .ip-ph:hover{border-color:rgba(255,255,255,.22)} + @media(max-width:720px){ + .lb{padding:20px 14px 60px} + .lb-prev{left:10px} + .lb-next{right:10px} + } + + /* ---------- reduced motion ---------- */ + @media (prefers-reduced-motion:reduce){ + *{animation:none!important;transition:none!important} + .reveal{opacity:1;transform:none} + .stamp{opacity:.88} + html{scroll-behavior:auto} + } + + /* ---------- responsive ---------- */ + /* laptops & iPad landscape: narrower estimate column keeps the headline breathing */ + @media(max-width:1280px) and (min-width:1021px){ + .hero .wrap{grid-template-columns:minmax(0,1fr) minmax(0,470px);gap:40px} + } + @media(max-width:1020px){ + .hero .wrap{grid-template-columns:1fr;gap:44px} + .plans{grid-template-columns:1fr;max-width:640px} + .stamp{top:44px;right:26px} + .telemetry .wrap{grid-template-columns:repeat(2,1fr)} + .tele:nth-child(3){border-left:0} + .tele{border-top:1px solid var(--border)} + .tele:nth-child(-n+2){border-top:0} + .founder-grid{grid-template-columns:1fr} + .founder-card{justify-self:start} + .vs-totals{grid-template-columns:repeat(2,1fr)} + .inds{grid-template-columns:1fr} + .risks{grid-template-columns:1fr} + .feats{grid-template-columns:repeat(2,1fr)} + } + .menu-btn{display:none} + @media(max-width:720px){ + .nav-links{ + display:none;position:absolute;top:100%;left:0;right:0; + flex-direction:column;gap:0;padding:8px 24px 14px; + background:rgba(11,13,16,.96);border-bottom:1px solid var(--border); + -webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px); + } + body.nav-open .nav-links{display:flex} + .nav-links a{padding:12px 0;font-size:15px;border-bottom:1px solid var(--border)} + .nav-links a::before,.nav-links a::after{display:none} + .menu-btn{ + display:inline-flex;flex-direction:column;gap:4px;justify-content:center; + margin-left:auto;padding:10px;background:none;border:1px solid var(--border-strong); + border-radius:5px;cursor:pointer; + } + .menu-btn i{display:block;width:16px;height:1.5px;background:var(--text);transition:transform .15s cubic-bezier(.2,0,0,1),opacity .12s ease} + body.nav-open .menu-btn i:nth-child(1){transform:translateY(5.5px) rotate(45deg)} + body.nav-open .menu-btn i:nth-child(2){opacity:0} + body.nav-open .menu-btn i:nth-child(3){transform:translateY(-5.5px) rotate(-45deg)} + section{padding:64px 0} + .hero{padding:52px 0 72px} + .sheet{font-size:62px;top:38px;right:16px} + .est-slot{aspect-ratio:1.55} + .est-line{flex-wrap:wrap} + .est-line .k{white-space:normal} + .incl{grid-template-columns:repeat(2,1fr)} + .cta{padding:32px 20px 26px} + .cta-form{flex-direction:column} + .foot-grid{grid-template-columns:1fr 1fr} + footer{padding-bottom:170px} + .car-card{width:256px} + .feats{grid-template-columns:1fr} + } + + /* ---------- small phones: intel overlays share the frame politely ---------- */ + @media(max-width:640px){ + .intel-region{padding:7px 11px} + .intel-region b{font-size:13px} + .hail-watch{max-width:178px;padding:9px 11px;gap:8px} + .hail-watch span.hw-ico{width:28px;height:28px} + .hail-watch span.hw-ico svg{width:15px;height:15px} + .hail-watch b{font-size:12px} + .hail-watch span{font-size:10.5px} + .hail-watch i{display:none} + .conflict{width:min(300px,calc(100% - 28px));padding:11px 13px 11px 15px} + .intel-stats{gap:10px} + .istat{padding:16px 14px 12px} + .istat > .num{font-size:25px} + } + + /* ---------- touch ergonomics ---------- */ + @media(hover:none){ + /* timeline diamonds grow into honest tap targets; stories arrive on tap */ + .tl-node{width:18px;height:18px} + .lb-prev,.lb-next{width:44px;height:44px} + .car-btn{width:40px;height:40px} + } + + /* ---------- large-canvas scaling (2K / 27in and up) ---------- */ + @media(min-width:1700px){ + body{font-size:17px} + .wrap{max-width:1340px} + h1{font-size:clamp(3.4rem,4.4vw,5.5rem)} + h2{font-size:clamp(2.1rem,2.6vw,3.1rem)} + section{padding:112px 0} + .hero .wrap{grid-template-columns:minmax(0,1fr) minmax(0,600px)} + .est-body{font-size:13px} + .tl-lane.bad .tl-track{height:164px} + .tl-lane.good .tl-track{height:190px} + .cta{max-width:760px;padding:54px 60px 40px} + .foot-glass{padding:42px 46px 24px} + .intel-map{height:640px} + .intel-panel{width:330px} + .car-card{width:330px} + .car-track{padding-left:max(24px,calc(50vw - 670px));padding-right:max(24px,calc(50vw - 670px))} + .crane{width:min(880px,100%)} + } + + /* ============================================================ + BLOG + TERMS PAGES + ============================================================ */ + + /* ---------- blog: listing grid (cards reuse .feat/.feat-media/.feat-thumb/.feat-body) ---------- */ + .blog-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;margin-top:8px} + a.feat.blog-card{text-decoration:none;color:inherit} + .blog-foot-note{margin-top:36px;text-align:center} + + /* ---------- blog post: header, cover, prose ---------- */ + .post-meta{ + display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-top:16px; + font-family:var(--mono);font-size:11px;letter-spacing:.08em;color:var(--faint);text-transform:uppercase; + } + .post-meta i{width:3px;height:3px;border-radius:50%;background:var(--faint);flex:none} + .post-cover{margin:32px 0 6px;border-radius:10px;overflow:hidden;border:1px solid var(--border-strong);aspect-ratio:2.1/1} + .post-cover img{width:100%;height:100%;object-fit:cover;display:block;filter:saturate(.92) brightness(.88)} + .post-body{max-width:680px;margin-top:12px} + .post-body h2{font-size:clamp(1.4rem,2.4vw,1.9rem);margin:46px 0 16px;letter-spacing:-.02em;line-height:1.2} + .post-body h3{font-size:1.15rem;margin:30px 0 12px;font-weight:650} + .post-body p{color:var(--dim);font-size:15.5px;line-height:1.85;margin-bottom:18px} + .post-body p strong{color:var(--text);font-weight:650} + .post-body ul,.post-body ol{color:var(--dim);font-size:15.5px;line-height:1.85;margin:0 0 18px;padding-left:22px} + .post-body li{margin-bottom:8px} + .post-body li::marker{color:var(--amber)} + .post-body a{color:var(--sky);text-decoration:underline;text-decoration-color:rgba(69,175,201,.4);text-underline-offset:3px} + .post-body a:hover{color:var(--sky-bright)} + .post-body .risks{margin:12px 0 36px} + .pull-quote{ + border-left:2px solid var(--amber);padding:2px 0 2px 24px;margin:34px 0; + font-size:20px;line-height:1.5;font-weight:620;color:var(--text);letter-spacing:-.01em; + } + .pull-quote cite{display:block;margin-top:10px;font-family:var(--mono);font-size:11px;letter-spacing:.08em;color:var(--faint);font-style:normal;text-transform:uppercase} + .post-foot-nav{ + display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap; + margin-top:56px;padding-top:24px;border-top:1px solid var(--border);max-width:680px; + } + .post-foot-nav a{color:var(--sky);font-family:var(--mono);font-size:12px;letter-spacing:.04em;transition:color .12s ease} + .post-foot-nav a:hover{color:var(--sky-bright)} + + /* ---------- terms: quicknav + numbered clause cards with icon thumbnails ---------- */ + .terms-grid{display:grid;gap:16px;max-width:800px;margin-top:8px} + .terms-card{padding:26px 28px} + .terms-card .tc-head{display:flex;align-items:center;gap:14px;margin-bottom:14px} + .terms-card .tc-ico{ + flex:none;width:38px;height:38px;border-radius:8px;display:grid;place-items:center; + background:rgba(242,168,59,.1);border:1px solid rgba(242,168,59,.28);color:var(--amber); + } + .terms-card .tc-num{font-family:var(--mono);font-size:10px;color:var(--faint);letter-spacing:.1em;display:block;margin-bottom:2px} + .terms-card h2{font-size:19px;font-weight:650;margin:0} + .terms-card p{color:var(--dim);font-size:14px;line-height:1.75;margin-top:0} + .terms-card p+p{margin-top:12px} + .terms-card ul{margin:12px 0 0;padding-left:19px;color:var(--dim);font-size:14px;line-height:1.8} + .terms-card ul li{margin-bottom:5px} + .terms-card ul li::marker{color:var(--amber)} + .terms-eff{margin-top:16px} + .terms-cta{max-width:560px;margin:40px auto 0;text-align:center;padding:36px 32px} + .terms-cta p{color:var(--dim);margin:10px 0 20px;max-width:44ch;margin-left:auto;margin-right:auto} + + @media(max-width:1020px){ + .blog-grid{grid-template-columns:repeat(2,1fr)} + } + @media(max-width:720px){ + .blog-grid{grid-template-columns:1fr} + .post-cover{aspect-ratio:16/11} + .terms-card{padding:20px 20px} + } diff --git a/terms/index.html b/terms/index.html new file mode 100644 index 0000000..ae508d1 --- /dev/null +++ b/terms/index.html @@ -0,0 +1,372 @@ + + + + + +Terms of Service - LynkedUp Pro + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+
+ + +
+ Legal +

Terms of Service.

+

The agreement between you and LynkedUp Pro for using the platform. Written to be readable — skip to the section you need with the index below.

+

EFFECTIVE JAN 1, 2026 · LAST UPDATED JUL 14, 2026

+
+ +
+
+ + +
+
+
+ +
+
+ +
01

Acceptance of Terms

+
+

By creating an account, accessing, or using LynkedUp Pro (the "Platform"), you agree to be bound by these Terms of Service and our Privacy Policy. If you're accepting on behalf of a company, you confirm you have authority to bind that company to these terms.

+

If you don't agree with any part of these terms, don't use the Platform. We may update these terms from time to time; continued use after an update means you accept the revised terms.

+
+ +
+
+ +
02

Use of the Platform

+
+

LynkedUp Pro grants you a limited, non-exclusive, non-transferable license to access and use the Platform for your internal business operations, subject to these terms and your subscription tier.

+
    +
  • You may not resell, sublicense, or white-label the Platform without written permission.
  • +
  • You may not reverse-engineer, decompile, or attempt to extract the underlying source code.
  • +
  • Features, integrations, and AI tools may change, expand, or be deprecated as the product evolves; we'll provide reasonable notice for changes that remove core functionality.
  • +
+
+ +
+
+ +
03

Accounts & Eligibility

+
+

You must be at least 18 years old and able to form a binding contract to use LynkedUp Pro. You're responsible for the accuracy of the information you provide and for keeping your login credentials secure.

+

Any activity that happens under your account is your responsibility, whether performed by you, an employee, or a subcontractor you've granted access to. Notify us immediately if you suspect unauthorized access.

+
+ +
+
+ +
04

Subscriptions & Billing

+
+

Standard per-seat plans (Starter, Pro) are currently on hold; new accounts are onboarded through the Founders Lifetime License, a one-time fee that reserves lifetime access to the core platform. Details and current pricing are listed on our pricing page.

+

Where recurring billing does apply (e.g. add-ons or future per-seat plans), charges renew automatically on your billing cycle unless cancelled beforehand. Fees are non-refundable except where required by law or explicitly stated at time of purchase.

+
+ +
+
+ +
05

Data & Content Ownership

+
+

You own the data you put into LynkedUp Pro: your job files, photos, scans, customer records, and documents. We don't sell or share your job records with third parties, and every export leaves in standard formats you can take with you at any time.

+

We may use de-identified, aggregated data to improve estimation models and product features. This never includes customer contact details or identifiable job records.

+
+ +
+
+ +
06

Acceptable Use

+
+

You agree not to use the Platform to:

+
    +
  • Upload unlawful, fraudulent, or misleading content, including falsified inspection or claim evidence.
  • +
  • Interfere with or disrupt the integrity or performance of the Platform, its APIs, or connected integrations.
  • +
  • Attempt to access another customer's account, job data, or workspace without authorization.
  • +
  • Use the Platform in a way that violates any applicable local, state, or federal law.
  • +
+
+ +
+
+ +
07

Disclaimers & Limitation of Liability

+
+

The Platform, including AI-generated estimates, damage assessments, and automation, is provided "as is." AI outputs are decision support, not a replacement for professional judgment — a qualified human should review estimates, claims, and safety-critical decisions before acting on them.

+

To the fullest extent permitted by law, LynkedUp Pro is not liable for indirect, incidental, or consequential damages arising from use of the Platform, including denied claims, missed jobs, or business interruption, beyond the amount paid for the service in the preceding 12 months.

+
+ +
+
+ +
08

Termination

+
+

You may cancel your account at any time; your data remains exportable for 30 days after cancellation. We may suspend or terminate accounts that violate these terms, with notice where practical, or immediately in cases of fraud, security risk, or non-payment.

+

Founders Lifetime License holders retain lifetime access to core features under this agreement as long as the account remains in good standing and in compliance with these terms.

+
+ +
+
+ +
09

Changes to These Terms

+
+

We may revise these terms as the product and business evolve. Material changes will be posted here with an updated "last updated" date, and where required, communicated directly to account owners. Continued use of the Platform after changes take effect constitutes acceptance.

+
+ +
+
+ +
10

Governing Law & Contact

+
+

These terms are governed by the laws of the State of Texas, without regard to conflict-of-law principles. Any disputes will be resolved in the state or federal courts located in Dallas County, Texas.

+

Questions about these terms? Reach us at Info@LynkedUpPro.com or (866) 259-6533.

+
+ +
+ +
+ ✓ QUESTIONS ABOUT THESE TERMS? +

Talk to a real person.

+

Legal questions, data requests, or account-specific terms — our team can walk you through any section above.

+ +
+
+
+ +
+ + + + + diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..cd36910 --- /dev/null +++ b/vercel.json @@ -0,0 +1,6 @@ +{ + "framework": "vite", + "buildCommand": "npm run build", + "outputDirectory": "dist", + "installCommand": "npm install" +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..0b49e02 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,26 @@ +import { defineConfig } from "vite"; +import { fileURLToPath } from "node:url"; +import path from "node:path"; + +const root = path.dirname(fileURLToPath(import.meta.url)); + +export default defineConfig({ + base: "./", + server: { port: 4190 }, + build: { + rollupOptions: { + input: { + main: path.resolve(root, "index.html"), + faq: path.resolve(root, "faq.html"), + blog: path.resolve(root, "blog/index.html"), + blogCostOfPaperwork: path.resolve(root, "blog/cost-of-paperwork/index.html"), + blogStormChecklist: path.resolve(root, "blog/48-hour-storm-checklist/index.html"), + blogClaimsDenied: path.resolve(root, "blog/why-claims-get-denied/index.html"), + terms: path.resolve(root, "terms/index.html"), + }, + output: { + manualChunks: { three: ["three"], gsap: ["gsap"] }, + }, + }, + }, +});