/* Kinobee Ink -- public pages.
 *
 * Built to the Unsolved Addresses house style rather than invented. The things
 * that make it that house and not a generic dark theme:
 *
 *   Hand-drawn is done with geometry, not filters. Every box gets an asymmetric
 *   border-radius and half a degree of rotation, alternating direction on even
 *   children, so a column of them looks set down by hand rather than stamped.
 *   An SVG turbulence filter reaches the same place and costs a repaint of the
 *   whole element every frame it moves; this costs nothing.
 *
 *   Separators are 2px dashed. Rules, chrome edges, list dividers, the lot --
 *   which is what keeps the drawn feeling from reading as sloppy.
 *
 *   One unit, --u, drives every size, clamped against both vw and vh. Type,
 *   padding and gaps scale together, so a phone gets the same layout at a
 *   smaller size rather than a different layout.
 *
 *   Jua names things, Nanum Gothic explains them. Nothing is bold Jua: Jua has
 *   no bold weight and the browser would synthesise one by smearing it.
 *
 * The house blue #9cc1f8 becomes pastel purple. On the near-black ground
 * #b8a6ea sits at 12.3:1, so unlike the light build it needs no second darker
 * value for small text -- one accent does both fills and labels.
 */

@font-face { font-family: 'Jua'; src: url('/fonts/jua-400.woff2') format('woff2'); font-weight: 400; font-display: swap; }
@font-face { font-family: 'Nanum Gothic'; src: url('/fonts/nanum-400.woff2') format('woff2'); font-weight: 400; font-display: swap; }
@font-face { font-family: 'Nanum Gothic'; src: url('/fonts/nanum-700.woff2') format('woff2'); font-weight: 700; font-display: swap; }
@font-face { font-family: 'Nanum Gothic'; src: url('/fonts/nanum-800.woff2') format('woff2'); font-weight: 800; font-display: swap; }

:root {
  --bg: #0b0c10;
  --panel: #111319;
  --raised: rgba(255, 255, 255, 0.022);
  --ink: #e9ebf2;
  --muted: #8c92a6;
  --hair: #23262f;

  --accent: #b8a6ea;
  --accent-deep: #9781dd;
  --accent-ink: #0b0c10;      /* text that sits ON the accent */
  --accent-wash: rgba(184, 166, 234, 0.1);

  --good: #8fd6ac;
  --danger: #ff9a9a;

  --display: 'Jua', system-ui, sans-serif;
  --text: 'Nanum Gothic', system-ui, sans-serif;
  --ease: cubic-bezier(0.62, 0.02, 0.24, 1);

  /* the blueprint grid, at two densities */
  --grid-minor: rgba(184, 166, 234, 0.045);
  --grid-major: rgba(184, 166, 234, 0.085);

  /* Clamped on both axes, so a short laptop window is treated like a narrow
     phone rather than being ignored. The reference can run its unit larger
     because it gives each section a whole viewport; these pages scroll, so the
     ceiling is lower and the floor is higher. */
  --u: clamp(15px, calc(0.5vw + 1.0vh), 22px);
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* The grid goes on the root, not on body.
 *
 * A background set on body is propagated to the canvas but positioned from
 * body's box, so on any page where body is shorter than the window the grid
 * stops partway down and the rest is flat. On the root it covers the canvas
 * whatever the content does.
 *
 * Deliberately not background-attachment: fixed, which would size it to the
 * viewport instead and repaint the whole thing on every scroll frame -- the
 * expensive option on exactly the phones this page is for. */
html {
  /* Every box here is rotated between half a degree and two degrees, and a
     rotated corner on a box that sits against the page edge sticks out past
     it. One stray pixel is enough to give a phone a horizontal scrollbar and
     let the whole page slide sideways under a thumb.
     clip rather than hidden: hidden on the root would make it a scroll
     container and break position: sticky on the preview. */
  overflow-x: clip;
  background-color: var(--bg);
  background-image:
    linear-gradient(var(--grid-minor) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-minor) 1px, transparent 1px),
    linear-gradient(var(--grid-major) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-major) 1px, transparent 1px);
  background-size: 28px 28px, 28px 28px, 140px 140px, 140px 140px;
}

body {
  font-family: var(--text);
  color: var(--muted);
  /* dvh, not vh: on a phone 100vh is the viewport with the browser chrome
     retracted, which is not the viewport you are looking at when the page
     loads. Both declarations stay, so a browser without dvh keeps the old
     behaviour instead of losing the rule. */
  min-height: 100vh;
  min-height: 100dvh;
  line-height: 1.65;
  font-size: calc(var(--u) * 0.92);
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
}

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 400;
  letter-spacing: 0.005em;
  color: var(--ink);
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

