├── .gitignore ├── CNAME ├── images ├── 4ad.svg ├── 4ae.svg ├── 4x.svg ├── 5a.svg ├── 5b.svg ├── 6a.png ├── 7a.jpg ├── Figure 0100 horizontal.svg ├── Figure 0100.svg ├── Figure 0200.svg ├── Figure 0300.svg ├── Figure 0700 Spreadsheet.png ├── Figure 0800.svg ├── Figure 1300.svg ├── Figure 1600.jpg ├── Figure 1900.svg ├── Figure 2000-2Percent-DelaySimulation.png ├── Figure 2000-4Percent-DelaySimulation.png ├── Figure 2100.svg ├── Figure 2200.svg ├── Figure 2300.svg ├── Figure 2400.svg ├── Figure 2500 Figure 4.w.svg ├── Figure 3000 General Costs.png ├── favicon.ico ├── meta.jpg └── pipecat.svg ├── index.html ├── script ├── binary-numbers.css ├── fetch-contributors.py ├── footnotes.js ├── pagination.js └── position-footnotes.js └── styles.css /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | voiceaiandvoiceagents.com -------------------------------------------------------------------------------- /images/4ae.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /images/6a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pipecat-ai/voice-ai-primer-web/534dca469508f1ca12f4403e42b676e20c67f230/images/6a.png -------------------------------------------------------------------------------- /images/7a.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pipecat-ai/voice-ai-primer-web/534dca469508f1ca12f4403e42b676e20c67f230/images/7a.jpg -------------------------------------------------------------------------------- /images/Figure 0100 horizontal.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /images/Figure 0100.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /images/Figure 0300.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /images/Figure 0700 Spreadsheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pipecat-ai/voice-ai-primer-web/534dca469508f1ca12f4403e42b676e20c67f230/images/Figure 0700 Spreadsheet.png -------------------------------------------------------------------------------- /images/Figure 1600.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pipecat-ai/voice-ai-primer-web/534dca469508f1ca12f4403e42b676e20c67f230/images/Figure 1600.jpg -------------------------------------------------------------------------------- /images/Figure 2000-2Percent-DelaySimulation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pipecat-ai/voice-ai-primer-web/534dca469508f1ca12f4403e42b676e20c67f230/images/Figure 2000-2Percent-DelaySimulation.png -------------------------------------------------------------------------------- /images/Figure 2000-4Percent-DelaySimulation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pipecat-ai/voice-ai-primer-web/534dca469508f1ca12f4403e42b676e20c67f230/images/Figure 2000-4Percent-DelaySimulation.png -------------------------------------------------------------------------------- /images/Figure 2200.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /images/Figure 3000 General Costs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pipecat-ai/voice-ai-primer-web/534dca469508f1ca12f4403e42b676e20c67f230/images/Figure 3000 General Costs.png -------------------------------------------------------------------------------- /images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pipecat-ai/voice-ai-primer-web/534dca469508f1ca12f4403e42b676e20c67f230/images/favicon.ico -------------------------------------------------------------------------------- /images/meta.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pipecat-ai/voice-ai-primer-web/534dca469508f1ca12f4403e42b676e20c67f230/images/meta.jpg -------------------------------------------------------------------------------- /images/pipecat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /script/binary-numbers.css: -------------------------------------------------------------------------------- 1 | /* Binary paragraph numbers */ 2 | .binary-paragraph-number { 3 | font-family: monospace; 4 | font-size: 0.8rem; 5 | color: var(--muted); 6 | opacity: 0.3; 7 | text-align: right; 8 | letter-spacing: 1px; 9 | white-space: nowrap; 10 | padding-right: 15px; /* Space between binary text and content */ 11 | width: 60px; /* Fixed width for alignment */ 12 | } 13 | 14 | .binary-container { 15 | position: absolute; 16 | left: -120px; /* Position outside the content area */ 17 | width: auto; /* Allow natural width */ 18 | text-align: right; /* Align text to the right */ 19 | pointer-events: none; 20 | z-index: 5; 21 | transition: opacity 0.2s ease; /* Add smooth transition */ 22 | } 23 | 24 | /* Adjust positioning for smaller screens but not mobile */ 25 | @media (max-width: 1200px) and (min-width: 769px) { 26 | .binary-container { 27 | left: -60px; /* Slightly closer on smaller screens */ 28 | } 29 | } 30 | 31 | /* Media query for mobile */ 32 | @media (max-width: 768px) { 33 | .binary-container { 34 | display: none; /* Hide binary numbers on mobile */ 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /script/fetch-contributors.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | def fetch_contributors_by_commits(owner, repo, include_anonymous=False): 5 | """ 6 | Fetches contributors to the specified GitHub repository, sorted by the number of commits in descending order. 7 | 8 | Parameters: 9 | - owner (str): The username or organization that owns the repository. 10 | - repo (str): The name of the repository. 11 | - include_anonymous (bool): Whether to include anonymous contributors. 12 | 13 | Returns: 14 | - str: A comma-separated list of contributor usernames. 15 | """ 16 | contributors = [] 17 | page = 1 18 | per_page = 100 # Maximum allowed per GitHub API 19 | 20 | while True: 21 | url = f"https://api.github.com/repos/{owner}/{repo}/contributors" 22 | params = {"per_page": per_page, "page": page} 23 | if include_anonymous: 24 | params["anon"] = "true" 25 | response = requests.get(url, params=params) 26 | 27 | if response.status_code != 200: 28 | raise Exception(f"GitHub API error: {response.status_code} - {response.text}") 29 | 30 | data = response.json() 31 | if not data: 32 | break 33 | 34 | # Extract usernames 35 | for user in data: 36 | if "login" in user: 37 | contributors.append(user["login"]) 38 | elif include_anonymous and "name" in user: 39 | contributors.append(user["name"]) 40 | page += 1 41 | 42 | return ", ".join(contributors) 43 | 44 | 45 | # Example usage: 46 | if __name__ == "__main__": 47 | owner = "pipecat-ai" 48 | repo = "pipecat" 49 | usernames = fetch_contributors_by_commits(owner, repo) 50 | print(usernames) 51 | -------------------------------------------------------------------------------- /script/footnotes.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', () => { 2 | // Get all superscript elements 3 | const superscripts = document.querySelectorAll('sup'); 4 | 5 | superscripts.forEach(sup => { 6 | sup.style.cursor = 'pointer'; 7 | 8 | sup.addEventListener('click', (e) => { 9 | e.preventDefault(); 10 | 11 | // Get the footnote number from the superscript text 12 | const footnoteNum = sup.textContent.replace(/[\[\]]/g, ''); 13 | 14 | // Find the footnote by matching the text content 15 | const footnotes = document.querySelectorAll('.footnote p'); 16 | const footnote = Array.from(footnotes).find(p => 17 | p.textContent.startsWith(`[${footnoteNum}]`) 18 | )?.closest('.footnote'); 19 | 20 | if (footnote) { 21 | footnote.scrollIntoView({ 22 | behavior: 'smooth', 23 | block: 'center' 24 | }); 25 | 26 | // Add a brief highlight effect 27 | footnote.style.transition = 'background-color 0.8s'; 28 | footnote.style.backgroundColor = 'rgba(0,0,0,0.1)'; 29 | footnote.style.padding = '0.1rem'; 30 | footnote.style.paddingLeft = '0.2rem'; 31 | footnote.style.paddingRight = '0.2rem'; 32 | footnote.style.borderRadius = '0.1rem'; 33 | setTimeout(() => { 34 | footnote.style.backgroundColor = 'transparent'; 35 | }, 1000); 36 | } 37 | }); 38 | }); 39 | }); -------------------------------------------------------------------------------- /script/pagination.js: -------------------------------------------------------------------------------- 1 | function setupBinaryParagraphNumbers() { 2 | // Remove any existing page indicator 3 | const existingIndicator = document.getElementById('page-indicator'); 4 | if (existingIndicator) { 5 | existingIndicator.remove(); 6 | } 7 | 8 | // Clear any existing binary numbers first 9 | document.querySelectorAll('.binary-container').forEach(el => el.remove()); 10 | 11 | // Get all paragraphs in the chapter content 12 | const paragraphs = document.querySelectorAll('.chapter-content p'); 13 | 14 | // Function to convert decimal to 12-bit binary with "/" for 0 and "\" for 1 15 | function decimalToBinary(decimal) { 16 | let binary = decimal.toString(2).padStart(12, '0'); 17 | return binary.replace(/0/g, '/').replace(/1/g, '\\'); 18 | } 19 | 20 | // Process each paragraph 21 | paragraphs.forEach((paragraph, index) => { 22 | // Get the corresponding chapter row 23 | const chapterRow = paragraph.closest('.chapter-row'); 24 | if (!chapterRow) return; 25 | 26 | // Create binary number element 27 | const binaryNumber = document.createElement('div'); 28 | binaryNumber.className = 'binary-paragraph-number'; 29 | binaryNumber.textContent = decimalToBinary(index + 1); 30 | 31 | // Create a container for the binary number that will be positioned 32 | const binaryContainer = document.createElement('div'); 33 | binaryContainer.className = 'binary-container'; 34 | binaryContainer.style.opacity = '0'; // Start hidden 35 | binaryContainer.appendChild(binaryNumber); 36 | 37 | // Add the binary container to the chapter row instead of chapter notes 38 | chapterRow.appendChild(binaryContainer); 39 | 40 | // Set initial position 41 | updateBinaryPosition(paragraph, binaryContainer); 42 | 43 | // Add a data attribute to link the paragraph and its binary number 44 | paragraph.dataset.binaryIndex = index; 45 | binaryContainer.dataset.binaryIndex = index; 46 | 47 | // Add hover event listeners 48 | paragraph.addEventListener('mouseenter', () => { 49 | binaryContainer.style.opacity = '1'; 50 | }); 51 | 52 | paragraph.addEventListener('mouseleave', () => { 53 | binaryContainer.style.opacity = '0'; 54 | }); 55 | }); 56 | } 57 | 58 | // Function to update the position of a binary number container 59 | function updateBinaryPosition(paragraph, binaryContainer) { 60 | const chapterRow = paragraph.closest('.chapter-row'); 61 | if (!chapterRow) return; 62 | 63 | const paragraphRect = paragraph.getBoundingClientRect(); 64 | const rowRect = chapterRow.getBoundingClientRect(); 65 | 66 | // Calculate the top position relative to the chapter row 67 | // Add a small offset to align with the first line of text 68 | const topPosition = paragraphRect.top - rowRect.top + 3; 69 | 70 | // Set the top position only - left is handled by CSS 71 | binaryContainer.style.top = `${topPosition}px`; 72 | } 73 | 74 | // Update all binary positions on scroll 75 | function updateAllBinaryPositions() { 76 | const paragraphs = document.querySelectorAll('.chapter-content p'); 77 | 78 | paragraphs.forEach(paragraph => { 79 | const index = paragraph.dataset.binaryIndex; 80 | if (index !== undefined) { 81 | const binaryContainer = document.querySelector(`.binary-container[data-binary-index="${index}"]`); 82 | if (binaryContainer) { 83 | updateBinaryPosition(paragraph, binaryContainer); 84 | } 85 | } 86 | }); 87 | } 88 | 89 | // Run on load and whenever the window is resized or scrolled 90 | document.addEventListener('DOMContentLoaded', () => { 91 | setupBinaryParagraphNumbers(); 92 | // Initial update after a short delay to ensure everything is rendered 93 | setTimeout(updateAllBinaryPositions, 100); 94 | }); 95 | window.addEventListener('resize', () => { 96 | setupBinaryParagraphNumbers(); 97 | // Update after a short delay to ensure everything is rendered 98 | setTimeout(updateAllBinaryPositions, 100); 99 | }); 100 | window.addEventListener('scroll', updateAllBinaryPositions); -------------------------------------------------------------------------------- /script/position-footnotes.js: -------------------------------------------------------------------------------- 1 | function positionFootnotes() { 2 | const anchors = document.querySelectorAll('[data-footnote-ref]'); 3 | anchors.forEach(anchor => { 4 | const footnoteId = anchor.getAttribute('data-footnote-ref'); 5 | const footnote = document.querySelector(`#${footnoteId}`); 6 | if (footnote) { 7 | const anchorRect = anchor.getBoundingClientRect(); 8 | const containerRect = document.querySelector('.chapter-row').getBoundingClientRect(); 9 | const offsetTop = anchorRect.top - containerRect.top; 10 | footnote.style.top = offsetTop + 'px'; 11 | } 12 | }); 13 | } 14 | window.addEventListener('load', positionFootnotes); 15 | window.addEventListener('resize', positionFootnotes); -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --font-dm-sans: "DM Sans", sans-serif; 3 | --font-source-serif: "source-serif-4-subhead", serif; 4 | --foreground: #000000; 5 | --background: #f4f5f6; 6 | --border: #000000; 7 | --muted: #6b7280; 8 | } 9 | 10 | * { 11 | box-sizing: border-box; 12 | padding: 0; 13 | margin: 0; 14 | } 15 | 16 | html, 17 | body { 18 | max-width: 140vw; 19 | overflow-x: hidden; 20 | font-family: var(--font-source-serif); 21 | background-color: var(--background); 22 | color: var(--foreground); 23 | line-height: 1.6; 24 | font-size: 1.1rem; 25 | } 26 | 27 | a { 28 | color: inherit; 29 | text-decoration: none; 30 | border-bottom: 1px solid var(--foreground); 31 | } 32 | 33 | a:hover { 34 | opacity: 0.8; 35 | } 36 | 37 | h1, 38 | h2, 39 | h3 { 40 | font-weight: 700; 41 | line-height: 1.2; 42 | margin-top: 1rem; 43 | margin-bottom: 0.75rem; 44 | } 45 | 46 | h1 { 47 | font-size: 2rem; 48 | font-weight: 600; 49 | } 50 | h2 { 51 | font-size: 1.5rem; 52 | font-weight: 600; 53 | } 54 | 55 | h3 { 56 | font-size: 1.25rem; 57 | font-weight: 600; 58 | } 59 | 60 | .title { 61 | font-size: 4rem; 62 | font-weight: 700; 63 | margin-bottom: 0.5rem; 64 | } 65 | 66 | .subtitle { 67 | font-size: 2.2rem; 68 | font-weight: 400; 69 | margin-top: 0.1rem; 70 | } 71 | 72 | .header { 73 | margin-bottom: 0.5rem; 74 | } 75 | 76 | .table-of-contents-title { 77 | margin-top: 0.5rem; 78 | margin-bottom: 1.2rem; 79 | } 80 | 81 | code { 82 | font-family: monospace; 83 | background-color: rgba(0, 0, 0, 0.05); 84 | padding: 0.2em 0.4em; 85 | border-radius: 3px; 86 | font-size: 0.9em; 87 | } 88 | 89 | /* Container */ 90 | .container { 91 | max-width: 1300px; 92 | margin: 0 auto; 93 | padding: 2rem; 94 | } 95 | 96 | /* Header */ 97 | header { 98 | margin-bottom: 2rem; 99 | padding-bottom: 1rem; 100 | /* border-bottom: 1px solid var(--border); */ 101 | } 102 | 103 | /* Table of Contents */ 104 | #table-of-contents { 105 | margin-bottom: 3rem; 106 | padding: 1.5rem; 107 | border: 1px solid var(--border); 108 | background-color: rgba(0, 0, 0, 0.02); 109 | } 110 | 111 | #table-of-contents ol { 112 | margin-left: 1.5rem; 113 | } 114 | 115 | #table-of-contents li { 116 | margin-bottom: 0.5rem; 117 | } 118 | 119 | /* New table of contents styling for better hierarchy */ 120 | #table-of-contents > ol > li > a { 121 | font-size: 1.1rem; 122 | font-weight: 500; 123 | } 124 | 125 | #table-of-contents > ol > li > ul > li > a { 126 | font-size: 0.95rem; 127 | font-weight: 400; 128 | } 129 | 130 | #table-of-contents > ol > li > ul > li > ul > li > a { 131 | font-size: 0.9rem; 132 | font-weight: 400; 133 | color: var(--muted); 134 | } 135 | 136 | #table-of-contents ul { 137 | margin-top: 0.3rem; 138 | margin-bottom: 0.5rem; 139 | } 140 | 141 | /* Content */ 142 | section { 143 | margin-bottom: 0; 144 | } 145 | 146 | p { 147 | margin-bottom: 1rem; 148 | } 149 | 150 | ul, 151 | ol { 152 | margin-left: 1.5rem; 153 | margin-bottom: 1.5rem; 154 | } 155 | 156 | li { 157 | margin-bottom: 0.5rem; 158 | } 159 | 160 | sup { 161 | font-size: 0.75em; 162 | vertical-align: super; 163 | line-height: 0; 164 | cursor: pointer; 165 | } 166 | 167 | sup:hover { 168 | text-decoration: underline; 169 | } 170 | 171 | .arrow-list { 172 | list-style: none; 173 | margin-left: 0; 174 | } 175 | 176 | .arrow-list li { 177 | position: relative; 178 | padding-left: 1.5rem; 179 | } 180 | 181 | .arrow-list li::before { 182 | content: "→"; 183 | position: absolute; 184 | left: 0; 185 | color: var(--foreground); 186 | } 187 | 188 | .footnote .arrow-list li { 189 | margin: 0; 190 | } 191 | 192 | ol.list-decimal { 193 | list-style-type: decimal; 194 | } 195 | 196 | ol.list-decimal li { 197 | margin-bottom: 0.75rem; 198 | } 199 | 200 | /* Grid */ 201 | .grid { 202 | display: grid; 203 | grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); 204 | gap: 2rem; 205 | margin-bottom: 2rem; 206 | } 207 | 208 | /* Footer */ 209 | footer { 210 | margin-top: 4rem; 211 | padding-top: 1.5rem; 212 | border-top: 1px solid var(--border); 213 | font-size: 0.9rem; 214 | color: var(--muted); 215 | } 216 | 217 | footer p { 218 | margin-bottom: 0.5rem; 219 | } 220 | 221 | .github-link { 222 | margin-top: 1rem; 223 | } 224 | 225 | .github-link a { 226 | color: var(--foreground); 227 | font-weight: 500; 228 | border-bottom: 1px solid var(--foreground); 229 | transition: opacity 0.2s ease; 230 | } 231 | 232 | .github-link a:hover { 233 | opacity: 0.7; 234 | } 235 | 236 | /* Chapter Row Layout */ 237 | .chunk-row { 238 | display: flex; 239 | position: relative; 240 | gap: 3rem; 241 | justify-content: space-between; 242 | } 243 | 244 | .chunk-content { 245 | width: calc(75% - 1.5rem); 246 | flex: 0 0 auto; 247 | } 248 | 249 | .chunk-notes { 250 | width: calc(25% - 1.5rem); 251 | flex: 0 0 auto; 252 | font-size: 0.9rem; 253 | color: var(--muted); 254 | display: flex; 255 | flex-direction: column; 256 | position: relative; 257 | } 258 | 259 | .chapter-image { 260 | margin-top: 3rem; 261 | } 262 | 263 | /* Footnotes aligned at the bottom of chapter content */ 264 | .chunk-footnotes { 265 | position: static; 266 | padding-left: 1rem; 267 | /* border-left: 1px solid var(--border); */ 268 | margin-top: auto; 269 | } 270 | 271 | .chunk-footnotes h3 { 272 | font-size: 1rem; 273 | margin-top: 0; 274 | margin-bottom: 1rem; 275 | color: var(--foreground); 276 | opacity: 0.7; 277 | } 278 | 279 | .footnote { 280 | margin-bottom: 1.5rem; 281 | } 282 | 283 | /* Responsive adjustments */ 284 | @media (max-width: 768px) { 285 | .chunk-row { 286 | flex-direction: column; 287 | gap: 1rem; 288 | } 289 | 290 | .chunk-content { 291 | width: 100%; 292 | flex: 1 1 auto; 293 | } 294 | 295 | .chunk-notes { 296 | width: 100%; 297 | flex: 1 1 auto; 298 | justify-content: flex-start; 299 | } 300 | 301 | .chunk-footnotes { 302 | border-left: none; 303 | border-top: 1px solid var(--border); 304 | padding-left: 0; 305 | padding-top: 1rem; 306 | margin-top: 1rem; 307 | } 308 | 309 | .image-hide-narrow { 310 | display: none; 311 | } 312 | 313 | } 314 | 315 | .chunk-image { 316 | margin-top: 40px; 317 | width: 100%; 318 | display: flex; 319 | flex-direction: column; 320 | align-items: center; 321 | } 322 | 323 | /* Class for images that should appear within the content flow */ 324 | .chunk-image-inline { 325 | display: flex; 326 | margin: 2rem auto; 327 | } 328 | 329 | 330 | .image-caption { 331 | margin-top: 0.75rem; 332 | margin-bottom: 1.5rem; 333 | font-size: 0.9rem; 334 | color: var(--muted); 335 | text-align: center; 336 | font-style: italic; 337 | font-weight: 400; 338 | max-width: 90%; 339 | margin-left: auto; 340 | margin-right: auto; 341 | } 342 | 343 | /* Binary paragraph numbers */ 344 | .binary-paragraph-number { 345 | font-family: monospace; 346 | font-size: 0.9rem; 347 | color: var(--muted); 348 | opacity: 0.6; 349 | text-align: left; 350 | letter-spacing: 1px; 351 | } 352 | 353 | .binary-container { 354 | position: absolute; 355 | right: 0; 356 | width: 100%; 357 | text-align: left; 358 | pointer-events: none; 359 | } 360 | 361 | /* Remove page indicator styles since we're not using it anymore */ 362 | #page-indicator { 363 | display: none; 364 | } 365 | 366 | pre { 367 | background-color: #bdbdbd; 368 | border: 1px solid #ddd; 369 | border-radius: 4px; 370 | padding: 16px; 371 | overflow: auto; 372 | margin: 16px 0; 373 | max-width: 100%; 374 | box-sizing: border-box; 375 | } 376 | 377 | pre code { 378 | background-color: transparent; 379 | padding: 0; 380 | border: none; 381 | font-family: "DM Mono", monospace, Consolas, Monaco, "Andale Mono", 382 | "Ubuntu Mono"; 383 | font-size: 0.7em; 384 | line-height: 1.5; 385 | white-space: pre-wrap; 386 | word-break: break-word; 387 | display: block; 388 | width: 100%; 389 | } 390 | 391 | pre code .pre-highlight { 392 | background-color: #f2f2f2; 393 | } 394 | 395 | .nobreak { 396 | white-space: nowrap; 397 | word-break: keep-all; 398 | } 399 | 400 | 401 | .data-table { 402 | width: 100%; 403 | border-collapse: collapse; 404 | margin-top: 1em; 405 | margin-bottom: 1em; 406 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 407 | font-size: 75%; 408 | font-family: "DM Mono", monospace; 409 | } 410 | 411 | .data-table th { 412 | background-color: #f2f2f2; 413 | color: #333; 414 | font-weight: 600; 415 | text-align: left; 416 | padding: 0.75em 0.5em; 417 | border: 1px solid #ddd; 418 | } 419 | 420 | .data-table td { 421 | padding: 0.25em 0.5em; 422 | border: 1px solid #ddd; 423 | vertical-align: middle; 424 | } 425 | 426 | .data-table tr:nth-child(even) { 427 | background-color: #f8f8f8; 428 | } 429 | 430 | .data-table tr:hover { 431 | background-color: #f0f0f0; 432 | } 433 | 434 | .latency-breakdown { 435 | font-size: 60%; 436 | } 437 | 438 | .latency-breakdown th:last-child { 439 | text-align: right; 440 | } 441 | 442 | .latency-breakdown tr { 443 | background-color: #f8f8f8 444 | } 445 | 446 | .latency-breakdown .network-row { 447 | background-color: #f2f2f2; 448 | } 449 | 450 | .latency-breakdown td:last-child { 451 | text-align: right; 452 | font-family: "DM Mono", monospace; 453 | } 454 | 455 | .table-caption { 456 | font-size: 0.85em; 457 | margin-top: 8px; 458 | margin-bottom: 16px; 459 | font-style: italic; 460 | color: #555; 461 | } 462 | 463 | /* Specific styling for cost and latency tables */ 464 | 465 | .model-comparison td:first-child { 466 | font-weight: 500; 467 | } 468 | 469 | .model-comparison td:not(:first-child) { 470 | text-align: center; 471 | } 472 | 473 | .total-row { 474 | border-top: 2px solid #ccc; 475 | background-color: #f2f2f2 !important; 476 | } 477 | 478 | .total-row td { 479 | font-weight: 600; 480 | } 481 | 482 | .footnote-image { 483 | display: block; 484 | max-width: 100%; 485 | height: auto; 486 | margin: 12px 0; 487 | border-radius: 4px; 488 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 489 | } 490 | 491 | .footnote .image-caption { 492 | font-size: 0.85em; 493 | margin-top: 4px; 494 | margin-bottom: 12px; 495 | font-style: italic; 496 | color: #555; 497 | } 498 | 499 | .language-python { 500 | font-family: "DM Mono", monospace; 501 | background-color: #343433; 502 | color: #f8f8f8; 503 | padding: 0; 504 | } 505 | 506 | .subtle-code { 507 | background-color: #f4f5f6; 508 | color: #1e773e; 509 | } 510 | --------------------------------------------------------------------------------