From e8590752453a7c6865ec119ee98ccde548c63cda Mon Sep 17 00:00:00 2001 From: Amirhossein Khalili Date: Thu, 18 Jun 2026 20:42:22 +0330 Subject: [PATCH] docs(slides): improve slides images and theme --- docs/slides/README.md | 15 +- docs/slides/assets/app-demo.svg | 47 +++ docs/slides/assets/chatroom-architecture.svg | 67 ++-- docs/slides/assets/http-mechanism.svg | 43 +++ docs/slides/assets/long-polling-mechanism.svg | 46 +++ docs/slides/assets/polling-mechanism.svg | 49 +++ docs/slides/assets/websocket-handshake.svg | 45 ++- docs/slides/assets/websocket-history.svg | 4 +- docs/slides/assets/websocket-osi.svg | 70 ++-- docs/slides/assets/websocket-pros-cons.svg | 4 +- docs/slides/index.html | 298 +++++---------- docs/slides/script.js | 4 +- docs/slides/styles.css | 347 +++++++++++++++++- 13 files changed, 736 insertions(+), 303 deletions(-) create mode 100644 docs/slides/assets/app-demo.svg create mode 100644 docs/slides/assets/http-mechanism.svg create mode 100644 docs/slides/assets/long-polling-mechanism.svg create mode 100644 docs/slides/assets/polling-mechanism.svg diff --git a/docs/slides/README.md b/docs/slides/README.md index d2a2cc7..74493f7 100644 --- a/docs/slides/README.md +++ b/docs/slides/README.md @@ -1,9 +1,8 @@ -# ارائه WebSocket فارسی +# WebSocket Presentation -- فایل `index.html` را در مرورگر باز کنید. -- با کلیدهای چپ/راست بین اسلایدها حرکت کنید. -- کلید `N` یادداشت سخنران را نشان می‌دهد. -- کلید `F` حالت تمام‌صفحه را فعال می‌کند. -- فونت اصلی `Vazirmatn` است؛ فایل ارائه از Google Fonts / فونت نصب‌شده روی سیستم استفاده می‌کند. -- در نمایشگرهای بزرگ، اسلایدها به صورت ۱۶:۹ تا جای ممکن ارتفاع صفحه را پر می‌کنند. -- در نمایشگرهای کوچک، اندازه فونت و چینش اسلایدها کوچک‌تر و عمودی‌تر می‌شود. +- Open `index.html` in a browser. +- Use Left/Right arrows to move between slides. +- Press `N` to show speaker notes. +- Press `F` for fullscreen mode. +- The deck uses the local Vazirmatn font file. +- Visible slide content is limited to titles, subtitles, diagrams, images, and bullet points. diff --git a/docs/slides/assets/app-demo.svg b/docs/slides/assets/app-demo.svg new file mode 100644 index 0000000..82429a3 --- /dev/null +++ b/docs/slides/assets/app-demo.svg @@ -0,0 +1,47 @@ + + Chatroom app mockup + English mockup of the WebSocket chatroom user interface. + + + + + + + + + + + + + + Simple WebSocket Chatroom + + Connected + + Room: internet-engineering + You are chatting as Ali. + + Hello from WebSocket! + + Instant delivery + + Type a message... + + Send + + Online Users + Ali + Sara + Reza + + + + + diff --git a/docs/slides/assets/chatroom-architecture.svg b/docs/slides/assets/chatroom-architecture.svg index 2f38f02..4475e45 100644 --- a/docs/slides/assets/chatroom-architecture.svg +++ b/docs/slides/assets/chatroom-architecture.svg @@ -1,6 +1,6 @@ - + Chatroom architecture - A compact architecture diagram for the chatroom project. + A compact architecture diagram for the FastAPI WebSocket chatroom project. - + - - - - Chatroom Architecture - Browser Clients - FastAPI - Room Manager + + + + - - - - - + + + + + + + Client A + Browser tab + Client B + Browser tab + Server + WebSocket Endpoint + ConnectionManager + /ws/{room}/{user} + Room: general + A - B - C + Room: class + D - E + + + + + WebSocket Frames + Broadcast by room - Client A - Browser tab - Client B - Browser tab - FastAPI - WebSocket Endpoint - ConnectionManager - /ws/{room}/{user} - Room: general - A - B - C - Room: class - D - E - - - - - WebSocket Frames - Broadcast by room diff --git a/docs/slides/assets/http-mechanism.svg b/docs/slides/assets/http-mechanism.svg new file mode 100644 index 0000000..f0e4bfe --- /dev/null +++ b/docs/slides/assets/http-mechanism.svg @@ -0,0 +1,43 @@ + + HTTP mechanism + A short HTTP request response exchange. + + + + + + + + + + + + + + + + + Browser + HTTP Client + Server + HTTP Endpoint + + 1. GET /resource + + 2. 200 OK Response + + 3. Connection closes + + diff --git a/docs/slides/assets/long-polling-mechanism.svg b/docs/slides/assets/long-polling-mechanism.svg new file mode 100644 index 0000000..677aceb --- /dev/null +++ b/docs/slides/assets/long-polling-mechanism.svg @@ -0,0 +1,46 @@ + + Long Polling mechanism + An HTTP request held by the server until data is ready. + + + + + + + + + + + + + + + + + + Browser + Waiting request + Server + Holds response + + 1. HTTP Request + + 2. Hold Open + + 3. Message Response + + 4. New Request + + diff --git a/docs/slides/assets/polling-mechanism.svg b/docs/slides/assets/polling-mechanism.svg new file mode 100644 index 0000000..fc5955b --- /dev/null +++ b/docs/slides/assets/polling-mechanism.svg @@ -0,0 +1,49 @@ + + Polling mechanism + Repeated HTTP requests on a timer. + + + + + + + + + + + + + + + + + Browser + Timer based checks + Server + HTTP Endpoint + + Request 1 + + Empty Response + + Request 2 + + Empty Response + + Request 3 + + Message Response + + diff --git a/docs/slides/assets/websocket-handshake.svg b/docs/slides/assets/websocket-handshake.svg index a69eb6e..9a19c9c 100644 --- a/docs/slides/assets/websocket-handshake.svg +++ b/docs/slides/assets/websocket-handshake.svg @@ -1,4 +1,4 @@ - + WebSocket handshake A compact HTTP Upgrade handshake diagram. @@ -9,32 +9,37 @@ font-weight: 100 900; } text { font-family: "Vazirmatn", Arial, sans-serif; } + .title { fill: #f8fafc; font-size: 38px; font-weight: 850; } + .sub { fill: #a8adbd; font-size: 18px; font-weight: 500; } + .node-title { fill: #f8fafc; font-size: 25px; font-weight: 850; } + .node-sub { fill: #a8adbd; font-size: 16px; } + .label { font-size: 18px; font-weight: 850; } - + - - - - HTTP Upgrade Handshake - Starts with HTTP, continues with WebSocket Frames + + + + - - + + + + Browser + JavaScript WebSocket + Server + /ws/{room}/{user} + + 1. GET + Upgrade + + 2. 101 Switching Protocols + + + 3. Full-duplex WebSocket Frames - Browser - JavaScript WebSocket - FastAPI Server - /ws/{room}/{user} - - 1. GET + Upgrade - - 2. 101 Switching Protocols - - - 3. Full-duplex WebSocket Frames diff --git a/docs/slides/assets/websocket-history.svg b/docs/slides/assets/websocket-history.svg index 2951da2..02c27a2 100644 --- a/docs/slides/assets/websocket-history.svg +++ b/docs/slides/assets/websocket-history.svg @@ -17,8 +17,7 @@ - WebSocket History - From Classic HTTP to Real-time communication + @@ -60,4 +59,5 @@ Dashboard - IoT + diff --git a/docs/slides/assets/websocket-osi.svg b/docs/slides/assets/websocket-osi.svg index 60ea5be..dab97fd 100644 --- a/docs/slides/assets/websocket-osi.svg +++ b/docs/slides/assets/websocket-osi.svg @@ -1,6 +1,6 @@ - + WebSocket in OSI model - A compact OSI stack with English technical names and Persian supporting labels. + A compact OSI stack showing WebSocket at the application layer over TCP/IP. - + - - WebSocket in the OSI Model - Application Layer over TCP/IP - - - - - - Layer 7 - Application - WebSocket - JSON - Chat - - Layer 6 - Presentation - UTF-8 - Text - - Layer 5 - Session - Long-lived conversation - - Layer 4 - Transport - TCP reliable stream - - Layer 3 - Network - IP routing - - Layers 2-1 - Link / Physical - Ethernet - Wi-Fi + + + + + + + + + + + + + Layer 7 - Application + WebSocket - JSON - Chat + + + Layer 6 - Presentation + UTF-8 - Text + + + Layer 5 - Session + Long-lived conversation + + + Layer 4 - Transport + TCP reliable stream + + + Layer 3 - Network + IP routing + + + Layers 2-1 - Link / Physical + Ethernet - Wi-Fi diff --git a/docs/slides/assets/websocket-pros-cons.svg b/docs/slides/assets/websocket-pros-cons.svg index 80de71b..61482ee 100644 --- a/docs/slides/assets/websocket-pros-cons.svg +++ b/docs/slides/assets/websocket-pros-cons.svg @@ -17,8 +17,7 @@ - WebSocket Trade-offs - Strengths and limitations at a glance + @@ -41,4 +40,5 @@ + diff --git a/docs/slides/index.html b/docs/slides/index.html index 69062c1..e9952d7 100644 --- a/docs/slides/index.html +++ b/docs/slides/index.html @@ -1,241 +1,143 @@ - + - ارائه WebSocket و Socket Programming + WebSocket Protocol
-
-
پروژه مهندسی اینترنت
-

