/* ============================================================
   My Songs — hand-drawn kid player

   PALETTE: watercolour pigments on cold-press paper.
   Every accent is a real pigment name so the set stays coherent
   when you swap in your own artwork.

   TYPE: Marker Felt and Chalkboard SE both ship with iOS, so the
   hand-drawn look costs zero download and works fully offline.
   To use your own handwriting font, drop the file in art/ and
   fill in the @font-face block below.
   ============================================================ */

/* Bundled so the app looks the same everywhere and still works offline.
   Both are subset to Latin plus the few typographic marks the interface
   uses. Both SIL Open Font License 1.1.

   Gochi Hand is the current choice: chunkier and bouncier, which sits
   closer to the cut-paper logo and buttons.
   To go back to Shantell Sans — lighter, cleaner at small sizes — just
   swap the two names in --font-hand below. Both files ship. */
@font-face {
  font-family: "Gochi Hand";
  src: url("./art/GochiHand.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Shantell Sans";
  src: url("./art/ShantellSans.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* Your own handwriting goes here when it's ready — it will win over
   Shantell Sans without any other change.
@font-face {
  font-family: "MyHand";
  src: url("./art/myhand.woff2") format("woff2");
  font-display: swap;
}
*/

:root {
  --paper:        #FCF9F2;
  --paper-shade:  #EFE8D7;
  --ink:          #26304A;
  --ink-soft:     #6C7590;

  --danger:       #A63A22;
  --vermilion:    #E4572E;
  --cerulean:     #2E86AB;
  --sap:          #5F9E56;

  /* Comic Sans and Segoe Print were only ever Windows fallbacks — dropped. */
  --font-hand: "MyHand", "Gochi Hand", "Shantell Sans", "Marker Felt", "Chalkboard SE", cursive;

  /* Swapped per face. Declared here so the wordmark renders even if the
     script hasn't run yet. */
  /* Type colour. Song titles and controls read better neutral than
     tinted: it clears every contrast threshold with room to spare, and
     it leaves the drawings as the only coloured thing on the screen.
     The About page keeps the face ink — a grown-up reading at length
     benefits more from the warmth than from the last few points of
     contrast. */
  --type: #1B1B1B;
  /* Secondary interface text. #454545 is the lightest grey that still
     clears 4.5:1 on all three papers — sage is the limiting one at
     5.0:1. The About page keeps the face's own softer ink. */
  --type-soft: #454545;
  --logo-src: url("./art/logo.webp");
  --rule-src: url("./art/wave.webp");

  --stroke: 3px;
  /* Transport button sizes. --tap-big stays well above the 44px minimum
     while leaving the song grid as the thing that dominates the screen. */
  --tap: 64px;
  --tap-big: 85px;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* The `hidden` attribute is display:none in the browser's own
   stylesheet, which any author `display:` rule silently outranks. Set
   `display: flex` on an element and hiding it stops working, with no
   error. This makes the attribute win everywhere. */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  margin: 0;
  overflow: hidden;
}

body {
  background-color: var(--paper);
  background-image: var(--paper-gradient, var(--face-bg, none));
  background-position: center center;
  background-attachment: fixed;
  color: var(--type, var(--ink));
  font-family: var(--font-hand);
  -webkit-font-smoothing: antialiased;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  overscroll-behavior: none;
}

/* A flat-silhouette background is masked rather than drawn, so a drawn
   pattern takes the face's ink colour like everything else. Opacity does
   the work the paper wash does for a full-colour scene. */
body[data-bg-tint] { background-image: none; }
body[data-bg-tint]::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-color: var(--ink);
  -webkit-mask-image: var(--face-bg);
  mask-image: var(--face-bg);
  opacity: var(--face-bg-strength, .12);
}
body[data-bg-tint][data-bg-mode="tile"]::after {
  -webkit-mask-size: var(--face-bg-tile, 220px) auto;
  mask-size: var(--face-bg-tile, 220px) auto;
  -webkit-mask-repeat: repeat;
  mask-repeat: repeat;
}
body[data-bg-tint][data-bg-mode="cover"]::after {
  -webkit-mask-size: cover;
  mask-size: cover;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
}

/* Background modes. Cover for a painted scene, tile for a repeating
   pattern. Both sit behind everything; the panels stay opaque. */
body[data-bg-mode="cover"] {
  background-size: cover;
  background-repeat: no-repeat;
}
body[data-bg-mode="tile"] {
  background-size: var(--face-bg-tile, 220px) auto;
  background-repeat: repeat;
}

/* Faint paper tooth, and — when a face has a background — a wash of the
   face's own paper colour over it. Without this, tile text and the
   grown-up screen lose contrast against a busy painting. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  /* Was a crosshatch of two gradients meant to read as paper tooth. At
     phone scale it read as graph paper instead, and it was a fixed navy,
     so it laid a blue grid over the green and lilac papers. The layer
     stays for the wash a full-colour backdrop needs; the lines are gone. */
  background-color: var(--face-bg-wash, transparent);
  opacity: .5;
}
/* The wash needs full strength; the tooth gradients don't. */
body[data-bg-mode]::before { opacity: 1; }

.app {
  position: relative;
  z-index: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  padding-top: env(safe-area-inset-top);
}

/* ---------------- Top bar ---------------- */

.topbar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 16px 6px;
}

