π The Core Concept: Request-Response Model
π» Your browser (client) sends a request to a server.
ποΈ The server sends back a response with the requested page or data.
π This back-and-forth is called the request-response model or client-server architecture.
<!-- Infographic: Client-Server request/response cycle β 16:9, responsive, label-only, animated -->
<div class="infographic-clientserver">
<style>
.infographic-clientserver {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-deep:#173d8c;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
width: 100%;
max-width: 960px;
margin: 0 auto;
box-sizing: border-box;
aspect-ratio: 16 / 9;
display: flex;
flex-direction: column;
overflow: hidden;
container-type: inline-size;
container-name: cscard;
}
.infographic-clientserver[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-deep:#2f5bb8;
--accent-soft:#202942;
--line: #2c313d;
}
.infographic-clientserver * { box-sizing: border-box; }
.cs-stage {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: clamp(0.3rem, 3cqw, 1.5rem);
min-height: 0;
min-width: 0;
padding: clamp(0.5rem, 3cqw, 1.5rem);
}
/* --- circular node: Client / Server --- */
.cs-node {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: clamp(0.15rem, 0.8cqw, 0.35rem);
flex: 0 0 auto;
width: clamp(80px, 20cqw, 168px);
height: clamp(80px, 20cqw, 168px);
border-radius: 50%;
text-align: center;
padding: clamp(0.3rem, 1.5cqw, 0.5rem);
}
.cs-node.client { background: var(--accent); }
.cs-node.server { background: var(--accent-deep); }
.cs-node svg {
width: clamp(20px, 6cqw, 42px);
height: clamp(20px, 6cqw, 42px);
stroke: #fff;
margin-top: clamp(0.1rem, 0.6cqw, 0.3rem);
}
.cs-node-label {
font-size: clamp(0.5rem, 2cqw, 1rem);
font-weight: 700;
color: #fff;
letter-spacing: 0.02em;
white-space: nowrap;
}
.cs-node-sub {
font-size: clamp(0.38rem, 1.2cqw, 0.62rem);
color: rgba(255,255,255,0.8);
font-weight: 500;
white-space: nowrap;
}
/* --- request/response lanes between nodes --- */
.cs-lanes {
display: flex;
flex-direction: column;
gap: clamp(0.5rem, 3cqw, 1.4rem);
flex: 1 1 auto;
min-width: clamp(60px, 20cqw, 260px);
max-width: 340px;
}
.cs-lane { display: flex; flex-direction: column; align-items: center; gap: clamp(0.1rem, 0.6cqw, 0.25rem); }
.cs-lane-label {
font-size: clamp(0.4rem, 1.5cqw, 0.74rem);
font-weight: 700;
letter-spacing: 0.06em;
color: var(--ink);
}
.cs-track {
position: relative;
width: 100%;
height: 2.5px;
background: var(--line);
}
.cs-track.request::after {
content: "";
position: absolute; right: -1px; top: 50%;
width: clamp(6px, 1.6cqw, 10px); height: clamp(6px, 1.6cqw, 10px);
border-top: 2.5px solid var(--accent);
border-right: 2.5px solid var(--accent);
transform: translateY(-50%) rotate(45deg);
}
.cs-track.response::before {
content: "";
position: absolute; left: -1px; top: 50%;
width: clamp(6px, 1.6cqw, 10px); height: clamp(6px, 1.6cqw, 10px);
border-bottom: 2.5px solid var(--accent-deep);
border-left: 2.5px solid var(--accent-deep);
transform: translateY(-50%) rotate(45deg);
}
.cs-track.request { background: var(--accent); opacity: 0.35; }
.cs-track.response { background: var(--accent-deep); opacity: 0.35; }
.cs-packet {
position: absolute; top: 50%;
width: clamp(7px, 2cqw, 12px); height: clamp(7px, 2cqw, 12px);
border-radius: 50%;
transform: translateY(-50%);
}
.cs-track.request .cs-packet {
left: 0;
background: var(--accent);
animation: cs-flow-right 2.2s ease-in-out infinite;
}
.cs-track.response .cs-packet {
right: 0;
background: var(--accent-deep);
animation: cs-flow-left 2.2s ease-in-out infinite;
animation-delay: 1.1s;
}
@keyframes cs-flow-right {
0% { left: 0%; opacity: 0; }
10% { opacity: 1; }
88% { opacity: 1; }
100% { left: calc(100% - 12px); opacity: 0; }
}
@keyframes cs-flow-left {
0% { right: 0%; opacity: 0; }
10% { opacity: 1; }
88% { opacity: 1; }
100% { right: calc(100% - 12px); opacity: 0; }
}
@container cscard (max-width: 460px) {
.cs-stage { flex-direction: column; gap: 0.6rem; }
.cs-lanes { flex-direction: row; width: 100%; max-width: none; gap: 0.5rem; }
.cs-lane { flex: 1 1 0; }
.cs-track { width: 100%; height: 2.5px; }
}
@media (prefers-reduced-motion: reduce) {
.infographic-clientserver *, .infographic-clientserver *::before, .infographic-clientserver *::after {
animation-duration: 0.001ms !important;
}
}
</style>
<div class="cs-stage">
<div class="cs-node client">
<div class="cs-node-label">Client</div>
<div class="cs-node-sub">e.g. browser</div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 5a2 2 0 0 1 2 2v8.526a2 2 0 0 0 .212.897l1.068 2.127a1 1 0 0 1-.9 1.45H3.62a1 1 0 0 1-.9-1.45l1.068-2.127A2 2 0 0 0 4 15.526V7a2 2 0 0 1 2-2z"/><path d="M20.054 15.987H3.946"/></svg>
</div>
<div class="cs-lanes">
<div class="cs-lane">
<div class="cs-lane-label">Request</div>
<div class="cs-track request"><div class="cs-packet"></div></div>
</div>
<div class="cs-lane">
<div class="cs-lane-label">Response</div>
<div class="cs-track response"><div class="cs-packet"></div></div>
</div>
</div>
<div class="cs-node server">
<div class="cs-node-label">Server</div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></svg>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-clientserver')
|| document.querySelector('.infographic-clientserver');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
</script>π£οΈ Step-by-Step: What Happens When You Type a URL
π Example URL
<https://google.com/maps>π§© Breaking Down the URL
Example: https://google.com/maps
π Part | π Example | π‘ Meaning |
|---|---|---|
π Protocol |
| The protocol used for the connection (HTTP or HTTPS) |
π Domain name |
| A human-friendly name for the server |
π Resource path |
| The specific resource being requested on that server |
<!-- Interactive Infographic: Anatomy of a URL β Protocol / IP / Port -->
<div class="infographic-urlanatomy">
<style>
@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,600;9..144,700&family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@500;600&display=swap');
.infographic-urlanatomy {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--bar-bg: #2b2b33;
--bar-text: #b9b6b0;
--protocol: #173d8c;
--protocol-soft: #e2e8f7;
--ip: #2563eb;
--ip-soft: #e3ebfd;
--port: #7ea2f2;
--port-soft: #eef3fd;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
max-width: 760px;
margin: 0 auto;
box-sizing: border-box;
padding: clamp(1.5rem, 5vw, 2.5rem) clamp(1.25rem, 5vw, 2rem) clamp(1.75rem, 5vw, 2.5rem);
}
.infographic-urlanatomy[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--bar-bg: #0c0c13;
--bar-text: #8f8c86;
--protocol: #6f93e0;
--protocol-soft: #202a42;
--ip: #5b8def;
--ip-soft: #202942;
--port: #8fb3f5;
--port-soft: #1c2638;
}
.infographic-urlanatomy * { box-sizing: border-box; }
.ua-eyebrow {
text-align: center;
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink-soft);
margin-bottom: 0.5rem;
}
.ua-title {
font-family: 'Fraunces', Georgia, serif;
font-weight: 700;
font-size: clamp(1.2rem, 3.6vw, 1.55rem);
text-align: center;
margin-bottom: 1.5rem;
}
/* --- client node --- */
.ua-client-row { display: flex; justify-content: center; margin-bottom: 1.5rem; }
.ua-client {
width: clamp(110px, 24vw, 150px);
height: clamp(110px, 24vw, 150px);
border-radius: 50%;
background: var(--accent);
color: #fff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.25rem;
text-align: center;
}
.ua-client svg { width: clamp(24px, 6vw, 34px); height: clamp(24px, 6vw, 34px); stroke: #fff; margin-top: 0.2rem; }
.ua-client-label { font-weight: 700; font-size: clamp(0.85rem, 3vw, 1rem); letter-spacing: 0.02em; }
.ua-client-sub { font-size: clamp(0.62rem, 2vw, 0.72rem); color: rgba(255,255,255,0.85); }
/* --- protocol toggle --- */
.ua-toggle { display: flex; justify-content: center; gap: 0.4rem; margin-bottom: 1.1rem; }
.ua-toggle button {
border: 1.5px solid var(--line);
background: var(--paper);
color: var(--ink-soft);
border-radius: 999px;
padding: 0.4rem 0.95rem;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.76rem;
font-weight: 600;
cursor: pointer;
transition: all 0.15s;
}
.ua-toggle button.is-active { background: var(--accent); border-color: var(--accent); color: #fff; }
/* --- url bar --- */
.ua-bar {
background: var(--bar-bg);
border-radius: 999px;
padding: clamp(0.6rem, 2.5vw, 0.85rem) clamp(0.9rem, 3.5vw, 1.4rem);
display: flex;
justify-content: center;
align-items: baseline;
flex-wrap: wrap;
gap: 1px;
font-family: 'IBM Plex Mono', monospace;
font-size: clamp(0.78rem, 3vw, 1.05rem);
font-weight: 600;
margin-bottom: clamp(1.5rem, 5vw, 2.25rem);
cursor: default;
}
.ua-bar .ua-punc { color: var(--bar-text); }
.ua-seg {
cursor: pointer;
border-radius: 5px;
padding: 1px 3px;
transition: background 0.2s, color 0.2s;
}
.ua-seg.protocol { color: #a9b8e0; }
.ua-seg.ip { color: #ffffff; }
.ua-seg.port { color: #c9d6f5; }
.ua-seg.is-active.protocol { background: var(--protocol); color: #fff; }
.ua-seg.is-active.ip { background: var(--ip); color: #fff; }
.ua-seg.is-active.port { background: var(--port); color: #1a1a1a; }
/* --- cards --- */
.ua-columns { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: clamp(0.5rem, 2vw, 1rem); }
.ua-col { display: flex; flex-direction: column; align-items: center; }
.ua-connector { width: 2px; height: clamp(14px, 3.5vw, 22px); background: var(--line); transition: background 0.25s; }
.ua-col.protocol .ua-connector.is-active { background: var(--protocol); }
.ua-col.ip .ua-connector.is-active { background: var(--ip); }
.ua-col.port .ua-connector.is-active { background: var(--port); }
.ua-card {
width: 100%;
border: 1.5px solid var(--line);
border-radius: 12px;
padding: clamp(0.6rem, 2.5vw, 1rem) clamp(0.5rem, 2vw, 0.75rem);
text-align: center;
background: var(--paper);
cursor: pointer;
transition: border-color 0.25s, background 0.25s;
}
.ua-col.protocol .ua-card { border-top: 3px solid var(--protocol); }
.ua-col.ip .ua-card { border-top: 3px solid var(--ip); }
.ua-col.port .ua-card { border-top: 3px solid var(--port); }
.ua-col.protocol .ua-card.is-active { background: var(--protocol-soft); }
.ua-col.ip .ua-card.is-active { background: var(--ip-soft); }
.ua-col.port .ua-card.is-active { background: var(--port-soft); }
.ua-card-title { font-size: clamp(0.68rem, 2.4vw, 0.85rem); font-weight: 700; letter-spacing: 0.03em; margin-bottom: 0.3rem; }
.ua-col.protocol .ua-card-title { color: var(--protocol); }
.ua-col.ip .ua-card-title { color: var(--ip); }
.ua-col.port .ua-card-title { color: var(--port); }
.ua-card-caption { font-size: clamp(0.6rem, 2vw, 0.72rem); color: var(--ink-soft); line-height: 1.4; }
@media (max-width: 480px) {
.ua-card-caption { font-size: 0.58rem; }
}
</style>
<div class="ua-eyebrow">Try It</div>
<div class="ua-title">Anatomy of a URL</div>
<div class="ua-client-row">
<div class="ua-client">
<div class="ua-client-label">Client</div>
<div class="ua-client-sub">e.g. browser</div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 5a2 2 0 0 1 2 2v8.526a2 2 0 0 0 .212.897l1.068 2.127a1 1 0 0 1-.9 1.45H3.62a1 1 0 0 1-.9-1.45l1.068-2.127A2 2 0 0 0 4 15.526V7a2 2 0 0 1 2-2z"/><path d="M20.054 15.987H3.946"/></svg>
</div>
</div>
<div class="ua-toggle" id="uaToggle">
<button data-proto="https" class="is-active">HTTPS</button>
<button data-proto="http">HTTP</button>
</div>
<div class="ua-bar" id="uaBar">
<span class="ua-seg protocol" data-part="protocol" id="uaProtocol">https</span><span class="ua-punc">://</span><span class="ua-seg ip" data-part="ip">216.58.211.206</span><span class="ua-punc">:</span><span class="ua-seg port" data-part="port" id="uaPort">443</span>
</div>
<div class="ua-columns" id="uaColumns">
<div class="ua-col protocol" data-part="protocol">
<div class="ua-connector"></div>
<div class="ua-card" data-part="protocol">
<div class="ua-card-title">Protocol</div>
<div class="ua-card-caption">HTTP or HTTPS</div>
</div>
</div>
<div class="ua-col ip" data-part="ip">
<div class="ua-connector"></div>
<div class="ua-card" data-part="ip">
<div class="ua-card-title">IP Address</div>
<div class="ua-card-caption">Where the server lives</div>
</div>
</div>
<div class="ua-col port" data-part="port">
<div class="ua-connector"></div>
<div class="ua-card" data-part="port" id="uaPortCard">
<div class="ua-card-title">Port Number</div>
<div class="ua-card-caption">Default 443 for HTTPS</div>
</div>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-urlanatomy')
|| document.querySelector('.infographic-urlanatomy');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
const protocolEl = wrapper.querySelector('#uaProtocol');
const portEl = wrapper.querySelector('#uaPort');
const portCaption = wrapper.querySelector('#uaPortCard .ua-card-caption');
const toggleEl = wrapper.querySelector('#uaToggle');
const columns = wrapper.querySelectorAll('.ua-col');
const segs = wrapper.querySelectorAll('.ua-seg');
function setProtocol(proto) {
protocolEl.textContent = proto;
portEl.textContent = proto === 'https' ? '443' : '80';
portCaption.textContent = proto === 'https' ? 'Default 443 for HTTPS' : 'Default 80 for HTTP';
toggleEl.querySelectorAll('button').forEach(function (b) {
b.classList.toggle('is-active', b.getAttribute('data-proto') === proto);
});
}
function setActive(part) {
columns.forEach(function (col) {
const on = col.getAttribute('data-part') === part;
col.querySelector('.ua-connector').classList.toggle('is-active', on);
col.querySelector('.ua-card').classList.toggle('is-active', on);
});
segs.forEach(function (seg) {
seg.classList.toggle('is-active', seg.getAttribute('data-part') === part);
});
}
function clearActive() {
columns.forEach(function (col) {
col.querySelector('.ua-connector').classList.remove('is-active');
col.querySelector('.ua-card').classList.remove('is-active');
});
segs.forEach(function (seg) { seg.classList.remove('is-active'); });
}
let current = null;
function toggleActive(part) {
if (current === part) {
current = null;
clearActive();
} else {
current = part;
setActive(part);
}
}
toggleEl.querySelectorAll('button').forEach(function (btn) {
btn.addEventListener('click', function () { setProtocol(btn.getAttribute('data-proto')); });
});
segs.forEach(function (seg) {
seg.addEventListener('click', function () { toggleActive(seg.getAttribute('data-part')); });
});
wrapper.querySelectorAll('.ua-card').forEach(function (card) {
card.addEventListener('click', function () { toggleActive(card.getAttribute('data-part')); });
});
})();
</script>π Step 1: DNS Lookup (Domain Name β IP Address)
β The Problem
A domain name like
google.comis easy for people to remember, but computers can't use it directly.To connect to a website, a computer needs the website's IP address.
π‘ The Solution: DNS
DNS (Domain Name System) works like the phone book of the internet. It converts domain names into IP addresses.
π How It Works
<!-- Infographic: DNS Resolution Flow β Browser -> DNS Server -> IP -> Target Server, 16:9, responsive, animated -->
<div class="infographic-dnsflow">
<style>
.infographic-dnsflow {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
width: 100%;
max-width: 960px;
margin: 0 auto;
box-sizing: border-box;
aspect-ratio: 16 / 9;
display: flex;
flex-direction: column;
overflow: hidden;
container-type: inline-size;
container-name: dnscard;
}
.infographic-dnsflow[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
}
.infographic-dnsflow * { box-sizing: border-box; }
.dns-stage {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: clamp(0.15rem, 1.6cqw, 0.6rem);
min-height: 0;
min-width: 0;
padding: clamp(0.4rem, 3cqw, 1.5rem);
}
/* --- node --- */
.dns-node {
display: flex;
flex-direction: column;
align-items: center;
gap: clamp(0.2rem, 1.2cqw, 0.55rem);
flex: 0 0 auto;
min-width: 0;
}
.dns-node-box {
width: clamp(42px, 13cqw, 104px);
height: clamp(42px, 13cqw, 104px);
border-radius: 50%;
border: 2px solid var(--line);
background: var(--paper-alt);
display: flex;
align-items: center;
justify-content: center;
color: var(--accent);
position: relative;
flex: 0 0 auto;
}
.dns-node-box svg { width: clamp(18px, 5.5cqw, 42px); height: clamp(18px, 5.5cqw, 42px); stroke: var(--accent); }
.dns-node-label { font-size: clamp(0.4rem, 1.7cqw, 0.82rem); font-weight: 700; white-space: nowrap; }
.dns-node-sub { font-size: clamp(0.34rem, 1.2cqw, 0.62rem); color: var(--ink-soft); white-space: nowrap; }
/* dns server: dashed container + pulse the instant the dot from Browser arrives (~30% of the shared cycle) */
.dns-node.server .dns-node-box { border-radius: 16px; border: 2px dashed var(--accent); background: var(--accent-soft); }
.dns-node.server .dns-node-box::before {
content: "";
position: absolute; inset: -6px;
border-radius: 19px;
border: 1.5px solid var(--accent);
opacity: 0;
animation: dns-ring 4.5s ease-out infinite;
}
@keyframes dns-ring {
0%, 26% { opacity: 0; transform: scale(0.9); }
30% { opacity: 0.6; transform: scale(0.95); }
42% { opacity: 0; transform: scale(1.18); }
100% { opacity: 0; }
}
/* ip address chip: pops the instant the dot from DNS Server arrives (~63% of the shared cycle) */
.dns-node.ip .dns-node-box {
animation: dns-ip-pop 4.5s ease-in-out infinite;
}
@keyframes dns-ip-pop {
0%, 60% { transform: scale(0.85); opacity: 0.45; border-color: var(--line); }
64%, 74% { transform: scale(1.12); opacity: 1; border-color: var(--accent); }
85%, 100% { transform: scale(0.85); opacity: 0.45; border-color: var(--line); }
}
/* final server node: pulses the instant the last dot arrives (~96% of the shared cycle) */
.dns-node.target .dns-node-box::before {
content: "";
position: absolute; inset: -6px;
border-radius: 50%;
border: 1.5px solid var(--accent);
opacity: 0;
animation: dns-server-ring 4.5s ease-out infinite;
}
@keyframes dns-server-ring {
0%, 92% { opacity: 0; transform: scale(0.9); }
96% { opacity: 0.6; transform: scale(0.96); }
100% { opacity: 0; transform: scale(1.18); }
}
/* --- connectors --- */
.dns-connector {
position: relative;
flex: 1 1 auto;
min-width: 14px;
max-width: clamp(28px, 8cqw, 90px);
height: 2px;
background: var(--line);
margin-bottom: clamp(0.9rem, 3cqw, 1.4rem);
}
.dns-connector::after {
content: "";
position: absolute; right: -1px; top: 50%;
width: clamp(5px, 1.4cqw, 8px); height: clamp(5px, 1.4cqw, 8px);
border-top: 2px solid var(--line);
border-right: 2px solid var(--line);
transform: translateY(-50%) rotate(45deg);
}
.dns-connector .dns-dot {
position: absolute; top: 50%; left: 0;
width: clamp(4px, 1.2cqw, 7px); height: clamp(4px, 1.2cqw, 7px);
border-radius: 50%;
background: var(--accent);
transform: translateY(-50%);
}
.dns-connector.c1 .dns-dot { animation: dns-travel-1 4.5s linear infinite; }
.dns-connector.c2 .dns-dot { animation: dns-travel-2 4.5s linear infinite; }
.dns-connector.c3 .dns-dot { animation: dns-travel-3 4.5s linear infinite; }
/* segment 1: Browser -> DNS Server, arrives right as the DNS Server pulses (~30%) */
@keyframes dns-travel-1 {
0% { left: 0%; opacity: 0; }
3% { opacity: 1; }
28% { left: calc(100% - 7px); opacity: 1; }
31%, 100% { left: calc(100% - 7px); opacity: 0; }
}
/* segment 2: DNS Server -> IP, arrives right as the IP chip pops (~63%) */
@keyframes dns-travel-2 {
0%, 33% { left: 0%; opacity: 0; }
36% { opacity: 1; }
61% { left: calc(100% - 7px); opacity: 1; }
64%, 100% { left: calc(100% - 7px); opacity: 0; }
}
/* segment 3: IP -> Server, arrives right as the Server node pulses (~96%) */
@keyframes dns-travel-3 {
0%, 66% { left: 0%; opacity: 0; }
69% { opacity: 1; }
94% { left: calc(100% - 7px); opacity: 1; }
97%, 100% { left: calc(100% - 7px); opacity: 0; }
}
.dns-connector-label {
position: absolute;
top: -1.5em;
left: 50%;
transform: translateX(-50%);
font-size: clamp(0.36rem, 1.4cqw, 0.62rem);
font-weight: 700;
color: var(--ink-soft);
white-space: nowrap;
letter-spacing: 0.02em;
}
@container dnscard (max-width: 460px) {
.dns-stage { flex-direction: column; gap: 0.6rem; padding: 0.5rem; }
.dns-connector { display: none; }
.dns-node:not(:first-child)::before {
content: ""; display: block; width: 2px; height: 16px;
background: var(--line); margin: 0 auto 0.4rem;
}
}
@media (prefers-reduced-motion: reduce) {
.infographic-dnsflow *, .infographic-dnsflow *::before, .infographic-dnsflow *::after {
animation-duration: 0.001ms !important;
}
}
</style>
<div class="dns-stage">
<div class="dns-node">
<div class="dns-node-box">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 5a2 2 0 0 1 2 2v8.526a2 2 0 0 0 .212.897l1.068 2.127a1 1 0 0 1-.9 1.45H3.62a1 1 0 0 1-.9-1.45l1.068-2.127A2 2 0 0 0 4 15.526V7a2 2 0 0 1 2-2z"/><path d="M20.054 15.987H3.946"/></svg>
</div>
<div class="dns-node-label">Browser</div>
<div class="dns-node-sub">google.com</div>
</div>
<div class="dns-connector c1">
<div class="dns-connector-label">Ask DNS</div>
<div class="dns-dot"></div>
</div>
<div class="dns-node server">
<div class="dns-node-box">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></svg>
</div>
<div class="dns-node-label">DNS Server</div>
<div class="dns-node-sub">your ISP</div>
</div>
<div class="dns-connector c2">
<div class="dns-connector-label">Returns IP</div>
<div class="dns-dot"></div>
</div>
<div class="dns-node ip">
<div class="dns-node-box">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></svg>
</div>
<div class="dns-node-label">IP Address</div>
<div class="dns-node-sub">142.250.x.x</div>
</div>
<div class="dns-connector c3">
<div class="dns-connector-label">Connect</div>
<div class="dns-dot"></div>
</div>
<div class="dns-node target">
<div class="dns-node-box">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="8" x="2" y="2" rx="2" ry="2"/><rect width="20" height="8" x="2" y="14" rx="2" ry="2"/><line x1="6" x2="6.01" y1="6" y2="6"/><line x1="6" x2="6.01" y1="18" y2="18"/></svg>
</div>
<div class="dns-node-label">Server</div>
<div class="dns-node-sub">google.com</div>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-dnsflow')
|| document.querySelector('.infographic-dnsflow');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
</script>π You enter
google.comin your browser.π The browser asks a DNS server (usually provided by your ISP) for the IP address.
π€ The DNS server returns the website's IP address.
π The browser uses that IP address to connect to the server.
π The Actual Address Used
Once the IP address is found, the browser connects using: protocol://IP_address:port
π What Is a Port?
A port identifies a specific service on a server.
Example: our Node.js server used port 8000.
The resource path (like
/maps) is sent separately in the HTTP request.
π€ Step 2: Establishing a Connection (TCP Socket)
<!-- Infographic: TCP Connection + TCP/IP roles β 16:9, responsive, animated, synced timeline -->
<div class="infographic-tcpip">
<style>
.infographic-tcpip {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
width: 100%;
max-width: 960px;
margin: 0 auto;
box-sizing: border-box;
aspect-ratio: 16 / 9;
display: flex;
flex-direction: column;
overflow: hidden;
container-type: inline-size;
container-name: tcpcard;
}
.infographic-tcpip[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
}
.infographic-tcpip * { box-sizing: border-box; }
.tcp-stage {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: clamp(0.4rem, 3cqw, 1.5rem);
min-height: 0;
min-width: 0;
padding: clamp(0.5rem, 3cqw, 1.5rem) clamp(0.6rem, 4cqw, 2rem) 0;
}
/* --- node --- */
.tcp-node {
display: flex;
flex-direction: column;
align-items: center;
gap: clamp(0.2rem, 1.2cqw, 0.55rem);
flex: 0 0 auto;
}
.tcp-node-box {
width: clamp(48px, 14cqw, 108px);
height: clamp(48px, 14cqw, 108px);
border-radius: 50%;
border: 2px solid var(--line);
background: var(--paper-alt);
display: flex;
align-items: center;
justify-content: center;
color: var(--accent);
position: relative;
flex: 0 0 auto;
}
.tcp-node-box svg { width: clamp(20px, 5.8cqw, 44px); height: clamp(20px, 5.8cqw, 44px); stroke: var(--accent); }
.tcp-node-label { font-size: clamp(0.44rem, 1.8cqw, 0.86rem); font-weight: 700; white-space: nowrap; }
/* "IP received" chip: appears above Browser first, stays lit for the whole cycle */
.tcp-ip-chip {
display: flex; align-items: center; gap: clamp(0.15rem, 0.8cqw, 0.3rem);
padding: clamp(0.15rem, 0.9cqw, 0.3rem) clamp(0.4rem, 1.6cqw, 0.6rem);
border: 1px solid var(--accent);
border-radius: 999px;
background: var(--accent-soft);
font-size: clamp(0.34rem, 1.3cqw, 0.6rem);
font-weight: 700;
color: var(--accent);
white-space: nowrap;
opacity: 0;
animation: tcp-ip-chip-fade 5s ease-in-out infinite;
}
.tcp-ip-chip svg { width: clamp(9px, 2.4cqw, 13px); height: clamp(9px, 2.4cqw, 13px); stroke: var(--accent); flex: 0 0 auto; }
@keyframes tcp-ip-chip-fade {
0%, 4% { opacity: 0; transform: translateY(3px); }
9% { opacity: 1; transform: translateY(0); }
92% { opacity: 1; }
97%, 100% { opacity: 0; }
}
/* server node pulses exactly when the handshake dot arrives (~20% of shared cycle) */
.tcp-node.server .tcp-node-box::before {
content: "";
position: absolute; inset: -6px;
border-radius: 50%;
border: 1.5px solid var(--accent);
opacity: 0;
animation: tcp-server-ring 5s ease-out infinite;
}
@keyframes tcp-server-ring {
0%, 25% { opacity: 0; transform: scale(0.9); }
28% { opacity: 0.6; transform: scale(0.95); }
40% { opacity: 0; transform: scale(1.18); }
100% { opacity: 0; }
}
/* --- middle: the TCP connection itself --- */
.tcp-middle {
flex: 1 1 auto;
min-width: clamp(70px, 22cqw, 320px);
max-width: 380px;
display: flex;
flex-direction: column;
align-items: center;
gap: clamp(0.3rem, 1.4cqw, 0.5rem);
}
.tcp-middle-label {
font-size: clamp(0.4rem, 1.6cqw, 0.72rem);
font-weight: 700;
letter-spacing: 0.03em;
color: var(--accent);
opacity: 0;
animation: tcp-label-fade 5s ease-in-out infinite;
}
@keyframes tcp-label-fade {
0%, 26% { opacity: 0; }
30%, 84% { opacity: 1; }
90%, 100% { opacity: 0; }
}
.tcp-line-wrap {
position: relative;
width: 100%;
height: clamp(3px, 1cqw, 5px);
}
.tcp-line {
position: absolute; inset: 0;
border-radius: 999px;
background: var(--line);
animation: tcp-line-state 5s ease-in-out infinite;
}
@keyframes tcp-line-state {
0%, 26% { background: var(--line); box-shadow: none; }
30%, 84% { background: var(--accent); box-shadow: 0 0 8px 0 var(--accent); }
90%, 100% { background: var(--line); box-shadow: none; }
}
/* handshake dot: travels once, Browser -> Server, then the line + server ring react */
.tcp-hs-dot {
position: absolute; top: 50%; left: 0;
width: clamp(5px, 1.5cqw, 9px); height: clamp(5px, 1.5cqw, 9px);
border-radius: 50%;
background: var(--accent);
transform: translateY(-50%);
animation: tcp-hs-travel 5s linear infinite;
}
@keyframes tcp-hs-travel {
0%, 13% { left: 0%; opacity: 0; }
16% { opacity: 1; }
28% { left: calc(100% - 9px); opacity: 1; }
31%, 100% { left: calc(100% - 9px); opacity: 0; }
}
/* packets: shuttle back and forth continuously while the connection is open (~22%-84%) */
.tcp-packets {
position: absolute; inset: 0;
opacity: 0;
animation: tcp-packets-gate 5s ease-in-out infinite;
}
@keyframes tcp-packets-gate {
0%, 29% { opacity: 0; }
33%, 82% { opacity: 1; }
87%, 100% { opacity: 0; }
}
.tcp-packet {
position: absolute; top: 50%;
width: clamp(5px, 1.4cqw, 8px); height: clamp(5px, 1.4cqw, 8px);
border-radius: 2px;
background: var(--accent);
transform: translateY(-50%) rotate(45deg);
}
.tcp-packet.right { animation: tcp-shuttle-right 1.3s linear infinite; }
.tcp-packet.left { animation: tcp-shuttle-left 1.3s linear infinite; animation-delay: 0.65s; }
@keyframes tcp-shuttle-right {
0% { left: 0%; opacity: 0; }
10% { opacity: 1; }
90% { opacity: 1; }
100% { left: calc(100% - 8px); opacity: 0; }
}
@keyframes tcp-shuttle-left {
0% { left: calc(100% - 8px); opacity: 0; }
10% { opacity: 1; }
90% { opacity: 1; }
100% { left: 0%; opacity: 0; }
}
/* --- footer: TCP + IP roles --- */
.tcp-footer {
display: flex;
align-items: center;
justify-content: center;
gap: clamp(0.4rem, 2cqw, 0.9rem);
padding: clamp(0.5rem, 3cqw, 1.1rem) clamp(0.6rem, 4cqw, 2rem) clamp(0.6rem, 3cqw, 1.25rem);
flex-wrap: wrap;
}
.tcp-chip {
display: flex; align-items: center; gap: clamp(0.25rem, 1cqw, 0.4rem);
padding: clamp(0.25rem, 1.2cqw, 0.4rem) clamp(0.5rem, 2cqw, 0.75rem);
border: 1px solid var(--line);
border-radius: 999px;
background: var(--paper-alt);
font-size: clamp(0.42rem, 1.6cqw, 0.76rem);
font-weight: 700;
white-space: nowrap;
}
.tcp-chip svg { width: clamp(11px, 3cqw, 16px); height: clamp(11px, 3cqw, 16px); stroke: var(--accent); flex: 0 0 auto; }
.tcp-chip span.tcp-chip-sub { font-weight: 500; color: var(--ink-soft); }
.tcp-plus { font-size: clamp(0.5rem, 2cqw, 0.9rem); font-weight: 700; color: var(--ink-soft); }
.tcp-eq {
font-size: clamp(0.42rem, 1.6cqw, 0.76rem);
font-weight: 700;
color: var(--accent);
white-space: nowrap;
}
@container tcpcard (max-width: 460px) {
.tcp-stage { flex-direction: column; gap: 0.6rem; padding: 0.5rem; }
.tcp-middle { width: 100%; max-width: none; }
.tcp-footer { gap: 0.3rem; }
.tcp-chip span.tcp-chip-sub { display: none; }
}
@media (prefers-reduced-motion: reduce) {
.infographic-tcpip *, .infographic-tcpip *::before, .infographic-tcpip *::after {
animation-duration: 0.001ms !important;
}
}
</style>
<div class="tcp-stage">
<div class="tcp-node">
<div class="tcp-ip-chip">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
IP Received
</div>
<div class="tcp-node-box">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 5a2 2 0 0 1 2 2v8.526a2 2 0 0 0 .212.897l1.068 2.127a1 1 0 0 1-.9 1.45H3.62a1 1 0 0 1-.9-1.45l1.068-2.127A2 2 0 0 0 4 15.526V7a2 2 0 0 1 2-2z"/><path d="M20.054 15.987H3.946"/></svg>
</div>
<div class="tcp-node-label">Browser</div>
</div>
<div class="tcp-middle">
<div class="tcp-middle-label">TCP Connection Open</div>
<div class="tcp-line-wrap">
<div class="tcp-line"></div>
<div class="tcp-hs-dot"></div>
<div class="tcp-packets">
<div class="tcp-packet right"></div>
<div class="tcp-packet left"></div>
</div>
</div>
</div>
<div class="tcp-node server">
<div class="tcp-node-box">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="8" x="2" y="2" rx="2" ry="2"/><rect width="20" height="8" x="2" y="14" rx="2" ry="2"/><line x1="6" x2="6.01" y1="6" y2="6"/><line x1="6" x2="6.01" y1="18" y2="18"/></svg>
</div>
<div class="tcp-node-label">Server</div>
</div>
</div>
<div class="tcp-footer">
<div class="tcp-chip">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/><path d="m3.3 7 8.7 5 8.7-5"/><path d="M12 22V12"/></svg>
TCP <span class="tcp-chip-sub">Reliable delivery</span>
</div>
<div class="tcp-plus">+</div>
<div class="tcp-chip">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></svg>
IP <span class="tcp-chip-sub">Correct destination</span>
</div>
<div class="tcp-eq">= TCP/IP</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-tcpip')
|| document.querySelector('.infographic-tcpip');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
</script>π After getting the server's IP address, the browser creates a TCP connection with the server.
π This connection stays open while the website's data is being transferred.
π¦ TCP (Transmission Control Protocol) ensures data is sent and received reliably.
π IP (Internet Protocol) helps data reach the correct destination.
π Together, TCP/IP are the rules that allow devices to communicate over the internet.
π‘ A protocol is simply a set of rules for communication.
π¨ Step 3: Making the HTTP Request
<!-- Interactive Infographic: Anatomy of an HTTP Request -->
<div class="infographic-httpreq">
<style>
@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,600;9..144,700&family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@500;600&display=swap');
.infographic-httpreq {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--code-bg: #f0ece4;
--code-border:#ddd6c9;
--method: #16825d;
--startline: #173d8c;
--startline-soft: #e2e8f7;
--headers: #2563eb;
--headers-soft: #e3ebfd;
--body: #7ea2f2;
--body-soft: #eef3fd;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
max-width: 760px;
margin: 0 auto;
box-sizing: border-box;
padding: clamp(1.5rem, 5vw, 2.5rem) clamp(1.25rem, 5vw, 2rem) clamp(1.75rem, 5vw, 2.5rem);
}
.infographic-httpreq[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--code-bg: #1a1a22;
--code-border:#2c313d;
--method: #4fd8a4;
--startline: #6f93e0;
--startline-soft: #202a42;
--headers: #5b8def;
--headers-soft: #202942;
--body: #8fb3f5;
--body-soft: #1c2638;
}
.infographic-httpreq * { box-sizing: border-box; }
.hr-eyebrow {
text-align: center;
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink-soft);
margin-bottom: 0.5rem;
}
.hr-title {
font-family: 'Fraunces', Georgia, serif;
font-weight: 700;
font-size: clamp(1.2rem, 3.6vw, 1.55rem);
text-align: center;
margin-bottom: 1.5rem;
}
/* --- method toggle --- */
.hr-toggle { display: flex; justify-content: center; gap: 0.4rem; margin-bottom: 1.25rem; }
.hr-toggle button {
border: 1.5px solid var(--line);
background: var(--paper);
color: var(--ink-soft);
border-radius: 999px;
padding: 0.42rem 1rem;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.76rem;
font-weight: 700;
cursor: pointer;
transition: all 0.15s;
}
.hr-toggle button.is-active { background: var(--accent); border-color: var(--accent); color: #fff; }
/* --- code block --- */
.hr-code {
background: var(--code-bg);
border: 1px solid var(--code-border);
border-radius: 12px;
padding: clamp(0.75rem, 3vw, 1.1rem);
font-family: 'IBM Plex Mono', monospace;
font-size: clamp(0.72rem, 2.6vw, 0.88rem);
line-height: 1.8;
margin-bottom: clamp(1.25rem, 4vw, 1.75rem);
}
.hr-seg {
display: block;
cursor: pointer;
border: 1.5px solid transparent;
border-radius: 6px;
padding: 0.15rem 0.5rem;
margin: 0.15rem 0;
transition: border-color 0.2s, background 0.2s;
}
.hr-seg.startline.is-active { border-color: var(--startline); background: var(--startline-soft); }
.hr-seg.headers.is-active { border-color: var(--headers); background: var(--headers-soft); }
.hr-seg.body.is-active { border-color: var(--body); background: var(--body-soft); }
.hr-method { color: var(--method); font-weight: 700; }
.hr-headers-block { color: var(--ink); }
.hr-headers-block .hr-key { color: var(--ink-soft); }
.hr-body-block {
color: var(--ink);
transition: opacity 0.2s;
}
.hr-body-block.is-empty { color: var(--ink-soft); opacity: 0.55; font-style: italic; }
/* --- cards --- */
.hr-columns { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: clamp(0.5rem, 2vw, 1rem); }
.hr-col { display: flex; flex-direction: column; align-items: center; }
.hr-connector { width: 2px; height: clamp(14px, 3.5vw, 22px); background: var(--line); transition: background 0.25s; }
.hr-col.startline .hr-connector.is-active { background: var(--startline); }
.hr-col.headers .hr-connector.is-active { background: var(--headers); }
.hr-col.body .hr-connector.is-active { background: var(--body); }
.hr-card {
width: 100%;
border: 1.5px solid var(--line);
border-radius: 12px;
padding: clamp(0.6rem, 2.5vw, 1rem) clamp(0.5rem, 2vw, 0.75rem);
text-align: center;
background: var(--paper);
cursor: pointer;
transition: border-color 0.25s, background 0.25s;
}
.hr-col.startline .hr-card { border-top: 3px solid var(--startline); }
.hr-col.headers .hr-card { border-top: 3px solid var(--headers); }
.hr-col.body .hr-card { border-top: 3px solid var(--body); }
.hr-col.startline .hr-card.is-active { background: var(--startline-soft); }
.hr-col.headers .hr-card.is-active { background: var(--headers-soft); }
.hr-col.body .hr-card.is-active { background: var(--body-soft); }
.hr-card-title { font-size: clamp(0.66rem, 2.4vw, 0.85rem); font-weight: 700; letter-spacing: 0.03em; margin-bottom: 0.3rem; }
.hr-col.startline .hr-card-title { color: var(--startline); }
.hr-col.headers .hr-card-title { color: var(--headers); }
.hr-col.body .hr-card-title { color: var(--body); }
.hr-card-caption { font-size: clamp(0.58rem, 2vw, 0.72rem); color: var(--ink-soft); line-height: 1.4; }
@media (max-width: 480px) {
.hr-card-caption { font-size: 0.56rem; }
.hr-code { font-size: 0.6rem; line-height: 1.6; }
}
</style>
<div class="hr-eyebrow">Try It</div>
<div class="hr-toggle" id="hrToggle">
<button data-method="GET" class="is-active">GET</button>
<button data-method="POST">POST</button>
</div>
<div class="hr-code" id="hrCode">
<span class="hr-seg startline" data-part="startline"><span class="hr-method" id="hrMethod">GET</span> <span id="hrPath">/maps</span> HTTP/1.1</span>
<span class="hr-seg headers" data-part="headers"><span class="hr-headers-block"><span class="hr-key">Host:</span> www.google.com<br><span class="hr-key">User-Agent:</span> Mozilla/5.0<br><span class="hr-key">Accept-Language:</span> en-US</span></span>
<span class="hr-seg body" data-part="body"><span class="hr-body-block" id="hrBody"><BODY></span></span>
</div>
<div class="hr-columns" id="hrColumns">
<div class="hr-col startline" data-part="startline">
<div class="hr-connector"></div>
<div class="hr-card" data-part="startline">
<div class="hr-card-title">Start Line</div>
<div class="hr-card-caption">HTTP method + request target + HTTP version</div>
</div>
</div>
<div class="hr-col headers" data-part="headers">
<div class="hr-connector"></div>
<div class="hr-card" data-part="headers">
<div class="hr-card-title">Headers</div>
<div class="hr-card-caption">Extra request info β many different possibilities</div>
</div>
</div>
<div class="hr-col body" data-part="body">
<div class="hr-connector"></div>
<div class="hr-card" data-part="body">
<div class="hr-card-title">Body</div>
<div class="hr-card-caption" id="hrBodyCaption">Only when sending data, e.g. POST</div>
</div>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-httpreq')
|| document.querySelector('.infographic-httpreq');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
const methodEl = wrapper.querySelector('#hrMethod');
const pathEl = wrapper.querySelector('#hrPath');
const bodyEl = wrapper.querySelector('#hrBody');
const bodyCaption = wrapper.querySelector('#hrBodyCaption');
const toggleEl = wrapper.querySelector('#hrToggle');
const columns = wrapper.querySelectorAll('.hr-col');
const segs = wrapper.querySelectorAll('.hr-seg');
function setMethod(method) {
methodEl.textContent = method;
if (method === 'POST') {
pathEl.textContent = '/search';
bodyEl.textContent = '{"query": "coffee near me"}';
bodyEl.classList.remove('is-empty');
bodyCaption.textContent = 'Sent with this POST β carries the data';
} else {
pathEl.textContent = '/maps';
bodyEl.textContent = '(none β GET has no body)';
bodyEl.classList.add('is-empty');
bodyCaption.textContent = 'Only when sending data, e.g. POST';
}
toggleEl.querySelectorAll('button').forEach(function (b) {
b.classList.toggle('is-active', b.getAttribute('data-method') === method);
});
}
function setActive(part) {
columns.forEach(function (col) {
const on = col.getAttribute('data-part') === part;
col.querySelector('.hr-connector').classList.toggle('is-active', on);
col.querySelector('.hr-card').classList.toggle('is-active', on);
});
segs.forEach(function (seg) {
seg.classList.toggle('is-active', seg.getAttribute('data-part') === part);
});
}
function clearActive() {
columns.forEach(function (col) {
col.querySelector('.hr-connector').classList.remove('is-active');
col.querySelector('.hr-card').classList.remove('is-active');
});
segs.forEach(function (seg) { seg.classList.remove('is-active'); });
}
let current = null;
function toggleActive(part) {
if (current === part) { current = null; clearActive(); }
else { current = part; setActive(part); }
}
toggleEl.querySelectorAll('button').forEach(function (btn) {
btn.addEventListener('click', function () { setMethod(btn.getAttribute('data-method')); });
});
segs.forEach(function (seg) {
seg.addEventListener('click', function () { toggleActive(seg.getAttribute('data-part')); });
});
wrapper.querySelectorAll('.hr-card').forEach(function (card) {
card.addEventListener('click', function () { toggleActive(card.getAttribute('data-part')); });
});
})();
</script>Once connected, the browser sends an HTTP request with three parts:
π Start line β method (
GET,POST,PUT/PATCH), path, HTTP versionπ Headers β metadata (browser type, language, time, etc.)
π¦ Body (optional) β data being sent, e.g. a form submission
π HTTP vs. HTTPS
π HTTPS is the same as HTTP, but encrypted using TLS or SSL (additional security protocols).
β Aside from this encryption layer, the same request/response logic applies identically to both HTTP and HTTPS.
π¨ Step 4: The Server Responds (HTTP Response)
<!-- Interactive Infographic: Anatomy of an HTTP Response -->
<div class="infographic-httpres">
<style>
@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,600;9..144,700&family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@500;600&display=swap');
.infographic-httpres {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--code-bg: #f0ece4;
--code-border:#ddd6c9;
--ok: #16825d;
--err: #b3453a;
--startline: #173d8c;
--startline-soft: #e2e8f7;
--headers: #2563eb;
--headers-soft: #e3ebfd;
--body: #7ea2f2;
--body-soft: #eef3fd;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
max-width: 760px;
margin: 0 auto;
box-sizing: border-box;
padding: clamp(1.5rem, 5vw, 2.5rem) clamp(1.25rem, 5vw, 2rem) clamp(1.75rem, 5vw, 2.5rem);
}
.infographic-httpres[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--code-bg: #1a1a22;
--code-border:#2c313d;
--ok: #4fd8a4;
--err: #e08579;
--startline: #6f93e0;
--startline-soft: #202a42;
--headers: #5b8def;
--headers-soft: #202942;
--body: #8fb3f5;
--body-soft: #1c2638;
}
.infographic-httpres * { box-sizing: border-box; }
.hs-eyebrow {
text-align: center;
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink-soft);
margin-bottom: 0.5rem;
}
.hs-title {
font-family: 'Fraunces', Georgia, serif;
font-weight: 700;
font-size: clamp(1.2rem, 3.6vw, 1.55rem);
text-align: center;
margin-bottom: 1.5rem;
}
/* --- status toggle --- */
.hs-toggle { display: flex; justify-content: center; gap: 0.4rem; margin-bottom: 1.25rem; flex-wrap: wrap; }
.hs-toggle button {
border: 1.5px solid var(--line);
background: var(--paper);
color: var(--ink-soft);
border-radius: 999px;
padding: 0.42rem 0.9rem;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.72rem;
font-weight: 700;
cursor: pointer;
transition: all 0.15s;
}
.hs-toggle button.is-active { background: var(--accent); border-color: var(--accent); color: #fff; }
/* --- code block --- */
.hs-code {
background: var(--code-bg);
border: 1px solid var(--code-border);
border-radius: 12px;
padding: clamp(0.75rem, 3vw, 1.1rem);
font-family: 'IBM Plex Mono', monospace;
font-size: clamp(0.72rem, 2.6vw, 0.88rem);
line-height: 1.8;
margin-bottom: clamp(1.25rem, 4vw, 1.75rem);
}
.hs-seg {
display: block;
cursor: pointer;
border: 1.5px solid transparent;
border-radius: 6px;
padding: 0.15rem 0.5rem;
margin: 0.15rem 0;
transition: border-color 0.2s, background 0.2s;
}
.hs-seg.startline.is-active { border-color: var(--startline); background: var(--startline-soft); }
.hs-seg.headers.is-active { border-color: var(--headers); background: var(--headers-soft); }
.hs-seg.body.is-active { border-color: var(--body); background: var(--body-soft); }
.hs-status { font-weight: 700; transition: color 0.2s; }
.hs-status.ok { color: var(--ok); }
.hs-status.err { color: var(--err); }
.hs-headers-block { color: var(--ink); }
.hs-headers-block .hs-key { color: var(--ink-soft); }
.hs-body-block { color: var(--ink); transition: opacity 0.2s; white-space: pre-wrap; display: inline-block; }
/* --- cards --- */
.hs-columns { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: clamp(0.5rem, 2vw, 1rem); }
.hs-col { display: flex; flex-direction: column; align-items: center; }
.hs-connector { width: 2px; height: clamp(14px, 3.5vw, 22px); background: var(--line); transition: background 0.25s; }
.hs-col.startline .hs-connector.is-active { background: var(--startline); }
.hs-col.headers .hs-connector.is-active { background: var(--headers); }
.hs-col.body .hs-connector.is-active { background: var(--body); }
.hs-card {
width: 100%;
border: 1.5px solid var(--line);
border-radius: 12px;
padding: clamp(0.6rem, 2.5vw, 1rem) clamp(0.5rem, 2vw, 0.75rem);
text-align: center;
background: var(--paper);
cursor: pointer;
transition: border-color 0.25s, background 0.25s;
}
.hs-col.startline .hs-card { border-top: 3px solid var(--startline); }
.hs-col.headers .hs-card { border-top: 3px solid var(--headers); }
.hs-col.body .hs-card { border-top: 3px solid var(--body); }
.hs-col.startline .hs-card.is-active { background: var(--startline-soft); }
.hs-col.headers .hs-card.is-active { background: var(--headers-soft); }
.hs-col.body .hs-card.is-active { background: var(--body-soft); }
.hs-card-title { font-size: clamp(0.66rem, 2.4vw, 0.85rem); font-weight: 700; letter-spacing: 0.03em; margin-bottom: 0.3rem; }
.hs-col.startline .hs-card-title { color: var(--startline); }
.hs-col.headers .hs-card-title { color: var(--headers); }
.hs-col.body .hs-card-title { color: var(--body); }
.hs-card-caption { font-size: clamp(0.58rem, 2vw, 0.72rem); color: var(--ink-soft); line-height: 1.4; }
@media (max-width: 480px) {
.hs-card-caption { font-size: 0.56rem; }
.hs-code { font-size: 0.6rem; line-height: 1.6; }
}
</style>
<div class="hs-eyebrow">Try It</div>
<div class="hs-toggle" id="hsToggle">
<button data-status="200" class="is-active">200 OK</button>
<button data-status="404">404 Not Found</button>
<button data-status="500">500 Server Error</button>
</div>
<div class="hs-code" id="hsCode">
<span class="hs-seg startline" data-part="startline">HTTP/1.1 <span class="hs-status ok" id="hsStatus">200 OK</span></span>
<span class="hs-seg headers" data-part="headers"><span class="hs-headers-block"><span class="hs-key">Date:</span> Fri, 18 Jan 2021<br><span class="hs-key">Content-Type:</span> <span id="hsContentType">text/html</span><br><span class="hs-key">Transfer-Encoding:</span> chunked</span></span>
<span class="hs-seg body" data-part="body"><span class="hs-body-block" id="hsBody"><BODY></span></span>
</div>
<div class="hs-columns" id="hsColumns">
<div class="hs-col startline" data-part="startline">
<div class="hs-connector"></div>
<div class="hs-card" data-part="startline">
<div class="hs-card-title">Start Line</div>
<div class="hs-card-caption">HTTP version + status code + status message</div>
</div>
</div>
<div class="hs-col headers" data-part="headers">
<div class="hs-connector"></div>
<div class="hs-card" data-part="headers">
<div class="hs-card-title">Headers</div>
<div class="hs-card-caption">Extra response info β many different possibilities</div>
</div>
</div>
<div class="hs-col body" data-part="body">
<div class="hs-connector"></div>
<div class="hs-card" data-part="body">
<div class="hs-card-title">Body</div>
<div class="hs-card-caption" id="hsBodyCaption">The actual content β present in most responses</div>
</div>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-httpres')
|| document.querySelector('.infographic-httpres');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
const statusEl = wrapper.querySelector('#hsStatus');
const contentTypeEl = wrapper.querySelector('#hsContentType');
const bodyEl = wrapper.querySelector('#hsBody');
const bodyCaption = wrapper.querySelector('#hsBodyCaption');
const toggleEl = wrapper.querySelector('#hsToggle');
const columns = wrapper.querySelectorAll('.hs-col');
const segs = wrapper.querySelectorAll('.hs-seg');
const statuses = {
'200': {
text: '200 OK', cls: 'ok', contentType: 'text/html',
body: '<html>\n <head><title>Google Maps</title></head>\n <body>\n <div id="map">...</div>\n </body>\n</html>',
caption: 'The requested page β present in most responses',
},
'404': {
text: '404 Not Found', cls: 'err', contentType: 'text/html',
body: '<h1>Page not found</h1>', caption: 'An error page explaining what went wrong',
},
'500': {
text: '500 Internal Server Error', cls: 'err', contentType: 'application/json',
body: '{"error": "Something went wrong"}', caption: 'A server-side failure message',
},
};
function setStatus(code) {
const s = statuses[code];
statusEl.textContent = s.text;
statusEl.className = 'hs-status ' + s.cls;
contentTypeEl.textContent = s.contentType;
bodyEl.innerHTML = s.body;
bodyCaption.textContent = s.caption;
toggleEl.querySelectorAll('button').forEach(function (b) {
b.classList.toggle('is-active', b.getAttribute('data-status') === code);
});
}
function setActive(part) {
columns.forEach(function (col) {
const on = col.getAttribute('data-part') === part;
col.querySelector('.hs-connector').classList.toggle('is-active', on);
col.querySelector('.hs-card').classList.toggle('is-active', on);
});
segs.forEach(function (seg) {
seg.classList.toggle('is-active', seg.getAttribute('data-part') === part);
});
}
function clearActive() {
columns.forEach(function (col) {
col.querySelector('.hs-connector').classList.remove('is-active');
col.querySelector('.hs-card').classList.remove('is-active');
});
segs.forEach(function (seg) { seg.classList.remove('is-active'); });
}
let current = null;
function toggleActive(part) {
if (current === part) { current = null; clearActive(); }
else { current = part; setActive(part); }
}
toggleEl.querySelectorAll('button').forEach(function (btn) {
btn.addEventListener('click', function () { setStatus(btn.getAttribute('data-status')); });
});
segs.forEach(function (seg) {
seg.addEventListener('click', function () { toggleActive(seg.getAttribute('data-part')); });
});
wrapper.querySelectorAll('.hs-card').forEach(function (card) {
card.addEventListener('click', function () { toggleActive(card.getAttribute('data-part')); });
});
setStatus('200');
})();
</script>The server replies with a similarly structured message:
π Start line β HTTP version, status code (β
200OK, β404Not Found), status messageπ Headers β set by the backend developer
π¦ Body β the actual content (HTML, JSON, etc.)
π Step 5: Multiple Requests for a Single Website
<!-- Infographic: Page Load Flow β HTML -> scan -> parallel fetch -> render, 16:9, responsive, synced timeline -->
<div class="infographic-pageload">
<style>
.infographic-pageload {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
width: 100%;
max-width: 1000px;
margin: 0 auto;
box-sizing: border-box;
aspect-ratio: 16 / 9;
display: flex;
flex-direction: column;
container-type: inline-size;
container-name: plcard;
}
.infographic-pageload[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
}
.infographic-pageload * { box-sizing: border-box; }
.pl-stage {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: clamp(0.15rem, 1.4cqw, 0.55rem);
min-height: 0;
min-width: 0;
padding: clamp(0.35rem, 2.5cqw, 1.25rem);
}
/* --- endpoint nodes: Browser / HTML / Render --- */
.pl-node {
display: flex;
flex-direction: column;
align-items: center;
gap: clamp(0.15rem, 1cqw, 0.45rem);
flex: 0 0 auto;
}
.pl-node-box {
width: clamp(34px, 10cqw, 82px);
height: clamp(34px, 10cqw, 82px);
border-radius: 50%;
border: 2px solid var(--line);
background: var(--paper-alt);
display: flex;
align-items: center;
justify-content: center;
color: var(--accent);
position: relative;
flex: 0 0 auto;
}
.pl-node-box svg { width: clamp(15px, 4.4cqw, 34px); height: clamp(15px, 4.4cqw, 34px); stroke: var(--accent); }
.pl-node-label { font-size: clamp(0.34rem, 1.4cqw, 0.68rem); font-weight: 700; white-space: nowrap; }
/* html node: pulses once the browser's request arrives (~14%), scans right after */
.pl-node.html .pl-node-box::before {
content: "";
position: absolute; inset: -5px;
border-radius: 50%;
border: 1.5px solid var(--accent);
opacity: 0;
animation: pl-html-ring 6s ease-out infinite;
}
@keyframes pl-html-ring {
0%, 12% { opacity: 0; transform: scale(0.9); }
15% { opacity: 0.6; transform: scale(0.95); }
27% { opacity: 0; transform: scale(1.2); }
100% { opacity: 0; }
}
/* render node: pops only once the slowest resource finishes (~88%) */
.pl-node.render .pl-node-box {
animation: pl-render-pop 6s ease-in-out infinite;
}
@keyframes pl-render-pop {
0%, 85% { transform: scale(1); border-color: var(--line); background: var(--paper-alt); }
90% { transform: scale(1.14); border-color: var(--accent); background: var(--accent-soft); }
97%, 100% { transform: scale(1); border-color: var(--line); background: var(--paper-alt); }
}
.pl-node.render .pl-node-box svg { transition: stroke 0.2s; }
/* --- straight connectors (Browser->HTML, HTML->cluster, cluster->Render) --- */
.pl-connector {
position: relative;
flex: 0 1 auto;
width: clamp(14px, 3.4cqw, 44px);
height: 2px;
background: var(--line);
}
.pl-connector::after {
content: "";
position: absolute; right: -1px; top: 50%;
width: clamp(4px, 1.1cqw, 7px); height: clamp(4px, 1.1cqw, 7px);
border-top: 2px solid var(--line);
border-right: 2px solid var(--line);
transform: translateY(-50%) rotate(45deg);
}
.pl-connector .pl-dot {
position: absolute; top: 50%; left: 0;
width: clamp(3.5px, 1cqw, 6px); height: clamp(3.5px, 1cqw, 6px);
border-radius: 50%;
background: var(--accent);
transform: translateY(-50%);
}
.pl-connector.c1 .pl-dot { animation: pl-c1 6s linear infinite; }
.pl-connector.c2 .pl-dot { animation: pl-c2 6s linear infinite; }
.pl-connector.c3 .pl-dot { animation: pl-c3 6s linear infinite; }
@keyframes pl-c1 {
0%, 1% { left: 0%; opacity: 0; }
4% { opacity: 1; }
12% { left: calc(100% - 6px); opacity: 1; }
15%, 100% { left: calc(100% - 6px); opacity: 0; }
}
@keyframes pl-c2 {
0%, 16% { left: 0%; opacity: 0; }
19% { opacity: 1; }
22% { left: calc(100% - 6px); opacity: 1; }
25%, 100% { left: calc(100% - 6px); opacity: 0; }
}
@keyframes pl-c3 {
0%, 85% { left: 0%; opacity: 0; }
87% { opacity: 1; }
89% { left: calc(100% - 6px); opacity: 1; }
92%, 100% { left: calc(100% - 6px); opacity: 0; }
}
/* --- the parallel-fetch cluster --- */
.pl-cluster {
flex: 1 1 auto;
min-width: clamp(150px, 34cqw, 420px);
max-width: 460px;
border: 1.5px dashed var(--line);
border-radius: 14px;
padding: clamp(0.35rem, 1.6cqw, 0.65rem) clamp(0.45rem, 2cqw, 0.85rem);
display: flex;
flex-direction: column;
gap: clamp(0.2rem, 1cqw, 0.4rem);
animation: pl-cluster-active 6s ease-in-out infinite;
}
@keyframes pl-cluster-active {
0%, 21% { border-color: var(--line); }
25%, 84% { border-color: var(--accent); }
89%, 100% { border-color: var(--line); }
}
.pl-cluster-label {
font-size: clamp(0.32rem, 1.2cqw, 0.6rem);
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--ink-soft);
text-align: center;
margin-bottom: clamp(0.05rem, 0.5cqw, 0.15rem);
}
.pl-lane { display: flex; align-items: center; gap: clamp(0.2rem, 1cqw, 0.4rem); }
.pl-lane-icon {
width: clamp(14px, 4cqw, 24px); height: clamp(14px, 4cqw, 24px);
border-radius: 6px;
border: 1.5px solid var(--line);
background: var(--paper);
display: flex; align-items: center; justify-content: center;
flex: 0 0 auto;
}
.pl-lane-icon svg { width: 60%; height: 60%; stroke: var(--ink-soft); }
.pl-lane-label {
font-size: clamp(0.3rem, 1.15cqw, 0.58rem);
font-weight: 600;
width: clamp(24px, 6cqw, 46px);
flex: 0 0 auto;
white-space: nowrap;
}
.pl-lane-track {
position: relative; flex: 1 1 auto; min-width: 0;
height: clamp(4px, 1.3cqw, 8px);
border-radius: 999px;
background: var(--paper-alt);
border: 1px solid var(--line);
overflow: hidden;
}
.pl-lane-fill { position: absolute; left: 0; top: 0; bottom: 0; width: 0%; background: var(--accent); border-radius: 999px; }
.pl-lane-check {
position: absolute; right: 1px; top: 50%; transform: translateY(-50%);
font-size: clamp(4px, 1.1cqw, 7px); font-weight: 900; color: #fff; opacity: 0;
}
/* each lane starts together (~25%) but finishes at a different point β the last one gates the render node */
.pl-lane.css .pl-lane-fill { animation: pl-fill 6s ease-in-out infinite; animation-timing-function: cubic-bezier(.4,0,.2,1); }
.pl-lane.js .pl-lane-fill { animation: pl-fill-js 6s ease-in-out infinite; }
.pl-lane.images .pl-lane-fill { animation: pl-fill-images 6s ease-in-out infinite; }
.pl-lane.fonts .pl-lane-fill { animation: pl-fill-fonts 6s ease-in-out infinite; }
.pl-lane.css .pl-lane-check { animation: pl-check 6s ease-in-out infinite; }
.pl-lane.js .pl-lane-check { animation: pl-check-js 6s ease-in-out infinite; }
.pl-lane.images .pl-lane-check { animation: pl-check-images 6s ease-in-out infinite; }
.pl-lane.fonts .pl-lane-check { animation: pl-check-fonts 6s ease-in-out infinite; }
@keyframes pl-fill {
0%, 24% { width: 0%; }
44% { width: 100%; }
89% { width: 100%; }
95%, 100% { width: 0%; }
}
@keyframes pl-check {
0%, 42% { opacity: 0; }
46%, 88% { opacity: 1; }
93% { opacity: 0; }
100% { opacity: 0; }
}
@keyframes pl-fill-js {
0%, 24% { width: 0%; }
57% { width: 100%; }
89% { width: 100%; }
95%, 100% { width: 0%; }
}
@keyframes pl-check-js {
0%, 55% { opacity: 0; }
59%, 88% { opacity: 1; }
93% { opacity: 0; }
100% { opacity: 0; }
}
@keyframes pl-fill-images {
0%, 24% { width: 0%; }
73% { width: 100%; }
89% { width: 100%; }
95%, 100% { width: 0%; }
}
@keyframes pl-check-images {
0%, 71% { opacity: 0; }
75%, 88% { opacity: 1; }
93% { opacity: 0; }
100% { opacity: 0; }
}
@keyframes pl-fill-fonts {
0%, 24% { width: 0%; }
86% { width: 100%; }
89% { width: 100%; }
95%, 100% { width: 0%; }
}
@keyframes pl-check-fonts {
0%, 84% { opacity: 0; }
88%, 88% { opacity: 1; }
93% { opacity: 0; }
100% { opacity: 0; }
}
@container plcard (max-width: 620px) {
.infographic-pageload { aspect-ratio: auto; height: auto; overflow: visible; }
.pl-stage { flex: none; min-height: auto; flex-direction: column; gap: 0.35rem; padding: 0.6rem 0.5rem; }
.pl-connector { display: none; }
.pl-node { gap: 0.15rem; }
.pl-cluster { width: 100%; max-width: none; gap: 0.3rem; padding: 0.4rem 0.6rem; }
.pl-lane-label { width: auto; }
}
@media (prefers-reduced-motion: reduce) {
.infographic-pageload *, .infographic-pageload *::before, .infographic-pageload *::after {
animation-duration: 0.001ms !important;
}
}
</style>
<div class="pl-stage">
<div class="pl-node">
<div class="pl-node-box">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 5a2 2 0 0 1 2 2v8.526a2 2 0 0 0 .212.897l1.068 2.127a1 1 0 0 1-.9 1.45H3.62a1 1 0 0 1-.9-1.45l1.068-2.127A2 2 0 0 0 4 15.526V7a2 2 0 0 1 2-2z"/><path d="M20.054 15.987H3.946"/></svg>
</div>
<div class="pl-node-label">Browser</div>
</div>
<div class="pl-connector c1"><div class="pl-dot"></div></div>
<div class="pl-node html">
<div class="pl-node-box">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"/><path d="M14 2v5a1 1 0 0 0 1 1h5"/><circle cx="11.5" cy="14.5" r="2.5"/><path d="M13.3 16.3 15 18"/></svg>
</div>
<div class="pl-node-label">HTML</div>
</div>
<div class="pl-connector c2"><div class="pl-dot"></div></div>
<div class="pl-cluster">
<div class="pl-cluster-label">Fetching in Parallel</div>
<div class="pl-lane css">
<div class="pl-lane-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m14.622 17.897-10.68-2.913"/><path d="M18.376 2.622a1 1 0 1 1 3.002 3.002L17.36 9.643a.5.5 0 0 0 0 .707l.944.944a2.41 2.41 0 0 1 0 3.408l-.944.944a.5.5 0 0 1-.707 0L8.354 7.348a.5.5 0 0 1 0-.707l.944-.944a2.41 2.41 0 0 1 3.408 0l.944.944a.5.5 0 0 0 .707 0z"/><path d="M9 8c-1.804 2.71-3.97 3.46-6.583 3.948a.507.507 0 0 0-.302.819l7.32 8.883a1 1 0 0 0 1.185.204C12.735 20.405 16 16.792 16 15"/></svg></div>
<div class="pl-lane-label">CSS</div>
<div class="pl-lane-track"><div class="pl-lane-fill"></div><div class="pl-lane-check">✓</div></div>
</div>
<div class="pl-lane js">
<div class="pl-lane-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 16 4-4-4-4"/><path d="m6 8-4 4 4 4"/><path d="m14.5 4-5 16"/></svg></div>
<div class="pl-lane-label">JS</div>
<div class="pl-lane-track"><div class="pl-lane-fill"></div><div class="pl-lane-check">✓</div></div>
</div>
<div class="pl-lane images">
<div class="pl-lane-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg></div>
<div class="pl-lane-label">Images</div>
<div class="pl-lane-track"><div class="pl-lane-fill"></div><div class="pl-lane-check">✓</div></div>
</div>
<div class="pl-lane fonts">
<div class="pl-lane-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 4v16"/><path d="M4 7V5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2"/><path d="M9 20h6"/></svg></div>
<div class="pl-lane-label">Fonts</div>
<div class="pl-lane-track"><div class="pl-lane-fill"></div><div class="pl-lane-check">✓</div></div>
</div>
</div>
<div class="pl-connector c3"><div class="pl-dot"></div></div>
<div class="pl-node render">
<div class="pl-node-box">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 10 2 2 4-4"/><rect width="20" height="14" x="2" y="3" rx="2"/><path d="M12 17v4"/><path d="M8 21h8"/></svg>
</div>
<div class="pl-node-label">Render</div>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-pageload')
|| document.querySelector('.infographic-pageload');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
</script>π The first request retrieves the HTML.
π The browser scans the HTML for CSS, JavaScript, images, and fonts.
π¨ The browser requests each of these files separately.
β¨ Once all the files are downloaded, the browser renders the webpage.
π§ How TCP/IP Actually Moves the Data
<!-- Infographic: TCP splits data into packets, IP routes them, TCP reassembles β 16:9, responsive, synced timeline -->
<div class="infographic-tcppackets">
<style>
.infographic-tcppackets {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
width: 100%;
max-width: 1000px;
margin: 0 auto;
box-sizing: border-box;
aspect-ratio: 16 / 9;
display: flex;
flex-direction: column;
container-type: inline-size;
container-name: tpcard;
}
.infographic-tcppackets[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
}
.infographic-tcppackets * { box-sizing: border-box; }
.tp-stage {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: clamp(0.2rem, 2cqw, 0.8rem);
min-height: 0;
min-width: 0;
padding: clamp(0.4rem, 3cqw, 1.5rem);
}
/* --- endpoint nodes: Data (source) / Data (destination) --- */
.tp-node {
display: flex;
flex-direction: column;
align-items: center;
gap: clamp(0.2rem, 1.2cqw, 0.5rem);
flex: 0 0 auto;
min-width: 0;
}
.tp-node-box {
width: clamp(48px, 15cqw, 108px);
height: clamp(48px, 15cqw, 108px);
border-radius: 16px;
border: 2px solid var(--line);
background: var(--paper-alt);
display: flex;
align-items: center;
justify-content: center;
color: var(--accent);
position: relative;
flex: 0 0 auto;
}
.tp-node-box svg { width: clamp(20px, 6cqw, 46px); height: clamp(20px, 6cqw, 46px); stroke: var(--accent); }
.tp-node-label { font-size: clamp(0.44rem, 1.8cqw, 0.86rem); font-weight: 700; white-space: nowrap; }
.tp-node-tag {
font-size: clamp(0.32rem, 1.2cqw, 0.6rem);
font-weight: 700;
letter-spacing: 0.03em;
color: var(--accent);
opacity: 0;
white-space: nowrap;
}
/* source: pulses + "TCP splits" tag fades in right as the packets depart (~8%) */
.tp-node.source .tp-node-box::before {
content: "";
position: absolute; inset: -6px;
border-radius: 20px;
border: 1.5px solid var(--accent);
opacity: 0;
animation: tp-src-ring 6s ease-out infinite;
}
@keyframes tp-src-ring {
0%, 4% { opacity: 0; transform: scale(0.92); }
8% { opacity: 0.6; transform: scale(0.96); }
20% { opacity: 0; transform: scale(1.16); }
100% { opacity: 0; }
}
.tp-node.source .tp-node-tag { animation: tp-src-tag 6s ease-in-out infinite; }
@keyframes tp-src-tag {
0%, 5% { opacity: 0; }
9%, 30% { opacity: 1; }
36%, 100%{ opacity: 0; }
}
/* destination: reassembles + pops only once the slowest packet arrives (~55%) */
.tp-node.dest .tp-node-box {
animation: tp-dest-pop 6s ease-in-out infinite;
}
@keyframes tp-dest-pop {
0%, 52% { transform: scale(1); border-color: var(--line); background: var(--paper-alt); }
58% { transform: scale(1.12); border-color: var(--accent); background: var(--accent-soft); }
70%, 100%{ transform: scale(1); border-color: var(--line); background: var(--paper-alt); }
}
.tp-node.dest .tp-node-tag { animation: tp-dest-tag 6s ease-in-out infinite; }
@keyframes tp-dest-tag {
0%, 55% { opacity: 0; }
60%, 88% { opacity: 1; }
94%, 100%{ opacity: 0; }
}
/* --- middle: 3 curved routes with numbered packets, IP tag --- */
.tp-middle {
position: relative;
flex: 1 1 auto;
min-width: clamp(140px, 34cqw, 420px);
max-width: 460px;
height: clamp(90px, 24cqw, 190px);
}
.tp-middle svg { width: 100%; height: 100%; display: block; }
.tp-route {
fill: none;
stroke: var(--line);
stroke-width: 2;
stroke-dasharray: 5 5;
vector-effect: non-scaling-stroke;
}
.tp-ip-tag {
position: absolute;
left: 50%; top: -0.2em;
transform: translate(-50%, -100%);
font-size: clamp(0.34rem, 1.4cqw, 0.66rem);
font-weight: 700;
letter-spacing: 0.03em;
color: var(--accent);
white-space: nowrap;
opacity: 0;
animation: tp-ip-tag 6s ease-in-out infinite;
}
@keyframes tp-ip-tag {
0%, 9% { opacity: 0; }
14%, 50% { opacity: 1; }
56%, 100%{ opacity: 0; }
}
.tp-packet {
width: clamp(9px, 2.6cqw, 16px);
height: clamp(9px, 2.6cqw, 16px);
border-radius: 3px;
background: var(--accent);
color: #fff;
font-size: clamp(5px, 1.6cqw, 9px);
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
}
@media (prefers-reduced-motion: reduce) {
.infographic-tcppackets *, .infographic-tcppackets *::before, .infographic-tcppackets *::after {
animation-duration: 0.001ms !important;
}
}
@container tpcard (max-width: 560px) {
.infographic-tcppackets { aspect-ratio: auto; height: auto; overflow: visible; }
.tp-stage { flex: none; min-height: auto; flex-direction: column; gap: 0.5rem; padding: 0.6rem; }
.tp-middle { width: clamp(70px, 40cqw, 140px); max-width: none; min-width: 0; height: clamp(140px, 55cqw, 260px); }
}
</style>
<div class="tp-stage">
<div class="tp-node source">
<div class="tp-node-box">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/><path d="m3.3 7 8.7 5 8.7-5"/><path d="M12 22V12"/></svg>
</div>
<div class="tp-node-label">Data</div>
<div class="tp-node-tag">TCP splits</div>
</div>
<div class="tp-middle">
<div class="tp-ip-tag">IP routes each packet</div>
<svg id="tpSvg" viewBox="0 0 200 100" preserveAspectRatio="none">
<path id="tp-visible-top" class="tp-route" d="M4,50 C60,12 140,12 196,50" />
<path id="tp-visible-mid" class="tp-route" d="M4,50 L196,50" />
<path id="tp-visible-bot" class="tp-route" d="M4,50 C60,88 140,88 196,50" />
<path id="tp-path-top" d="M4,50 C60,12 140,12 196,50" fill="none" stroke="none" />
<path id="tp-path-mid" d="M4,50 L196,50" fill="none" stroke="none" />
<path id="tp-path-bot" d="M4,50 C60,88 140,88 196,50" fill="none" stroke="none" />
</svg>
<div class="tp-packet tp-p1" style="position:absolute; top:0; left:0; opacity:0;">1</div>
<div class="tp-packet tp-p2" style="position:absolute; top:0; left:0; opacity:0;">2</div>
<div class="tp-packet tp-p3" style="position:absolute; top:0; left:0; opacity:0;">3</div>
</div>
<div class="tp-node dest">
<div class="tp-node-box">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/><path d="m3.3 7 8.7 5 8.7-5"/><path d="M12 22V12"/></svg>
</div>
<div class="tp-node-label">Data</div>
<div class="tp-node-tag">TCP reassembles</div>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-tcppackets')
|| document.querySelector('.infographic-tcppackets');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
// Animate the three packets along their SVG paths using JS (getPointAtLength),
// synced to the same 6s master timeline as the CSS keyframes.
const svg = wrapper.querySelector('#tpSvg');
const middle = wrapper.querySelector('.tp-middle');
const stage = wrapper.querySelector('.tp-stage');
const visible = {
p1: wrapper.querySelector('#tp-visible-top'),
p2: wrapper.querySelector('#tp-visible-mid'),
p3: wrapper.querySelector('#tp-visible-bot'),
};
const paths = {
p1: wrapper.querySelector('#tp-path-top'),
p2: wrapper.querySelector('#tp-path-mid'),
p3: wrapper.querySelector('#tp-path-bot'),
};
const dots = {
p1: wrapper.querySelector('.tp-p1'),
p2: wrapper.querySelector('.tp-p2'),
p3: wrapper.querySelector('.tp-p3'),
};
// [startPct, endPct] within the 0-100 master cycle for each packet's travel window
const windows = {
p1: [8, 50],
p2: [10, 40],
p3: [12, 55],
};
const shapes = {
horizontal: {
viewBox: '0 0 200 100',
d: {
p1: 'M4,50 C60,12 140,12 196,50',
p2: 'M4,50 L196,50',
p3: 'M4,50 C60,88 140,88 196,50',
},
},
vertical: {
viewBox: '0 0 100 200',
d: {
p1: 'M50,4 C15,60 15,140 50,196',
p2: 'M50,4 L50,196',
p3: 'M50,4 C85,60 85,140 50,196',
},
},
};
let orientation = null;
function applyOrientation() {
const isColumn = getComputedStyle(stage).flexDirection === 'column';
const next = isColumn ? 'vertical' : 'horizontal';
if (next === orientation) return;
orientation = next;
const shape = shapes[orientation];
svg.setAttribute('viewBox', shape.viewBox);
Object.keys(shape.d).forEach(function (key) {
visible[key].setAttribute('d', shape.d[key]);
paths[key].setAttribute('d', shape.d[key]);
});
}
applyOrientation();
if (window.ResizeObserver) {
new ResizeObserver(applyOrientation).observe(wrapper);
} else {
window.addEventListener('resize', applyOrientation);
}
const DURATION = 6000; // ms, matches the 6s CSS cycle
let startTime = null;
function frame(ts) {
if (!startTime) startTime = ts;
const elapsed = (ts - startTime) % DURATION;
const pct = (elapsed / DURATION) * 100;
const vb = svg.viewBox.baseVal;
const rect = middle.getBoundingClientRect();
const scaleX = rect.width / vb.width;
const scaleY = rect.height / vb.height;
Object.keys(paths).forEach(function (key) {
const [start, end] = windows[key];
const path = paths[key];
const dot = dots[key];
const len = path.getTotalLength();
if (pct < start || pct > end) {
dot.style.opacity = '0';
return;
}
const localPct = (pct - start) / (end - start);
const point = path.getPointAtLength(localPct * len);
const x = point.x * scaleX - 8;
const y = point.y * scaleY - 8;
dot.style.transform = 'translate(' + x + 'px,' + y + 'px)';
dot.style.opacity = '1';
});
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
})();
</script>π TCP splits data into small packets and reassembles them at the destination β much faster than sending one giant chunk
π§ IP handles routing, making sure each packet reaches the right address
π£οΈ Full Journey, Summarized
<!-- Infographic: Full Page Load Journey β 7-step chain + TCP/IP foundation, 16:9, responsive, dynamic synced timeline -->
<div class="infographic-journey">
<style>
.infographic-journey {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
width: 100%;
max-width: 1080px;
margin: 0 auto;
box-sizing: border-box;
aspect-ratio: 16 / 9;
display: flex;
flex-direction: column;
container-type: inline-size;
container-name: jcard;
}
.infographic-journey[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
}
.infographic-journey * { box-sizing: border-box; }
.jr-chain {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: clamp(0.08rem, 0.9cqw, 0.35rem);
min-height: 0;
min-width: 0;
padding: clamp(0.5rem, 3cqw, 1.4rem) clamp(0.4rem, 3cqw, 1.4rem) clamp(0.2rem, 1.5cqw, 0.6rem);
}
/* --- step node --- */
.jr-node {
display: flex;
flex-direction: column;
align-items: center;
gap: clamp(0.12rem, 0.8cqw, 0.35rem);
flex: 0 0 auto;
min-width: 0;
}
.jr-node-box {
width: clamp(26px, 7.4cqw, 66px);
height: clamp(26px, 7.4cqw, 66px);
border-radius: 50%;
border: 2px solid var(--line);
background: var(--paper-alt);
display: flex;
align-items: center;
justify-content: center;
color: var(--accent);
position: relative;
flex: 0 0 auto;
box-shadow: 0 0 0 0 rgba(37,99,235,0);
}
.jr-node-box svg {
width: clamp(11px, 3.2cqw, 28px); height: clamp(11px, 3.2cqw, 28px);
stroke: var(--accent);
}
.jr-node-label {
font-size: clamp(0.26rem, 1.15cqw, 0.6rem);
font-weight: 700;
white-space: nowrap;
text-align: center;
transition: color 0.2s;
}
.jr-node-num {
position: absolute; top: -4px; right: -4px;
width: clamp(10px, 2.4cqw, 17px); height: clamp(10px, 2.4cqw, 17px);
border-radius: 50%;
background: var(--paper);
border: 1.5px solid var(--line);
color: var(--ink-soft);
font-size: clamp(6px, 1.5cqw, 9px);
font-weight: 700;
display: flex; align-items: center; justify-content: center;
}
.jr-node-check {
position: absolute; top: -4px; right: -4px;
width: clamp(10px, 2.4cqw, 17px); height: clamp(10px, 2.4cqw, 17px);
border-radius: 50%;
background: var(--accent);
color: #fff;
font-size: clamp(6px, 1.5cqw, 9px);
font-weight: 900;
display: flex; align-items: center; justify-content: center;
opacity: 0;
transform: scale(0.4);
}
/* Each node: bounces in hard on arrival, settles into a lit "completed" state,
and stays that way until the whole loop resets β so the chain visibly fills up
left to right instead of just flickering node by node. */
.jr-node.n1 .jr-node-box { animation: jr-box-1 8s cubic-bezier(.34,1.56,.64,1) infinite; }
.jr-node.n2 .jr-node-box { animation: jr-box-2 8s cubic-bezier(.34,1.56,.64,1) infinite; }
.jr-node.n3 .jr-node-box { animation: jr-box-3 8s cubic-bezier(.34,1.56,.64,1) infinite; }
.jr-node.n4 .jr-node-box { animation: jr-box-4 8s cubic-bezier(.34,1.56,.64,1) infinite; }
.jr-node.n5 .jr-node-box { animation: jr-box-5 8s cubic-bezier(.34,1.56,.64,1) infinite; }
.jr-node.n6 .jr-node-box { animation: jr-box-6 8s cubic-bezier(.34,1.56,.64,1) infinite; }
.jr-node.n7 .jr-node-box { animation: jr-box-7 8s cubic-bezier(.34,1.56,.64,1) infinite; }
.jr-node.n1 .jr-node-box svg { animation: jr-icon-1 8s ease-in-out infinite; }
.jr-node.n2 .jr-node-box svg { animation: jr-icon-2 8s ease-in-out infinite; }
.jr-node.n3 .jr-node-box svg { animation: jr-icon-3 8s ease-in-out infinite; }
.jr-node.n4 .jr-node-box svg { animation: jr-icon-4 8s ease-in-out infinite; }
.jr-node.n5 .jr-node-box svg { animation: jr-icon-5 8s ease-in-out infinite; }
.jr-node.n6 .jr-node-box svg { animation: jr-icon-6 8s ease-in-out infinite; }
.jr-node.n7 .jr-node-box svg { animation: jr-icon-7 8s ease-in-out infinite; }
.jr-node.n1 .jr-node-num { animation: jr-num-1 8s steps(1) infinite; }
.jr-node.n2 .jr-node-num { animation: jr-num-2 8s steps(1) infinite; }
.jr-node.n3 .jr-node-num { animation: jr-num-3 8s steps(1) infinite; }
.jr-node.n4 .jr-node-num { animation: jr-num-4 8s steps(1) infinite; }
.jr-node.n5 .jr-node-num { animation: jr-num-5 8s steps(1) infinite; }
.jr-node.n6 .jr-node-num { animation: jr-num-6 8s steps(1) infinite; }
.jr-node.n7 .jr-node-num { animation: jr-num-7 8s steps(1) infinite; }
.jr-node.n1 .jr-node-check { animation: jr-check-1 8s cubic-bezier(.34,1.56,.64,1) infinite; }
.jr-node.n2 .jr-node-check { animation: jr-check-2 8s cubic-bezier(.34,1.56,.64,1) infinite; }
.jr-node.n3 .jr-node-check { animation: jr-check-3 8s cubic-bezier(.34,1.56,.64,1) infinite; }
.jr-node.n4 .jr-node-check { animation: jr-check-4 8s cubic-bezier(.34,1.56,.64,1) infinite; }
.jr-node.n5 .jr-node-check { animation: jr-check-5 8s cubic-bezier(.34,1.56,.64,1) infinite; }
.jr-node.n6 .jr-node-check { animation: jr-check-6 8s cubic-bezier(.34,1.56,.64,1) infinite; }
.jr-node.n7 .jr-node-check { animation: jr-check-7 8s cubic-bezier(.34,1.56,.64,1) infinite; }
/* box: idle -> hard bounce pop -> settle lit -> hold -> reset */
@keyframes jr-box-1 { 0%{transform:scale(1);border-color:var(--accent);background:var(--accent-soft);box-shadow:0 0 0 0 rgba(37,99,235,0);} 2%{transform:scale(1.35);box-shadow:0 0 0 6px rgba(37,99,235,0.15);} 6%{transform:scale(1.05);} 90%{transform:scale(1.05);border-color:var(--accent);background:var(--accent-soft);} 96%,100%{transform:scale(1);border-color:var(--line);background:var(--paper-alt);box-shadow:0 0 0 0 rgba(37,99,235,0);} }
@keyframes jr-box-2 { 0%,10%{transform:scale(1);border-color:var(--line);background:var(--paper-alt);box-shadow:0 0 0 0 rgba(37,99,235,0);} 13%{transform:scale(1.35);border-color:var(--accent);background:var(--accent-soft);box-shadow:0 0 0 6px rgba(37,99,235,0.15);} 17%{transform:scale(1.05);} 90%{transform:scale(1.05);border-color:var(--accent);background:var(--accent-soft);} 96%,100%{transform:scale(1);border-color:var(--line);background:var(--paper-alt);box-shadow:0 0 0 0 rgba(37,99,235,0);} }
@keyframes jr-box-3 { 0%,22%{transform:scale(1);border-color:var(--line);background:var(--paper-alt);box-shadow:0 0 0 0 rgba(37,99,235,0);} 25%{transform:scale(1.35);border-color:var(--accent);background:var(--accent-soft);box-shadow:0 0 0 6px rgba(37,99,235,0.15);} 29%{transform:scale(1.05);} 90%{transform:scale(1.05);border-color:var(--accent);background:var(--accent-soft);} 96%,100%{transform:scale(1);border-color:var(--line);background:var(--paper-alt);box-shadow:0 0 0 0 rgba(37,99,235,0);} }
@keyframes jr-box-4 { 0%,34%{transform:scale(1);border-color:var(--line);background:var(--paper-alt);box-shadow:0 0 0 0 rgba(37,99,235,0);} 37%{transform:scale(1.35);border-color:var(--accent);background:var(--accent-soft);box-shadow:0 0 0 6px rgba(37,99,235,0.15);} 41%{transform:scale(1.05);} 90%{transform:scale(1.05);border-color:var(--accent);background:var(--accent-soft);} 96%,100%{transform:scale(1);border-color:var(--line);background:var(--paper-alt);box-shadow:0 0 0 0 rgba(37,99,235,0);} }
@keyframes jr-box-5 { 0%,46%{transform:scale(1);border-color:var(--line);background:var(--paper-alt);box-shadow:0 0 0 0 rgba(37,99,235,0);} 49%{transform:scale(1.35);border-color:var(--accent);background:var(--accent-soft);box-shadow:0 0 0 6px rgba(37,99,235,0.15);} 53%{transform:scale(1.05);} 90%{transform:scale(1.05);border-color:var(--accent);background:var(--accent-soft);} 96%,100%{transform:scale(1);border-color:var(--line);background:var(--paper-alt);box-shadow:0 0 0 0 rgba(37,99,235,0);} }
@keyframes jr-box-6 { 0%,58%{transform:scale(1);border-color:var(--line);background:var(--paper-alt);box-shadow:0 0 0 0 rgba(37,99,235,0);} 61%{transform:scale(1.35);border-color:var(--accent);background:var(--accent-soft);box-shadow:0 0 0 6px rgba(37,99,235,0.15);} 65%{transform:scale(1.05);} 90%{transform:scale(1.05);border-color:var(--accent);background:var(--accent-soft);} 96%,100%{transform:scale(1);border-color:var(--line);background:var(--paper-alt);box-shadow:0 0 0 0 rgba(37,99,235,0);} }
@keyframes jr-box-7 { 0%,70%{transform:scale(1);border-color:var(--line);background:var(--paper-alt);box-shadow:0 0 0 0 rgba(37,99,235,0);} 74%{transform:scale(1.5);border-color:var(--accent);background:var(--accent-soft);box-shadow:0 0 0 9px rgba(37,99,235,0.18);} 79%{transform:scale(1.1);} 90%{transform:scale(1.1);border-color:var(--accent);background:var(--accent-soft);} 96%,100%{transform:scale(1);border-color:var(--line);background:var(--paper-alt);box-shadow:0 0 0 0 rgba(37,99,235,0);} }
/* icon: quick wiggle right at the moment of arrival */
@keyframes jr-icon-1 { 0%{transform:rotate(0);} 2%{transform:rotate(-14deg) scale(1.1);} 4%{transform:rotate(10deg) scale(1.1);} 7%,100%{transform:rotate(0) scale(1);} }
@keyframes jr-icon-2 { 0%,10%{transform:rotate(0);} 13%{transform:rotate(-14deg) scale(1.1);} 15%{transform:rotate(10deg) scale(1.1);} 18%,100%{transform:rotate(0) scale(1);} }
@keyframes jr-icon-3 { 0%,22%{transform:rotate(0);} 25%{transform:rotate(-14deg) scale(1.1);} 27%{transform:rotate(10deg) scale(1.1);} 30%,100%{transform:rotate(0) scale(1);} }
@keyframes jr-icon-4 { 0%,34%{transform:rotate(0);} 37%{transform:rotate(-14deg) scale(1.1);} 39%{transform:rotate(10deg) scale(1.1);} 42%,100%{transform:rotate(0) scale(1);} }
@keyframes jr-icon-5 { 0%,46%{transform:rotate(0);} 49%{transform:rotate(-14deg) scale(1.1);} 51%{transform:rotate(10deg) scale(1.1);} 54%,100%{transform:rotate(0) scale(1);} }
@keyframes jr-icon-6 { 0%,58%{transform:rotate(0);} 61%{transform:rotate(-14deg) scale(1.1);} 63%{transform:rotate(10deg) scale(1.1);} 66%,100%{transform:rotate(0) scale(1);} }
@keyframes jr-icon-7 { 0%,70%{transform:rotate(0) scale(1);} 74%{transform:rotate(-16deg) scale(1.25);} 77%{transform:rotate(12deg) scale(1.25);} 81%,100%{transform:rotate(0) scale(1);} }
/* number badge fades out, checkmark badge bounces in β same arrival instants as above */
@keyframes jr-num-1 { 0%,1%{opacity:1;} 2%,100%{opacity:0;} }
@keyframes jr-num-2 { 0%,12%{opacity:1;} 13%,100%{opacity:0;} }
@keyframes jr-num-3 { 0%,24%{opacity:1;} 25%,100%{opacity:0;} }
@keyframes jr-num-4 { 0%,36%{opacity:1;} 37%,100%{opacity:0;} }
@keyframes jr-num-5 { 0%,48%{opacity:1;} 49%,100%{opacity:0;} }
@keyframes jr-num-6 { 0%,60%{opacity:1;} 61%,100%{opacity:0;} }
@keyframes jr-num-7 { 0%,73%{opacity:1;} 74%,100%{opacity:0;} }
@keyframes jr-check-1 { 0%,1%{opacity:0;transform:scale(0.3);} 3%{opacity:1;transform:scale(1.3);} 6%{transform:scale(1);} 95%{opacity:1;transform:scale(1);} 97%,100%{opacity:0;transform:scale(0.3);} }
@keyframes jr-check-2 { 0%,12%{opacity:0;transform:scale(0.3);} 14%{opacity:1;transform:scale(1.3);} 17%{transform:scale(1);} 95%{opacity:1;transform:scale(1);} 97%,100%{opacity:0;transform:scale(0.3);} }
@keyframes jr-check-3 { 0%,24%{opacity:0;transform:scale(0.3);} 26%{opacity:1;transform:scale(1.3);} 29%{transform:scale(1);} 95%{opacity:1;transform:scale(1);} 97%,100%{opacity:0;transform:scale(0.3);} }
@keyframes jr-check-4 { 0%,36%{opacity:0;transform:scale(0.3);} 38%{opacity:1;transform:scale(1.3);} 41%{transform:scale(1);} 95%{opacity:1;transform:scale(1);} 97%,100%{opacity:0;transform:scale(0.3);} }
@keyframes jr-check-5 { 0%,48%{opacity:0;transform:scale(0.3);} 50%{opacity:1;transform:scale(1.3);} 53%{transform:scale(1);} 95%{opacity:1;transform:scale(1);} 97%,100%{opacity:0;transform:scale(0.3);} }
@keyframes jr-check-6 { 0%,60%{opacity:0;transform:scale(0.3);} 62%{opacity:1;transform:scale(1.3);} 65%{transform:scale(1);} 95%{opacity:1;transform:scale(1);} 97%,100%{opacity:0;transform:scale(0.3);} }
@keyframes jr-check-7 { 0%,73%{opacity:0;transform:scale(0.3);} 76%{opacity:1;transform:scale(1.4);} 80%{transform:scale(1);} 95%{opacity:1;transform:scale(1);} 97%,100%{opacity:0;transform:scale(0.3);} }
/* --- connectors between the 7 steps --- */
.jr-connector {
position: relative;
flex: 1 1 auto;
min-width: 4px;
max-width: clamp(10px, 2.4cqw, 30px);
height: 2px;
background: var(--line);
margin-bottom: clamp(0.6rem, 2cqw, 1rem);
overflow: visible;
}
.jr-connector-fill {
position: absolute; left: 0; top: 0; bottom: 0;
width: 0%;
background: var(--accent);
}
.jr-connector.c1 .jr-connector-fill { animation: jr-fill-1 8s cubic-bezier(.2,.8,.2,1) infinite; }
.jr-connector.c2 .jr-connector-fill { animation: jr-fill-2 8s cubic-bezier(.2,.8,.2,1) infinite; }
.jr-connector.c3 .jr-connector-fill { animation: jr-fill-3 8s cubic-bezier(.2,.8,.2,1) infinite; }
.jr-connector.c4 .jr-connector-fill { animation: jr-fill-4 8s cubic-bezier(.2,.8,.2,1) infinite; }
.jr-connector.c5 .jr-connector-fill { animation: jr-fill-5 8s cubic-bezier(.2,.8,.2,1) infinite; }
.jr-connector.c6 .jr-connector-fill { animation: jr-fill-6 8s cubic-bezier(.2,.8,.2,1) infinite; }
@keyframes jr-fill-1 { 0%,3%{width:0%;} 12%{width:100%;} 95%{width:100%;} 97%,100%{width:0%;} }
@keyframes jr-fill-2 { 0%,15%{width:0%;} 24%{width:100%;} 95%{width:100%;} 97%,100%{width:0%;} }
@keyframes jr-fill-3 { 0%,27%{width:0%;} 36%{width:100%;} 95%{width:100%;} 97%,100%{width:0%;} }
@keyframes jr-fill-4 { 0%,39%{width:0%;} 48%{width:100%;} 95%{width:100%;} 97%,100%{width:0%;} }
@keyframes jr-fill-5 { 0%,51%{width:0%;} 60%{width:100%;} 95%{width:100%;} 97%,100%{width:0%;} }
@keyframes jr-fill-6 { 0%,63%{width:0%;} 72%{width:100%;} 95%{width:100%;} 97%,100%{width:0%;} }
.jr-connector .jr-dot {
position: absolute; top: 50%; left: 0;
width: clamp(4px, 1.1cqw, 7px); height: clamp(4px, 1.1cqw, 7px);
border-radius: 50%;
background: var(--accent);
box-shadow: 0 0 6px 1px rgba(37,99,235,0.5);
transform: translateY(-50%);
}
.jr-connector.c1 .jr-dot { animation: jr-c1 8s linear infinite; }
.jr-connector.c2 .jr-dot { animation: jr-c2 8s linear infinite; }
.jr-connector.c3 .jr-dot { animation: jr-c3 8s linear infinite; }
.jr-connector.c4 .jr-dot { animation: jr-c4 8s linear infinite; }
.jr-connector.c5 .jr-dot { animation: jr-c5 8s linear infinite; }
.jr-connector.c6 .jr-dot { animation: jr-c6 8s linear infinite; }
@keyframes jr-c1 { 0%,3%{left:0%;opacity:0;} 5%{opacity:1;} 12%{left:calc(100% - 7px);opacity:1;} 14%,100%{left:calc(100% - 7px);opacity:0;} }
@keyframes jr-c2 { 0%,15%{left:0%;opacity:0;} 17%{opacity:1;} 24%{left:calc(100% - 7px);opacity:1;} 26%,100%{left:calc(100% - 7px);opacity:0;} }
@keyframes jr-c3 { 0%,27%{left:0%;opacity:0;} 29%{opacity:1;} 36%{left:calc(100% - 7px);opacity:1;} 38%,100%{left:calc(100% - 7px);opacity:0;} }
@keyframes jr-c4 { 0%,39%{left:0%;opacity:0;} 41%{opacity:1;} 48%{left:calc(100% - 7px);opacity:1;} 50%,100%{left:calc(100% - 7px);opacity:0;} }
@keyframes jr-c5 { 0%,51%{left:0%;opacity:0;} 53%{opacity:1;} 60%{left:calc(100% - 7px);opacity:1;} 62%,100%{left:calc(100% - 7px);opacity:0;} }
@keyframes jr-c6 { 0%,63%{left:0%;opacity:0;} 65%{opacity:1;} 72%{left:calc(100% - 7px);opacity:1;} 74%,100%{left:calc(100% - 7px);opacity:0;} }
/* --- TCP/IP foundation band: always-on, underneath it all --- */
.jr-foundation {
position: relative;
margin: 0 clamp(0.4rem, 3cqw, 1.4rem) clamp(0.5rem, 2.5cqw, 1.1rem);
border-top: 1.5px dashed var(--line);
padding-top: clamp(0.3rem, 1.4cqw, 0.6rem);
display: flex;
align-items: center;
gap: clamp(0.3rem, 1.4cqw, 0.6rem);
}
.jr-foundation-icon {
width: clamp(18px, 5cqw, 34px); height: clamp(18px, 5cqw, 34px);
border-radius: 8px;
border: 1.5px dashed var(--accent);
background: var(--accent-soft);
display: flex; align-items: center; justify-content: center;
flex: 0 0 auto;
animation: jr-found-pulse 2.4s ease-in-out infinite;
}
.jr-foundation-icon svg { width: 55%; height: 55%; stroke: var(--accent); }
@keyframes jr-found-pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.12); }
}
.jr-foundation-text { flex: 0 0 auto; }
.jr-foundation-title { font-size: clamp(0.36rem, 1.5cqw, 0.7rem); font-weight: 700; color: var(--accent); }
.jr-foundation-caption { font-size: clamp(0.3rem, 1.2cqw, 0.58rem); color: var(--ink-soft); }
.jr-foundation-track {
position: relative;
flex: 1 1 auto;
min-width: 0;
height: clamp(3px, 1cqw, 5px);
border-radius: 999px;
background: var(--paper-alt);
border: 1px dashed var(--line);
overflow: hidden;
}
.jr-packet {
position: absolute; top: 50%;
width: clamp(4px, 1.1cqw, 7px); height: clamp(4px, 1.1cqw, 7px);
border-radius: 2px;
background: var(--accent);
transform: translateY(-50%) rotate(45deg);
}
.jr-packet.a { animation: jr-drift 1.7s linear infinite; }
.jr-packet.b { animation: jr-drift 1.7s linear infinite; animation-delay: 0.42s; }
.jr-packet.c { animation: jr-drift 1.7s linear infinite; animation-delay: 0.84s; }
.jr-packet.d { animation: jr-drift 1.7s linear infinite; animation-delay: 1.26s; }
@keyframes jr-drift {
0% { left: 0%; opacity: 0; transform: translateY(-50%) rotate(45deg) scale(0.8); }
8% { opacity: 1; transform: translateY(-50%) rotate(45deg) scale(1); }
92% { opacity: 1; }
100% { left: calc(100% - 7px); opacity: 0; }
}
@container jcard (max-width: 720px) {
.infographic-journey { aspect-ratio: auto; height: auto; overflow: visible; }
.jr-chain { flex: none; min-height: auto; flex-wrap: wrap; row-gap: 0.6rem; padding: 0.7rem 0.5rem 0.3rem; }
.jr-connector { display: none; }
.jr-foundation { flex-wrap: wrap; margin: 0 0.5rem 0.6rem; }
.jr-foundation-track { width: 100%; order: 3; }
}
@media (prefers-reduced-motion: reduce) {
.infographic-journey *, .infographic-journey *::before, .infographic-journey *::after {
animation-duration: 0.001ms !important;
}
}
</style>
<div class="jr-chain">
<div class="jr-node n1">
<div class="jr-node-box">
<div class="jr-node-num">1</div>
<div class="jr-node-check">✓</div>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 8h.01"/><path d="M12 12h.01"/><path d="M14 8h.01"/><path d="M16 12h.01"/><path d="M18 8h.01"/><path d="M6 8h.01"/><path d="M7 16h10"/><path d="M8 12h.01"/><rect width="20" height="16" x="2" y="4" rx="2"/></svg>
</div>
<div class="jr-node-label">Type URL</div>
</div>
<div class="jr-connector c1"><div class="jr-connector-fill"></div><div class="jr-dot"></div></div>
<div class="jr-node n2">
<div class="jr-node-box">
<div class="jr-node-num">2</div>
<div class="jr-node-check">✓</div>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></svg>
</div>
<div class="jr-node-label">DNS</div>
</div>
<div class="jr-connector c2"><div class="jr-connector-fill"></div><div class="jr-dot"></div></div>
<div class="jr-node n3">
<div class="jr-node-box">
<div class="jr-node-num">3</div>
<div class="jr-node-check">✓</div>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 19a1 1 0 0 1-1-1v-2a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2a1 1 0 0 1-1 1z"/><path d="M17 21v-2"/><path d="M19 14V6.5a1 1 0 0 0-7 0v11a1 1 0 0 1-7 0V10"/><path d="M21 21v-2"/><path d="M3 5V3"/><path d="M4 10a2 2 0 0 1-2-2V6a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2a2 2 0 0 1-2 2z"/><path d="M7 5V3"/></svg>
</div>
<div class="jr-node-label">TCP</div>
</div>
<div class="jr-connector c3"><div class="jr-connector-fill"></div><div class="jr-dot"></div></div>
<div class="jr-node n4">
<div class="jr-node-box">
<div class="jr-node-num">4</div>
<div class="jr-node-check">✓</div>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v12"/><path d="m17 8-5-5-5 5"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/></svg>
</div>
<div class="jr-node-label">Request</div>
</div>
<div class="jr-connector c4"><div class="jr-connector-fill"></div><div class="jr-dot"></div></div>
<div class="jr-node n5">
<div class="jr-node-box">
<div class="jr-node-num">5</div>
<div class="jr-node-check">✓</div>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg>
</div>
<div class="jr-node-label">Response</div>
</div>
<div class="jr-connector c5"><div class="jr-connector-fill"></div><div class="jr-dot"></div></div>
<div class="jr-node n6">
<div class="jr-node-box">
<div class="jr-node-num">6</div>
<div class="jr-node-check">✓</div>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z"/><path d="M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12"/><path d="M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17"/></svg>
</div>
<div class="jr-node-label">Assets</div>
</div>
<div class="jr-connector c6"><div class="jr-connector-fill"></div><div class="jr-dot"></div></div>
<div class="jr-node n7">
<div class="jr-node-box">
<div class="jr-node-num">7</div>
<div class="jr-node-check">✓</div>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 10 2 2 4-4"/><rect width="20" height="14" x="2" y="3" rx="2"/><path d="M12 17v4"/><path d="M8 21h8"/></svg>
</div>
<div class="jr-node-label">Render</div>
</div>
</div>
<div class="jr-foundation">
<div class="jr-foundation-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/><path d="m3.3 7 8.7 5 8.7-5"/><path d="M12 22V12"/></svg>
</div>
<div class="jr-foundation-text">
<div class="jr-foundation-title">TCP/IP</div>
<div class="jr-foundation-caption">Carries every packet, underneath it all</div>
</div>
<div class="jr-foundation-track">
<div class="jr-packet a"></div>
<div class="jr-packet b"></div>
<div class="jr-packet c"></div>
<div class="jr-packet d"></div>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-journey')
|| document.querySelector('.infographic-journey');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
</script>β¨οΈ User types a URL.
π DNS resolves the domain to an IP.
π Browser opens a TCP connection.
π¨ Browser sends an HTTP request.
π€ Server sends back an HTTP response.
π Browser requests additional assets (CSS/JS/images).
β¨ Browser renders the final page.
π TCP/IP handles packet transfer underneath it all.