Загрузка данных


<main class="landing-page">
  <section class="hero">
    <div class="hero-content">
      <p class="eyebrow">Онлайн-сервис для гостей</p>
      <h1>Оценка наследственного имущества без лишней бюрократии</h1>
      <p class="hero-description">
        Продажная страница для знакомства с возможностями платформы: понятные этапы, прозрачные сроки, сопровождение
        и быстрый переход к подаче заявки.
      </p>
      <div class="hero-actions">
        <a routerLink="/auth" class="btn btn-primary">Начать сейчас</a>
        <button type="button" class="btn btn-ghost" (click)="onRequestDemo()">Запросить демо</button>
      </div>
      <p class="stub-message" *ngIf="actionMessage">{{ actionMessage }}</p>
    </div>

    <aside class="hero-card">
      <h2>Быстрый расчет</h2>
      <p>Выберите тип имущества и получите ориентир по сроку и стоимости.</p>

      <div class="calc-actions">
        <button type="button" [class.active]="selectedObjectType === 'Квартира'" (click)="calculateEstimate('Квартира')">
          Квартира
        </button>
        <button type="button" [class.active]="selectedObjectType === 'Дом'" (click)="calculateEstimate('Дом')">Дом</button>
        <button
          type="button"
          [class.active]="selectedObjectType === 'Земельный участок'"
          (click)="calculateEstimate('Земельный участок')"
        >
          Участок
        </button>
      </div>

      <div class="estimate">
        <div>
          <span>Срок</span>
          <strong>{{ estimatedDays }} дн.</strong>
        </div>
        <div>
          <span>Стоимость</span>
          <strong>{{ estimatedPrice | number: '1.0-0' }} ₽</strong>
        </div>
      </div>
    </aside>
  </section>

  <section class="features">
    <h2>Почему выбирают наш сервис</h2>
    <div class="features-grid">
      <article class="feature-card" *ngFor="let feature of features">
        <div class="feature-icon">{{ feature.icon }}</div>
        <h3>{{ feature.title }}</h3>
        <p>{{ feature.description }}</p>
      </article>
    </div>
  </section>

  <section class="scenarios">
    <h2>Сценарии использования для гостя</h2>
    <ul>
      <li *ngFor="let scenario of scenarios">{{ scenario }}</li>
    </ul>
  </section>

  <section class="plans">
    <h2>Тарифы и пакеты</h2>
    <div class="plans-grid">
      <article class="plan-card" [class.highlighted]="plan.highlighted" *ngFor="let plan of plans">
        <p class="plan-name">{{ plan.name }}</p>
        <p class="plan-price">{{ plan.price }}</p>
        <p class="plan-description">{{ plan.description }}</p>
        <ul>
          <li *ngFor="let benefit of plan.benefits">{{ benefit }}</li>
        </ul>
      </article>
    </div>
  </section>

  <section class="faq">
    <h2>Частые вопросы</h2>
    <div class="faq-list">
      <article class="faq-item" *ngFor="let item of faq; let i = index">
        <button type="button" class="faq-question" (click)="toggleFaq(i)">
          <span>{{ item.question }}</span>
          <span class="faq-toggle" [class.open]="item.opened">+</span>
        </button>
        <p class="faq-answer" *ngIf="item.opened">{{ item.answer }}</p>
      </article>
    </div>
  </section>

  <section class="cta">
    <h2>Готовы начать?</h2>
    <p>Создайте учетную запись и отправьте первую заявку на оценку имущества уже сегодня.</p>
    <div class="cta-actions">
      <a routerLink="/auth" class="btn btn-primary">Перейти к регистрации</a>
      <button type="button" class="btn btn-ghost" (click)="onStartNow()">Открыть сценарий (stub)</button>
    </div>
  </section>
</main>


___________________________________CSS__________________________________