/* 68px is the floor. Below it the three overlapping LAs stop reading
   as letters — tested at 44, 56, 68 and 80. The lockup is already
   off-kilter, so no extra rotation here. */
/* Readable by search engines and screen readers, invisible on screen.
   The wordmark is a masked span, so without this the only h1 on the
   page has no text in it at all. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.logo {
  margin: 0;
  line-height: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

/* The wordmark is drawn as a flat silhouette, so it's painted as a mask
   and picks up the face's ink colour. A face that ships its own coloured
   wordmark gets a plain background-image instead. JS sets the pixel
   dimensions, since a masked span has no intrinsic size. */
.logo-art {
  display: block;
  /* Falls back to the face ink, so a face that says nothing looks
     exactly as it did. */
  background-color: var(--logo-ink, var(--ink));
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  -webkit-mask-image: var(--logo-src);
  mask-image: var(--logo-src);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

/* Sizes that work with no script at all. JS refines them once it knows
   the artwork's true aspect ratio. */
.logo .logo-art { width: 69px; height: 68px; }
.empty__logo { width: 170px; height: 168px; margin-bottom: 4px; }

/* A face shipping its own coloured wordmark: draw it, don't tint it. */
.logo-art--image {
  background-color: transparent;
  background-image: var(--logo-src);
  -webkit-mask-image: none;
  mask-image: none;
}

/* If masking isn't available, show the artwork as-is rather than
   nothing. Untinted, but present. */
@supports not ((-webkit-mask-image: url("")) or (mask-image: url(""))) {
  .logo-art {
    background-color: transparent;
    background-image: var(--logo-src);
  }
}

/* The paint dot. Deliberately unlabelled and boring-looking so a
   child has no reason to press it, and useless if they do. */
.parent-dot {
  width: 46px;
  height: 46px;
  border: 0;
  background: none;
  padding: 0;
  display: grid;
  place-items: center;
  cursor: pointer;
}
.parent-dot__ink {
  width: 17px;
  height: 17px;
  background: var(--paper-shade);
  border: 2px solid var(--ink-soft);
  border-radius: 62% 38% 55% 45% / 48% 60% 40% 52%;
  transition: transform .3s ease, background-color .3s ease;
}
/* Holding it fills the dot with ink and grows it. Using the face's own
   ink rather than a fixed yellow keeps it in palette, and the jump from
   pale to dark is a bigger change than any hue swap would be — which is
   what a grown-up needs to see while waiting out the 1.5 seconds. */
.parent-dot--holding .parent-dot__ink {
  transform: scale(1.7) rotate(25deg);
  background: var(--ink);
  border-color: var(--ink);
}







/* ---------------- Song grid ---------------- */

.grid {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  padding: 10px 16px 20px;
  align-content: start;
}

/* No frame. The mark is the thing a child aims at, and twelve boxes in
   a grid competed with twelve drawings for attention. The tap target is
   still the whole cell. */
.tile {
  position: relative;
  min-height: 132px;
  padding: 12px 10px 10px;
  border: 0;
  background: none;
  color: var(--type, var(--ink));
  font-family: inherit;
  font-size: 17px;
  line-height: 1.15;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  transition: transform .12s ease;
}
.tile:active { transform: scale(.94); }

.tile__blob {
  width: 62px;
  height: 62px;
  display: grid;
  place-items: center;
  font-size: 34px;
  line-height: 1;
  border-radius: 58% 42% 47% 53% / 51% 45% 55% 49%;
}

/* When a face has a backdrop, give the name a soft halo of paper so it
   always separates from whatever drifts behind it. Contrast alone isn't
   enough — a leaf passing under 16px text reads as clutter even at 11:1.
   Faces without a backdrop get nothing, so the default stays clean. */
body[data-bg-mode] .tile__name {
  text-shadow:
    0 0 7px var(--paper), 0 0 7px var(--paper),
    0 0 4px var(--paper), 0 0 2px var(--paper);
}

.tile__name {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 16px;
}

/* Playing state, without re-introducing a box: a drawn squiggle appears
   under the name. "The one with the wiggle is the one that's singing."
   Also nudges the mark so there is motion as well as a new element. */
.tile__wave {
  width: 74%;
  height: var(--rule-height, 11px);
  flex: 0 0 auto;
  background-color: var(--ink);
  -webkit-mask-image: var(--rule-src);
  mask-image: var(--rule-src);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  opacity: 0;
  transition: opacity .18s ease;
}
.tile--playing .tile__wave { opacity: 1; }
.tile--playing .tile__blob { transform: scale(1.06); }

@supports not ((-webkit-mask-image: url("")) or (mask-image: url(""))) {
  .tile__wave {
    background-color: transparent;
    background-image: var(--rule-src);
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
  }
}

/* ---------------- Empty state ---------------- */

.empty {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 0 36px 60px;
  text-align: center;
}
/* The lockup only holds together above about 120px, so the empty
   screen — which has room — is where it goes. The header uses type. */
/* One line here, not two — the wordmark is 170px wide with room beneath,
   so it reads as a title and subtitle rather than a lockup. Same colour
   as the wordmark, so they stay one thing. */
.empty__tagline {
  margin: 2px 0 14px;
  font-size: 19px;
  line-height: 1.25;
  color: var(--logo-ink, var(--ink));
}
.empty__text { margin: 6px 0 0; font-size: 24px; }
.empty__hint { margin: 0; font-size: 16px; color: var(--type-soft, var(--ink-soft)); line-height: 1.35; }

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

.player {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 26px;
  padding: 26px 16px calc(14px + env(safe-area-inset-bottom));
  background: var(--paper);
}

/* With a gradient behind, an opaque control strip would show as a flat
   band across the bottom. Nothing is painted behind the buttons on such
   a face, so it can let the gradient through. */
body[data-paper-gradient] .player { background: transparent; }
body[data-paper-gradient] .app { background: transparent; }

/* The squiggle divides the controls from the songs — the same drawing
   that marks the playing song and the selected face, so there isn't a
   single ruled line anywhere in the app. Stretched to the full width
   rather than tiled: the drawn stroke tapers at both ends, so repeating
   it leaves visible breaks at every join. Stretching a horizontal line
   along its own length only lengthens the waves — it doesn't thicken
   the stroke. */
.player::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 13px;
  background-color: var(--ink);
  -webkit-mask-image: var(--rule-src);
  mask-image: var(--rule-src);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
}
@supports not ((-webkit-mask-image: url("")) or (mask-image: url(""))) {
  .player::before {
    background-color: transparent;
    background-image: var(--rule-src);
    background-repeat: no-repeat;
    background-position: center;
    background-size: 100% 100%;
  }
}