چت‌روم ساده با WebSocket

-

نمایش Socket Programming و ارتباط Real-time در وب

- -
- نمای صفحه چت‌روم -
- مسیر ارائه - History → Polling → Handshake → OSI → Architecture → Demo -
-
1
HandshakeHTTP Upgrade
-
2
ChannelPersistent TCP
-
3
FramesFull-duplex
-
4
BroadcastRoom delivery
-
-
+
+
+

Websocket Protocol

+

Socket Programming with a FastAPI real-time demo chat app

+

Amirhossein Khalili • Morteza Khanbabaie • Alireza Khosravi

- +
-
-
History
-

چرا WebSocket لازم شد؟

-

مسیر تکامل از HTTP کلاسیک تا اتصال پایدار دوطرفه

- -
-
- تاریخچه WebSocket -
-
-
HTTP
Request / Response
  • ساده
  • بدون Server Push
-
AJAX
Polling
  • درخواست تکراری
  • هزینه شبکه بیشتر
-
LONG
Long Polling
  • اتصال منتظر
  • پیچیدگی بیشتر
-
WS
WebSocket
  • اتصال پایدار
  • پیام دوطرفه
-
+
+

WebSocket History Timeline

+

The communication model evolved from simple request/response to persistent real-time channels.

+
+
Classic HTTP1991
+
Polling1995+
+
Long Polling2006
+
WebSocket2011
+
Real-time Web2010s+
- +
-
-
Resource Cost
-

