9 커밋 ef964669b6 ... b836e25de1

작성자 SHA1 메시지 날짜
  xander b836e25de1 Clients: scale up whitespace-heavy logos (Норникель, Атлас, УГХ) 1 주 전
  xander 06d81e55e7 Clients: 3x3 checkerboard arrangement, heavy/light logos alternating, uniform cells 1 주 전
  xander 60a519f906 Clients: remove grafana dashboard screenshot from logo strip 1 주 전
  xander d2626dff5a Why us: colored logos right after the heading, cards below 1 주 전
  xander 3f548c1861 Home: merge client logos into why-us block, larger logos, drop separate section 1 주 전
  xander 14dd629545 Services: remove arrow chips from cards 1 주 전
  xander f9f4a86d36 Header: drop wordmark, logo only 1 주 전
  xander ac569d4127 Widget: playable videos in feed (lazy mp4 from Telegram CDN, thumb as poster) 1 주 전
  xander de03bdb608 Widget: show full-quality video preview frames with play overlay 1 주 전
5개의 변경된 파일과 76개의 추가작업 그리고 34개의 파일을 삭제
  1. 9 2
      scripts/fetch-telegram.mjs
  2. 1 2
      src/components/Header.astro
  3. 23 3
      src/components/TelegramWidget.astro
  4. 6 0
      src/data/telegram-news.json
  5. 37 27
      src/pages/index.astro

+ 9 - 2
scripts/fetch-telegram.mjs

@@ -46,11 +46,18 @@ for (let page = 0; page < MAX_PAGES; page++) {
     const text = textNode.html()?.trim() || "";
     const datetime = $el.find("time").first().attr("datetime") || "";
     const photoStyle = $el.find(".tgme_widget_message_photo_wrap").first().attr("style") || "";
-    const photo = photoStyle.match(/url\('([^']+)'\)/)?.[1]?.split("?")[0] || "";
+    let photo = photoStyle.match(/url\('([^']+)'\)/)?.[1]?.split("?")[0] || "";
     const video = $el.find(".tgme_widget_message_video_wrap, .tgme_widget_message_video_player").length > 0;
+    // direct mp4 (tokenized URL — expires after a while; re-run fetch-news to refresh)
+    const videoSrc = video ? $el.find("video").first().attr("src") || "" : "";
+    // video posts carry a full-size preview frame in .tgme_widget_message_video_thumb
+    if (video && !photo) {
+      const thumbStyle = $el.find(".tgme_widget_message_video_thumb").first().attr("style") || "";
+      photo = thumbStyle.match(/url\('([^']+)'\)/)?.[1] || "";
+    }
     const link = $el.find(".tgme_widget_message_date").first().attr("href") || `https://t.me/${CHANNEL}/${postId}`;
     if (text || photo || video) {
-      posts.push({ id: Number(postId), text, datetime, photo, video, link });
+      posts.push({ id: Number(postId), text, datetime, photo, video, videoSrc, link });
     }
   });
 

+ 1 - 2
src/components/Header.astro

@@ -23,8 +23,7 @@ const isActive = (href: string) =>
 >
   <div class="mx-auto flex h-20 max-w-[1400px] items-center justify-between gap-4 px-5 sm:px-8">
     <a href="/" class="flex shrink-0 items-center gap-3" aria-label={site.name}>
-      <Logo class="h-12 w-auto" />
-      <span class="hidden text-2xl font-medium tracking-tight sm:block" style="font-family: var(--font-display)">Scientia</span>
+      <Logo class="h-30 w-auto" />
     </a>
 
     <nav class="hidden items-center gap-1 lg:flex" aria-label="Основная навигация">

+ 23 - 3
src/components/TelegramWidget.astro