/* A face can replace the drawn baseline with artwork — a horizon, a
   table edge, a wave for the buttons to stand on. */
/* A face can hand the whole control strip over to artwork — a horizon,
   a shelf, strawberries for the buttons to stand above. It anchors to
   the bottom and grows upward, and the row grows with it so the buttons
   sit clear rather than on top: dark ink on a dark painting measured
   1.7:1, well under the 3:1 a graphic needs to stay readable. */
body[data-shelf] .player {
  padding-bottom: calc(var(--shelf-height, 22px) + 10px + env(safe-area-inset-bottom));
}
body[data-shelf] .player::before {
  -webkit-mask-image: var(--shelf-src);
  mask-image: var(--shelf-src);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  height: var(--shelf-height, 22px);
  top: auto;
  /* Sits above the home indicator rather than under it — otherwise the
     bottom of the artwork is hidden by the system bar on any phone with
     a gesture area. */
  bottom: env(safe-area-inset-bottom, 0px);
}
@supports not ((-webkit-mask-image: url("")) or (mask-image: url(""))) {
  body[data-shelf] .player::before {
    background-size: 100% 100%;
  }
}

/* Full-colour shelf artwork — a painting rather than a silhouette.
   Multiply blending is what makes this work: white multiplied against
   the paper leaves the paper untouched, so the unpainted parts of the
   sheet vanish with no keying, and the washes darken the paper exactly
   the way real watercolour does. Keying the white out instead would
   punch holes through every pale highlight. */
