carlos / movement Public

Clone
git clone https://amadan.net/carlos/movement

Plain git — no account needed to clone.

Download

Download this file

1/* The flyposted bill.
2 Composition only; colour, type and shape are theme.css.
3
4 Two surfaces. The bill is drenched acid yellow and shouts once. The
5 sheet is bone and is meant to be read. Everything else follows from
6 that split. */
7
8/* ─────────────────────────────────────────────────────────────────────
9 The sheet the whole thing is printed on
10 ───────────────────────────────────────────────────────────────────── */
11
12body {
13 background: var(--rst-bg);
14 color: var(--bill-ink);
15 font-family: var(--rst-font);
16 -webkit-font-smoothing: antialiased;
17}
18
19.bill-page {
20 background-color: var(--bill-stock);
21 background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='6' height='6'%3E%3Ccircle cx='1' cy='1' r='0.65' fill='%2317140f' fill-opacity='0.13'/%3E%3Ccircle cx='4' cy='4' r='0.65' fill='%2317140f' fill-opacity='0.13'/%3E%3C/svg%3E");
22 --rst-bg: var(--bill-stock);
23 --rst-surface: var(--bill-stock);
24 --rst-text-muted: var(--bill-dim);
25 --rst-line: rgba(23, 20, 15, 0.3);
26}
27
28.sheet-page {
29 background-color: var(--bill-bone);
30 background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='6' height='6'%3E%3Ccircle cx='1' cy='1' r='0.6' fill='%2317140f' fill-opacity='0.07'/%3E%3Ccircle cx='4' cy='4' r='0.6' fill='%2317140f' fill-opacity='0.07'/%3E%3C/svg%3E");
31}
32
33.bill {
34 max-width: 78rem;
35 box-sizing: border-box;
36 margin: 0 auto;
37 display: flex;
38 flex-direction: column;
39 padding: clamp(1.25rem, 3vw, 2.5rem) clamp(1.25rem, 4vw, 3.5rem)
40 clamp(1.5rem, 3vw, 2.25rem);
41}
42.bill__top {
43 flex: 0 0 auto;
44}
45
46/* A bill fills the wall it is pasted on — but only where the wall is big
47 enough that the strip still lands near the fold. */
48@media (min-width: 46rem) and (min-height: 40rem) {
49 .bill {
50 min-height: 100svh;
51 }
52 .bill__top {
53 flex: 1 1 auto;
54 }
55}
56
57/* The printer's slug. A real bill carries who printed it and when; this
58 one carries what it is. Set small in the body face, never in mono —
59 monospace here would be a costume for "technical" on a page that is
60 not about machines. */
61.bill__slug {
62 margin: 0;
63 font-size: var(--t-sm);
64}
65/* No underline here: a 3px rule ten pixels above the perforation reads as a
66 doubled rule, not as a link. */
67.bill__slug .bill__out {
68 border-bottom: 0;
69 color: var(--bill-blue);
70}
71
72/* ─────────────────────────────────────────────────────────────────────
73 The shout
74 ───────────────────────────────────────────────────────────────────── */
75
76.bill {
77 --shout: var(--t-shout);
78}
79
80.bill__shout {
81 font-family: var(--bill-display);
82 font-weight: 800;
83 text-transform: uppercase;
84 color: var(--bill-blue);
85 font-size: var(--shout);
86 line-height: 0.84;
87 letter-spacing: -0.02em;
88 margin: 0;
89 max-width: 15ch;
90 text-wrap: balance;
91 position: relative;
92}
93
94/* The second ink, laid across the whole headline block. Anchored to the h1
95 so it scales with the shout instead of with one line of it, and offset a
96 couple of pixels off true — a two-colour job never registers perfectly,
97 and that misregistration is the tell that it was printed. */
98.bill__shout::after {
99 content: "";
100 position: absolute;
101 left: -100vw;
102 right: -100vw;
103 top: 46%;
104 height: 0.26em;
105 background: var(--bill-cyan);
106 mix-blend-mode: multiply;
107 transform: translateY(2px);
108 z-index: 1;
109 pointer-events: none;
110}
111
112
113/* ─────────────────────────────────────────────────────────────────────
114 The argument, set in hard columns
115 ───────────────────────────────────────────────────────────────────── */
116
117.bill__body {
118 margin: clamp(1.75rem, 4vw, 3rem) 0 0;
119 columns: 2;
120 column-gap: clamp(1.5rem, 4vw, 3.5rem);
121 column-rule: 1px solid var(--rst-line);
122 max-width: 62rem;
123 font-size: var(--t-body);
124 line-height: 1.55;
125}
126.bill__body p {
127 margin: 0 0 1em;
128}
129.bill__body p:last-child {
130 margin-bottom: 0;
131}
132
133.bill__out {
134 display: inline-block;
135 font-weight: 700;
136 color: var(--bill-ink);
137 text-decoration: none;
138 border-bottom: 3px solid var(--bill-blue);
139 padding-bottom: 1px;
140}
141.bill__out:hover {
142 color: var(--bill-blue);
143}
144
145/* ─────────────────────────────────────────────────────────────────────
146 The tear-off strip — the whole point of the page
147 ───────────────────────────────────────────────────────────────────── */
148
149.tear {
150 margin-top: clamp(1.75rem, 4vw, 3rem);
151}
152
153.tear__head {
154 display: flex;
155 flex-wrap: wrap;
156 align-items: baseline;
157 justify-content: space-between;
158 gap: 0.5rem 1.5rem;
159 margin: 0 0 0.6rem;
160}
161.tear__instruction {
162 font-weight: 700;
163 font-size: var(--t-lede);
164 margin: 0;
165}
166
167/* The perforation. A dashed rule the tabs hang from, drawn as a real
168 repeating gradient rather than a border so the dashes stay square. */
169.tear__perf {
170 height: 2px;
171 background-image: repeating-linear-gradient(
172 to right,
173 var(--bill-ink) 0 7px,
174 transparent 7px 14px
175 );
176}
177
178.tear__tabs {
179 display: grid;
180 grid-template-columns: repeat(3, 1fr);
181 gap: 0;
182 border-bottom: 2px solid var(--bill-ink);
183}
184
185.tab {
186 display: flex;
187 flex-direction: column;
188 justify-content: flex-start;
189 gap: 0.5rem;
190 min-height: clamp(5rem, 7vw, 6.5rem);
191 padding: 0.9rem 1.1rem 1rem;
192 background: var(--bill-stock);
193 color: var(--bill-ink);
194 text-decoration: none;
195 border-right: 2px dashed var(--bill-ink);
196 transform-origin: top center;
197 transition: transform 260ms cubic-bezier(0.16, 1, 0.3, 1),
198 box-shadow 260ms cubic-bezier(0.16, 1, 0.3, 1);
199}
200.tab:last-child {
201 border-right: 0;
202}
203
204.tab:hover,
205.tab:focus-visible {
206 transform: translateY(-10px);
207 box-shadow: var(--rst-shadow-pop);
208 z-index: 1;
209}
210
211.tab__addr {
212 margin-top: 0.15rem;
213 font-size: var(--t-xs);
214 font-weight: 700;
215 color: var(--bill-blue);
216 overflow-wrap: anywhere;
217 word-break: normal;
218}
219
220.tab__word {
221 font-family: var(--bill-display);
222 font-weight: 800;
223 text-transform: uppercase;
224 font-size: var(--t-display-2);
225 line-height: 0.92;
226 min-height: 1.84em;
227 letter-spacing: -0.01em;
228 text-wrap: balance;
229}
230
231/* ─────────────────────────────────────────────────────────────────────
232 The stance sheet — bone, and meant to be read
233 ───────────────────────────────────────────────────────────────────── */
234
235.sheet {
236 max-width: 78rem;
237 margin: 0 auto;
238 padding: clamp(1.25rem, 3vw, 2.5rem) clamp(1.25rem, 4vw, 3.5rem)
239 clamp(3rem, 7vw, 5rem);
240}
241
242/* The reply and the slip sit side by side under one rule, the way the bill
243 sets its argument in columns — not stacked down a centred strip. */
244.sheet__split {
245 display: grid;
246 grid-template-columns: minmax(0, 1.55fr) minmax(19rem, 1fr);
247 gap: clamp(2rem, 5vw, 4.5rem);
248 align-items: start;
249 border-top: 2px solid var(--bill-ink);
250 padding-top: clamp(1.5rem, 3vw, 2.25rem);
251}
252@media (max-width: 60rem) {
253 .sheet__split {
254 grid-template-columns: 1fr;
255 gap: 2rem;
256 }
257}
258
259/* The tab you took, carried over torn. Its bottom edge is ragged because
260 it came off a perforation. */
261.torn {
262 background: var(--bill-stock);
263 width: fit-content;
264 max-width: 100%;
265 padding: 0.9rem 1.4rem 1.5rem;
266 margin-bottom: clamp(1.75rem, 4vw, 2.75rem);
267 position: relative;
268 clip-path: polygon(
269 0 0, 100% 0, 100% calc(100% - 9px),
270 96% 100%, 92% calc(100% - 7px), 88% 100%, 84% calc(100% - 8px),
271 80% 100%, 76% calc(100% - 6px), 72% 100%, 68% calc(100% - 9px),
272 64% 100%, 60% calc(100% - 7px), 56% 100%, 52% calc(100% - 8px),
273 48% 100%, 44% calc(100% - 6px), 40% 100%, 36% calc(100% - 9px),
274 32% 100%, 28% calc(100% - 7px), 24% 100%, 20% calc(100% - 8px),
275 16% 100%, 12% calc(100% - 6px), 8% 100%, 4% calc(100% - 9px),
276 0 100%
277 );
278}
279.torn__word {
280 font-family: var(--bill-display);
281 font-weight: 800;
282 text-transform: uppercase;
283 font-size: var(--t-display-2);
284 line-height: 0.9;
285 margin: 0;
286}
287
288.sheet__reply {
289 font-size: var(--t-lede);
290 line-height: 1.62;
291 max-width: 68ch;
292}
293.sheet__reply p {
294 margin: 0 0 1.05em;
295}
296.sheet__reply p:first-child {
297 font-size: 1.15em;
298}
299
300/* ─────────────────────────────────────────────────────────────────────
301 The form, in the bill's own vocabulary
302 Fields are ruled lines on paper, not rounded boxes on a screen.
303 ───────────────────────────────────────────────────────────────────── */
304
305.slip {
306 margin-top: 0;
307}
308.slip__title {
309 font-family: var(--bill-display);
310 font-weight: 800;
311 text-transform: uppercase;
312 font-size: var(--t-display-3);
313 line-height: 1;
314 margin: 0 0 1.25rem;
315}
316
317.field {
318 display: block;
319 margin-bottom: 1.35rem;
320}
321.field > span {
322 display: block;
323 font-size: var(--t-xs);
324 font-weight: 700;
325 letter-spacing: 0.06em;
326 margin-bottom: 0.35rem;
327}
328.field input[type="email"],
329.field input[type="text"],
330.field textarea {
331 width: 100%;
332 box-sizing: border-box;
333 font: inherit;
334 color: var(--bill-ink);
335 background: transparent;
336 border: 0;
337 border-bottom: 2px solid var(--bill-ink);
338 padding: 0.45rem 0;
339 border-radius: 0;
340}
341.field textarea {
342 resize: vertical;
343 min-height: 7.5rem;
344 line-height: 1.5;
345 padding: 0.55rem 0.7rem;
346 background: rgba(23, 20, 15, 0.05);
347}
348.field input::placeholder,
349.field textarea::placeholder {
350 color: var(--bill-dim-bone);
351}
352
353.stamp {
354 position: relative;
355 overflow: hidden;
356 font: inherit;
357 font-weight: 700;
358 letter-spacing: 0.14em;
359 text-transform: uppercase;
360 font-size: var(--t-sm);
361 color: var(--bill-stock);
362 background: var(--bill-blue);
363 border: 0;
364 border-radius: 0;
365 padding: 0.85rem 1.75rem;
366 cursor: pointer;
367 transition: background 160ms ease-out;
368}
369.stamp::after {
370 content: "";
371 position: absolute;
372 left: 1.15rem;
373 right: 1.15rem;
374 top: 56%;
375 height: 7px;
376 background: var(--bill-cyan);
377 mix-blend-mode: multiply;
378 transform: translateY(1px);
379 pointer-events: none;
380}
381.stamp:hover:not(:disabled) {
382 background: var(--rst-accent-strong);
383}
384.stamp:disabled::after {
385 display: none;
386}
387.stamp:disabled {
388 background: transparent;
389 color: var(--bill-dim-bone);
390 border: 2px dashed var(--rst-line-strong);
391 cursor: not-allowed;
392}
393
394.slip__status {
395 margin: 0.85rem 0 0;
396 font-size: var(--t-xs);
397 font-weight: 700;
398 letter-spacing: 0.1em;
399 text-transform: uppercase;
400 color: var(--bill-dim-bone);
401}
402
403.notice {
404 border-left: 0;
405 border-top: 2px solid var(--rst-tone-negative-fg);
406 background: var(--rst-tone-negative-bg);
407 color: var(--rst-tone-negative-fg);
408 padding: 0.75rem 0.9rem;
409 margin: 0 0 1.25rem;
410 font-weight: 700;
411}
412
413/* The honeypot: present in the page, never on the screen. */
414.hp {
415 position: absolute;
416 width: 1px;
417 height: 1px;
418 overflow: hidden;
419 clip-path: inset(50%);
420 white-space: nowrap;
421}
422
423/* ─────────────────────────────────────────────────────────────────────
424 Receipts — what a signup, a confirmation and a dead link look like
425 ───────────────────────────────────────────────────────────────────── */
426
427.receipt {
428 max-width: 46rem;
429 margin: 0 auto;
430 padding: clamp(2.5rem, 10vh, 6rem) clamp(1.25rem, 5vw, 2.5rem);
431 border-top: 2px solid var(--bill-ink);
432 border-bottom: 2px solid var(--bill-ink);
433 margin-top: clamp(2rem, 12vh, 7rem);
434 position: relative;
435}
436/* The stamp mark: the second ink, struck across the top rule. */
437.receipt::before {
438 content: "";
439 position: absolute;
440 top: -3px;
441 left: clamp(1.25rem, 5vw, 2.5rem);
442 width: clamp(5rem, 14vw, 9rem);
443 height: 8px;
444 background: var(--bill-cyan);
445 mix-blend-mode: multiply;
446}
447.receipt .receipt__mark {
448 font-family: var(--bill-display);
449 font-weight: 800;
450 text-transform: uppercase;
451 font-size: var(--t-display-1);
452 line-height: 0.92;
453 color: var(--bill-blue);
454 margin: 0 0 0.85rem;
455 text-wrap: balance;
456}
457.receipt p {
458 margin: 0;
459 font-size: var(--t-lede);
460 line-height: 1.6;
461}
462
463/* ─────────────────────────────────────────────────────────────────────
464 Narrow screens: the strip stacks, the shout stays a shout
465 ───────────────────────────────────────────────────────────────────── */
466
467@media (max-width: 46rem) {
468 .bill__body {
469 columns: 1;
470 }
471 .tear__tabs {
472 grid-template-columns: 1fr;
473 }
474 .tab {
475 min-height: 0;
476 border-right: 0;
477 border-bottom: 2px dashed var(--bill-ink);
478 flex-direction: column;
479 align-items: flex-start;
480 gap: 0.2rem;
481 padding: 0.85rem 0.25rem;
482 }
483 .tab:last-child {
484 border-bottom: 0;
485 }
486 .tab__word {
487 min-height: 0;
488 }
489 .tab:hover,
490 .tab:focus-visible {
491 transform: none;
492 box-shadow: none;
493 background: var(--bill-bone);
494 }
495}
496
497@media (prefers-reduced-motion: reduce) {
498 .tab {
499 transition: none;
500 }
501 .tab:hover,
502 .tab:focus-visible {
503 transform: none;
504 }
505}
506
507/* ─────────────────────────────────────────────────────────────────────
508 The ledger — admin, in the same hand
509 Operate, not persuade: the tally has to be scannable before it is
510 anything else, so the bill's voice survives only in the type and rules.
511 ───────────────────────────────────────────────────────────────────── */
512
513.ledger {
514 max-width: 66rem;
515 margin: 0 auto;
516 padding: clamp(1.25rem, 3vw, 2.5rem);
517}
518.ledger__title {
519 font-family: var(--bill-display);
520 font-weight: 800;
521 text-transform: uppercase;
522 font-size: var(--t-display-1);
523 line-height: 0.9;
524 margin: 0 0 1.25rem;
525 color: var(--bill-blue);
526}
527.ledger__tally,
528.ledger__split {
529 margin: 0 0 0.65rem;
530 font-size: var(--t-base);
531}
532.ledger__tally b,
533.ledger__split b {
534 font-family: var(--bill-display);
535 font-size: 1.5em;
536 line-height: 1;
537 vertical-align: -0.06em;
538 margin-right: 0.15em;
539}
540.ledger__split span + span {
541 margin-left: 1.25rem;
542}
543.ledger__table {
544 width: 100%;
545 border-collapse: collapse;
546 margin-top: 1.5rem;
547 font-size: var(--t-base);
548 font-variant-numeric: tabular-nums;
549}
550.ledger__table th {
551 text-align: left;
552 font-size: var(--t-xxs);
553 letter-spacing: 0.12em;
554 text-transform: uppercase;
555 border-bottom: 2px solid var(--bill-ink);
556 padding: 0 0.75rem 0.5rem 0;
557}
558.ledger__table td {
559 border-bottom: 1px solid var(--rst-line);
560 padding: 0.7rem 0.75rem 0.7rem 0;
561 vertical-align: top;
562}
563.ledger__table td:last-child {
564 color: var(--bill-dim-bone);
565 max-width: 28rem;
566}
567
568/* ─────────────────────────────────────────────────────────────────────
569 The pills
570 Drawn on the stock in one stroke weight. Solid, not multiplied: a
571 marker does not overprint with what was already printed under it.
572 ───────────────────────────────────────────────────────────────────── */
573
574.pill {
575 display: block;
576 width: clamp(3.5rem, 6vw, 5rem);
577 height: auto;
578 margin-bottom: 0.55rem;
579 overflow: visible;
580 transition: transform 260ms cubic-bezier(0.16, 1, 0.3, 1);
581}
582
583/* The tab lifts; the pill rolls very slightly with it, the way a drawn
584 thing does when the paper it is on moves. */
585.tab:hover .pill,
586.tab:focus-visible .pill {
587 transform: rotate(-3deg) translateY(-2px);
588}
589
590@media (prefers-reduced-motion: reduce) {
591 .pill,
592 .tab:hover .pill,
593 .tab:focus-visible .pill {
594 transition: none;
595 transform: none;
596 }
597}
598
599@media (max-width: 46rem) {
600 .pill {
601 width: 3.25rem;
602 margin-bottom: 0.35rem;
603 }
604}
605
606/* The all-in reply's disclosure. Set apart from the text it describes by a
607 rule, and quieter than it: it is a note about the copy, not more copy. */
608.sheet__disclosure {
609 margin-top: 1.6rem;
610 padding-top: 0.85rem;
611 border-top: 2px solid var(--rst-line);
612 font-size: var(--t-xs);
613 font-weight: 700;
614 letter-spacing: 0.04em;
615 color: var(--bill-dim-bone);
616}
617
618/* Links inside the argument. They sit in the body's own colour with the
619 second ink under them, so they read as marked-up text rather than as
620 buttons dropped into a paragraph. */
621.lede-link {
622 color: inherit;
623 text-decoration: none;
624 box-shadow: inset 0 -0.34em 0 rgba(20, 160, 200, 0.45);
625 transition: box-shadow 160ms ease-out;
626}
627.lede-link:hover {
628 box-shadow: inset 0 -0.9em 0 rgba(20, 160, 200, 0.5);
629}
630