.landing-page {
  --primary: #4f46e5;
  --primary-soft: #e0e7ff;
  --primary-hover: #4338ca;
  --text-main: #111827;
  --text-secondary: #6b7280;
  --surface: #fff;
  --surface-soft: #f8fafc;
  --border: #e5e7eb;
  --radius: 16px;

  display: flex;
  flex-direction: column;
  gap: 2rem;
  padding: 2rem 1.5rem 3rem;

  color: var(--text-main);

  .hero,
  .features,
  .scenarios,
  .plans,
  .faq,
  .cta {
    width: 100%;
    max-width: 1250px;
    margin: 0 auto;
  }

  .hero {
    display: grid;
    grid-template-columns: 1.5fr 1fr;
    gap: 1.5rem;
    align-items: stretch;
  }

  .hero-content,
  .hero-card,
  .features,
  .scenarios,
  .plans,
  .faq,
  .cta {
    padding: 1.75rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: 0 10px 30px rgb(15 23 42 / 4%);
  }

  .eyebrow {
    display: inline-flex;
    margin: 0 0 0.75rem;
    padding: 0.35rem 0.75rem;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    color: var(--primary);
    background: var(--primary-soft);
  }

  h1 {
    margin: 0;
    font-size: clamp(1.9rem, 3.5vw, 3rem);
    line-height: 1.15;
    letter-spacing: -0.02em;
  }

  h2 {
    margin: 0 0 1rem;
    font-size: clamp(1.2rem, 2vw, 1.8rem);
    letter-spacing: -0.01em;
  }

  h3 {
    margin: 0.8rem 0 0.6rem;
    font-size: 1.08rem;
  }

  p {
    margin: 0;
    color: var(--text-secondary);
  }

  .hero-description {
    max-width: 60ch;
    margin-top: 1rem;
    line-height: 1.6;
  }

  .hero-actions,
  .cta-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 1.25rem;
  }

  .btn {
    cursor: pointer;
    padding: 0.72rem 1.1rem;
    border: 1px solid transparent;
    border-radius: 10px;
    font-size: 0.95rem;
    font-weight: 600;
    text-decoration: none;
    transition: all 0.2s ease;
  }

  .btn-primary {
    color: #fff;
    background: var(--primary);

    &:hover {
      background: var(--primary-hover);
      transform: translateY(-1px);
    }
  }

  .btn-ghost {
    color: var(--primary);
    background: #fff;
    border-color: #c7d2fe;

    &:hover {
      background: var(--primary-soft);
    }
  }

  .stub-message {
    margin-top: 0.8rem;
    padding: 0.75rem;
    border: 1px dashed #c7d2fe;
    border-radius: 10px;
    font-size: 0.9rem;
    color: #4338ca;
    background: #eef2ff;
  }

  .hero-card {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    background: linear-gradient(180deg, #fff 0%, #f8faff 100%);
  }

  .calc-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;

    button {
      cursor: pointer;
      padding: 0.5rem 0.8rem;
      border: 1px solid var(--border);
      border-radius: 8px;
      font-size: 0.85rem;
      color: #374151;
      background: #fff;
      transition: all 0.2s ease;
    }

    .active {
      border-color: #a5b4fc;
      color: #3730a3;
      background: #eef2ff;
    }
  }

  .estimate {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.75rem;

    div {
      padding: 0.75rem;
      border: 1px solid #dbeafe;
      border-radius: 10px;
      background: #f8fbff;
    }

    span {
      display: block;
      margin-bottom: 0.3rem;
      font-size: 0.8rem;
      color: #64748b;
    }

    strong {
      font-size: 1.1rem;
      color: #1e293b;
    }
  }

  .features-grid,
  .plans-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1rem;
    margin-top: 1rem;
  }

  .feature-card,
  .plan-card {
    padding: 1rem;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface-soft);
  }

  .feature-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 8px;
    font-weight: 700;
    color: #fff;
    background: var(--primary);
  }

  .scenarios ul,
  .plan-card ul {
    margin: 1rem 0 0;
    padding: 0 0 0 1.2rem;
    color: #475569;
  }

  .scenarios li,
  .plan-card li {
    margin-bottom: 0.45rem;
    line-height: 1.45;
  }

  .plan-name {
    font-weight: 700;
    color: #0f172a;
  }

  .plan-price {
    margin-top: 0.35rem;
    font-size: 1.3rem;
    font-weight: 800;
    color: var(--primary);
  }

  .plan-description {
    margin-top: 0.35rem;
  }

  .plan-card.highlighted {
    border-color: #a5b4fc;
    background: #eef2ff;
  }

  .faq-list {
    display: flex;
    flex-direction: column;
    gap: 0.8rem;
    margin-top: 1rem;
  }

  .faq-item {
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface-soft);
  }

  .faq-question {
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 0.95rem 1rem;
    border: 0;
    font: inherit;
    font-weight: 600;
    text-align: left;
    color: #1f2937;
    background: transparent;
  }

  .faq-toggle {
    font-size: 1.2rem;
    transition: transform 0.2s ease;
  }

  .faq-toggle.open {
    transform: rotate(45deg);
  }

  .faq-answer {
    margin: 0;
    padding: 0 1rem 1rem;
    line-height: 1.6;
  }

  .cta {
    text-align: center;
    background: linear-gradient(180deg, #fff 0%, #f8faff 100%);
  }

  .cta p {
    max-width: 62ch;
    margin: 0 auto;
  }

  .cta-actions {
    justify-content: center;
  }

  @media (width <= 980px) {
    .hero {
      grid-template-columns: 1fr;
    }

    .features-grid,
    .plans-grid {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }

  @media (width <= 640px) {
    padding: 1rem 0.75rem 2rem;
    gap: 1rem;

    .hero-content,
    .hero-card,
    .features,
    .scenarios,
    .plans,
    .faq,
    .cta {
      padding: 1rem;
      border-radius: 12px;
    }

    .features-grid,
    .plans-grid,
    .estimate {
      grid-template-columns: 1fr;
    }
  }
}



_____________________________________landing page spec ts_______________________________________________________________

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LandingPage } from './landing-page';