body[data-shelf-image] .player::before {
  -webkit-mask-image: none;
  mask-image: none;
  background-color: transparent;
  background-image: var(--shelf-src);
  background-repeat: no-repeat;
  background-position: center bottom;
  background-size: 100% 100%;
  mix-blend-mode: multiply;
}

/* An opaque painting — one with no white to disappear — must not
   multiply, or its own colour is darkened by the paper beneath it. It
   covers instead, and the face picks a paper that matches its top edge
   so there is no seam. */
body[data-shelf-plain] .player::before { mix-blend-mode: normal; }

/* Kept for a face that wants it, but note the trap: mix-blend-mode only
   blends within the nearest stacking context, and .app creates one. On a
   face whose .app and .player are transparent there is no backdrop to
   blend against, so the artwork renders unblended. Artwork with real
   transparency and shelfBlend "normal" avoids the whole question. */
body[data-shelf-screen] .player::before { mix-blend-mode: screen; }
/* SIGNATURE ELEMENT
   Each button is an ink outline with a watercolour wash sitting
   slightly off-register behind it, the way a child fills in a
   colouring book. This is the one flourish — everything else stays
   quiet. Swap .ic for your own <svg> or <img> and the wash still works. */
.hand-btn {
  position: relative;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  display: grid;
  place-items: center;
  color: var(--type, var(--ink));
  flex: 0 0 auto;
  transition: transform .1s ease;
}
.hand-btn--big   { width: var(--tap-big); height: var(--tap-big); }
.hand-btn--small { width: var(--tap); height: var(--tap); }
.hand-btn:active { transform: scale(.92) rotate(-2deg); }
.hand-btn[disabled] { opacity: .3; }

.wash {
  position: absolute;
  inset: 6%;
  z-index: 0;
  opacity: .62;
  mix-blend-mode: multiply;
  border-radius: 54% 46% 49% 51% / 47% 52% 48% 53%;
  transform: translate(5px, -4px) rotate(-7deg);
}
.hand-btn--small .wash { transform: translate(3px, -3px) rotate(6deg); }
.wash--red   { background: var(--vermilion); }
.wash--blue  { background: var(--cerulean); }
.wash--green { background: var(--sap); }

/* A plain face drops the loose colour behind the buttons. Three
   different hues under three controls competes with the artwork and
   gives a child nothing useful — the shapes already carry the meaning. */
body[data-nowash] .wash { display: none; }

/* Plain tiles: no coloured disc, so the mark uses the whole space
   instead of sitting inside a circle. */
body[data-plain] .tile__blob {
  background: none;
  width: 72px;
  height: 72px;
  border-radius: 0;
  transform: none;
}
body[data-plain] .tile__blob .mark { width: 100%; height: 100%; }
body[data-plain] .tile--playing .tile__blob { transform: scale(1.06); }
body[data-plain] .erow__swatch {
  background: none;
  border-radius: 0;
}
body[data-plain] .erow__swatch .mark { width: 92%; height: 92%; }

