/* VENDORED, UNMODIFIED except this header.
   Read-only copy of the JUWEL product design system (face-next/css/porcelain.css), refreshed 2026-10-28 and re-checked against its current main branch (no rule has changed).
   Do not edit by hand: re-export from origin/main. The film cascade overrides body background and layering in base.css, never here. */

/* face-next / Porcelain — the shared "conversation first" style layer for the whole app.
   Canon: Figma BT8PA8nvRjJQ1M0C13D0QX 733:1098 (home G0 J1 1.1 982:36530 / no approved frame (GAP-96), conversation G0 J2 2.1 979:48978 / no approved frame (GAP-104),
   bot-first screens no approved frame (GAP-95), states no approved frame (GAP-124), workspace + settings no approved frame (GAP-129)). Spec: SPEC-666-1098.
   Every --pf-* value maps onto an existing --jb-* token (assets/bubbly/tokens.css), so the dark theme and
   data-solid keep working; no new raw colours. Linked last so screen CSS can opt in with the pf-* classes
   below and the shared overrides win the cascade. Strict CSP: this file is the only place these rules live.

   DEPTH (founder ruling 2026-10-08, launch day: the JUWEL face is "way too similar to grokbot"; work item C2 / VEX-798
   under VEX-795; docs/design/JUWEL_DEPTH_RULING_2026-10-08.md is C0's amendment). The ruling overrides, for the JUWEL
   face only: Simplicity Overrides section 1.8 (no decorative backdrop), Bubbly canon 2.1 #2 and #6 (no animated blur,
   no ambient iridescence) and SPEC-666-1098's flat Porcelain look (no glass, no shadow, no sphere). It does not
   override the ATTACHMENT LAW in js/mascot.js. This file now carries the shared depth material: the page wash, the
   floating glass header bar, bubbles with elevation, the faceted composer / send gem and the starter cards.
   Figma: Depth kit no approved frame (GAP-149) (header bar no approved frame (GAP-150), composer tray no approved frame (GAP-151), starter cards no approved frame (GAP-152) / no approved frame (GAP-153)), home
   no approved frame (GAP-155) / no approved frame (GAP-159) (desktop) and no approved frame (GAP-156) / no approved frame (GAP-160) (phone), thread no approved frame (GAP-157) / no approved frame (GAP-161) (desktop) and
   no approved frame (GAP-158) / no approved frame (GAP-162) (phone). Tokens: --jb-depth-* in assets/bubbly/tokens.css. */

:root {
  --pf-paper: var(--jb-paper);
  --pf-surface: var(--jb-surface);
  --pf-ink: var(--jb-ink);
  --pf-ink-2: var(--jb-ink-2);
  --pf-line: var(--jb-line);
  --pf-topaz-soft: var(--jb-bubble-you);
  --pf-focus: var(--jb-focus-ring);

  --pf-r16: var(--jb-r-16);
  --pf-r24: 24px;
  --pf-r32: var(--jb-r-32);
  --pf-r999: var(--jb-r-full);

  --pf-s8: var(--jb-s-8);
  --pf-s16: var(--jb-s-16);
  --pf-s20: var(--jb-s-20);
  --pf-s32: var(--jb-s-32);

  /* Type (Nunito): title 800 28/34 · heading 800 20/26 · body 400 16/23 · label 700 14/20 · caption 400 12/17 */
  --pf-type-title: 800 28px/34px var(--jb-font-ui);
  --pf-type-heading: 800 20px/26px var(--jb-font-ui);
  --pf-type-body: 400 16px/23px var(--jb-font-ui);
  --pf-type-label: 700 14px/20px var(--jb-font-ui);
  --pf-type-caption: 400 12px/17px var(--jb-font-ui);

  --pf-target: var(--jb-target);
  --pf-input-h: var(--jb-control-h);
  --pf-header-h: 52px;
  /* Depth: the conversation bar is the same floating glass bar as home (56 desktop / 52 phone), so the presence
     pill (presence.css) stays centred in it and the 64px companion overhangs its top edge. */
  --pf-header-h-bot: var(--jb-depth-bar-h);
  --pf-composer-h: var(--jb-depth-composer-h);

  /* Column widths (666): home composer and welcome 660 (no approved frame (GAP-116) / no approved frame (GAP-118)), conversation 720 on desktop, 350 on a 390 phone
     (20px gutters). */
  --pf-col-home: 660px;
  --pf-col-copy: 600px;
  --pf-col-thread: 720px;
  --pf-gutter: var(--pf-s20);

  /* Mascot tiers (666): home hero 220 desktop / 200 phone (no approved frame (GAP-117) / no approved frame (GAP-101), freestanding), capped on short screens (32dvh /
     30dvh: both are the full size from 688px of height up, so 1440x900 and 390x844 are exact) so the copy and the composer keep
     their room; conversation header companion 64 (44 on a phone). */
  --pf-mark-home: min(220px, 32dvh);
  --pf-mark-home-sm: min(200px, 30dvh);
  --pf-mark-header: 64px;
  --pf-glyph-h: 32px;
}

