/* Matches SMHW1 (Droplet) throughout, not just the controls: black-on-
   white, thin gray borders, monospace, sharp corners, no colour except the
   one red record light. A Rorschach blot is itself black ink on white
   paper, so a white page makes the picture and the chrome read as one
   surface instead of a dark void the image sits in. */
* { box-sizing: border-box; }
html, body {
  margin: 0;
  width: 100%;
  height: 100%;
  background: #fff;
  color: #111;
  font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  overflow: hidden;
}

/* The right of the screen, above the say bar's band (#sayband); the left
   column (36vw) is the words' paragraph (see main.js's placeWords), so none
   of it ever sits on the picture. A class, not #shown: for the length of a swap
   the outgoing picture is still here, underneath the incoming one. */
.picture {
  position: absolute;
  top: 0;
  left: 36vw;
  width: 64vw;
  height: calc(100% - 88px);
  /* contain, not cover — the image is a 1:1 ink blot, and cover was
     cropping ~40% off top and bottom on a landscape screen (visible in
     Cody's screenshot: the top of the head was already cut off). Now the
     page is white and the blot is on white paper, so contain's letterbox
     bars blend in rather than showing as a dark void. */
  object-fit: contain;
  background: #fff;
  /* Clear, crisp black and white whatever the model sends back: no sepia
     or colour tint (grayscale), and greys pushed toward black or white. */
  filter: grayscale(1) contrast(1.35);
  /* No hard edge where the picture stops and the page begins: it fades to
     the white page in a circle the size of the picture (closest-side is
     half the shorter side, which with contain is the picture's own half
     width), so it and the words beside it are one surface. */
  -webkit-mask-image: radial-gradient(circle closest-side, #000 85%, transparent 100%);
  mask-image: radial-gradient(circle closest-side, #000 85%, transparent 100%);
  /* main.js's swapIn() sets opacity 0 on the incoming <img>, inserts it
     over the outgoing one, then flips this to 1 on the next frame — the
     transition is what turns that into a crossfade rather than a cut. */
  transition: opacity 0.6s ease;
}

/* The type box and record button's own section: the band across the whole
   bottom of the screen, with the say bar centred in it. */
#sayband {
  position: fixed;
  left: 0;
  width: 100%;
  bottom: 0;
  height: 88px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-top: 1px solid #eee;
}
#say {
  width: 30vw;
  display: flex;
  align-items: center;
  gap: 8px;
}
#type {
  flex: 1 1 auto;
  min-width: 0;
  padding: 9px 12px;
  border: 1px solid #ccc;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.86);
  font: inherit;
  color: #111;
}
#type:focus { outline: none; border-color: #111; }

/* Droplet's own micbtn, verbatim — the one red thing in a black-and-white
   piece, because a record light is a record light and needs no label. */
#micbtn {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1px solid #ccc;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.86);
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: border-color 0.15s;
  font: inherit;
  color: #888;
}
#micbtn span {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: #bbb;
  transition: background 0.15s;
}
#micbtn:hover { border-color: #111; }
#micbtn:focus-visible { outline: 2px solid #111; outline-offset: 2px; }
#micbtn.on { border-color: #c00; }
#micbtn.on span { background: #c00; animation: listening 1.4s ease-in-out infinite; }
/* it could not start — hollow, so it never reads as recording */
#micbtn.bad { border-color: #c00; }
#micbtn.bad span { background: transparent; box-shadow: inset 0 0 0 2px #c00; }
@keyframes listening { 50% { opacity: 0.3; } }
@media (prefers-reduced-motion: reduce) {
  #micbtn.on span { animation: none; }
}

/* #panel — Droplet's own #panel, verbatim from SMHW1/style.css: fixed to
   the right edge, scrollable, hidden by default with Esc the only way to
   it ("what a visitor gets is the piece and nothing else"). Holds the
   status readout (#hud, now just a row inside it) and the live-editable
   image prompts. */
#panel {
  position: fixed;
  top: 0;
  right: 0;
  z-index: 2;
  width: 340px;
  max-width: 90vw;
  height: 100%;
  overflow-y: auto;
  padding: 12px 12px 40px;
  background: rgba(255, 255, 255, 0.94);
  border-left: 1px solid #e2e2e2;
}
#panel.hidden { display: none; }

#hud {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6em;
  color: #888;
  margin: 0 0 10px;
}
#hud .note { color: #888; }
.sep { opacity: 0.5; }

#panel details { border-top: 1px solid #eee; padding: 4px 0; }
#panel summary { cursor: pointer; color: #555; padding: 3px 0; user-select: none; }
#panel summary:hover { color: #111; }

.promptlabel {
  display: block;
  color: #666;
  font-size: 11px;
  margin: 8px 0 4px;
}
.promptlabel textarea {
  display: block;
  width: 100%;
  margin-top: 4px;
  padding: 6px;
  border: 1px solid #ddd;
  background: #fafafa;
  color: #111;
  font: 11px/1.45 ui-monospace, monospace;
  resize: vertical;
  box-sizing: border-box;
}

/* The outskirts water effect — Droplet's Pool+Renderer, full-viewport and
   otherwise untouched, but masked to only show a border ring: transparent
   (hidden) through most of the middle where the image and the rest of the
   piece live, opaque (shown) toward the edges. pointer-events: none so it
   never steals clicks meant for the say bar or the corner controls, which
   sit inside the very region this reveals. */
#dropletcanvas {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  mask-image: radial-gradient(
    ellipse farthest-corner at center,
    transparent 0%,
    transparent 62%,
    black 82%,
    black 100%
  );
  -webkit-mask-image: radial-gradient(
    ellipse farthest-corner at center,
    transparent 0%,
    transparent 62%,
    black 82%,
    black 100%
  );
}

/* The corner preview: eyeviz draws on top, the video underneath is never
   shown. opacity, not display:none — an undisplayed video can have its
   frame delivery throttled, which would silently stall detection. */
#camwrap {
  position: fixed;
  right: 1em;
  top: 1em;
  width: 8em;
  height: 6em;
  border-radius: 2px;
  border: 1px solid #ccc;
  overflow: hidden;
  transform: scaleX(-1); /* mirror, so gaze reads like a reflection would */
}
#cam, #eyeviz {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
#cam { opacity: 0; object-fit: cover; }
#eyeviz { background: rgba(255, 255, 255, 0.86); }

/* Words form here as they're captured — typed or spoken — wrapping left to
   right like ordinary reading text. The canvas covers the screen so words
   can fly into the picture, but they only form inside the left column
   that main.js gives wordsviz. src/wordsviz.js draws each letter as
   a white halo stroke with black ink on top — legible against anything
   underneath without relying on mix-blend-mode, which was the original
   approach here and was replaced after a report of it not rendering; a
   later GPT review found the actual cause was unrelated to blend-mode
   (WordsViz.clear() wasn't clearing the canvas pixels, only its own word
   list — fixed in wordsviz.js), but the halo stroke is a strictly more
   robust technique regardless and was kept. */
#words {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

@media (max-width: 600px) {
  #camwrap { width: 5em; height: 3.75em; }
  #sayband { left: 0; width: 100%; }
  #say { width: 90vw; }
}