/* A slot holds whichever icon is current — the built-in drawing, or a
   face's artwork. It's a <span> rather than the <svg> itself because
   `hidden` is an HTMLElement property and SVG elements don't have it. */
.slot {
  position: relative;
  z-index: 1;
  width: 62%;
  height: 62%;
  display: grid;
  place-items: center;
}
.slot[hidden] { display: none; }

.ic {
  width: 100%;
  height: 100%;
  fill: var(--ink);
  stroke: var(--ink);
  stroke-width: 3;
  stroke-linejoin: round;
}

/* A masked button silhouette. Takes the face's control colour if it
   sets one, otherwise the ink — so a face can put the transport in a
   colour of its own without touching the type. */
.ic--mask {
  display: block;
  background-color: var(--control-ink, var(--ink));
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  -webkit-mask-image: var(--btn-src);
  mask-image: var(--btn-src);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

/* No masking available: draw the artwork itself rather than nothing.
   Untinted, but visible. */
@supports not ((-webkit-mask-image: url("")) or (mask-image: url(""))) {
  .ic--mask {
    background-color: transparent;
    background-image: var(--btn-src);
  }
}

/* Face artwork. It fills more of the button than the ink drawings do,
   since a painting has its own margins built in. */
img.ic--art {
  object-fit: contain;
  -webkit-user-drag: none;
  user-select: none;
}
.hand-btn--art .slot { width: 96%; height: 96%; }

/* Artwork carries its own colour, so the wash behind it would only
   muddy the paint. */
.hand-btn--art .wash { display: none; }

/* ---------------- Sheets ---------------- */

.sheet {
  position: fixed;
  inset: 0;
  z-index: 20;
  background: rgba(38, 48, 74, .38);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.sheet[hidden] { display: none; }

.sheet__card {
  width: 100%;
  max-width: 420px;
  background: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 0;
  padding: 24px 20px;
  text-align: center;
}
.sheet--full { align-items: stretch; padding: 0; }
.sheet__card--full {
  max-width: none;
  border: 0;
  border-radius: 0;
  text-align: left;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: calc(16px + env(safe-area-inset-top)) 18px calc(28px + env(safe-area-inset-bottom));
}
.sheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}
/* Same wordmark as the kid screen, smaller. Ties the two together.
   Sized here as well as in JS so it never depends on a script running. */
.sheet__logo { flex: 0 0 auto; width: 45px; height: 44px; }
/* The About heading introduces the prose, so it sits with it on the
   face ink rather than with the neutral interface type. */
.sheet__title { margin: 0 0 8px; font-size: 25px; font-weight: normal; color: var(--ink); }

.gate__sum { font-size: 44px; margin: 4px 0 18px; letter-spacing: 2px; }
.gate__keys {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-bottom: 14px;
}
.gate__keys button {
  font-family: inherit;
  font-size: 24px;
  padding: 14px 0;
  color: var(--type, var(--ink));
  background: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 0;
  cursor: pointer;
}
.gate__keys button:active { background: var(--paper-shade); }

.plain-btn {
  font-family: inherit;
  font-size: 18px;
  color: var(--type, var(--ink));
  background: none;
  border: 0;
  padding: 10px 6px;
  cursor: pointer;
}
.plain-btn--danger { color: var(--danger); }

/* The input is laid transparently over the whole label and is itself
   the tap target. Nothing depends on label-activation, which is the
   part Safari gets wrong when the input is display:none. */
.add-btn {
  position: relative;
  overflow: hidden;
  display: inline-block;
  text-align: left;
  font-size: 22px;
  padding: 10px 0 calc(8px + var(--rule-height, 10px));
  margin: 8px 0 2px;
  color: var(--type, var(--ink));
  border: 0;
  cursor: pointer;
}
/* Same drawn rule as the selected face, so every underline in the app
   is the squiggle rather than a mix of drawn and ruled lines. */
.add-btn::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--rule-height, 10px);
  background-color: var(--ink);
  -webkit-mask-image: var(--rule-src);
  mask-image: var(--rule-src);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}
.add-btn:active { opacity: .55; }