:where(a, button, input, select, textarea, summary):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* ---------- header / footer bars ---------- */

.bar-top {
  background: var(--bg);
  padding: calc(var(--u) * 0.8) clamp(18px, 3vw, 40px);
  display: flex;
  align-items: center;
  gap: calc(var(--u) * 0.8);
  border-bottom: 2px dashed var(--hair);
  flex-wrap: wrap;
}
.bar-top .wordmark {
  font-family: var(--display);
  font-size: calc(var(--u) * 1.15);
  color: var(--ink);
  line-height: 1.2;
  display: inline-flex;
  align-items: center;
  gap: calc(var(--u) * 0.45);
}
.bar-top .wordmark a {
  color: inherit;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: calc(var(--u) * 0.45);
}
.bar-top .wordmark a:hover { color: var(--accent); }

/* The tagline is the reference's chip: dashed, wobbly, slightly off-square. */
.bar-top .tagline {
  font-size: calc(var(--u) * 0.58);
  font-weight: 700;
  color: var(--muted);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  border: 2px dashed var(--hair);
  border-radius: 12px 5px 14px 6px / 6px 14px 5px 12px;
  padding: calc(var(--u) * 0.14) calc(var(--u) * 0.5);
  transform: rotate(-0.6deg);
  white-space: nowrap;
}

.bar-foot {
  border-top: 2px dashed var(--hair);
  margin-top: calc(var(--u) * 2.4);
  padding: calc(var(--u) * 1.1) clamp(18px, 3vw, 40px);
  font-size: calc(var(--u) * 0.7);
  color: var(--muted);
  text-align: center;
  letter-spacing: 0.06em;
}

/* ---------- layout ---------- */

.page {
  max-width: calc(var(--u) * 46);
  width: 100%;
  margin: 0 auto;
  padding: calc(var(--u) * 2) clamp(18px, 4vw, 40px);
}
.page-wide { max-width: calc(var(--u) * 64); }

h1 { font-size: calc(var(--u) * 2.6); line-height: 1.14; }
h2 { font-size: calc(var(--u) * 1.35); line-height: 1.2; margin-bottom: calc(var(--u) * 0.5); }
h3 { font-size: calc(var(--u) * 1.02); line-height: 1.25; }

/* The eyebrow: rides above a heading, names the section, never explains it. */
.sub-label {
  display: block;
  font-size: calc(var(--u) * 0.62);
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: calc(var(--u) * 0.45);
}

.head { margin-bottom: calc(var(--u) * 1.1); }
.head h1 { margin-bottom: calc(var(--u) * 0.6); }

.head h1 + .lede, h1 + .lede { margin-top: calc(var(--u) * 0.8); }

.lede {
  font-size: calc(var(--u) * 0.95);
  line-height: 1.7;
  max-width: calc(var(--u) * 34);
  margin-bottom: calc(var(--u) * 0.7);
}
.lede strong { color: var(--ink); font-weight: 700; }

.hint {
  color: var(--muted);
  font-size: calc(var(--u) * 0.78);
  line-height: 1.6;
  max-width: calc(var(--u) * 36);
  margin-bottom: calc(var(--u) * 0.5);
}

.contact { margin-top: calc(var(--u) * 0.8); font-size: calc(var(--u) * 0.88); }

/* "Coming soon", set as a chip so it reads as a status rather than as a
   heading nobody bothered to write. */
.soon {
  display: inline-block;
  font-family: var(--display);
  font-size: calc(var(--u) * 1.05);
  color: var(--accent);
  border: 2px solid var(--hair);
  border-radius: 14px 5px 16px 6px / 6px 16px 5px 14px;
  padding: calc(var(--u) * 0.25) calc(var(--u) * 0.9);
  transform: rotate(-1deg);
  margin-bottom: calc(var(--u) * 0.7);
}

/* The three inline SVGs draw themselves in currentColor, so the palette
   reaches them like it reaches everything else. Set here rather than inherited:
   the wordmark link is ink and turns accent on hover, and the ink drop beside
   it should stay accent through both. */
.mark { display: block; margin-bottom: calc(var(--u) * 0.9); color: var(--accent); }
.glyph { flex: none; color: var(--accent); }
.arrow { color: var(--accent); }

/* ---------- the portrait ---------- */

.portrait { margin: calc(var(--u) * 1.1) 0 0; }

.portrait-row {
  display: flex;
  align-items: center;
  gap: calc(var(--u) * 0.9);
}