@media (min-width: 768px) {
  :root { --pf-gutter: var(--pf-s32); }
}
@media (max-width: 767.98px) {
  :root { --pf-mark-header: 44px; }
}

/* Page: flat paper under the depth wash (body::before below), so a browser without :has() still shows paper. */
body { background: var(--pf-paper); color: var(--pf-ink); }

/* Focus: 2px violet ring, 3px offset, everywhere a pf control lives. */
.pf-btn:focus-visible,
.pf-iconbtn:focus-visible,
.pf-send:focus-visible,
.pf-row:focus-visible,
.pf-input:focus-visible,
.pf-starter:focus-visible {
  outline: 2px solid var(--pf-focus);
  outline-offset: 3px;
}

/* Header: no card, no pill. Glyph32 mark (Glyph28 on a phone) + "JUWEL" heading 20/26, gap 12; quiet buttons on the right. */
.pf-header {
  display: flex;
  align-items: center;
  gap: var(--pf-s8);
  min-block-size: var(--pf-header-h);
  background: none;
  border: 0;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.pf-header--bot { min-block-size: var(--pf-header-h-bot); }

/* The face's header (main.js renderTopbar, .jb-topbar) is this same plain row (666: no approved frame (GAP-114)): no card, no glass, no rim, no
   elevation. The 2026-10-08 glass bar (no approved frame (GAP-150)) is retired by ruling 42 / 43 (VEX-1006). */
.pf-brand { display: inline-flex; align-items: center; gap: 12px; color: var(--pf-ink); text-decoration: none; }
.pf-brand__mark { block-size: var(--pf-glyph-h); inline-size: auto; flex: none; }
.pf-brand__name { font: var(--pf-type-heading); letter-spacing: 0; }
.pf-header__spacer { flex: 1 1 auto; min-inline-size: 0; }
.pf-header__actions { display: flex; align-items: center; gap: var(--pf-s8); }

/* Buttons. Secondary = white, 1px line, r16, 44h, label 700 14/20. Quiet = no chrome, ink-2 label.
   Primary = filled ink, r16. All native <button>s with a 44px minimum target. */
.pf-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--pf-s8);
  min-block-size: var(--pf-target);
  min-inline-size: var(--pf-target);
  padding: 0 var(--pf-s16);
  border-radius: var(--pf-r16);
  font: var(--pf-type-label);
  letter-spacing: 0;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  /* Compositor-only motion (e2e motion-compositor-only): hover colour changes snap, only opacity eases. */
  transition: opacity var(--jb-fade-fast-dur) var(--jb-fade-fast);
}
.pf-btn--secondary { background: var(--pf-surface); color: var(--pf-ink); border: 1px solid var(--pf-line); box-shadow: none; }
.pf-btn--secondary:hover { background: var(--jb-surface-2); }
.pf-btn--quiet { background: transparent; color: var(--pf-ink-2); border: 1px solid transparent; box-shadow: none; padding-inline: var(--pf-s8); }
.pf-btn--quiet:hover { color: var(--pf-ink); background: var(--jb-surface-2); }
.pf-btn--primary { background: var(--pf-ink); color: var(--pf-surface); border: 1px solid var(--pf-ink); box-shadow: none; }
.pf-btn--primary:hover { opacity: .88; }
.pf-btn:disabled, .pf-btn[aria-disabled="true"] { opacity: .44; cursor: default; }