@supports not ((-webkit-mask-image: url("")) or (mask-image: url(""))) {
  .add-btn::after {
    background-color: transparent;
    background-image: var(--rule-src);
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
  }
}

.add-btn input[type="file"] {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
  /* Older WebKit only registers taps on the button part of a file
     input; oversizing the text guarantees it covers the label. */
  font-size: 200px;
}
.add-btn span { position: relative; pointer-events: none; }

.note--build { font-size: 13px; opacity: .7; margin-top: 0; }
.note { font-size: 15px; color: var(--type-soft, var(--ink-soft)); margin: 6px 0; line-height: 1.4; }

/* ---------------- Grown-up editor rows ---------------- */

.editor { list-style: none; margin: 16px 0 0; padding: 0; }

.erow {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 0;
  border-bottom: 1px solid var(--paper-shade);
}
/* Bigger than the 48px tile slot on purpose. This is the only place a
   grown-up judges a mark, and hand-drawn line work needs the room —
   at 30px thin strokes fell below one pixel and broke up. */
.erow__swatch {
  width: 64px;
  height: 64px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  font-size: 30px;
  border: 0;
  padding: 0;
  cursor: pointer;
  border-radius: 56% 44% 50% 50% / 49% 47% 53% 51%;
}
.erow__name {
  flex: 1 1 auto;
  min-width: 0;
  font-family: inherit;
  font-size: 17px;
  color: var(--type, var(--ink));
  background: none;
  border: 0;
  border-bottom: 1.5px dashed var(--paper-shade);
  padding: 6px 2px;
}
.erow__btn {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  font-family: inherit;
  font-size: 17px;
  color: var(--type-soft, var(--ink-soft));
  background: none;
  border: 0;
  cursor: pointer;
}
.erow__btn--del { color: var(--danger); font-size: 20px; }

.danger { margin-top: 26px; }

/* Same drawn underline as Add songs, so every action on this screen
   reads the same way. */
.about-btn {
  position: relative;
  display: inline-block;
  font-family: inherit;
  font-size: 20px;
  color: var(--type, var(--ink));
  background: none;
  border: 0;
  padding: 10px 0 calc(8px + var(--rule-height, 10px));
  margin: 30px 0 0;
  cursor: pointer;
}
.about-btn::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--rule-height, 10px);
  background-color: var(--ink);
  -webkit-mask-image: var(--rule-src);
  mask-image: var(--rule-src);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}
.about-btn:active { opacity: .55; }

@supports not ((-webkit-mask-image: url("")) or (mask-image: url(""))) {
  .about-btn::after {
    background-color: transparent;
    background-image: var(--rule-src);
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
  }
}

/* A small drawing above the credits. Masked like everything else, so it
   takes the face's colour. */
/* Mark and colour picker. Six across sits comfortably on the narrowest
   phone and keeps every cell well past the 44px tap minimum. */
.picker-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 10px;
  margin: 10px 0 6px;
  /* The picker opens in a full-bleed sheet, which has no width cap — so on
     an iPad these cells were stretching to 150px+ while staying 52px tall,
     and a border-radius:50% swatch inside one is an ellipse, not a circle.
     6 x 52 + 5 x 10 holds the cells square on any screen wider than a
     phone. Below that the columns still shrink as they always did. */
  max-width: 362px;
}
.picker-cell {
  position: relative;
  display: grid;
  place-items: center;
  height: 52px;
  padding: 4px;
  background: none;
  border: 0;
  border-radius: 0;
  cursor: pointer;
}
.picker-cell .mark { width: 88%; height: 88%; }
.picker-cell[aria-pressed="true"] { background: var(--paper-shade); }
/* Already on another song — still choosable, just flagged. */
.picker-cell--used { opacity: .38; }
.picker-cell--used::after {
  content: "";
  position: absolute;
  right: 2px;
  bottom: 2px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--ink-soft);
}
/* Sized in pixels rather than percentages: a percentage of a cell that is
   wider than it is tall gives an oval. 40px is what 78% resolved to at the
   phone size this was drawn for. */