.portrait-caption {
  display: flex;
  align-items: center;
  gap: calc(var(--u) * 0.5);
  flex: none;
}
.portrait-label {
  font-family: var(--display);
  font-size: calc(var(--u) * 1.15);
  color: var(--ink);
  white-space: nowrap;
}
.portrait-caption .arrow { flex: none; }

/* Pinned to the board a touch crooked, in the house frame. Square in the source
   and square here, so nothing is cropped off her. */
.portrait-photo {
  display: block;
  width: calc(var(--u) * 8);
  height: calc(var(--u) * 8);
  max-width: 40vw;
  max-height: 40vw;
  object-fit: cover;
  border: 2px solid var(--hair);
  border-radius: 18px 6px 20px 7px / 7px 20px 6px 18px;
  transform: rotate(1.6deg);
  background: var(--panel);
  filter: saturate(0.92);
}

.portrait-note {
  margin-top: calc(var(--u) * 0.55);
  font-size: calc(var(--u) * 0.88);
  color: var(--muted);
}
.portrait-note::before {
  /* the beat before the punchline */
  content: '';
  display: inline-block;
  width: calc(var(--u) * 1.4);
  border-top: 2px dashed var(--hair);
  vertical-align: middle;
  margin-right: calc(var(--u) * 0.5);
}

/* ---------- the countdown ---------- */

.countdown { margin: calc(var(--u) * 0.9) 0 0; }
.countdown .sub-label { margin-bottom: calc(var(--u) * 0.4); }

.cd-row {
  display: flex;
  gap: calc(var(--u) * 0.5);
  list-style: none;
  flex-wrap: wrap;
}
.cd-row > li {
  border: 2px solid var(--hair);
  border-radius: 14px 5px 16px 6px / 6px 16px 5px 14px;
  background: var(--raised);
  padding: calc(var(--u) * 0.4) calc(var(--u) * 0.7);
  text-align: center;
  transform: rotate(-0.5deg);
  min-width: calc(var(--u) * 2.8);
}
.cd-row > li:nth-child(even) {
  border-radius: 6px 15px 5px 17px / 17px 5px 15px 6px;
  transform: rotate(0.5deg);
}
.cd-row b {
  display: block;
  font-family: var(--display);
  font-weight: 400;
  font-size: calc(var(--u) * 1.35);
  line-height: 1.1;
  color: var(--accent);
  /* The seconds cell rewrites every second. Tabular figures stop the box
     twitching wider on an 8 and narrower on a 1. */
  font-variant-numeric: tabular-nums;
}
.cd-row span {
  display: block;
  font-size: calc(var(--u) * 0.55);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: calc(var(--u) * 0.1);
}

/* ---------- rules ---------- */

.rule-dashed {
  border: 0;
  border-top: 2px dashed var(--hair);
  margin: calc(var(--u) * 1.4) 0;
}

/* The chalk squiggle, in the house hand and now in purple. Left-aligned and
   short: a beat between thoughts, not a divider across the page. */