/* Square/circle icon buttons (attach "+", sheet close, Back chevron). */
.pf-iconbtn {
  display: inline-grid;
  place-items: center;
  inline-size: var(--pf-target);
  block-size: var(--pf-target);
  flex: none;
  padding: 0;
  border: 0;
  border-radius: var(--pf-r999);
  background: transparent;
  color: var(--pf-ink-2);
  cursor: pointer;
}
.pf-iconbtn:hover { background: var(--jb-surface-2); color: var(--pf-ink); }

/* Send gem (Figma no approved frame (GAP-151)): a 48px hit box; the drawn shape is a 34px square rotated 45deg (it echoes the crown), r8.
   Empty = ink diamond at .32; with text = the irid diamond with an ink arrow; streaming = the stop square. The face's
   own send button is .jb-composer__action (composer.css); .pf-send is the same shape for any other shell. */
.pf-send {
  position: relative;
  display: inline-grid;
  place-items: center;
  inline-size: var(--jb-depth-send);
  block-size: var(--jb-depth-send);
  flex: none;
  padding: 0;
  border: 0;
  border-radius: var(--jb-depth-facet-bubble);
  background: transparent;
  color: var(--jb-depth-on-irid);
  cursor: pointer;
}
.pf-send::before {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  inline-size: 34px;
  block-size: 34px;
  border-radius: var(--jb-depth-facet-bubble);
  background: var(--jb-irid);
  box-shadow: var(--jb-depth-e1);
  transform: rotate(45deg);
  pointer-events: none;
}
.pf-send > * { position: relative; }
.pf-send:disabled, .pf-send[aria-disabled="true"] { color: var(--pf-surface); cursor: default; }
.pf-send:disabled::before, .pf-send[aria-disabled="true"]::before { background: var(--pf-ink); box-shadow: none; opacity: .32; }

/* Composer tray (Figma no approved frame (GAP-151)): faceted glass, not a pill. 72h desktop / 64h phone, chamfered corners
   (corner-shape: bevel where supported, a 14 / 10px rounded rectangle otherwise: never a full radius), the rim, e2 and
   a 1px static irid hairline along the top edge. The face composer is .jb-composer (composer.css, same material). */
.pf-composer {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--pf-s8);
  min-block-size: var(--pf-composer-h);
  padding: 0 var(--pf-s8);
  border: 0;
  border-radius: var(--jb-depth-facet-sm);
  background: var(--jb-depth-facet-light), var(--jb-depth-glass-bg);
  -webkit-backdrop-filter: blur(var(--jb-depth-glass-blur)) saturate(var(--jb-depth-glass-sat));
  backdrop-filter: blur(var(--jb-depth-glass-blur)) saturate(var(--jb-depth-glass-sat));
  box-shadow: var(--jb-depth-rim), var(--jb-depth-e2);
}
@media (min-width: 768px) {
  .pf-composer { border-radius: var(--jb-depth-facet); }
}
@supports (corner-shape: bevel) {
  .pf-composer { corner-shape: bevel; }
}
.pf-composer::after {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 24px;
  block-size: 1px;
  background: var(--jb-depth-irid-hair);
  opacity: .7;
  pointer-events: none;
}
.pf-composer__input {
  flex: 1 1 auto;
  min-inline-size: 0;
  border: 0;
  background: transparent;
  font: var(--pf-type-body);
  color: var(--pf-ink);
  resize: none;
}
.pf-composer__input::placeholder { color: var(--pf-ink-2); opacity: 1; }
.pf-composer:focus-within { outline: 2px solid var(--pf-focus); outline-offset: 2px; }
:root[data-solid="on"] .pf-composer { -webkit-backdrop-filter: none; backdrop-filter: none; }
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
  .pf-composer { -webkit-backdrop-filter: none; backdrop-filter: none; }
}