describe('LandingPage', () => {
  let component: LandingPage;
  let fixture: ComponentFixture<LandingPage>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [LandingPage],
    }).compileComponents();

    fixture = TestBed.createComponent(LandingPage);
    component = fixture.componentInstance;
    await fixture.whenStable();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});



______________________________spec ts шляпа________________________________________

import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { RouterLink } from '@angular/router';

type FaqItem = {
  question: string;
  answer: string;
  opened?: boolean;
};

@Component({
  selector: 'lib-landing-page',
  imports: [CommonModule, RouterLink],
  templateUrl: './landing-page.html',
  styleUrl: './landing-page.scss',
})
export class LandingPage {
  readonly features = [
    {
      title: 'Проверка документов онлайн',
      description: 'Система подсказывает, каких данных не хватает, и помогает собрать полный пакет без лишних визитов.',
      icon: '✓',
    },
    {
      title: 'Прозрачные статусы заявки',
      description: 'Каждый этап виден в личном кабинете: от подачи до готового отчета и оплаты.',
      icon: '◉',
    },
    {
      title: 'Поддержка специалистов',
      description: 'Гость может получить консультацию до регистрации и быстро перейти к оформлению заявки.',
      icon: '∞',
    },
  ];

  readonly scenarios = [
    'Наследство: оценка квартиры, дома, участка, гаража',
    'Подготовка к нотариальным действиям и подаче документов',
    'Получение копий нотариальных документов с трекингом статуса',
    'Быстрый переход в личный кабинет после регистрации',
  ];

  readonly plans = [
    {
      name: 'Базовый',
      price: '0 ₽',
      description: 'Для знакомства с сервисом',
      benefits: ['Просмотр справочного раздела', 'Калькулятор стоимости', 'Запрос консультации'],
      highlighted: false,
    },
    {
      name: 'Старт',
      price: 'от 1 990 ₽',
      description: 'Для первой оценки наследственного имущества',
      benefits: ['Подача заявки', 'Загрузка документов', 'Отслеживание статусов'],
      highlighted: true,
    },
    {
      name: 'Профи',
      price: 'Индивидуально',
      description: 'Для регулярной работы с оценкой и документами',
      benefits: ['Приоритетная поддержка', 'Расширенные уведомления', 'История и выгрузка операций'],
      highlighted: false,
    },
  ];

  readonly faq: FaqItem[] = [
    {
      question: 'Можно ли пользоваться сервисом без регистрации?',
      answer:
        'Да, гость может изучить возможности платформы, просчитать примерные сроки и стоимость, а также отправить запрос на консультацию.',
      opened: true,
    },
    {
      question: 'Сколько занимает подготовка отчета по оценке?',
      answer:
        'Срок зависит от типа имущества и полноты документов. В среднем подготовка занимает от 1 до 3 рабочих дней.',
    },
    {
      question: 'Как проходит оплата?',
      answer:
        'После создания заявки система покажет доступные тарифы и шаги оплаты. История платежей сохраняется в личном кабинете.',
    },
  ];

  estimatedDays = 2;
  estimatedPrice = 3490;
  selectedObjectType = 'Квартира';
  actionMessage = '';

  onRequestDemo(): void {
    this.actionMessage = 'Демо-запрос отправлен. Это учебный stub: здесь будет интеграция с формой.';
  }

  onStartNow(): void {
    this.actionMessage = 'Переход к регистрации доступен по кнопке "Войти". Это интерактив-заглушка.';
  }

  toggleFaq(index: number): void {
    this.faq[index].opened = !this.faq[index].opened;
  }

  calculateEstimate(objectType: string): void {
    this.selectedObjectType = objectType;

    if (objectType === 'Квартира') {
      this.estimatedDays = 2;
      this.estimatedPrice = 3490;
      return;
    }

    if (objectType === 'Дом') {
      this.estimatedDays = 3;
      this.estimatedPrice = 4990;
      return;
    }

    this.estimatedDays = 4;
    this.estimatedPrice = 6290;
  }
}