.rule-squiggle {
  border: 0;
  height: calc(var(--u) * 0.6);
  width: calc(var(--u) * 8);
  margin: calc(var(--u) * 1.3) 0;
  /* Masked rather than drawn, so the squiggle takes the page's accent like
     everything else. A data-URI background is its own little document and
     inherits nothing, so the colour used to be baked into the URI and stayed
     lavender on the cream page. The stroke colour inside the mask is only
     there to be opaque; the mask reads alpha, and the visible colour is the
     background underneath it. */
  background: var(--accent);
  -webkit-mask: var(--squiggle) no-repeat center / contain;
  mask: var(--squiggle) no-repeat center / contain;
}
:root {
  --squiggle: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 130 10'%3E%3Cpath d='M2 6 C 14 2, 22 9, 34 5 S 54 2, 66 6 S 88 9, 100 4 S 120 3, 128 6' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* ---------- the drawn box ---------- */

/* The house's one structural move. Alternating radius and rotation on even
   siblings is what stops a stack of them looking like a repeated stamp. */
.card {
  background: var(--raised);
  border: 2px solid var(--hair);
  border-radius: 18px 6px 20px 7px / 7px 20px 6px 18px;
  padding: calc(var(--u) * 1) calc(var(--u) * 1.15);
  transform: rotate(-0.4deg);
  margin-bottom: calc(var(--u) * 1.1);
}
.card:nth-of-type(even) {
  border-radius: 7px 19px 6px 17px / 17px 6px 19px 7px;
  transform: rotate(0.45deg);
}
/* Content inside a tilted box gets the tilt taken back out. Rotated running
   text is the one thing here that reads as a mistake rather than as a hand. */
.card > * { transform: rotate(0.4deg); }
.card:nth-of-type(even) > * { transform: rotate(-0.45deg); }

.card h2 { margin-bottom: calc(var(--u) * 0.5); }
.card-accent { border-color: var(--accent); background: var(--accent-wash); }
.card-attention { border-color: var(--accent); }

/* The blobby count badge, borrowed from the reference's section numbers. */
.count {
  font-family: var(--display);
  font-size: calc(var(--u) * 0.8);
  color: var(--accent);
  border: 2px solid var(--hair);
  border-radius: 52% 46% 55% 48% / 48% 55% 45% 52%;
  padding: calc(var(--u) * 0.05) calc(var(--u) * 0.55);
  display: inline-block;
  transform: rotate(-2deg);
  vertical-align: middle;
  margin-left: calc(var(--u) * 0.4);
}

/* ---------- forms ---------- */

.stack { display: flex; flex-direction: column; gap: calc(var(--u) * 0.9); }
.row { display: flex; gap: calc(var(--u) * 0.8); flex-wrap: wrap; align-items: flex-end; }
.row > .field { flex: 1; min-width: calc(var(--u) * 9); }

.field { display: block; }
.field > span {
  display: block;
  font-size: calc(var(--u) * 0.62);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  opacity: 0.9;
  margin-bottom: calc(var(--u) * 0.25);
}
.field > span em { font-style: normal; color: var(--muted); float: right; letter-spacing: 0.06em; }

input[type="text"], input[type="password"], input[type="number"],
input[type="datetime-local"], textarea, select {
  width: 100%;
  display: block;
  font-family: var(--text);
  font-size: calc(var(--u) * 0.85);
  color: var(--ink);
  background: rgba(255, 255, 255, 0.03);
  border: 2px solid var(--hair);
  border-radius: 12px 5px 14px 6px / 6px 14px 5px 12px;
  padding: calc(var(--u) * 0.42) calc(var(--u) * 0.65);
  outline: none;
  transition: border-color 0.2s ease, background 0.2s ease;
}
textarea { resize: vertical; min-height: calc(var(--u) * 5.2); line-height: 1.6; }
input::placeholder, textarea::placeholder { color: var(--muted); opacity: 0.65; }
input:focus, textarea:focus, select:focus {
  border-color: var(--accent);
  background: var(--accent-wash);
}
/* A native dropdown paints its list in the OS palette; without this the options
   come up black on white in the middle of a dark page. */
select option { background: var(--panel); color: var(--ink); }

/* ---------- buttons ---------- */

.btn {
  font-family: var(--display);
  font-size: calc(var(--u) * 0.85);
  line-height: 1.2;
  padding: calc(var(--u) * 0.45) calc(var(--u) * 1.1);
  cursor: pointer;
  color: var(--ink);
  background: transparent;
  border: 2px solid var(--accent);
  border-radius: 14px 5px 16px 6px / 6px 16px 5px 14px;
  transform: rotate(-0.5deg);
  align-self: flex-start;
  white-space: nowrap;
  transition: transform 0.15s ease, background 0.15s ease, color 0.15s ease, border-color 0.3s ease;
}
.btn:hover { transform: rotate(0.5deg) scale(1.03); background: var(--accent); color: var(--accent-ink); }
.btn:disabled { opacity: 0.5; cursor: default; transform: none; }
.btn:disabled:hover { background: transparent; color: var(--ink); }

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-size: calc(var(--u) * 0.95);
  padding: calc(var(--u) * 0.6) calc(var(--u) * 1.5);
}
/* The hover keeps the dark text rather than switching to light, which is what
   the reference does. Light text on the deeper purple measures 2.71:1 -- fine
   as a colour, unreadable as a label. Dark on the same fill is 6.05:1. */
.btn-primary:hover { background: var(--accent-deep); border-color: var(--accent-deep); color: var(--accent-ink); }

.btn-small { padding: calc(var(--u) * 0.28) calc(var(--u) * 0.7); font-size: calc(var(--u) * 0.72); }
.btn-danger { color: var(--danger); border-color: var(--hair); background: transparent; }
.btn-danger:hover { background: var(--danger); border-color: var(--danger); color: var(--accent-ink); }

/* Dashed rather than solid, for actions that are an offer rather than the
   point of the page. */
.btn-quiet {
  font-family: var(--display);
  font-size: calc(var(--u) * 0.78);
  color: var(--muted);
  background: transparent;
  border: 2px dashed var(--hair);
  border-radius: 12px 5px 14px 6px / 6px 14px 5px 12px;
  padding: calc(var(--u) * 0.35) calc(var(--u) * 0.9);
  cursor: pointer;
  white-space: nowrap;
  transform: rotate(-0.5deg);
  transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}