@@ -25,17 +25,37 @@ const fmt = new Intl.DateTimeFormat("ru-RU", { day: "numeric", month: "long", ho
       <div class="tg-body space-y-2.5 p-3 pb-5">
         {telegramNews.map((post, i) => (
           <div class:list={["tg-bubble relative ml-3 block rounded-xl bg-white px-3 py-2.5 shadow-[0_1px_1px_rgba(16,35,47,0.14)]", i >= 8 && "hidden"]}>
-            {post.photo && (
+            {post.photo && !post.video && (
               <img src={post.photo} alt="" class="mb-2 w-full rounded-lg" loading="lazy" />
             )}
-            {post.video && (
+            {post.video && post.videoSrc ? (
+              <video
+                controls
+                preload="none"
+                playsinline
+                poster={post.photo || undefined}
+                src={post.videoSrc}
+                class="mb-2 w-full rounded-lg bg-night-950"
+              ></video>
+            ) : post.video && post.photo ? (
+              <span class="relative mb-2 block overflow-hidden rounded-lg">
+                <img src={post.photo} alt="" class="w-full" loading="lazy" />
+                <span class="absolute inset-0 flex items-center justify-center bg-night-950/25">
+                  <span class="flex h-12 w-12 items-center justify-center rounded-full bg-night-950/75 text-white">
+                    <svg class="h-6 w-6 translate-x-px" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
+                      <path d="M8 5.14v14l11-7-11-7z" />
+                    </svg>
+                  </span>
+                </span>
+              </span>
+            ) : post.video ? (
               <span class="mb-2 flex aspect-video w-full items-center justify-center gap-2 rounded-lg bg-night-950/90 text-sm font-medium text-white">
                 <svg class="h-8 w-8" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
                   <path d="M8 5.14v14l11-7-11-7z" />
                 </svg>
                 Видео
               </span>
-            )}
+            ) : null}
             {post.text && (
               <div class="text-[13px] leading-snug text-night-950 [&_a]:break-words [&_a]:text-[#2AABEE]">
                 <Fragment set:html={post.text} />

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 6 - 0
src/data/telegram-news.json


+ 37 - 27
src/pages/index.astro

@@ -25,11 +25,19 @@ const whyUs = [
   },
 ];
 
+// Client logos, arranged checkerboard: heavy dark logos alternate with light ones.
+// `big` — logos with lots of internal whitespace, render larger to match the others optically
 const clients = [
-  "___1.png", "_2.jpg", "photo-610z4l.jpg", "29595058_20159791717.jpg",
-  "9f97a370e8530d40a0ff.jpg", "unnamed.jpg", "321.JPG", "evraz.jpg",
-  "c5aa9760-2920-4b4d-b.png", "17304586690.jpg",
-].map((f) => "/images/" + f);
+  { file: "___1.png" },                         // УГМК — heavy
+  { file: "unnamed.jpg", big: true },           // Атлас Майнинг — light
+  { file: "c5aa9760-2920-4b4d-b.png" },         // ВНИМИ — heavy
+  { file: "29595058_20159791717.jpg", big: true }, // Норникель — light
+  { file: "9f97a370e8530d40a0ff.jpg" },         // РМК — heavy
+  { file: "_2.jpg" },                           // Полюс — light
+  { file: "evraz.jpg" },                        // Евраз — medium
+  { file: "17304586690.jpg", big: true },       // УГХ — light
+  { file: "photo-610z4l.jpg" },                 // Алроса — medium
+].map((c) => ({ ...c, src: "/images/" + c.file }));
 ---
 
 <Base
@@ -118,17 +126,14 @@ const clients = [
                 <span class="absolute left-4 top-4 bg-accent-300 px-2.5 py-1 text-xs font-bold text-night-950">{s.tag}</span>
               )}
             </div>
-            <div class="mt-5 flex items-start justify-between gap-4">
-              <div>
-                <h3 class="display text-2xl">{s.title}</h3>
-                {s.items && (
-                  <p class="mt-2 line-clamp-2 text-sm leading-relaxed text-slate-500">{s.items[0]}</p>
-                )}
-                {s.description && (
-                  <p class="mt-2 line-clamp-2 text-sm leading-relaxed text-slate-500">{s.description}</p>
-                )}
-              </div>
-              <span class="chip-arrow chip-arrow-dark mt-1 h-10 w-10" aria-hidden="true">→</span>
+            <div class="mt-5">
+              <h3 class="display text-2xl">{s.title}</h3>
+              {s.items && (
+                <p class="mt-2 line-clamp-2 text-sm leading-relaxed text-slate-500">{s.items[0]}</p>
+              )}
+              {s.description && (
+                <p class="mt-2 line-clamp-2 text-sm leading-relaxed text-slate-500">{s.description}</p>
+              )}
             </div>
           </a>
         ))}
@@ -173,6 +178,23 @@ const clients = [
       <h2 class="display max-w-3xl text-5xl sm:text-6xl">
         Почему выбирают нас<span class="dot">.</span>
       </h2>
+
+      <!-- Client logos -->
+      <div class="mt-12">
+        <div class="grid grid-cols-2 items-center justify-items-center gap-x-10 gap-y-8 sm:grid-cols-3">
+          {clients.map((c) => (
+            <span class="flex h-36 w-full items-center justify-center">
+              <img
+                src={c.src}
+                alt="Логотип заказчика"
+                class:list={["w-auto object-contain", c.big ? "max-h-36 max-w-[300px]" : "max-h-24 max-w-[220px]"]}
+                loading="lazy"
+              />
+            </span>
+          ))}
+        </div>
+      </div>
+
       <div class="mt-16 grid gap-x-8 gap-y-12 sm:grid-cols-2 lg:grid-cols-4">
         {whyUs.map((w, i) => (
           <div class="border-t-2 border-night-950 pt-6">
@@ -185,18 +207,6 @@ const clients = [
     </div>
   </section>
 
-  <!-- Clients -->
-  <section class="border-y border-slate-200 bg-white">
-    <div class="mx-auto max-w-[1400px] px-5 py-20 sm:px-8">
-      <h2 class="text-xs font-semibold uppercase tracking-[0.25em] text-slate-400">Наши заказчики</h2>
-      <div class="mt-10 grid grid-cols-2 items-center justify-items-center gap-x-10 gap-y-8 sm:grid-cols-3 lg:grid-cols-5">
-        {clients.map((src) => (
-          <img src={src} alt="Логотип заказчика" class="max-h-14 w-auto max-w-[150px] object-contain opacity-60 grayscale transition hover:opacity-100 hover:grayscale-0" loading="lazy" />
-        ))}
-      </div>
-    </div>
-  </section>
-
   <!-- Community — Porsche-style tiles -->
   <section class="bg-night-950 text-white">
     <div class="mx-auto grid max-w-[1400px] gap-px px-0 sm:grid-cols-2">

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.