/*
 * Jersey 10 (SIL Open Font License, see fonts/OFL-jersey-10.txt), for the
 * assistant's name, headings and buttons. Body text stays in Inter for reading.
 * It has one weight, so the browser mustn't fake a bold version of it.
 */
@font-face {
  font-family: 'Jersey 10';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/jersey-10-latin-7b088b77.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Jersey 10';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/jersey-10-latin-ext-c36a723f.woff2") format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
.font-pixel,
.pixel-button { font-weight: 400; font-synthesis: none; letter-spacing: 0.02em; }

/*
 * Brand stripe: the seven meeple colours, as on the TV's game shelf rules card.
 * The assistant uses it as the top and bottom edges of the app.
 */
.brand-stripe {
  background: linear-gradient(90deg, #ef4444 0 14.28%, #f97316 0 28.57%, #eab308 0 42.85%, #22c55e 0 57.14%, #3b82f6 0 71.42%, #a855f7 0 85.71%, #ec4899 0);
}

/*
 * The whole assistant screen is the monitor: faint scanlines and a vignette
 * over the navy, so the character looks like it lives in the screen.
 */
.assistant-backdrop {
  background-color: #0e1025;
  background-image:
    radial-gradient(ellipse at 50% 30%, color-mix(in srgb, var(--assistant-colour) 12%, transparent) 0%, transparent 60%),
    repeating-linear-gradient(0deg, rgb(255 255 255 / 0.018) 0 1px, transparent 1px 3px);
}

/*
 * The assistant's face (AssistantFaceComponent). --assistant-colour is today's
 * meeple colour; data-state is set by the assistant-face controller.
 *
 * hero: frameless, glowing on the screen itself. compact/avatar: a little screen.
 */
.assistant-face {
  --face-size: 11rem;
  --face-radius: 1.75rem;
  width: var(--face-size);
  flex-shrink: 0;
  border-radius: var(--face-radius);
  overflow: hidden;
  background: #070817;
  box-shadow:
    0 0 0 1px rgb(255 255 255 / 0.08),
    0 18px 40px -12px rgb(0 0 0 / 0.7),
    0 0 48px -8px color-mix(in srgb, var(--assistant-colour) 45%, transparent);
  transition: width 400ms ease;
}
.assistant-face--compact { --face-size: 3.5rem; --face-radius: 0.875rem; }
.assistant-face--avatar { --face-size: 2rem; --face-radius: 0.625rem; }
.assistant-face--hero { background: none; box-shadow: none; overflow: visible; }

.assistant-face__screen {
  position: relative;
  /* The sprite canvas is 660 × 610 */
  aspect-ratio: 660 / 610;
  background: radial-gradient(circle at 50% 40%, color-mix(in srgb, var(--assistant-colour) 30%, #0e1025) 0%, #0e1025 72%);
}
.assistant-face--hero .assistant-face__screen { background: none; }

/* The sprite layers sit exactly on top of each other */
.assistant-face__figure,
.assistant-face__layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.assistant-face__layer[hidden] { display: none; }
/* Centre the character: --art-offset-x comes from Settings.assistant.art_offset_x */
.assistant-face__screen > :is(.assistant-face__layer, .assistant-face__figure) { translate: var(--art-offset-x, 0%) 0; }
.assistant-face__figure .assistant-face__layer { translate: none; }
.assistant-face__figure:has(.assistant-face__placeholder) { display: flex; align-items: center; justify-content: center; }

/* Scanlines over the character, like an old monitor */
.assistant-face .assistant-face__screen::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    repeating-linear-gradient(0deg, rgb(0 0 0 / 0.22) 0 1px, transparent 1px 3px),
    radial-gradient(circle at 50% 50%, transparent 55%, rgb(0 0 0 / 0.45) 100%);
}
/* The hero has no frame, so its scanlines fade out around the character instead of ending in a square */
.assistant-face--hero .assistant-face__screen::after {
  background: repeating-linear-gradient(0deg, rgb(0 0 0 / 0.28) 0 1px, transparent 1px 3px);
  -webkit-mask-image: radial-gradient(circle at 50% 50%, #000 45%, transparent 72%);
  mask-image: radial-gradient(circle at 50% 50%, #000 45%, transparent 72%);
}
.assistant-face--avatar .assistant-face__screen::after { background: radial-gradient(circle at 50% 50%, transparent 55%, rgb(0 0 0 / 0.35) 100%) !important; }

/* The little sizes are mostly face: zoom in on the head */
.assistant-face--avatar .assistant-face__screen > *,
.assistant-face--compact .assistant-face__screen > * { transform-origin: 46% 28%; scale: 1.7; }

.assistant-face__placeholder {
  width: 52%;
  height: auto;
  color: var(--assistant-colour);
  filter: drop-shadow(0 0 12px color-mix(in srgb, var(--assistant-colour) 60%, transparent));
}

/* Movement: the figure breathes and reacts; the blob behind stays put. Stepped, to suit pixel art */
.assistant-face__figure { animation: assistant-breathe 3.2s steps(2) infinite; }
.assistant-face[data-state="listening"] .assistant-face__figure { animation: assistant-lean 1.6s steps(2) infinite; }
.assistant-face[data-state="thinking"] .assistant-face__figure { animation: assistant-ponder 2.4s steps(3) infinite; }
.assistant-face[data-state="success"] .assistant-face__figure { animation: assistant-hop 0.5s steps(3) 2; }
.assistant-face[data-state="error"] .assistant-face__figure { animation: assistant-shake 0.4s steps(4) 2; }
.assistant-face__reaction:not([hidden]) { animation: assistant-pop 240ms steps(3); }
.assistant-face[data-state="thinking"] .assistant-face__reaction:not([hidden]) { animation: assistant-blink 1s steps(2) infinite; }
.assistant-face[data-state="working"] .assistant-face__reaction:not([hidden]) { animation: assistant-pulse 0.8s steps(2) infinite; }

@keyframes assistant-breathe { 50% { transform: translateY(1%); } }
@keyframes assistant-lean { 50% { transform: translateY(-1.5%); } }
@keyframes assistant-ponder { 33% { transform: translateX(-1.5%); } 66% { transform: translateX(1.5%); } }
@keyframes assistant-hop { 50% { transform: translateY(-4%); } }
@keyframes assistant-shake { 25% { transform: translateX(-2%); } 75% { transform: translateX(2%); } }
@keyframes assistant-pop { 0% { transform: translateY(4%); opacity: 0; } }
@keyframes assistant-blink { 50% { opacity: 0.2; } }
@keyframes assistant-pulse { 50% { opacity: 0.55; } }

/*
 * Chat screen. Waiting (no conversation yet): the face takes all the room the
 * welcome and the prompt leave, like Holly filling the monitor. Chatting
 * (.is-chatting): it steps back to a stage at the top and the replies come up
 * beneath it as speech bubbles.
 */
.assistant-chat .assistant-header { flex: 1 1 0%; min-height: 0; justify-content: center; container-type: size; }
.assistant-chat .assistant-header .assistant-face { --face-size: min(100cqw, (100cqh - 6.5rem) * 660 / 610, 36rem); }
.assistant-chat.is-chatting .assistant-header { flex: none; container-type: normal; padding-top: 0.75rem; }
.assistant-chat.is-chatting .assistant-header .assistant-face { --face-size: clamp(5rem, 22dvh, 11rem); }

.assistant-chat .conversation { flex: none; }
.assistant-chat.is-chatting .conversation {
  flex: 1 1 0%;
  /* Older messages fade away under the stage as they scroll up */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 2rem);
  mask-image: linear-gradient(to bottom, transparent 0, #000 2rem);
}

.assistant-chat-only { display: none; }
.is-chatting .assistant-chat-only { display: flex; }
.is-chatting .assistant-hero-only { display: none; }

@keyframes assistant-blink-cursor { 50% { opacity: 0; } }
.assistant-status-dot { animation: assistant-blink-cursor 1.2s steps(2) infinite; }

/*
 * Pixel box: a hard border with notched corners, drawn outside the box with
 * shadows so it takes no room (hence the margin). Used for the chat bubbles and
 * the question box. --pixel-border sets the border colour.
 */
:where(.pixel-box) {
  --px: 3px;
  --pixel-border: rgb(255 255 255 / 0.12);
}
.pixel-box {
  margin: var(--px);
  border-radius: 0;
  box-shadow:
    0 calc(var(--px) * -1) 0 0 var(--pixel-border),
    0 var(--px) 0 0 var(--pixel-border),
    calc(var(--px) * -1) 0 0 0 var(--pixel-border),
    var(--px) 0 0 0 var(--pixel-border);
}

/*
 * Speech-bubble tails for pixel boxes: stepped, in the border colour.
 *   --up         centred, pointing up at the character (the welcome)
 *   --up-left    the character's replies, pointing up at the character
 *   --down-right the customer's messages, pointing down towards them
 */
.pixel-tail { position: relative; }
.pixel-tail::before {
  content: '';
  position: absolute;
  width: calc(var(--px) * 6);
  height: calc(var(--px) * 6);
  background: var(--pixel-border);
}
.pixel-tail--up::before {
  bottom: 100%;
  left: 50%;
  height: calc(var(--px) * 3);
  margin-bottom: var(--px);
  transform: translateX(-50%);
  clip-path: polygon(0 100%, 0 67%, 17% 67%, 17% 33%, 33% 33%, 33% 0, 67% 0, 67% 33%, 83% 33%, 83% 67%, 100% 67%, 100% 100%);
}
.pixel-tail--up-left::before {
  bottom: 100%;
  left: calc(var(--px) * 4);
  margin-bottom: var(--px);
  clip-path: polygon(0 0, 33.33% 0, 33.33% 33.33%, 66.67% 33.33%, 66.67% 66.67%, 100% 66.67%, 100% 100%, 0 100%);
}
.pixel-tail--down-right::before {
  top: 100%;
  right: calc(var(--px) * 4);
  margin-top: var(--px);
  clip-path: polygon(0 0, 100% 0, 100% 100%, 66.67% 100%, 66.67% 66.67%, 33.33% 66.67%, 33.33% 33.33%, 0 33.33%);
}

/*
 * Pixel button: stepped corners and a hard bevel, like a button in an old game.
 * --px is the size of one "pixel"; the fill defaults to today's colour.
 * Defaults sit in :where() so a utility such as [--px:5px] can override them.
 */
:where(.pixel-button) {
  --px: 4px;
  --pixel-fill: var(--assistant-colour);
}
.pixel-button {
  position: relative;
  border: 0;
  border-radius: 0;
  background: var(--pixel-fill);
  color: #fff;
  font-family: 'Jersey 10', ui-monospace, monospace;
  clip-path: polygon(
    0 calc(var(--px) * 2), var(--px) calc(var(--px) * 2), var(--px) var(--px), calc(var(--px) * 2) var(--px), calc(var(--px) * 2) 0,
    calc(100% - var(--px) * 2) 0, calc(100% - var(--px) * 2) var(--px), calc(100% - var(--px)) var(--px), calc(100% - var(--px)) calc(var(--px) * 2), 100% calc(var(--px) * 2),
    100% calc(100% - var(--px) * 2), calc(100% - var(--px)) calc(100% - var(--px) * 2), calc(100% - var(--px)) calc(100% - var(--px)), calc(100% - var(--px) * 2) calc(100% - var(--px)), calc(100% - var(--px) * 2) 100%,
    calc(var(--px) * 2) 100%, calc(var(--px) * 2) calc(100% - var(--px)), var(--px) calc(100% - var(--px)), var(--px) calc(100% - var(--px) * 2), 0 calc(100% - var(--px) * 2)
  );
  box-shadow:
    inset 0 calc(var(--px) * -1) 0 rgb(0 0 0 / 0.35),
    inset 0 var(--px) 0 rgb(255 255 255 / 0.25);
  transition: filter 120ms, transform 60ms;
}
.pixel-button:hover { filter: brightness(1.12); }
.pixel-button:active {
  transform: translateY(calc(var(--px) / 2));
  box-shadow: inset 0 calc(var(--px) / -2) 0 rgb(0 0 0 / 0.35), inset 0 calc(var(--px) / 2) 0 rgb(255 255 255 / 0.2);
}
.pixel-button:focus-visible {
  outline: none;
  box-shadow:
    inset 0 calc(var(--px) * -1) 0 rgb(0 0 0 / 0.35),
    inset 0 var(--px) 0 rgb(255 255 255 / 0.25),
    inset 0 0 0 calc(var(--px) * 1.5) rgb(255 255 255 / 0.7);
}

/* The customer's own messages, tinted with today's colour */
.assistant-bubble-user {
  background: color-mix(in srgb, var(--assistant-colour) 30%, #161a36);
  --pixel-border: color-mix(in srgb, var(--assistant-colour) 55%, #161a36);
}

/* Kiosk welcome screen: the face as big as the screen allows */
.assistant-landing .assistant-face { --face-size: min(86vw, 50dvh, 36rem); }

@media (prefers-reduced-motion: reduce) {
  .assistant-face,
  .assistant-face *,
  .assistant-face *::after { animation: none !important; transition: none !important; }
}