.btn-quiet:hover { color: var(--accent); border-color: var(--accent); background: var(--accent-wash); }

.link-quiet {
  font-family: var(--text);
  font-size: calc(var(--u) * 0.76);
  background: none;
  border: 0;
  padding: 0;
  color: var(--accent);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.inline, .inline-first, .inline-tight { display: inline-block; }
.inline-first { margin-left: auto; }
.inline-tight { margin-left: calc(var(--u) * 0.35); }

/* ---------- notes ---------- */

.note {
  border-left: 2px dashed var(--hair);
  padding: calc(var(--u) * 0.5) 0 calc(var(--u) * 0.5) calc(var(--u) * 0.9);
  margin-bottom: calc(var(--u) * 1.1);
  font-size: calc(var(--u) * 0.85);
}
.note-good { border-left-color: var(--good); color: var(--good); }
.note-bad { border-left-color: var(--danger); color: var(--danger); }

.empty { color: var(--muted); font-style: italic; font-size: calc(var(--u) * 0.85); }

/* ---------- the composer ---------- */

.composer {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, auto);
  gap: calc(var(--u) * 1.6);
  align-items: start;
}

.preview-pane { position: sticky; top: calc(var(--u) * 1); margin-bottom: 0; }
.preview-pane .sub-label { margin-bottom: calc(var(--u) * 0.5); }
.preview-pane .hint { margin: calc(var(--u) * 0.5) 0 0; max-width: 260px; }

/* The panel itself: a lit rectangle in a dark room, which is what the thing on
   the shelf actually looks like. Never smoothed -- this is the exact bitmap the
   device receives, and interpolating it would be the one lie the page cannot
   afford to tell. */
.panel-frame {
  background: #f4f4ef;
  border: 2px solid var(--hair);
  border-radius: 8px 4px 9px 4px / 4px 9px 4px 8px;
  padding: calc(var(--u) * 0.3);
  display: inline-block;
  max-width: 100%;
  box-shadow: 0 0 calc(var(--u) * 1.6) rgba(184, 166, 234, 0.13);
}
.panel-frame canvas {
  display: block;
  width: 250px;
  max-width: 100%;
  height: auto;
  image-rendering: pixelated;
}

/* ---------- the photo field ---------- */

/* The native file input is unstylable and says "No file chosen" in a voice
   nothing else here uses, so it is hidden and its own label becomes the button.
   Clipped rather than display:none: it still has to take keyboard focus and
   still has to announce itself to a screen reader. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.photo-row {
  display: flex;
  align-items: center;
  gap: calc(var(--u) * 0.7);
  flex-wrap: wrap;
  margin-bottom: calc(var(--u) * 0.4);
}
.photo-state {
  font-size: calc(var(--u) * 0.78);
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
/* The focus ring belongs on the button the eye is on, not on the clipped input. */
.field-photo:focus-within .btn-quiet { border-color: var(--accent); color: var(--accent); }
.field-photo > span em.optional { float: none; margin-left: calc(var(--u) * 0.4); }

/* Nothing to preview before a choice is made. The pane is an empty panel until
   there is a card to put in it, and on a phone that empty panel was 190px of
   nothing sitting between the header and the only control on the page that
   matters yet. */
.composer.mode-none .preview-pane { display: none; }

/* ---------- write or draw ---------- */

/* One form, two shapes, and a third state before either is picked. The mode
   class on the form hides the half not in use; mode-none hides both halves and
   everything below the choice, and shows the arrow.
   The class is on the composer as well as the form, because the preview is the
   form's sibling and not its child. */
.mode-write .only-draw { display: none; }
.mode-draw .only-write { display: none; }
.mode-none .needs-mode { display: none; }
.choose-arrow { display: none; }
.mode-none .choose-arrow { display: inline-flex; }

.choice-row {
  display: flex;
  align-items: center;
  gap: calc(var(--u) * 0.6);
}

/* Points at the choice and blinks until one is made. A hard on/off, not a
   fade: steps(1) holds each keyframe's value and jumps at the boundary, so it
   is visible for the first half of the cycle and gone for the second. Just
   over once a second, well under the three flashes a second that counts as a
   seizure risk. Under reduced motion it holds still, still pointing the right
   way. */
.choose-arrow .arrow { animation: choose-blink 0.9s steps(1, end) infinite; }
@keyframes choose-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}