هزینه هر روش برای سرور

-

هر روش، فشار متفاوتی روی Network، CPU، memory و socket دارد

- -
-
Classic HTTP
  • درخواست جداگانه
  • پردازش header
  • پایان چرخه
-
Polling
  • درخواست زیاد
  • پاسخ‌های خالی
  • سربار Network
-
Long Polling
  • اتصال باز
  • نگهداری state
  • مصرف buffer
-
WebSocket
  • یک اتصال پایدار
  • فریم سبک
  • state فعال
+
+

HTTP

+

A short-lived request/response cycle with no native live push.

+
+
+ HTTP request response mechanism +
+
+
Mechanism
  • One request returns one response
  • The connection closes after the exchange
+
Cost
  • Every update needs a new HTTP cycle
  • The server cannot push live data by itself
+
- -
Polling → درخواست تکراری · Long Polling → انتظار طولانی · WebSocket → کانال پایدار
- +
-
-
HTTP Upgrade Handshake
-

WebSocket چطور شروع می‌شود؟

-

ابتدا HTTP، سپس پاسخ 101 و بعد WebSocket Frames

- -
-
ClientBrowser
  • ساخت WebSocket
  • درخواست Upgrade
-
-
1. GET + Upgrade
-
2. 101 Switching Protocols
-
3. WebSocket Frames
+
+

Polling

+

The browser repeatedly asks the server for new data.

+
+
+ Polling repeated request mechanism +
+
+
Mechanism
  • The client checks on a fixed timer
  • The server answers even when nothing changed
+
Cost
  • Many empty requests waste network work
  • Latency depends on the timer interval
-
ServerFastAPI
  • پذیرش Upgrade
  • نگهداری socket
+ +
-
-
+
+

Long Polling

+

The server holds the request until an update is available.

+
+
+ Long Polling held request mechanism +
+
+
Mechanism
  • The server keeps the request open
  • The client reconnects after each response
+
Cost
  • Open requests consume server state
  • Reconnect and timeout handling is required
+
+
+ +
+ +
+

WebSocket

+

An HTTP Upgrade creates one persistent Full-duplex channel.