.picker-swatch {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 0;
}
.picker-grid--colours .picker-cell[aria-pressed="true"] .picker-swatch {
  box-shadow: 0 0 0 3px var(--paper), 0 0 0 5px var(--ink);
}

.credits {
  margin: 26px 0 8px;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ink-soft);
}

/* A way in: the opening paragraph set larger and in the wordmark's
   colour, so the page has a first line worth reading rather than an
   even wall of type. */
.prose__lead {
  font-size: 21px;
  line-height: 1.4;
  color: var(--logo-ink, var(--ink));
  margin-bottom: 4px;
}

/* The app's own argument, already the most quotable thing here. Pulled
   out rather than rewritten — the words and their order are unchanged. */
.prose__quote {
  margin: 22px 0;
  text-align: center;
  font-size: 22px;
  line-height: 1.45;
  color: var(--logo-ink, var(--ink));
}

/* The same squiggle that divides the controls and underlines the
   selected face. Used twice, not between every paragraph. */
.prose__rule {
  display: block;
  width: 150px;
  height: 11px;
  margin: 22px auto 20px;
  background-color: var(--ink-soft);
  -webkit-mask-image: var(--rule-src);
  mask-image: var(--rule-src);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}
@supports not ((-webkit-mask-image: url("")) or (mask-image: url(""))) {
  .prose__rule {
    background-color: transparent;
    background-image: var(--rule-src);
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
  }
}

.prose p {
  font-size: 17px;
  line-height: 1.5;
  color: var(--ink);
  margin-bottom: 16px;
  max-width: 34em;
}
.prose .credits { font-size: 14px; color: var(--ink-soft); }

/* A signature, not a call to action. */
/* Was .prose__signoff, scoped to the About page. The links moved out to the
   grown-up footer, so the name moved with them. */
.signoff {
  margin-top: 14px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--ink-soft);
}
.link {
  color: var(--ink);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}
.danger summary { font-size: 15px; color: var(--ink-soft); cursor: pointer; }

/* ---------------- Toast ---------------- */

.toast {
  position: fixed;
  left: 50%;
  bottom: calc(26px + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  z-index: 40;
  max-width: 84%;
  padding: 11px 18px;
  font-size: 16px;
  text-align: center;
  color: var(--paper);
  background: var(--ink);
  border-radius: 0;
}
.toast[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}

/* ---------------- Face picker ---------------- */

.sub {
  font-size: 21px;
  font-weight: normal;
  margin: 26px 0 2px;
  color: var(--type, var(--ink));
}


.faces {
  display: flex;
  gap: 22px;
  flex-wrap: wrap;
  margin: 10px 0 4px;
}

/* The selected face is marked with the same squiggle that marks the
   playing song, so "this is the active one" reads the same on both
   screens. */
.face-chip {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 8px 0 6px;
  font-family: inherit;
  font-size: 19px;
  color: var(--type-soft, var(--ink-soft));
  background: none;
  border: 0;
  cursor: pointer;
}
.face-chip__name {
  position: relative;
  display: block;
  padding-bottom: calc(4px + var(--rule-height, 10px));
}

.face-chip__name::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 2px;
  height: var(--rule-height, 10px);
  background-color: var(--ink);
  -webkit-mask-image: var(--rule-src);
  mask-image: var(--rule-src);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  opacity: 0;
  transition: opacity .18s ease;
}
.face-chip[aria-pressed="true"] { color: var(--ink); }

/* A row of the face's own colours, small enough to read as a hint
   rather than a control. Sits below the squiggle so the selected face
   still reads as selected. */
.face-chip__colors {
  display: flex;
  gap: 3px;
  margin-top: 7px;
  justify-content: flex-start;
}
.face-chip__colors span {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex: 0 0 auto;
}
.face-chip[aria-pressed="true"] .face-chip__name::after { opacity: 1; }

@supports not ((-webkit-mask-image: url("")) or (mask-image: url(""))) {
  .face-chip__name {
  position: relative;
  display: block;
  padding-bottom: calc(4px + var(--rule-height, 10px));
}

.face-chip__name::after {
    background-color: transparent;
    background-image: var(--rule-src);
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
  }
}