/* Freestanding mascot: NO sphere, NO glass, NO shadow — just the mark at its tier height. */
.pf-mark {
  display: block;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.pf-mark--home { block-size: var(--pf-mark-home-sm); inline-size: auto; }
.pf-mark--header { block-size: var(--pf-mark-header); inline-size: auto; flex: none; }
@media (min-width: 768px) {
  .pf-mark--home { block-size: var(--pf-mark-home); }
}

/* Message bubbles (Figma no approved frame (GAP-157) / no approved frame (GAP-158)): r20 with ONE facet cut (8px) as the "who spoke" cue, still NO tails, p16,
   body 16/23, elevation e1. You = bubble-you with the 1px top highlight, bottom-right corner cut, right, max 280. JUWEL = white, 1px line, top-left corner cut, a 1px static irid hairline on its top edge (.4),
   max 500 desktop / 320 phone (666), with a "JUWEL" sender label above (gap 6). Where corner-shape is unsupported the cut is
   an 8px rounded corner. Streamed tokens get no motion (G2.4). */
.pf-bubble {
  position: relative;
  padding: var(--pf-s16);
  border-radius: var(--jb-depth-r-bubble);
  font: var(--pf-type-body);
  color: var(--pf-ink);
  box-shadow: var(--jb-depth-e1);
  overflow-wrap: anywhere;
}
.pf-bubble--user {
  background: var(--pf-topaz-soft);
  margin-inline-start: auto;
  max-inline-size: 280px;
  border-bottom-right-radius: var(--jb-depth-facet-bubble);
  box-shadow: var(--jb-depth-hl), var(--jb-depth-e1);
}
.pf-bubble--bot {
  background: var(--pf-surface);
  border: 1px solid var(--pf-line);
  max-inline-size: 320px;
  border-top-left-radius: var(--jb-depth-facet-bubble);
}
.pf-bubble--bot::after {
  content: "";
  position: absolute;
  inset-block-start: -1px;
  inset-inline: 14px 14px;
  block-size: 1px;
  background: var(--jb-depth-irid-hair);
  opacity: .4;
  pointer-events: none;
}
@supports (corner-shape: bevel) {
  .pf-bubble--user { corner-bottom-right-shape: bevel; }
  .pf-bubble--bot { corner-top-left-shape: bevel; }
}
/* 666 (no approved frame (GAP-120) user message alignment 280 / assistant bubble 500, no approved frame (GAP-109) on a phone 320): the user bubble is 280 on every
   width, the JUWEL bubble 500 from 768 up and 320 on a phone (D-THREAD-02). */
@media (min-width: 768px) {
  .pf-bubble--bot { max-inline-size: 500px; }
}
.pf-sender { font: var(--pf-type-caption); color: var(--pf-ink-2); margin-block-end: 6px; }

/* Cards (white r24, 1px line), list rows, inputs (48h), captions, titles. */
.pf-card {
  background: var(--pf-surface);
  border: 1px solid var(--pf-line);
  border-radius: var(--pf-r24);
  box-shadow: none;
}
.pf-row {
  display: flex;
  align-items: center;
  gap: 12px;
  inline-size: 100%;
  min-block-size: 56px;
  padding: var(--pf-s8) var(--pf-s16);
  background: transparent;
  border: 0;
  border-block-end: 1px solid var(--pf-line);
  color: var(--pf-ink);
  font: var(--pf-type-body);
  text-align: start;
  cursor: pointer;
}
.pf-row:last-child { border-block-end: 0; }
.pf-row:hover { background: var(--jb-surface-2); }
.pf-row__label { flex: 1 1 auto; min-inline-size: 0; font: var(--pf-type-label); }
.pf-row__meta { font: var(--pf-type-caption); color: var(--pf-ink-2); }
.pf-input {
  inline-size: 100%;
  min-block-size: var(--pf-input-h);
  padding: 0 var(--pf-s16);
  border-radius: var(--pf-r16);
  border: 1px solid var(--pf-line);
  background: var(--pf-surface);
  color: var(--pf-ink);
  font: var(--pf-type-body);
}
.pf-input::placeholder { color: var(--pf-ink-2); opacity: 1; }
.pf-title { font: var(--pf-type-title); color: var(--pf-ink); letter-spacing: 0; margin: 0; }
.pf-heading { font: var(--pf-type-heading); color: var(--pf-ink); letter-spacing: 0; margin: 0; }
.pf-body { font: var(--pf-type-body); color: var(--pf-ink-2); margin: 0; }
.pf-label { font: var(--pf-type-label); color: var(--pf-ink); }
.pf-caption { font: var(--pf-type-caption); color: var(--pf-ink-2); margin: 0; }

/* Starters (666, Figma no approved frame (GAP-102) "Two optional conversation starters", D-HOME-03): two text-only buttons in a row, gap 12. White,
   1px line, r16, 44 tall, label 700 14/20 (the secondary button; no icon tile, no elevation, no cut corner). frontdoor.css gives
   them their 156 / 174 widths. */
.pf-starters { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; }
.pf-starter {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-block-size: var(--pf-target);
  padding: 0 var(--pf-s16);
  border-radius: var(--pf-r16);
  border: 1px solid var(--pf-line);
  background: var(--pf-surface);
  color: var(--pf-ink);
  font: var(--pf-type-label);
  letter-spacing: 0;
  text-align: center;
  cursor: pointer;
  box-shadow: none;
}
.pf-starter:hover { background: var(--jb-surface-2); }

/* Sheets: white r24 panel, 1px line, title heading, rows inside a card. */
.pf-sheet {
  background: var(--pf-surface);
  border: 1px solid var(--pf-line);
  border-radius: var(--pf-r24);
  box-shadow: var(--jb-e-dialog);
  color: var(--pf-ink);
}
.pf-sheet__head { display: flex; align-items: center; gap: var(--pf-s8); min-block-size: var(--pf-header-h); padding: var(--pf-s8) var(--pf-s8) var(--pf-s8) var(--pf-s20); }
.pf-sheet__title { flex: 1 1 auto; font: var(--pf-type-heading); margin: 0; }
.pf-sheet__body { padding: 0 var(--pf-s20) var(--pf-s20); display: grid; gap: var(--pf-s16); }

/* State block (empty / error): still mascot, heading, body, one action. */
.pf-state { display: grid; justify-items: center; gap: 12px; text-align: center; padding: var(--pf-s32) var(--pf-s20); }

@media (prefers-reduced-motion: reduce) {
  .pf-btn, .pf-starter, .pf-row, .pf-send, .pf-iconbtn { transition: none; }
}

/* ---- Depth layer: page wash, scrims under the floating chrome, layer order ----
   Layer model (DESIGN_SPEC section 1): 0 page wash (static gradient, never moves) / 1 ambient field canvas (C1, fixed,
   pointer-events none, aria-hidden) / 2 hero 3D (C4) / 3 content (#app, solid surfaces, e1) / 20 chrome (glass, e2) /
   40 sheets. porcelain.css is linked last, so the wash is set here (a depth.css before it could not win the body rule);
   C1's canvas must sit at z-index <= 1 so the content layer paints over it. Scoped to the face shell (#app
   [data-surface="face-next"]) so signin and upgrade-return keep their flat paper. */
body:has(> #app[data-surface="face-next"])::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: var(--jb-depth-wash);
}
:root[data-private] body:has(> #app[data-surface="face-next"])::before { background: var(--jb-private); }
#app[data-surface="face-next"] { position: relative; z-index: 3; }

/* Scrims (thread view only): the transcript scrolls under the floating bar and the composer tray. Both are fixed
   bands painted in the page wash itself (same gradient, viewport-sized and pinned to the top / bottom edge, so no seam)
   and masked to fade out toward the content, so text never runs through the bare gap around the glass or under the
   AI caption. They sit behind the chrome (z-index -1 inside its own stacking context). Home has nothing to scroll, so
   the ambient field stays visible there. */
:root:has(#app[data-view="thread"]) .jb-topbar::before,
:root:has(#app[data-view="thread"]) #composer-region::before {
  content: "";
  position: fixed;
  inset-inline: 0;
  z-index: -1;
  pointer-events: none;
  background: var(--jb-depth-wash);
  background-repeat: no-repeat;
  background-size: 100vw 100lvh;
}
:root:has(#app[data-view="thread"]) .jb-topbar::before {
  inset-block-start: 0;
  block-size: calc(var(--jb-hdr-top) + var(--jb-depth-bar-h) + 28px + env(safe-area-inset-top));
  background-position: 0 0;
  -webkit-mask-image: linear-gradient(to bottom, var(--jb-paper) 68%, transparent);
  mask-image: linear-gradient(to bottom, var(--jb-paper) 68%, transparent);
}
:root:has(#app[data-view="thread"]) #composer-region::before {
  inset-block-end: 0;
  block-size: calc(var(--composer-h, 160px) + 36px);
  background-position: 0 100%;
  -webkit-mask-image: linear-gradient(to top, var(--jb-paper) 62%, transparent);
  mask-image: linear-gradient(to top, var(--jb-paper) 62%, transparent);
}
:root[data-private]:has(#app[data-view="thread"]) .jb-topbar::before,
:root[data-private]:has(#app[data-view="thread"]) #composer-region::before { background: var(--jb-private); }
/* The bar's ROW carries the wash too (thread view), aligned to the same viewport-sized wash the scrim paints (the row sits --jb-hdr-inset from the left and --jb-hdr-top + the safe area from the top, so the
   background is offset by exactly that: no seam, no change a person can see). Why (acceptance r3, FZ-MOTION-06): the scrim is a ::before, which is not in the element stack that axe, or any contrast tool, walks to
   find what is behind a piece of text; the bar's caption, Sign in and More were judged against whatever the scrim was hiding (a light "Call 911" primary button scrolled up under the bar in dark: 1.53:1,
   though the eye sees the wash). The row is a real element with the wash for its background, so it IS what is behind the text. No fixed attachment (iOS Safari treats it as scroll). */
:root:has(#app[data-view="thread"]) .jb-hdr {
  background: var(--jb-depth-wash);
  background-repeat: no-repeat;
  background-size: 100vw 100lvh;
  background-position: calc(-1 * var(--jb-hdr-inset)) calc(-1 * (var(--jb-hdr-top) + env(safe-area-inset-top, 0px)));
}
:root[data-private]:has(#app[data-view="thread"]) .jb-hdr { background: var(--jb-private); }
@media (forced-colors: active) {
  body:has(> #app[data-surface="face-next"])::before,
  :root:has(#app[data-view="thread"]) .jb-topbar::before,
  :root:has(#app[data-view="thread"]) #composer-region::before { display: none; }
  :root:has(#app[data-view="thread"]) .jb-hdr { background: none; }
  .pf-bubble, .pf-starter { border: 1px solid CanvasText; box-shadow: none; }
}

/* Reduced motion (face-next only: porcelain.css is linked last, after assets/bubbly/base.css).
   base.css turns every motion into a 200 ms cross-fade on `*` with !important; on face-next that still
   fires the composer outline and ~85 theme-switch transitions when a sheet opens or the theme changes.
   Here nothing transitions or animates at all; the final state is shown at once. Same specificity
   (`*`), same !important, later in the cascade, so this wins. JS never waits on transitionend /
   animationend (sheets.js and mascot.js bypass their WAAPI calls under this query). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition: none !important;
    animation: none !important;
    scroll-behavior: auto !important;
  }
}