+
+
HTTP Upgrade Handshake
-
-
GET /ws/general/ali HTTP/1.1
-Upgrade: websocket
-Connection: Upgrade
-Sec-WebSocket-Key: ...
-
HTTP/1.1 101 Switching Protocols
-Upgrade: websocket
-Connection: Upgrade
+
+
Mechanism
  • HTTP Upgrade switches the protocol
  • Frames move both ways on one socket
+
Cost
  • Each client keeps a live connection
  • Scaling needs connection-aware design
- + +
+ +
+

WebSocket in the OSI Model

+

Application semantics at Layer 7, reliable byte delivery through TCP/IP.

+
+ WebSocket in OSI model +
+
-
HTTP Versions
-

WebSocket در نسخه‌های HTTP

-

مدل آموزشی پروژه روی HTTP/1.1 Upgrade تمرکز دارد

- -
-
HTTP/1.1Upgrade + 101
  • مدل کلاسیک
  • یک TCP connection
  • ساده برای آموزش
-
HTTP/2Extended CONNECT
  • روی یک stream
  • بدون Upgrade کلاسیک
  • پیچیده‌تر
-
HTTP/3QUIC + CONNECT
  • بدون TCP
  • روی QUIC
  • مدرن‌تر
+

HTTP Versions in WebSocket

+

Different HTTP versions start WebSocket connections differently.

+
+
HTTP/1.1Upgrade + 101 Switching Protocols
  • The browser sends a normal GET request with Upgrade: websocket.
  • The server replies 101 Switching Protocols if it accepts.
  • After that, the same TCP connection carries WebSocket frames both ways.
  • This is the classic and easiest model to understand.
+
HTTP/2Extended CONNECT over a Stream
  • HTTP/2 does not use the old connection-level Upgrade flow.
  • It uses Extended CONNECT, so WebSocket traffic lives inside one HTTP/2 stream.
  • Other HTTP/2 streams can share the same underlying connection.
  • Support depends more on servers, clients, and proxies.
+
HTTP/3CONNECT over QUIC
  • HTTP/3 runs on QUIC over UDP instead of TCP.
  • WebSocket uses a CONNECT-based mapping similar in idea to HTTP/2.
  • QUIC streams reduce transport-level head-of-line blocking.
  • It is modern, but the demo is easier to explain with HTTP/1.1.
-
HTTP/1.1: Upgrade → 101 → WebSocket Frames
- +
-
-
OSI Model
-

جایگاه WebSocket در شبکه

-

WebSocket در لایه Application استفاده می‌شود؛ TCP/IP حمل داده را انجام می‌دهد

- -
-
-
Layer 7WebSocket + JSON
-
Layer 6UTF-8 text
-
Layer 5Long-lived session
-
Layer 4TCP reliable stream
-
Layer 3IP routing
-
Layer 2/1Ethernet / Wi-Fi
-
-
-
AApplication
  • اتاق
  • کاربر
  • پیام
-
TTransport
  • TCP
  • تحویل قابل اعتماد
-
NNetwork
  • IP
  • مسیریابی
-
+
+

Project Architecture

+

Browsers connect to one WebSocket endpoint; ConnectionManager groups sockets by room.

+
+ WebSocket chatroom architecture
- +
-
-
Project Architecture
-

معماری پروژه چت‌روم

-

هر تب مرورگر یک WebSocket connection دارد

- -
-
-
Client ABrowser tab
-
Client BBrowser tab
-
-
- FastAPI WebSocket Endpoint -

/ws/{room}/{user}

-
ConnectionManager
-
Broadcast داخل همان room
-
-
-
Room: generalA · B · C
-
Room: classD · E
-
+
+

Keynotes and Takeaways

+

The essential points to remember from the project.

+
+
HTTP is request/response
  • Simple
  • Stateless
  • Not ideal for live push
+
Polling simulates real-time
  • Easy
  • Wasteful
  • Interval-based latency
+
WebSocket keeps a channel
  • Persistent
  • Full-duplex
  • Low latency
+
Server design matters
  • Connection state
  • Disconnect handling
  • Scaling strategy
- -
- Browser UI - FastAPI endpoint - Room Manager - Broadcast -
- -
- -
-
Message Flow
-

جریان پیام در پروژه

-

از ورود کاربر تا نمایش پیام بدون refresh

- -
-
1Joinname + room
-
2UpgradeHTTP → WebSocket
-
3SendJSON content
-
4Broadcastroom sockets
-
5Renderبدون refresh
-
- -
-
نام و اتاق
-
WebSocket در JS
-
HTTP Upgrade
-
قبول در FastAPI
-
Room Manager
-
JSON message
-
Broadcast
-
نمایش پیام
-
- -
- -
-
Trade-offs
-

