Repository navigation
Expand file tree
/
Copy pathindex.html
More file actions
179 lines (172 loc) · 17.6 KB
/
Copy pathindex.html
File metadata and controls
179 lines (172 loc) · 17.6 KB
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
<!doctype html>
<html lang="en" data-theme="night">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Carson Mitchell, Math Tutor</title>
<meta name="description" content="Carson Mitchell: mathematics tutor, and author of a free textbook for the GRE mathematics test.">
<meta name="color-scheme" content="dark light">
<script>
// Apply the saved day/night choice before the page paints, so it never flashes the wrong mode.
try { if (localStorage.getItem('theme') === 'day') document.documentElement.dataset.theme = 'day'; } catch (e) {}
document.documentElement.classList.add('js');
</script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=Newsreader:ital,opsz,wght@0,6..72,300..600;1,6..72,300..600&family=IBM+Plex+Sans:wght@400;500&display=swap">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.css">
<link rel="stylesheet" href="styles.css?v=2026-10-27-1">
<script type="importmap">{ "imports": { "three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js", "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/" } }</script>
<link rel="icon" href="favicon.ico" sizes="32x32">
<link rel="icon" href="favicon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="apple-touch-icon.png">
</head>
<body>
<a class="skip" href="#top">Skip to Content</a>
<header class="wrap top">
<a class="mark" href="./">Carson Mitchell</a>
<nav aria-label="Pages">
<a href="./" aria-current="page">Home</a>
<a href="about.html">About Me</a>
<a href="tutoring.html">Tutoring</a>
<a href="projects.html">Projects</a>
<a href="contact.html">Contact</a>
</nav>
<button class="mode" id="mode" type="button" role="switch" aria-checked="true" aria-label="Dark mode">
<svg class="ico sun" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="3" fill="currentColor"/><g stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 1.2v1.6M8 13.2v1.6M1.2 8h1.6M13.2 8h1.6M3.2 3.2l1.1 1.1M11.7 11.7l1.1 1.1M3.2 12.8l1.1-1.1M11.7 4.3l1.1-1.1"/></g></svg>
<svg class="ico moon" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M13.6 10.4A6 6 0 0 1 5.6 2.4a6 6 0 1 0 8 8z"/></svg>
<span class="knob"><svg class="ico sun" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="3" fill="currentColor"/><g stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 1.2v1.6M8 13.2v1.6M1.2 8h1.6M13.2 8h1.6M3.2 3.2l1.1 1.1M11.7 11.7l1.1 1.1M3.2 12.8l1.1-1.1M11.7 4.3l1.1-1.1"/></g></svg><svg class="ico moon" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M13.6 10.4A6 6 0 0 1 5.6 2.4a6 6 0 1 0 8 8z"/></svg></span>
</button>
</header>
<main id="top">
<section class="wrap hero" aria-labelledby="hero-title">
<div class="hg">
<div>
<img class="hero-pic" src="images/carson-thumb.jpg" width="256" height="256" alt="Carson Mitchell" decoding="async">
<h1 id="hero-title">Carson Mitchell</h1>
<p class="s">Mathematics tutor, and author of a free textbook for the GRE mathematics test.</p>
<div class="actions">
<a class="pb" href="contact.html">Get In Touch</a>
<a class="ql" href="pdf/GRE-Textbook.pdf">GRE textbook (PDF)</a>
</div>
</div>
<div class="fig">
<!-- Spinning circles (a Fourier series) draw the initials C and M as one curve.
Approved reference: design/mockups/hero-fourier.html. The two paths below hold the
true letters, so they still show if JavaScript is off; the script redraws them. -->
<svg viewBox="0 0 300 160" role="img" aria-label="Spinning circles drawing the initials C M as one curve">
<path id="gh" fill="none" stroke="var(--ghost)" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" d="M125.9 46.1A48 48 0 1 0 125.9 113.9M168 125L168 34L208 92L248 34L248 125"/>
<g id="fc"></g>
<path id="ft" fill="none" stroke="var(--ac)" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" d="M125.9 46.1A48 48 0 1 0 125.9 113.9M168 125L168 34L208 92L248 34L248 125"/>
<g id="pens"></g>
</svg>
<div class="ctl"><label for="fk"><span id="fl" aria-hidden="true">N</span> circles each way</label><span class="rng"><span class="fill"></span><input type="range" id="fk" min="1" max="100" value="30" step="1" aria-label="Number of circles each way"></span></div>
<div id="rd"></div>
<div id="math"><div class="eq" id="meq"></div><div class="eq2" id="meq2"></div><p class="cap" id="mcap"></p></div>
<noscript><p class="nojs">The initials C and M, drawn as one curve.</p></noscript>
</div>
</div>
</section>
<section class="wrap doors" aria-labelledby="doors-title">
<h2 id="doors-title" class="vh">Pages</h2>
<!-- The Study (study.js): the four doors as labels beside Carson's things in a small 3D room. Hidden unless this browser
can draw 3D; then the four rows below are hidden instead, and they come back if the room can't load. -->
<div class="d3s">
<p class="vh">An illustrated room of Carson's things: a bookshelf for About Me, a desk under a corkboard for Projects, a chalkboard of logic for Tutoring, and a letter-writing table for Contact. His dog Dexter sleeps on a bed in the middle.</p>
<div class="view"></div>
<svg class="d3l" aria-hidden="true"></svg>
<div class="cards">
<a class="c3" data-k="about" href="about.html"><span class="c3-t">About Me<span class="c3-go" aria-hidden="true">→</span></span><span class="c3-d"><span><span>Who I am and some math I find beautiful and interesting.</span></span></span></a>
<a class="c3" data-k="projects" href="projects.html"><span class="c3-t">Projects<span class="c3-go" aria-hidden="true">→</span></span><span class="c3-d"><span><span>Personal writings, tools, widgets, and more.</span></span></span></a>
<a class="c3" data-k="tutoring" href="tutoring.html"><span class="c3-t">Tutoring<span class="c3-go" aria-hidden="true">→</span></span><span class="c3-d"><span><span>Math tutoring for all levels, online or in person in South Orange County.</span></span></span></a>
<a class="c3" data-k="contact" href="contact.html"><span class="c3-t">Contact<span class="c3-go" aria-hidden="true">→</span></span><span class="c3-d"><span><span>Email, social media, message form, and booking links.</span></span></span></a>
</div>
<span class="dx" aria-hidden="true">Dexter</span>
<span class="bh" aria-hidden="true"></span>
<span class="dx tp" aria-hidden="true"></span>
</div>
<a class="door" href="about.html"><span class="door-t">About Me</span><span class="door-d">Who I am and some math I find beautiful and interesting.</span><img class="door-x dd-shape" src="images/permutahedron-still.jpg" width="545" height="546" alt="" loading="lazy" decoding="async"><span class="door-go" aria-hidden="true">→</span></a>
<a class="door" href="tutoring.html"><span class="door-t">Tutoring</span><span class="door-d">Math tutoring for all levels, online or in person in South Orange County.</span><span class="door-x dd-rates"><b>$60</b> online · <b>$90</b> in person</span><span class="door-go" aria-hidden="true">→</span></a>
<a class="door" href="projects.html"><span class="door-t">Projects</span><span class="door-d">Personal writings, tools, widgets, and more.</span><span class="door-x"><svg class="dd-graph dd-nd" viewBox="0 0 150 88" aria-hidden="true"><defs><marker id="dd-tip-nd" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="6" markerHeight="6" orient="auto"><path d="M0 0L8 4L0 8Z"/></marker></defs><path class="e" d="M29 20H52" marker-end="url(#dd-tip-nd)"/><path class="e" d="M80 25L118 41" marker-end="url(#dd-tip-nd)"/><path class="e" d="M29 68H52" marker-end="url(#dd-tip-nd)"/><path class="e" d="M80 63L118 47" marker-end="url(#dd-tip-nd)"/><path class="e" d="M25 29L56 59"/><circle class="dot" cx="57" cy="60" r="2.6"/><circle class="on" cx="18" cy="20" r="11"/><text class="on" x="18" y="20">c</text><circle class="on" cx="68" cy="20" r="11"/><text class="on" x="68" y="20">d</text><circle class="on" cx="130" cy="44" r="11"/><text class="on" x="130" y="44">e</text><circle class="on" cx="18" cy="68" r="11"/><text class="on" x="18" y="68">a</text><circle cx="68" cy="68" r="11"/><text x="68" y="68">b</text></svg></span><span class="door-go" aria-hidden="true">→</span></a>
<a class="door" href="contact.html"><span class="door-t">Contact</span><span class="door-d">Email, social media, message form, and booking links.</span><span class="door-x"><svg class="dd-graph" viewBox="0 0 150 70" aria-hidden="true"><defs><marker id="dd-tip" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="6" markerHeight="6" orient="auto"><path d="M0 0L8 4L0 8Z"/></marker></defs><path d="M31 23Q75 -3 119 23" marker-end="url(#dd-tip)"/><path d="M35 28Q75 16 115 28" marker-end="url(#dd-tip)"/><path d="M36 35H114" marker-end="url(#dd-tip)"/><path d="M35 42Q75 54 115 42" marker-end="url(#dd-tip)"/><path d="M31 47Q75 73 119 47" marker-end="url(#dd-tip)"/><circle cx="20" cy="35" r="15"/><text x="20" y="35">you</text><circle class="me" cx="130" cy="35" r="15"/><text class="me" x="130" y="35">CM</text></svg></span><span class="door-go" aria-hidden="true">→</span></a>
</section>
<script>
/* The Study: if this browser can draw 3D, lay out the room's stage now (before it paints), and load the room only once the
page has finished loading and the room is within 150 px of scrolling into view, so the hero stays as fast as before.
If the room doesn't come up within 10 s, or Three.js fails to download, the four door rows come back. */
(() => {
const s = document.querySelector('.d3s'); let ok = false;
try { const c = document.createElement('canvas'); ok = !!(c.getContext('webgl2') || c.getContext('webgl')); } catch (e) {}
if (!ok || !('IntersectionObserver' in window)) return;
s.classList.add('d3on');
const giveUp = () => { if (!s.classList.contains('ready')) { s.classList.remove('d3on'); s.dataset.gaveUp = '1'; s.querySelectorAll('.c3').forEach(c => c.style.transform = ''); } };
const start = () => new IntersectionObserver((es, io) => { if (!es.some(e => e.isIntersecting)) return; io.disconnect(); setTimeout(giveUp, 10000); import('./study.js?v=2026-10-27-1').catch(giveUp); }, { rootMargin: '0px 0px 150px 0px' }).observe(s);
if (document.readyState === 'complete') start(); else addEventListener('load', start, { once: true });
})();
</script>
</main>
<footer class="wrap foot">
<p>This website is a proud product of extensive vibe-coding with the help of <a href="https://claude.com/claude-code">Claude Code</a>.</p>
</footer>
<script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.js" defer></script>
<script src="site.js?v=2026-10-26-2"></script>
<script>
/* The About Me door's little 3D shape has a day and a night picture */
(() => { const i = document.querySelector('.dd-shape'), f = () => { const src = root.dataset.theme === 'day' ? 'images/permutahedron-still-day.jpg' : 'images/permutahedron-still.jpg'; if (i.getAttribute('src') !== src) i.src = src; }; f(); document.addEventListener('themechange', f); })();
</script>
<script>
/* ── Hero centerpiece: circles that write C M. Logic copied from design/mockups/hero-fourier.html. ── */
// The true path f: the C (arc), pen up to the M, the M (polyline), pen up back to the start.
const segs=[];const arc=[];for(let i=0;i<=60;i++){const t=Math.PI/4+i/60*1.5*Math.PI;arc.push([92+48*Math.cos(t),80-48*Math.sin(t)])}
const Mv=[[168,125],[168,34],[208,92],[248,34],[248,125]];
function addPoly(pts,pen){for(let i=0;i<pts.length-1;i++)segs.push({a:pts[i],b:pts[i+1],pen})}
addPoly(arc,true);addPoly([arc[arc.length-1],Mv[0]],false);addPoly(Mv,true);addPoly([Mv[4],arc[0]],false);
// 720 points evenly spaced by arc length along f.
const L=segs.map(s=>Math.hypot(s.b[0]-s.a[0],s.b[1]-s.a[1])),TOT=L.reduce((x,y)=>x+y,0),MS=720;
const F=[],PEN=[];let si=0,acc=0;for(let m=0;m<MS;m++){const d=m/MS*TOT;while(acc+L[si]<d){acc+=L[si];si++}const s=segs[si],f=(d-acc)/L[si];F.push([s.a[0]+(s.b[0]-s.a[0])*f,s.a[1]+(s.b[1]-s.a[1])*f]);PEN.push(s.pen)}
// Coefficients c_n for n = -100..100 (discrete Fourier transform of the 720 points).
const NMAX=100,C={};for(let n=-NMAX;n<=NMAX;n++){let re=0,im=0;for(let m=0;m<MS;m++){const a=-2*Math.PI*n*m/MS;re+=F[m][0]*Math.cos(a)-F[m][1]*Math.sin(a);im+=F[m][0]*Math.sin(a)+F[m][1]*Math.cos(a)}C[n]={re:re/MS,im:im/MS,r:Math.hypot(re,im)/MS}}
// Partial sum z(t) at s = t / 2π, terms ordered 0, 1, -1, 2, -2, ...
function z(N,s,circ){let x=0,y=0,out=[];for(let j=0;j<=2*N;j++){const n=j===0?0:(j%2?(j+1)/2:-j/2),c=C[n],a=2*Math.PI*n*s;if(circ&&j>0)out.push([x,y,c.r]);x+=c.re*Math.cos(a)-c.im*Math.sin(a);y+=c.re*Math.sin(a)+c.im*Math.cos(a)}return{x,y,out}}
let ghost='';F.forEach((p,m)=>{const prev=m>0&&PEN[m-1];if(PEN[m])ghost+=(prev?'L':'M')+p[0].toFixed(1)+' '+p[1].toFixed(1)});document.getElementById('gh').setAttribute('d',ghost);
const fk=document.getElementById('fk');let N=30,raf=null;const DUR=5200,fc=document.getElementById('fc'),ft=document.getElementById('ft'),math=document.getElementById('math');
const T=(s,d)=>window.katex?katex.renderToString(s,{displayMode:!!d,throwOnError:false}):s;
// Mean distance between z_N and f on the letters, as a percent of the letter height (92).
function miss(){let e=0,k=0;for(let m=0;m<MS;m+=2){if(!PEN[m])continue;const q=z(N,m/MS,false);e+=Math.hypot(q.x-F[m][0],q.y-F[m][1]);k++}return 100*e/k/92}
function readout(){document.getElementById('fl').innerHTML=T('N = '+N);const pc=miss(),ps=pc<0.1?pc.toFixed(2):pc.toFixed(1);document.getElementById('rd').innerHTML='<b>'+2*N+' circles</b> · they miss the true letters by <b>'+ps+'%</b> of their height, on average. More circles, sharper corners.';
fk.setAttribute('aria-valuetext',2*N+' circles, missing the letters by '+ps+' percent');fk.parentNode.style.setProperty('--p',(N-1)/99);eqs()}
// The equations are typeset from the start (hidden), so they hold their space and nothing jumps when they appear.
function eqs(){document.getElementById('meq').innerHTML=T('z(t) \\;=\\; \\sum_{n=-'+N+'}^{'+N+'} c_n\\, e^{int}',1)}
function showMath(){
document.getElementById('meq2').innerHTML=T('c_n \\;=\\; \\frac{1}{2\\pi}\\int_0^{2\\pi} f(t)\\, e^{-int}\\, dt',1);
document.getElementById('mcap').innerHTML='One curve '+T('f')+' writes both letters, lifting the pen in between. Each '+T('c_n')+' is '+T('f')+' unwound '+T('n')+' times and averaged, and each term is a circle of radius '+T('\\lvert c_n\\rvert')+'.';math.classList.add('on')}
// z_N at the 721 sample points, as path pieces, computed once per drawing so each frame only
// joins the pieces drawn so far (recomputing them every frame made phones stutter at large N).
let pieces=[];function curve(){pieces=[];for(let i=0;i<=MS;i++){const m=i%MS;if(!PEN[m]){pieces.push('');continue}const q=z(N,i/MS,false);pieces.push((i>0&&PEN[(i-1)%MS]?'L':'M')+q.x.toFixed(1)+' '+q.y.toFixed(1))}}
// The finished drawing of z_N in one go (used when the visitor prefers reduced motion).
function still(){cancelAnimationFrame(raf);curve();ft.setAttribute('d',pieces.join(''));circles([]);pen.style.display='none';showMath()}
// The circles and the pen dot are made once and moved every frame (rebuilding them each frame made phones work hard).
const SVGNS='http://www.w3.org/2000/svg',pool=[];
function circles(list){while(pool.length<list.length){const c=document.createElementNS(SVGNS,'circle');c.setAttribute('fill','none');c.setAttribute('stroke','var(--card2)');c.setAttribute('stroke-width','1');fc.appendChild(c);pool.push(c)}
pool.forEach((c,i)=>{const o=list[i];if(!o){c.style.display='none';return}c.style.display='';c.setAttribute('cx',o[0].toFixed(1));c.setAttribute('cy',o[1].toFixed(1));c.setAttribute('r',o[2].toFixed(1))})}
const pen=document.createElementNS(SVGNS,'circle');pen.setAttribute('r','3.5');pen.setAttribute('stroke','var(--ac)');pen.setAttribute('stroke-width','1.5');pen.style.display='none';document.getElementById('pens').appendChild(pen);
// Draw once over one full period, then fade the circles and show the math.
function run(){if(reduce.matches){still();return}
cancelAnimationFrame(raf);curve();fc.style.transition='none';fc.style.opacity=1;const t0=performance.now();
function fr(now){const p=Math.min(1,(now-t0)/DUR),k=Math.floor(p*MS);
const q=z(N,p,true),path=pieces.slice(0,k+1).join('')+(PEN[Math.min(MS-1,k)]&&k>0?'L'+q.x.toFixed(1)+' '+q.y.toFixed(1):'');circles(q.out.filter(c=>c[2]>0.4));
const down=PEN[Math.min(MS-1,Math.floor(p*MS))];ft.setAttribute('d',path);
pen.style.display=p<1?'':'none';pen.setAttribute('cx',q.x.toFixed(1));pen.setAttribute('cy',q.y.toFixed(1));pen.setAttribute('fill',down?'var(--ac)':'none');
if(p<1)raf=requestAnimationFrame(fr);else{fc.style.transition='opacity .6s cubic-bezier(0.23, 1, 0.32, 1)';fc.style.opacity=0;showMath()}}
raf=requestAnimationFrame(fr)}
// Slider: redraw once, 300 ms after the last change.
let deb;fk.oninput=()=>{N=+fk.value;readout();clearTimeout(deb);deb=setTimeout(run,300)};
// Start the first drawing when fonts and KaTeX are ready and the circles are on screen
// (on a phone they sit below the name, so they wait until you scroll them fully into view).
// The equations show from the start (no gap while the circles draw).
Promise.all([document.fonts.ready,katexReady]).then(()=>{readout();showMath();
const io=new IntersectionObserver(es=>{if(es.some(e=>e.isIntersecting)){io.disconnect();run()}},{threshold:.85});
io.observe(document.querySelector('.fig svg'))});
</script>
</body>
</html>