/* Drawn marks. The silhouette is a mask, not an image, so its colour is
   chosen per tile for contrast — one set of drawings works on every
   face and every pigment. */
.mark {
  width: 78%;
  height: 78%;
  display: block;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}
.erow__swatch .mark { width: 76%; height: 76%; }


/* ------------------------------------------------------------------
   Responsive layers, kept last on purpose.

   These override rules defined earlier in this file at the same
   specificity, so they only win if they come after them. Sitting
   higher up, the tablet shelf rules were silently dead and the wide
   layout stretched the artwork to 7.3:1.
   ------------------------------------------------------------------ */

/* Wider than a phone — iPad, or a desktop browser.
   Everything here is drawn for a phone: the shelf art stretches 267% at
   desktop width, and the two-column grid would give each song a 704pt
   tile. So the app stops at the width of the largest iPhone and centres
   itself, with the surround in the face's own paper-shade. Nothing in
   this block applies at 430px or below, so phones are untouched. */
@media (min-width: 431px) {
  body { background-color: var(--paper-shade); }
  .app {
    max-width: 430px;
    margin-left: auto;
    margin-right: auto;
    background: var(--paper);
  }
}

/* Tablet. The column widens and gains a third song per row, but the
   drawings stay 72px: the mark files are 144px, which is exactly 2x for
   that slot, so anything larger would upscale a brush line. More songs
   on screen is the gain here, not bigger ones.

   A face can supply shelfLarge for this width; without one it falls back
   to the phone shelf, which upscales 9% at 2x — barely visible. */
@media (min-width: 721px) {
  .app { max-width: 720px; }
  .grid { grid-template-columns: repeat(3, 1fr); }
  body[data-shelf] .player {
    padding-bottom: calc(var(--shelf-height-lg, 40px) + 10px + env(safe-area-inset-bottom));
  }
  body[data-shelf] .player::before {
    height: var(--shelf-height-lg, 40px);
    -webkit-mask-image: var(--shelf-src-lg, var(--shelf-src));
    mask-image: var(--shelf-src-lg, var(--shelf-src));
  }
  body[data-shelf-image] .player::before {
    background-image: var(--shelf-src-lg, var(--shelf-src));
  }
}

/* Landscape on a phone. The screen is barely 400pt tall, and the
   controls plus the shelf were eating 233 of them — leaving less than
   half a row of songs.

   The transport stays fixed rather than scrolling away with the page:
   a child mid-song needs the pause button under their thumb, not below
   the fold. Height is reclaimed instead — the shelf painting steps
   aside, the header and controls tighten, and the grid widens to four
   across to use the width landscape actually gives.

   The squiggle divider stays. It costs 13pt and it is the one thing
   that keeps the screen looking like the app. */
@media (orientation: landscape) and (max-height: 500px) {
  .app { max-width: none; }
  .grid { grid-template-columns: repeat(4, 1fr); }

  .topbar { padding: 8px 16px 4px; }
  .logo .logo-art { width: 44px; height: 44px; }

  .tile { min-height: 104px; padding: 8px 8px 6px; }
  body[data-plain] .tile__blob { width: 56px; height: 56px; }
  .tile__name { font-size: 14px; }

  .player { padding: 12px 16px calc(10px + env(safe-area-inset-bottom)); gap: 22px; }
  body[data-shelf] .player { padding-bottom: calc(10px + env(safe-area-inset-bottom)); }
  /* The painting needs height the screen does not have here. */
  body[data-shelf] .player::before,
  body[data-shelf-image] .player::before {
    -webkit-mask-image: var(--rule-src);
    mask-image: var(--rule-src);
    background-image: none;
    background-color: var(--ink);
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
    mix-blend-mode: normal;
    height: 13px;
    top: 0;
    bottom: auto;
  }
  .hand-btn { --tap: 52px; --tap-big: 66px; }

  .empty { padding: 0 24px 12px; }
  .empty__logo { width: 96px; height: 95px; }
  .empty__tagline { font-size: 16px; margin: 0 0 6px; }
  .empty__text { font-size: 19px; margin: 2px 0 0; }
  .empty__hint { font-size: 14px; }
}