مزایا و محدودیت‌ها

-

WebSocket برای Real-time عالی است، اما state سرور را بیشتر می‌کند

- -
-
Full-duplex
  • ارسال دوطرفه
  • بدون انتظار request بعدی
-
Low latency
  • تاخیر کم
  • فریم سبک‌تر
-
!Server state
  • socket باز
  • مدیریت disconnect
-
- -
- Chat - Notifications - Live dashboard - Online game -
- -
- -
-
Demo
-

برنامه اجرای دمو

-

دو تب، یک room، پیام Real-time

- -
- نمای دمو -
    -
  1. اجرای uvicorn app.main:app --reload
  2. -
  3. باز کردن دو tab
  4. -
  5. ورود دو user به یک room
  6. -
  7. ارسال message
  8. -
  9. نمایش بدون refresh
  10. -
  11. تغییر online users
  12. -
-
- +
-
← → حرکت · N یادداشت · F تمام‌صفحه
+
Left / Right to move - N for notes - F for fullscreen
diff --git a/docs/slides/script.js b/docs/slides/script.js index 21a8089..3b5e014 100644 --- a/docs/slides/script.js +++ b/docs/slides/script.js @@ -54,8 +54,8 @@ document.getElementById('prev').addEventListener('click', () => go(-1)); document.getElementById('next').addEventListener('click', () => go(1)); document.addEventListener('keydown', (e) => { - if (['ArrowRight', 'PageUp'].includes(e.key)) go(-1); - if (['ArrowLeft', 'PageDown', ' '].includes(e.key)) go(1); + if (['ArrowLeft', 'PageUp'].includes(e.key)) go(-1); + if (['ArrowRight', 'PageDown', ' '].includes(e.key)) go(1); if (e.key.toLowerCase() === 'n') notesBox.classList.toggle('show'); if (e.key.toLowerCase() === 'f') { if (!document.fullscreenElement) document.documentElement.requestFullscreen?.(); diff --git a/docs/slides/styles.css b/docs/slides/styles.css index 56c853d..cfd8fab 100644 --- a/docs/slides/styles.css +++ b/docs/slides/styles.css @@ -87,6 +87,16 @@ body { } .slide > * { position: relative; z-index: 1; } .slide.active { display: block; animation: fadeUp .28s ease-out; } +.slide.active.center-slide { + display: grid; + place-items: center; + text-align: center; +} +.slide.active.protocol-slide, +.slide.active.image-slide { + display: grid; + grid-template-rows: auto auto minmax(0, 1fr); +} @keyframes fadeUp { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .kicker { @@ -94,9 +104,9 @@ body { font-weight: 900; color: var(--amber); margin-bottom: clamp(5px, calc(8px * var(--slide-scale)), 14px); - letter-spacing: -.2px; + letter-spacing: 0; } -h1, h2 { margin: 0; line-height: 1.08; letter-spacing: -1px; } +h1, h2 { margin: 0; line-height: 1.08; letter-spacing: 0; } h1 { font-size: clamp(34px, calc(58px * var(--slide-scale)), 82px); } h2 { font-size: clamp(27px, calc(46px * var(--slide-scale)), 66px); } .subtitle { @@ -104,6 +114,26 @@ h2 { font-size: clamp(27px, calc(46px * var(--slide-scale)), 66px); } font-size: clamp(16px, calc(22px * var(--slide-scale)), 32px); margin: clamp(10px, calc(16px * var(--slide-scale)), 24px) 0 clamp(16px, calc(26px * var(--slide-scale)), 36px); } +.center-content { + width: min(900px, 100%); +} +.center-content .subtitle { + max-width: 760px; + margin-left: auto; + margin-right: auto; +} + +.members { + margin: clamp(6px, calc(10px * var(--slide-scale)), 14px) auto 0; + color: rgba(248, 250, 252, .72); + font-size: clamp(11px, calc(14px * var(--slide-scale)), 19px); + font-weight: 300; + letter-spacing: .01em; +} +.protocol-slide .subtitle, +.image-slide .subtitle { + margin-bottom: clamp(10px, calc(14px * var(--slide-scale)), 22px); +} .hero-grid { display: grid; @@ -260,6 +290,68 @@ h2 { font-size: clamp(27px, calc(46px * var(--slide-scale)), 66px); } display: block; margin-bottom: 3px; } +.timeline-only { + margin-top: clamp(24px, calc(42px * var(--slide-scale)), 62px); + padding: clamp(24px, calc(36px * var(--slide-scale)), 52px); + border-radius: clamp(12px, calc(18px * var(--slide-scale)), 24px); + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: clamp(10px, calc(16px * var(--slide-scale)), 24px); + position: relative; + overflow: hidden; +} +.timeline-only::before { + content: ""; + position: absolute; + left: 9%; + right: 9%; + top: 50%; + height: 2px; + background: linear-gradient(90deg, transparent, rgba(244, 183, 64, .8), transparent); + transform: translateY(-50%); +} +.timeline-method { + min-height: clamp(132px, calc(170px * var(--slide-scale)), 240px); + display: grid; + place-items: center; + text-align: center; + position: relative; + border: 1px solid rgba(255,255,255,.14); + border-radius: clamp(10px, calc(14px * var(--slide-scale)), 18px); + background: linear-gradient(145deg, rgba(255,255,255,.11), rgba(255,255,255,.045)); + box-shadow: inset 0 1px 0 rgba(255,255,255,.14), 0 18px 42px rgba(0,0,0,.18); +} +.timeline-method::before { + content: ""; + position: absolute; + top: 50%; + width: clamp(16px, calc(22px * var(--slide-scale)), 30px); + height: clamp(16px, calc(22px * var(--slide-scale)), 30px); + border-radius: 50%; + background: var(--amber); + box-shadow: 0 0 0 7px rgba(244, 183, 64, .15); + transform: translateY(-50%); +} +.timeline-method span { + max-width: 95%; + padding: 0 8px; + font-size: clamp(15px, calc(21px * var(--slide-scale)), 30px); + line-height: 1.25; + font-weight: 900; + color: #fff; + transform: translateY(calc(-44px * var(--slide-scale))); +} + +.timeline-method small { + position: absolute; + left: 50%; + top: calc(50% + clamp(18px, calc(28px * var(--slide-scale)), 40px)); + transform: translateX(-50%); + color: #ffe8aa; + font-size: clamp(13px, calc(17px * var(--slide-scale)), 24px); + font-weight: 300; + letter-spacing: .02em; +} ul { margin: 0; padding: 0; @@ -276,12 +368,193 @@ li::before { display: inline-block; width: .46em; height: .46em; - margin-left: .55em; + margin-right: .55em; + margin-left: 0; border-radius: 50%; background: var(--amber); vertical-align: .08em; } +.mechanism-grid { + display: grid; + grid-template-columns: 1.08fr .92fr; + gap: clamp(18px, calc(28px * var(--slide-scale)), 40px); + margin-top: clamp(20px, calc(34px * var(--slide-scale)), 48px); + align-items: stretch; +} +.protocol-layout { + --protocol-pad: clamp(10px, calc(16px * var(--slide-scale)), 24px); + min-height: 0; + width: 100%; + position: relative; + border-radius: clamp(10px, calc(14px * var(--slide-scale)), 18px); + overflow: hidden; +} +.protocol-visual { + position: absolute; + inset: 0; + padding: var(--protocol-pad); + border-radius: inherit; + overflow: hidden; +} +.protocol-visual img { + width: 100%; + height: 100%; + object-fit: fill; + display: block; + border-radius: clamp(8px, calc(12px * var(--slide-scale)), 16px); + background: #262a3a; +} +.protocol-points { + position: absolute; + right: clamp(18px, calc(26px * var(--slide-scale)), 38px); + left: clamp(18px, calc(26px * var(--slide-scale)), 38px); + bottom: clamp(14px, calc(22px * var(--slide-scale)), 32px); + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: clamp(12px, calc(18px * var(--slide-scale)), 28px); + padding: clamp(10px, calc(14px * var(--slide-scale)), 20px); + border-radius: clamp(10px, calc(14px * var(--slide-scale)), 18px); + background: rgba(16, 20, 32, .76); + border-color: rgba(255,255,255,.16); +} +.protocol-points article { + display: grid; + align-content: start; + gap: clamp(6px, calc(9px * var(--slide-scale)), 14px); +} +.protocol-points b { + display: block; + color: #fff; + font-size: clamp(15px, calc(19px * var(--slide-scale)), 28px); +} +.protocol-points li { + color: #d3d8e6; + font-size: clamp(11px, calc(13px * var(--slide-scale)), 18px); + line-height: 1.35; +} +.bullet-panel { + border-radius: clamp(10px, calc(14px * var(--slide-scale)), 18px); + padding: clamp(18px, calc(26px * var(--slide-scale)), 38px); + display: grid; + align-content: center; + gap: clamp(12px, calc(18px * var(--slide-scale)), 26px); +} +.bullet-panel b { + display: block; + font-size: clamp(18px, calc(24px * var(--slide-scale)), 34px); + color: #fff; +} +.bullet-panel ul + b { + margin-top: clamp(2px, calc(8px * var(--slide-scale)), 14px); +} +.mini-flow, +.repeat-stack, +.hold-diagram { + width: 100%; + min-height: clamp(280px, calc(360px * var(--slide-scale)), 500px); + direction: ltr; + text-align: center; +} +.mini-flow { + display: grid; + grid-template-columns: 1fr auto 1fr auto 1fr; + gap: clamp(10px, calc(14px * var(--slide-scale)), 22px); + align-items: center; +} +.mini-flow div, +.repeat-stack div, +.hold-diagram div { + min-height: clamp(70px, calc(94px * var(--slide-scale)), 138px); + border: 1px solid rgba(255,255,255,.14); + border-radius: clamp(10px, calc(14px * var(--slide-scale)), 18px); + background: linear-gradient(145deg, rgba(255,255,255,.12), rgba(255,255,255,.055)); + display: grid; + place-items: center; + padding: clamp(10px, calc(16px * var(--slide-scale)), 24px); + font-size: clamp(15px, calc(20px * var(--slide-scale)), 29px); + font-weight: 900; + line-height: 1.25; + box-shadow: inset 0 1px 0 rgba(255,255,255,.14); +} +.mini-flow span, +.hold-diagram span { + color: var(--amber); + font-size: clamp(14px, calc(18px * var(--slide-scale)), 26px); + font-weight: 900; +} +.repeat-stack, +.hold-diagram { + display: grid; + align-content: center; + gap: clamp(12px, calc(18px * var(--slide-scale)), 26px); +} +.repeat-stack div:nth-child(3), +.hold-diagram div:nth-child(3) { + border-color: rgba(67, 217, 163, .42); + background: linear-gradient(145deg, rgba(67, 217, 163, .18), rgba(255,255,255,.055)); +} + +.single-visual { + --visual-pad: clamp(12px, calc(18px * var(--slide-scale)), 26px); + margin-top: clamp(18px, calc(32px * var(--slide-scale)), 48px); + height: min(440px, calc(var(--slide-h) - 190px)); + border-radius: clamp(10px, calc(14px * var(--slide-scale)), 18px); + padding: var(--visual-pad); + display: grid; + place-items: center; + position: relative; + overflow: hidden; +} +.single-visual img { + position: absolute; + inset: var(--visual-pad); + width: calc(100% - var(--visual-pad) - var(--visual-pad)); + height: calc(100% - var(--visual-pad) - var(--visual-pad)); + object-fit: contain; + display: block; +} +.image-slide .single-visual { + margin-top: 0; + height: 100%; +} +.image-slide .single-visual img { + object-fit: fill; +} +.takeaway-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-template-rows: repeat(2, minmax(0, 1fr)); + gap: clamp(14px, calc(20px * var(--slide-scale)), 30px); + margin-top: clamp(12px, calc(20px * var(--slide-scale)), 32px); + min-height: clamp(390px, calc(455px * var(--slide-scale)), 540px); +} +.takeaway-grid article { + min-height: 0; + background: rgba(255,255,255,.075); + border: 1px solid rgba(255,255,255,.12); + border-radius: clamp(10px, calc(14px * var(--slide-scale)), 18px); + padding: clamp(16px, calc(24px * var(--slide-scale)), 36px); + display: flex; + flex-direction: column; + justify-content: center; +} +.takeaway-grid article::before { + content: ""; + display: block; + width: 42px; + height: 6px; + border-radius: 999px; + background: var(--amber); + margin-bottom: 18px; +} +.takeaway-grid b { + display: block; + font-size: clamp(17px, calc(22px * var(--slide-scale)), 32px); + line-height: 1.25; + margin-bottom: clamp(12px, calc(18px * var(--slide-scale)), 26px); +} + .resource-grid { display: grid; grid-template-columns: repeat(4, 1fr); @@ -379,6 +652,30 @@ pre { font-weight: 900; margin-bottom: clamp(12px, calc(20px * var(--slide-scale)), 30px); } + +.detailed-version-row { + margin-top: clamp(18px, calc(30px * var(--slide-scale)), 46px); +} +.detailed-version-row article { + min-height: clamp(300px, calc(385px * var(--slide-scale)), 500px); + padding: clamp(14px, calc(20px * var(--slide-scale)), 30px); +} +.detailed-version-row b { + font-size: clamp(16px, calc(21px * var(--slide-scale)), 30px); + line-height: 1.22; +} +.detailed-version-row li { + font-size: clamp(10px, calc(13px * var(--slide-scale)), 17px); + line-height: 1.42; +} +.detailed-version-row code { + color: #ffe8aa; + background: rgba(244, 183, 64, .10); + border: 1px solid rgba(244, 183, 64, .18); + border-radius: 6px; + padding: 0 4px; + font-size: .92em; +} .focus-line { margin-top: clamp(18px, calc(30px * var(--slide-scale)), 44px); background: rgba(255,255,255,.075); @@ -410,9 +707,9 @@ pre { position: relative; } .flow div:not(:nth-child(4n))::after { - content: "←"; + content: "->"; position: absolute; - left: clamp(-18px, calc(-16px * var(--slide-scale)), -10px); + right: clamp(-18px, calc(-16px * var(--slide-scale)), -10px); top: 38%; color: var(--amber); font-size: clamp(18px, calc(24px * var(--slide-scale)), 34px); @@ -569,7 +866,7 @@ pre { position: relative; } .pipe-step:not(:last-child)::after { - content: "→"; + content: "->"; position: absolute; right: -17px; top: 44%; @@ -608,7 +905,8 @@ pre { } ol { margin: 0; - padding-right: clamp(20px, calc(28px * var(--slide-scale)), 42px); + padding-left: clamp(34px, calc(46px * var(--slide-scale)), 64px); + padding-right: clamp(18px, calc(22px * var(--slide-scale)), 30px); font-size: clamp(15px, calc(21px * var(--slide-scale)), 30px); line-height: 2; color: #23324a; @@ -708,12 +1006,37 @@ code { h2 { font-size: clamp(24px, 7vw, 34px); } .subtitle { font-size: clamp(14px, 4vw, 18px); } .kicker { font-size: clamp(12px, 3.7vw, 16px); } - .hero-grid, .split, .demo-layout, .version-row, .resource-grid { grid-template-columns: 1fr; } - .hero-grid, .split, .demo-layout { gap: 18px; } - .resource-grid, .version-row { gap: 12px; margin-top: 22px; } - .resource-grid article, .version-row article { min-height: auto; padding: 16px; } - .resource-grid b, .version-row b { font-size: 18px; } + .slide.active.protocol-slide, + .slide.active.image-slide { display: block; } + .hero-grid, .split, .demo-layout, .version-row, .resource-grid, .mechanism-grid, .takeaway-grid { grid-template-columns: 1fr; } + .takeaway-grid { grid-template-rows: none; min-height: 0; } + .hero-grid, .split, .demo-layout, .mechanism-grid { gap: 18px; } + .resource-grid, .version-row, .takeaway-grid { gap: 12px; margin-top: 22px; } + .resource-grid article, .version-row article, .takeaway-grid article { min-height: auto; padding: 16px; } + .resource-grid b, .version-row b, .takeaway-grid b { font-size: 18px; } .resource-grid p, .version-row p { font-size: 13px; } + .timeline-only { grid-template-columns: 1fr; gap: 12px; padding: 16px; } + .timeline-only::before { display: none; } + .timeline-method { min-height: 74px; } + .timeline-method::before { left: 18px; top: 50%; } + .timeline-method span { transform: none; font-size: 16px; } + .timeline-method small { position: static; transform: none; font-size: 13px; margin-top: 6px; } + .mini-flow { grid-template-columns: 1fr; min-height: 0; } + .mini-flow span { transform: rotate(90deg); } + .repeat-stack, .hold-diagram { min-height: 0; } + .protocol-layout { display: block; min-height: 0; overflow: visible; } + .protocol-visual { position: relative; inset: auto; padding: 10px; } + .protocol-visual img { width: 100%; height: auto; object-fit: contain; } + .protocol-points { + position: static; + grid-template-columns: 1fr; + margin-top: 12px; + padding: 16px; + gap: 12px; + } + .bullet-panel { padding: 16px; gap: 12px; } + .single-visual { height: auto; min-height: 0; padding: 10px; display: block; } + .single-visual img { position: static; width: 100%; height: auto; padding: 0; object-fit: contain; } .image-focus, .image-focus.compact { height: auto; min-height: 0; padding: 10px; } .image-focus img { width: 100%; height: auto; } .flow { grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 22px; }