.segmented {
  display: inline-flex;
  border: 2px solid var(--hair);
  border-radius: 14px 5px 16px 6px / 6px 16px 5px 14px;
  overflow: hidden;
  transform: rotate(-0.4deg);
}
.segmented label { position: relative; }
.segmented input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
.segmented span {
  display: block;
  font-family: var(--display);
  font-size: calc(var(--u) * 0.88);
  padding: calc(var(--u) * 0.45) calc(var(--u) * 1.2);
  color: var(--muted);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.segmented label + label span { border-left: 2px solid var(--hair); }
.segmented input:checked + span { background: var(--accent); color: var(--accent-ink); }
.segmented input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: -2px; }

/* The pad. Full width of the column, held to the slot's aspect, and the canvas
   inside scaled up with the pixels left square: a stroke should look like the
   dots it is, not like a smoothed line that then prints jagged. */
.sketchpad {
  background: #f4f4ef;
  border: 2px solid var(--hair);
  border-radius: 8px 4px 9px 4px / 4px 9px 4px 8px;
  padding: calc(var(--u) * 0.3);
  margin-bottom: calc(var(--u) * 0.5);
  box-shadow: 0 0 calc(var(--u) * 1.4) rgba(184, 166, 234, 0.12);
}
.sketchpad canvas {
  display: block;
  width: 100%;
  height: auto;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  /* The one line that makes drawing on a phone possible. Without it the first
     movement of a finger is a scroll, and the stroke never starts. */
  touch-action: none;
  cursor: crosshair;
  background: #fff;
  /* No long-press callout or text selection halfway through a stroke. */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* Tools sized for a thumb. 44px is the smallest target a finger hits reliably
   and every button here clears it. */
.tools {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--u) * 0.6);
  align-items: center;
}
.tool-group { display: inline-flex; gap: calc(var(--u) * 0.3); }
.tool {
  font-family: var(--display);
  font-size: calc(var(--u) * 0.82);
  min-height: 44px;
  min-width: 44px;
  padding: 0 calc(var(--u) * 0.9);
  color: var(--muted);
  background: transparent;
  border: 2px solid var(--hair);
  border-radius: 12px 5px 14px 6px / 6px 14px 5px 12px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.tool:nth-child(even) { border-radius: 5px 13px 6px 15px / 15px 6px 13px 5px; }
.tool:hover { border-color: var(--accent); color: var(--ink); }
.tool.is-on { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.tool:disabled { opacity: 0.4; cursor: default; }
.tool:disabled:hover { border-color: var(--hair); color: var(--muted); }

/* Size buttons show the brush as a dot rather than as a word. */
.tool.size { padding: 0; }
.tool.size i {
  display: block;
  width: var(--d);
  height: var(--d);
  border-radius: 50%;
  background: currentColor;
}

/* ---------- messages: the moderation queue ---------- */

.msgs { list-style: none; }
.msg { border-bottom: 2px dashed var(--hair); padding: calc(var(--u) * 0.8) 0; }
.msg:last-child { border-bottom: 0; }

.msg-head {
  display: flex;
  align-items: center;
  gap: calc(var(--u) * 0.5);
  flex-wrap: wrap;
  margin-bottom: calc(var(--u) * 0.4);
}
.msg-head strong { color: var(--ink); font-family: var(--display); font-weight: 400; }
.msg-head time { font-size: calc(var(--u) * 0.68); color: var(--muted); }
.msg-head .when { font-size: calc(var(--u) * 0.68); color: var(--accent); }

.mod-drawing { height: auto; width: 236px; max-width: 100%; }
.msg-content { display: flex; gap: calc(var(--u) * 0.8); align-items: flex-start; }
.msg-body { margin: 0; min-width: 0; font-size: calc(var(--u) * 0.85); }

.msg-pending { border-left: 2px solid var(--accent); padding-left: calc(var(--u) * 0.7); }
.msg-scheduled { border-left: 2px dashed var(--hair); padding-left: calc(var(--u) * 0.7); }

/* The card's photo, as the panel will draw it, doubled. Pixelated on purpose:
   at 1:1 a 92px strip is too small to judge, and smoothing would hide the
   dithering, which is most of what there is to judge. */
.mod-photo {
  height: 184px;
  width: auto;
  image-rendering: pixelated;
  border: 2px solid var(--hair);
  background: #f4f4ef;
  flex: none;
}

/* ---------- settings odds and ends ---------- */

.net { align-items: flex-end; }
.design-list { display: flex; flex-wrap: wrap; gap: calc(var(--u) * 0.4); }
.check { display: flex; align-items: center; gap: calc(var(--u) * 0.4); }
.check input { width: auto; display: inline-block; }
.org { color: var(--muted); }

/* ---------- responsive ---------- */

/* Narrow, or short. Width alone was the wrong test: an iPhone Pro Max in
   landscape is 932 points wide and 430 tall, so it took the desktop sidebar
   layout on a screen with no vertical room for it. Any viewport that is short
   wants the phone treatment whatever its width. */
@media (max-width: 900px), (max-height: 620px) {
  /* The preview stops being a sidebar and follows the form in document order,
     scrolling with the page rather than sticking.
     It has been in all three positions now, so: sticky sat on top of whatever
     you were trying to draw into. Above the form, it pushed the write-or-draw
     choice off the first screen, which is worse, because that choice is the
     only thing anyone has to do before the page makes sense. Last is right. A
     preview is for checking after you have done something. */
  .composer { grid-template-columns: minmax(0, 1fr); gap: calc(var(--u) * 1.1); }
  .preview-pane { position: static; z-index: auto; }
  .preview-pane .hint { max-width: none; }
}

/* Short screens.
   The header is what pushes the choice off the page: wordmark, title, lede,
   portrait and countdown came to 436px before the dashed rule, measured on a
   viewport 390px tall. No amount of rearranging the composer fixes that, so
   the header has to give.
   The portrait and the countdown are decoration and the choice is the task, so
   the decoration shrinks rather than the decision moving below the fold. */
@media (max-height: 700px) {
  .page { padding-top: calc(var(--u) * 0.9); }
  .rule-dashed { margin: calc(var(--u) * 0.8) 0; }

  .head { margin-bottom: calc(var(--u) * 0.7); }
  .head h1 { font-size: calc(var(--u) * 2.1); margin-bottom: calc(var(--u) * 0.3); }
  .head h1 + .lede, h1 + .lede { margin-top: calc(var(--u) * 0.45); }

  .portrait { margin-top: calc(var(--u) * 0.55); }
  .portrait-photo { width: calc(var(--u) * 4.4); height: calc(var(--u) * 4.4); }
  .portrait-note { margin-top: calc(var(--u) * 0.3); }

  .countdown { margin-top: calc(var(--u) * 0.55); }
  .countdown .sub-label { margin-bottom: calc(var(--u) * 0.2); }
  .cd-row > li { padding: calc(var(--u) * 0.15) calc(var(--u) * 0.5); }
}

/* A phone turned sideways: short, but with width going spare.
   Stacking the portrait under a one-line lede spends 92 vertical pixels on a
   screen that has 390 of them, while most of the row sits empty. Side by side
   it costs nothing, because the photo is shorter than the text block it now
   sits next to. This is the one case where the layout genuinely differs rather
   than just scaling, and it is worth it: without it the write-or-draw choice
   cannot fit above the fold on any phone in landscape. */
@media (max-height: 560px) and (min-width: 560px) {
  .head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: calc(var(--u) * 1.2);
    align-items: start;
  }
  .head > h1, .head > .lede { grid-column: 1; }
  .head > .portrait {
    grid-column: 2;
    grid-row: 1 / span 2;
    /* The right margin is for the photo's 1.6deg rotation, which otherwise
       hangs its corner over the page edge. */
    margin: 0 calc(var(--u) * 0.2) 0 0;
    max-width: min(calc(var(--u) * 13), 34vw);
  }
  .head > .countdown { grid-column: 1 / -1; }
  .portrait-row { gap: calc(var(--u) * 0.6); }
}

/* The hardest case on the list: an SE-sized phone in landscape, around 320
   points tall. --u is already pinned to its 15px floor here, so nothing scales
   down any further on its own and the trims have to be named.
   The countdown keeps its numbers and loses its eyebrow, the photo gets
   smaller, and the lede drops a step. That is the whole budget. */
@media (max-height: 420px) and (min-width: 560px) {
  .head .lede { font-size: calc(var(--u) * 0.82); }
  .head > .portrait { max-width: 26vw; }
  .portrait-photo { width: calc(var(--u) * 3.4); height: calc(var(--u) * 3.4); }
  .portrait-label { font-size: calc(var(--u) * 0.95); }
  .countdown .sub-label { display: none; }
  .cd-row b { font-size: calc(var(--u) * 1.1); }
}

@media (max-width: 520px) {
  .bar-top { gap: calc(var(--u) * 0.5); }
  .msg-content { flex-direction: column; }
  .mod-photo { height: 138px; }
  .lede, .hint { max-width: none; }
}

/* Rotation is decoration, and for anyone who has asked the system to calm down
   it is the first thing that should go. The dashed edges and the palette carry
   the style without it. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transform: none !important;
    transition: none !important;
    animation: none !important;
  }
}

/* ---------- /abby: tutti frutti ---------- */

/*
 * One page, one palette of its own. Everything else here is a near-black
 * blueprint with a single purple accent; the birthday card page is cream
 * frosting with sprinkles, because it is a birthday card and not a telegram.
 *
 * It works by redefining the tokens on the root rather than by restyling
 * components, so there is exactly one place to look and no second copy of the
 * layout to keep in step. The class goes on <html>, not <body>, because the
 * blueprint grid is painted on the root and a class on body could not reach it.
 *
 * Every colour below was checked against the cream ground before it went in.
 * The five sprinkles all clear 4.5:1 as text on cream AND 4.5:1 for white text
 * sitting on them as a fill, so any of them can be used either way without
 * having to remember which. That is why they are jewel candy rather than neon:
 * neon sprinkles on cream land around 2.7:1 and are unreadable at eyebrow size.
 */
:root.tutti {
  --bg: #fdf6ee;              /* vanilla buttercream */
  --panel: #fffdfa;
  --raised: rgba(255, 255, 255, 0.72);
  --ink: #3a2f3f;             /* 11.8:1 */
  --muted: #655870;           /*  6.2:1, and this is the body text colour */
  --hair: #f0cfbc;            /* piping, not a border */

  --accent: #c2185b;          /*  5.5:1 as text, 5.9:1 under white */
  --accent-deep: #9c1148;
  --accent-ink: #fffdfa;
  --accent-wash: rgba(194, 24, 91, 0.075);

  --good: #1b7a52;
  --danger: #c0392b;

  --grid-minor: rgba(194, 24, 91, 0.05);
  --grid-major: rgba(194, 24, 91, 0.09);

  /* the sprinkles */
  --s1: #c2185b;  /* raspberry */
  --s2: #157a5c;  /* pistachio */
  --s3: #96690a;  /* honey */
  --s4: #1f6f99;  /* blueberry */
  --s5: #6b3fc4;  /* grape */
}

/* A near-invisible white wash over near-black is how the dark build raises a
   field off the ground. Over cream it is nothing at all, so fields go properly
   white and let the piping do the separating. */
.tutti input, .tutti textarea, .tutti select { background: #fffdfa; }
.tutti select option { background: #fffdfa; }

/* The one thing that must not go light.
   This is a two inch e-paper panel, and the preview earns its "actual size"
   label by looking like the object. On the dark build the panel is a lit
   rectangle in a dark room and the frame can be a hairline. On cream the
   e-paper white and the page are four percent apart, so the panel disappears
   unless it is given a real bezel to sit in. */
.tutti .panel-frame,
.tutti .sketchpad {
  background: #2e2733;
  border-color: #2e2733;
  box-shadow: 0 calc(var(--u) * 0.22) calc(var(--u) * 0.75) rgba(58, 47, 63, 0.22);
}
.tutti .panel-frame canvas { background: #f4f4ef; }

/* Sprinkles. The countdown is four boxes in a row doing nothing but counting,
   which makes it the one place on the page where four different colours reads
   as decoration rather than as four different meanings. */
.tutti .cd-row > li:nth-child(1) { border-color: var(--s1); }
.tutti .cd-row > li:nth-child(2) { border-color: var(--s2); }
.tutti .cd-row > li:nth-child(3) { border-color: var(--s3); }
.tutti .cd-row > li:nth-child(4) { border-color: var(--s4); }
.tutti .cd-row > li:nth-child(1) b { color: var(--s1); }
.tutti .cd-row > li:nth-child(2) b { color: var(--s2); }
.tutti .cd-row > li:nth-child(3) b { color: var(--s3); }
.tutti .cd-row > li:nth-child(4) b { color: var(--s4); }

/* The eyebrows take a flavour each, top to bottom, so the page reads as a
   sequence of flavours rather than one accent repeated five times. */
.tutti .countdown .sub-label { color: var(--s4); }
.tutti .preview-pane .sub-label { color: var(--s2); }

.tutti .portrait-photo { border-color: var(--s5); }
.tutti .portrait-label { color: var(--ink); }
/* The horizontal rules stay raspberry and halved, matching the footer's peach
   rather than taking a flavour each. Honey at full strength on a 2px dashed
   line reads olive, which is the one colour a tutti frutti palette cannot
   have in it. */
.tutti .rule-dashed { border-top-color: var(--s1); opacity: 0.5; }

/* The drawing tools are the only row of buttons that can be on at once, so
   they keep the one accent. Giving each tool its own colour would say they do
   related-but-different things, and pen and eraser already say that. */
.tutti .tool.is-on { background: var(--accent); border-color: var(--accent); }
