/* ============================================================================
   Harry's Content System — console v2 design system
   Agent A owns this file.

   LIGHT mode, grounded on --ground (#EDF6F9). The whole app is a tinted paper
   ground with white cards lifting off it; depth comes from soft, cool-tinted
   shadow, never from a darker fill. Every neutral here is a cool tinted neutral
   drawn from the palette — there is no pure grey in this file.

   Roles, and they do not overlap:
     --accent  (teal #006D77)  the ONLY action colour. Primary buttons, active
                               nav, focus rings, links. One primary per view.
     --warm    (#E29578)       the ATTENTION accent. Markers, borders, small
                               solid dots, or a fill behind DARK text. It can
                               never carry white text (2.39:1).
     --warm-soft / --mint      surfaces and graphics only. Never text, never a
                               text background that has to pass on its own.
     --ok / --warn             success and danger, both derived in-family.

   Contrast audit (WCAG 2.1, computed not guessed):
     --fg        on --ground     = 12.56:1  AA
     --fg        on --surface    = 13.77:1  AA
     --fg-dim    on --ground     =  5.97:1  AA   (all secondary + meta copy)
     --fg-dim    on --surface    =  6.55:1  AA
     --fg-faint  on --surface    =  3.56:1  NOT body text — placeholders,
                                            disabled chrome and hairlines only.
     --accent    on --ground     =  5.54:1  AA   (links, key numbers)
     --accent    on --surface    =  6.08:1  AA
     --surface   on --accent     =  6.08:1  AA   (primary button, white label)
     --surface   on --accent-hover= 4.52:1  AA   (primary button, hovered)
     --surface   on --warn       =  5.76:1  AA   (offline bar, alert badge)
     --fg        on --warm       =  5.77:1  AA   (demo marker: dark on peach)
     --fg        on --warm-soft  = 10.84:1  AA   (every attention surface)
     --warn      on --warm-soft  =  4.53:1  AA   (attention labels only)
     --surface   on --ok         =  3.88:1  graphic-only (the done tick)
     --ok        on --surface    =  3.88:1  graphic-only (marks, bars, borders)
     --mint      on --surface    =  1.96:1  fill only — never a hairline

   Glass has a second audit, because text on a material sits on whatever
   scrolled under it rather than on a known fill. It is computed against the
   worst case (a black image behind the bar) and lives with the material
   tokens in the V4 section at the foot of this file. The short version:
   --fg clears 7.37:1 on bar glass and --fg-dim does not clear 4.5 — which is
   why labels on the bars keep the full ink and separate by weight instead.
   ========================================================================= */

@font-face{
  font-family:'Bricolage Grotesque';
  src:url('/font/bricolage-grotesque.woff2') format('woff2');
  font-weight:400 700;
  font-display:swap;
}

/* ---- TOKENS (copied verbatim from V2-CONTRACT.md) ---------------------- */
:root{
color-scheme:light;
/* grounds — EDF6F9 is the page, white is the lifted surface */
--ground:#EDF6F9; --surface:#FFFFFF; --surface-2:#F5FBFC; --surface-3:#DCEBF0;
--line:#CFE3EA; --line-strong:#A9CDD6;
/* brand */
--teal:#006D77; --mint:#83C5BE; --warm:#E29578; --warm-soft:#FFDDD2;
--accent:#006D77; --accent-hover:#00838F; --accent-press:#00565E;
--ok:#3E8E86; --warn:#A9482B;
/* type — verified: ink 12.6:1, dim 6.0:1 on the ground */
--fg:#06323A; --fg-dim:#3F636B; --fg-faint:#6E8D95;
/* content surfaces */
--paper:#FFFFFF; --paper-2:#F5FBFC; --ink:#06323A; --ink-dim:#3F636B;
--r-xs:6px; --r-sm:8px; --r:12px; --r-lg:18px; --r-pill:99px;
/* Spacing and type are in rem, not px: a reader who raises their browser text
   size scales the layout with the words instead of bursting it (SKILL §15). */
--s1:.25rem; --s2:.5rem; --s3:.75rem; --s4:1rem; --s5:1.5rem; --s6:2rem; --s7:3rem;
--fs-meta:.75rem; --fs-sm:.8125rem; --fs-body:.9375rem; --fs-lead:1.125rem;
--fs-h3:1.25rem; --fs-h2:1.625rem; --fs-h1:2.125rem; --fs-display:2.75rem;
/* Tracking is size-specific — one letter-spacing is wrong somewhere. Letters
   read too far apart as type grows, so display tightens and small text opens. */
--track-display:-.028em; --track-h1:-.022em; --track-h2:-.016em; --track-h3:-.01em;
--track-body:0em; --track-meta:.01em; --track-caps:.08em;
/* Leading runs inverse to size: tight on headings, comfortable in body copy. */
--lead-display:1.04; --lead-h1:1.1; --lead-h2:1.16; --lead-h3:1.24;
--lead-tight:1.35; --lead-body:1.6; --lead-prose:1.7;
--font-display:'Bricolage Grotesque',system-ui,sans-serif;
--font-body:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
/* soft, cool-tinted depth — near-black shadows look like dirt on a light ground */
--shadow-1:0 1px 2px rgba(0,64,72,.06), 0 2px 8px rgba(0,64,72,.05);
--shadow-2:0 8px 28px rgba(0,64,72,.10), 0 2px 6px rgba(0,64,72,.06);
--dur-1:150ms; --dur-2:240ms; --ease:cubic-bezier(.2,.7,.3,1);
}

/* ---- derived layout vars (not colours — no palette expansion) ---------- */
:root{
  --rail:248px;          /* desktop left rail width */
  --navh:3.75rem;        /* mobile bottom bar height */
  --topbar-h:3.75rem;
  --composer-h:4.75rem;  /* resting height of the floating composer bar */
  --measure:72ch;        /* never let a text line run past ~72 characters */
  /* The chat's reading column. Bubbles that spread across the full 1080px
     content width leave the reader's eye travelling a void between a
     right-aligned turn and a left-aligned one; 45rem keeps the conversation
     one legible column no matter how wide the window gets. */
  --chat-measure:45rem;
  --safe-b:env(safe-area-inset-bottom,0px);
  --safe-t:env(safe-area-inset-top,0px);
  /* Palette-derived tints — not new hues, just the palette mixed into white.
     --mint-wash is the "positive / settled" surface (approved, picked, done);
     --warm-soft plays the same role for attention. Both take --fg happily. */
  --mint-wash:color-mix(in srgb,var(--mint) 30%,var(--surface));
  /* The LinkedIn mock redefines our tokens locally, so its mat needs a name
     that block does not shadow. Full --mint: a wash of it sat too close to the
     ground and the preview stopped reading as a preview. */
  --preview-well:var(--mint);
}

/* ---- MATERIALS (SKILL §12) ---------------------------------------------
   Chrome is not an opaque strip that eats a band of the window; it is a
   translucent layer with the page running underneath it. Three weights, and
   they encode hierarchy: the rail is the structural material (thickest blur,
   least transparent), the topbar and composer are the floating bars, and a
   chip or button is the lightest. A light translucent surface is never laid
   on another light translucent surface — the scrims below are ink films. */
:root{
  /* --- Material weight. Blur, opacity, saturation and shadow move together:
     one surface is one weight, and a bigger surface is a thicker slab.
       light      small floating things (a popover, a chip that must be glass)
       bar        the topbar and the composer
       thick      the rail — structural, separates a whole region
       heavy      sheets and the modal panel — the largest surfaces we float */
  --mat-blur-light:12px;
  --mat-blur:20px;          /* floating bars */
  --mat-blur-thick:30px;    /* the rail: a bigger surface reads as thicker */
  --mat-blur-heavy:38px;    /* sheets and popovers: the thickest slab we ship */
  --mat-blur-edge:6px;      /* the progressive blur in a scroll edge */
  /* Saturation lift — without it a light blur drains the colour behind the
     glass to grey. With it, the teal and peach under a bar refract through. */
  --mat-sat-light:1.4;
  --mat-sat:1.8;
  --mat-sat-heavy:1.9;
  --mat-sat-scrim:1.25;
  /* --- Tint + opacity. Opacity is a CONTRAST decision, not a taste one: it
     is set at the point where the worst case behind the glass still clears
     4.5:1 for the ink that sits on it (see the audit at the head of this
     file). Frostier beats pretty-but-unreadable. */
  --mat-ground:color-mix(in srgb,var(--ground) 78%,transparent);
  --mat-surface:color-mix(in srgb,var(--surface) 88%,transparent);
  --mat-panel:color-mix(in srgb,var(--surface) 90%,transparent);
  /* the bright line where light catches the top of a real material */
  --mat-edge:color-mix(in srgb,var(--surface) 72%,transparent);
  /* --- Specular edge. A translucent rectangle is a hole in the page; a slab
     catching light is a material. The bright inner line along the top is the
     lit edge, the faint inner line along the bottom is the shaded underside.
     1px each — never a stripe, never a coloured bar down one side. */
  --spec-hi:inset 0 1px 0 color-mix(in srgb,var(--surface) 90%,transparent);
  --spec-lo:inset 0 -1px 0 color-mix(in srgb,var(--line-strong) 38%,transparent);
  --spec:var(--spec-hi),var(--spec-lo);
  --spec-light:inset 0 1px 0 color-mix(in srgb,var(--surface) 66%,transparent);
  /* the rail is lit from above and turns away from the page on its far edge */
  --spec-rail:inset 0 1px 0 color-mix(in srgb,var(--surface) 92%,transparent),
              inset -1px 0 0 color-mix(in srgb,var(--line) 55%,transparent);
  /* Depth is context-aware: a bar floating over plain ground needs almost
     nothing, the same bar over text needs to separate itself from it. The
     specular pair rides inside every chrome shadow so the two never drift. */
  --shadow-chrome:var(--spec),0 4px 16px rgba(0,64,72,.045);
  --shadow-chrome-over:var(--spec),0 10px 30px rgba(0,64,72,.11), 0 1px 3px rgba(0,64,72,.06);
  /* Weight follows size: the sheet casts further than the bar above it. */
  --shadow-glass:var(--spec-hi),0 10px 34px rgba(0,64,72,.13), 0 2px 6px rgba(0,64,72,.06);
  --shadow-glass-heavy:var(--spec-hi),0 22px 56px rgba(0,64,72,.17), 0 4px 12px rgba(0,64,72,.07);
  --edge-h:1.75rem;         /* height of a scroll edge fade */
  --mat-dur:300ms;          /* how long a material takes to arrive */
}

/* ---- legacy aliases -----------------------------------------------------
   A few inline style="" attributes in the app still reference the old v1
   variable names. On the light palette --bg is the tinted page ground, --panel
   is the white card that lifts off it, --dim is the secondary ink (5.97:1 on
   the ground) — so a stray var(--dim) still lands on a legible colour. */
:root{
  --bg:var(--ground);
  --panel:var(--surface);
  --dim:var(--fg-dim);
  --radius:var(--r);
}

/* ============================ RESET / BASE ============================== */
*,*::before,*::after{box-sizing:border-box}
*{-webkit-tap-highlight-color:transparent}

html{-webkit-text-size-adjust:100%}

body{
  margin:0;
  background:var(--ground);
  color:var(--fg);
  font-family:var(--font-body);
  font-size:var(--fs-body);
  line-height:var(--lead-body);
  letter-spacing:var(--track-body);
  font-variant-numeric:tabular-nums;   /* all chrome numerics align */
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  overflow-wrap:break-word;
}

/* Long-form content reads better with proportional figures. */
.post,.msg.ai,.li,textarea.edit,.draft-preview{font-variant-numeric:normal}

/* Hierarchy is weight + size + leading + tracking as one set, never size
   alone. Each step tightens its leading and its tracking as it grows. */
h1,h2,h3,h4,h5{
  font-family:var(--font-display);
  font-weight:700;
  margin:0 0 var(--s3);
  font-optical-sizing:auto;
}
h1{font-size:var(--fs-h1);line-height:var(--lead-h1);letter-spacing:var(--track-h1)}
h2{font-size:var(--fs-h2);line-height:var(--lead-h2);letter-spacing:var(--track-h2)}
h3{font-size:var(--fs-h3);line-height:var(--lead-h3);letter-spacing:var(--track-h3)}
h4{font-size:var(--fs-lead);line-height:var(--lead-h3);letter-spacing:var(--track-h3)}
h5{font-size:var(--fs-body);line-height:var(--lead-tight);letter-spacing:var(--track-body);font-weight:600}
p{margin:0 0 var(--s3)}
p:last-child{margin-bottom:0}

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

img,svg,video,canvas{max-width:100%}
img{border-style:none}

hr{border:0;border-top:1px solid var(--line);margin:var(--s5) 0}

code,kbd,samp{
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:.9em;
}
pre{white-space:pre-wrap;overflow-wrap:anywhere;margin:0}

::selection{background:var(--accent);color:var(--surface)}
.post ::selection,textarea.edit::selection,.li ::selection{background:var(--ink);color:var(--paper)}

::placeholder{color:var(--fg-faint)}

/* scrollbars — thin, in-palette, never a bright rail */
*{scrollbar-width:thin;scrollbar-color:var(--line-strong) transparent}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--line-strong);border-radius:var(--r-pill);
  border:3px solid transparent;background-clip:content-box}
::-webkit-scrollbar-thumb:hover{background:var(--fg-faint);background-clip:content-box}

/* ---- focus: one visible ring, everywhere ------------------------------- */
:focus{outline:none}
a:focus-visible,button:focus-visible,input:focus-visible,textarea:focus-visible,
select:focus-visible,summary:focus-visible,[tabindex]:focus-visible,
.card:focus-visible,.idea-card:focus-visible,.idea-source:focus-visible,
.navitem:focus-visible,.post-item:focus-visible,.choices button:focus-visible{
  outline:2px solid var(--accent);
  outline-offset:2px;
}
/* on paper surfaces the orange ring needs a dark keyline to stay readable */
.post :focus-visible,.li :focus-visible,textarea.edit:focus-visible{
  outline:2px solid var(--accent-press);
  outline-offset:2px;
}

/* utility */
[hidden]{display:none!important}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}
.paper{background:var(--paper);color:var(--ink)}

/* ============================== ICONS =================================== */
.ic{
  width:20px;height:20px;flex:none;display:block;
  stroke:currentColor;fill:none;
  stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;
  color:inherit;
}
.ic-sm{width:16px;height:16px}
.ic-lg{width:24px;height:24px}
.ic-xl{width:32px;height:32px}

/* ============================== SHELL =================================== */
#shell{
  min-height:100vh;
  min-height:100dvh;
  background:var(--ground);
}

#content{
  min-width:0;
  display:flex;
  flex-direction:column;
}

#main{
  flex:1 1 auto;
  min-width:0;
  width:100%;
  max-width:1080px;
  margin:0 auto;
  padding:var(--s5) var(--s4) calc(var(--navh) + 6rem + var(--safe-b));
}
#main>*{min-width:0}
/* prose blocks inside main never exceed the measure */
#main>p,#main>.lede{max-width:var(--measure)}

/* ------------------------------ TOPBAR --------------------------------- */
#topbar,body>header{
  position:sticky;top:0;z-index:30;
  display:flex;align-items:center;gap:var(--s2);
  min-height:calc(var(--topbar-h) + var(--safe-t));
  padding:var(--s2) var(--s4);
  padding-top:calc(var(--s2) + var(--safe-t));
  background:var(--ground);
  background:var(--mat-ground);
  backdrop-filter:blur(var(--mat-blur)) saturate(var(--mat-sat));
  -webkit-backdrop-filter:blur(var(--mat-blur)) saturate(var(--mat-sat));
  /* light catching the top of the material, and depth instead of a hairline */
  border-top:1px solid var(--mat-edge);
  box-shadow:var(--shadow-chrome);
  transition:box-shadow var(--dur-2) var(--ease);
}
/* A hard 1px divider says "two rectangles". A scroll edge says "the page is
   running underneath this" — a short band of ground and progressive blur,
   masked to nothing, present only because content actually passes under. */
#topbar::after,.composer::before{
  content:"";
  position:absolute;left:0;right:0;
  height:var(--edge-h);
  pointer-events:none;
  background:var(--mat-ground);
  backdrop-filter:blur(var(--mat-blur-edge));
  -webkit-backdrop-filter:blur(var(--mat-blur-edge));
}
#topbar::after{
  top:100%;
  -webkit-mask-image:linear-gradient(to bottom,#000,transparent);
          mask-image:linear-gradient(to bottom,#000,transparent);
}
.composer::before{
  bottom:100%;
  -webkit-mask-image:linear-gradient(to top,#000,transparent);
          mask-image:linear-gradient(to top,#000,transparent);
}
/* JS raises the chrome's shadow once content is actually behind it. */
#topbar[data-scrolled],.composer[data-scrolled]{box-shadow:var(--shadow-chrome-over)}

/* Standalone hook, for chrome the JS floats over content itself. */
.scroll-edge{
  position:absolute;left:0;right:0;
  height:var(--edge-h);
  pointer-events:none;
  z-index:2;
  background:var(--mat-ground);
  backdrop-filter:blur(var(--mat-blur-edge));
  -webkit-backdrop-filter:blur(var(--mat-blur-edge));
  -webkit-mask-image:linear-gradient(to bottom,#000,transparent);
          mask-image:linear-gradient(to bottom,#000,transparent);
}
.scroll-edge.at-bottom{
  -webkit-mask-image:linear-gradient(to top,#000,transparent);
          mask-image:linear-gradient(to top,#000,transparent);
}

@supports not (backdrop-filter:blur(1px)){
  #topbar,body>header{background:var(--ground)}
  #topbar::after,.composer::before,.scroll-edge{display:none}
  #topbar,body>header{border-bottom:1px solid var(--line)}
}
/* PERFORMANCE. backdrop-filter is the most expensive thing in this file and a
   phone pays for every live layer. On a small screen the two bars are already
   blurring; their edge fades drop to a plain masked wash of ground, which is
   two fewer composited blur layers for a difference nobody can see at 390px. */
@media (max-width:1023.98px){
  #topbar::after,.composer::before,.scroll-edge{
    backdrop-filter:none;
    -webkit-backdrop-filter:none;
  }
}
#topbar h1,#topbar .title,body>header h1{
  flex:1 1 auto;min-width:0;
  margin:0;
  font-family:var(--font-display);
  font-size:var(--fs-lead);
  font-weight:700;
  line-height:var(--lead-h3);
  letter-spacing:var(--track-h3);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* Vibrancy (SKILL §12): this line sits ON the material, so whatever scrolled
   under the bar is its background. --fg-dim measures 3.49:1 against the worst
   case there and --fg measures 7.37:1 — so the subtitle separates from the
   title by size and weight, never by going grey. */
#topbar .sub{
  font-size:var(--fs-meta);color:var(--fg);font-weight:600;
  letter-spacing:calc(var(--track-meta) + .006em);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
#topbar .spacer{flex:1 1 auto}
#topbar>button{flex:none}

/* ----------------------------- SIDENAV --------------------------------- */
/* The rail is the heaviest material in the app: it separates a structural
   region, so it blurs hardest and stays the least transparent. The blur lives
   on a pseudo-element, never on #sidenav itself — a filtered element becomes
   the containing block for its fixed descendants, and on mobile .navfoot is
   fixed inside this bar and would be trapped in it. */
#sidenav{
  position:relative;
  display:flex;
  isolation:isolate;
  background:var(--mat-surface);
  border-right:1px solid var(--line);
  /* structural material: lit along the top, turning away on the far edge */
  box-shadow:var(--spec-rail);
}
#sidenav::before{
  content:"";
  position:absolute;inset:0;z-index:-1;
  backdrop-filter:blur(var(--mat-blur-thick)) saturate(var(--mat-sat));
  -webkit-backdrop-filter:blur(var(--mat-blur-thick)) saturate(var(--mat-sat));
}
@supports not (backdrop-filter:blur(1px)){
  #sidenav{background:var(--surface)}
}

.brand{
  display:flex;align-items:center;
  /* the mark is 26px where a nav glyph is 20px, so it is centred on the rail's
     icon column rather than aligned to its left edge, and the wordmark lands
     on the same x as every nav label below it */
  gap:calc(var(--s2) + 1px);
  padding:var(--s5) var(--s4) var(--s4) calc(var(--s4) + 2px);
  font-family:var(--font-display);
  font-size:var(--fs-lead);
  font-weight:700;
  letter-spacing:var(--track-h3);
  color:var(--fg);
  line-height:var(--lead-h3);
}
.brand .ic,.brand svg{width:26px;height:26px;color:var(--accent)}
.brand small,.brand .brand-sub{
  display:block;
  font-family:var(--font-body);
  font-size:var(--fs-meta);
  font-weight:500;
  letter-spacing:0;
  color:var(--fg-dim);
  margin-top:2px;
}

.navitem{
  position:relative;
  display:flex;align-items:center;gap:var(--s3);
  padding:11px var(--s3);
  margin:2px var(--s2);
  border-radius:var(--r);
  border:1px solid transparent;
  background:none;
  box-shadow:none;
  color:var(--fg-dim);
  font-family:var(--font-display);
  font-size:var(--fs-sm);
  font-weight:600;
  letter-spacing:var(--track-h3);
  text-decoration:none;
  cursor:pointer;
  transition:background var(--dur-1) var(--ease),color var(--dur-1) var(--ease);
}
.navitem:hover{background:var(--ground);color:var(--fg);text-decoration:none}
.navitem.on{
  background:var(--surface-3);
  border-color:var(--line);
  color:var(--fg);
}
.navitem.on::before{
  /* flush with the rail's own left edge, not floating 2px off the item */
  content:"";position:absolute;left:calc(var(--s2) * -1);top:50%;
  transform:translateY(-50%);
  width:3px;height:20px;border-radius:var(--r-pill);background:var(--accent);
}
.navitem.on .ic{color:var(--accent)}
.navitem .badge{
  margin-left:auto;
  min-width:20px;height:20px;padding:0 6px;
  display:inline-flex;align-items:center;justify-content:center;
  border-radius:var(--r-pill);
  background:var(--surface-3);border:1px solid var(--line-strong);
  color:var(--fg);font-size:11px;font-weight:700;
}
.navitem.has-alert .badge{background:var(--warn);border-color:var(--warn);color:var(--surface)}

.navfoot{
  display:flex;
  flex-direction:column;
  gap:var(--s2);
  margin-top:auto;
  /* The side inset matches .navitem's own margin, so everything in the rail
     hangs off one vertical line. */
  padding:var(--s3) var(--s2);
  border-top:1px solid var(--line);
}
.navfoot button,.navfoot .navitem{width:100%;justify-content:flex-start;margin:0}
.navfoot .status{
  display:flex;align-items:center;gap:var(--s2);
  font-size:var(--fs-meta);color:var(--fg-dim);
  letter-spacing:var(--track-meta);
  /* +1px for the border the buttons below have and this paragraph does not */
  padding:0 calc(var(--s3) + 1px);
}

/* ---------------------------- COMPOSER --------------------------------- */
.composer,#composer{
  position:relative;
  display:flex;align-items:flex-end;gap:var(--s2);
  padding:var(--s3) var(--s4);
  background:var(--ground);
  background:var(--mat-ground);
  backdrop-filter:blur(var(--mat-blur)) saturate(var(--mat-sat));
  -webkit-backdrop-filter:blur(var(--mat-blur)) saturate(var(--mat-sat));
  border-top:1px solid var(--mat-edge);
  box-shadow:var(--shadow-chrome);
  transition:box-shadow var(--dur-2) var(--ease);
}
@supports not (backdrop-filter:blur(1px)){
  .composer,#composer{background:var(--ground);border-top-color:var(--line)}
}
.composer textarea{
  flex:1 1 auto;min-width:0;
  background:var(--surface);
  border:1px solid var(--line-strong);
  color:var(--fg);
  border-radius:var(--r);
  padding:11px var(--s3);
  font-family:var(--font-body);
  font-size:var(--fs-body);
  line-height:1.5;
  resize:none;
  max-height:160px;
  transition:border-color var(--dur-1) var(--ease);
}
.composer textarea:focus{border-color:var(--accent)}
.composer button{flex:none;height:44px;min-width:52px}
.composer .composer-inner{
  display:flex;align-items:flex-end;gap:var(--s2);
  width:100%;max-width:1080px;margin:0 auto;
}

/* ====================== DESKTOP LAYOUT (≥1024px) ======================== */
@media (min-width:1024px){
  #shell{
    display:grid;
    grid-template-columns:var(--rail) minmax(0,1fr);
    height:100vh;height:100dvh;
    overflow:hidden;
  }
  #sidenav{
    flex-direction:column;
    position:sticky;top:0;
    height:100vh;height:100dvh;
    overflow-y:auto;
    padding-bottom:var(--s3);
  }
  #content{
    position:relative;
    height:100vh;height:100dvh;
    overflow:hidden;
  }
  /* Both bars leave the flow and float over the scroller, so the page passes
     beneath them and the translucency has something to be translucent about. */
  #main{
    position:absolute;inset:0;
    margin:0 auto;
    overflow-y:auto;
    padding:calc(var(--topbar-h) + var(--s4)) var(--s6) var(--s7);
    scroll-padding-top:calc(var(--topbar-h) + var(--s4));
    scroll-behavior:smooth;
  }
  #topbar,body>header{
    position:absolute;top:0;left:0;right:0;
    padding:var(--s3) var(--s6);
    min-height:var(--topbar-h);
  }
  #topbar h1,body>header h1{font-size:var(--fs-h3);letter-spacing:var(--track-h3)}
  /* A sidebar is a list, and a list reads from its left edge. Centred tab
     labels above left-aligned utility rows were two different navs in one
     rail; every glyph in the rail now starts on the same x. */
  .navitem{justify-content:flex-start}
  .navfoot>button,.navfoot>.navitem{padding-left:var(--s3);padding-right:var(--s3)}
  /* The bar is the full width of the region — it is the material — but what
     you type sits in the same column you read in. A 1230px input under a
     720px transcript is two different apps in one window. */
  .composer,#composer{
    position:absolute;left:0;right:0;bottom:0;z-index:35;
    padding:var(--s3) max(var(--s6),calc((100% - var(--chat-measure)) / 2)) var(--s4);
  }
  .brand{display:flex}
}

/* ====================== MOBILE LAYOUT (<1024px) ========================= */
@media (max-width:1023.98px){
  #sidenav{
    position:fixed;left:0;right:0;bottom:0;z-index:40;
    flex-direction:row;align-items:stretch;
    height:calc(var(--navh) + var(--safe-b));
    padding-bottom:var(--safe-b);
    border-right:0;
    /* The blur is on #sidenav::before, not on #sidenav: a filtered element
       becomes the containing block for its fixed descendants, and .navfoot is
       fixed inside this bar. The pseudo-element carries the material without
       capturing it. The top edge is the bright line, not a divider. */
    border-top:1px solid var(--mat-edge);
    background:var(--mat-surface);
    box-shadow:var(--spec-hi),
               0 -6px 20px color-mix(in srgb,var(--accent-press) 10%,transparent);
  }
  @supports not (backdrop-filter:blur(1px)){
    #sidenav{background:var(--surface);border-top-color:var(--line)}
  }
  .brand{display:none}
  .navitem{
    flex:1 1 0;min-width:0;
    flex-direction:column;justify-content:center;align-items:center;
    gap:3px;
    margin:0;padding:6px 2px;
    border-radius:0;border-color:transparent;
    background:none;
    font-size:10.5px;
    letter-spacing:var(--track-meta);
    min-height:44px;
  }
  .navitem span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
  .navitem.on{background:none;border-color:transparent;color:var(--accent)}
  .navitem.on .ic{color:var(--accent)}
  .navitem.on::before{
    left:50%;top:2px;transform:translateX(-50%);
    width:22px;height:3px;
  }
  .navitem .badge{
    position:absolute;top:2px;left:calc(50% + 8px);
    margin:0;min-width:16px;height:16px;font-size:10px;padding:0 4px;
  }
  /* Utility controls (status, Find new ideas, notifications, conversations)
     move to the top-right of the header band so the bottom bar stays a clean
     four-target tab row. #topbar reserves the space below. */
  .navfoot{
    position:fixed;top:0;right:0;z-index:41;
    flex-direction:row;align-items:center;justify-content:flex-end;
    gap:var(--s1);
    margin:0;padding:var(--s2) var(--s3);
    padding-top:calc(var(--s2) + var(--safe-t));
    height:calc(var(--topbar-h) + var(--safe-t));
    border-top:0;
    background:none;
  }
  .navfoot button,.navfoot .navitem{
    width:auto;min-width:40px;height:40px;flex:none;
    padding:0;
    justify-content:center;
    flex-direction:row;font-size:var(--fs-meta);
  }
  /* labels collapse to icons; the status label collapses to its dot */
  .navfoot .navitem span:not(.badge),
  .navfoot button span,
  .navfoot button .lbl{display:none}
  .navfoot .status{padding:0;gap:0;font-size:0;min-width:20px;justify-content:center}
  /* the header keeps clear of the cluster: 3 × 40 + gaps + gutters */
  #topbar,body>header{padding-right:160px}
  /* When title + badge + action can't share one line, the actions wrap onto a
     second row rather than the title shrinking to an ellipsis stub. */
  #topbar,body>header{flex-wrap:wrap;row-gap:var(--s2)}
  #topbar h1,body>header h1{flex:1 1 auto;min-width:min(100%,150px)}
  #topbar>button,#topbar .demo-badge,#topbar>a{flex:0 0 auto;max-width:100%}

  .composer,#composer{
    position:fixed;left:0;right:0;z-index:35;
    bottom:calc(var(--navh) + var(--safe-b));
  }
  #main{padding-bottom:calc(var(--navh) + 6.5rem + var(--safe-b))}
}

/* ======================= SPLIT (list / detail) ========================== */
.split{display:block}
.split-list{min-width:0}
.split-detail{min-width:0}

@media (min-width:1024px){
  .split{
    display:grid;
    grid-template-columns:minmax(0,1fr) minmax(0,420px);
    gap:var(--s5);
    align-items:start;
  }
  .split-detail{
    /* sticky offsets resolve against the scrollport, whose top edge now sits
       behind the floating topbar — so the offset has to clear the bar. */
    position:sticky;top:calc(var(--topbar-h) + var(--s4));
    align-self:start;
    max-height:calc(100dvh - var(--topbar-h) - var(--s7));
    overflow-y:auto;
    /* enters and exits along the same path, anchored to the row that opened
       it; the JS sets transform-origin and springs transform — no transition
       on transform here, or the panel could not be caught mid-flight. */
    will-change:transform;
    background:var(--surface);
    border:1px solid var(--line);
    border-radius:var(--r-lg);
    padding:var(--s5);
    box-shadow:var(--shadow-1);
  }
  .split-detail:empty{display:none}
}
@media (max-width:1023.98px){
  .split-detail{display:none}   /* detail opens in the bottom sheet instead */
}

/* ============================== BUTTONS ================================= */
button,.btn{
  font-family:var(--font-display);
  font-size:var(--fs-sm);
  font-weight:600;
  letter-spacing:var(--track-body);
  line-height:var(--lead-tight);
  display:inline-flex;align-items:center;justify-content:center;gap:var(--s2);
  min-height:40px;
  padding:10px var(--s4);
  border:1px solid var(--line-strong);
  border-radius:var(--r-sm);
  background:var(--surface);
  color:var(--fg);
  cursor:pointer;
  box-shadow:var(--shadow-1);
  transition:background var(--dur-1) var(--ease),
             border-color var(--dur-1) var(--ease),
             color var(--dur-1) var(--ease),
             transform var(--dur-1) var(--ease);
}
button:hover,.btn:hover{background:var(--surface-3);border-color:var(--accent)}
button:active,.btn:active{transform:scale(.97)}
button:disabled,.btn:disabled{
  opacity:.65;cursor:not-allowed;transform:none;box-shadow:none;
  background:var(--surface-2);border-color:var(--line);color:var(--fg-faint);
}

button.primary,.btn.primary{
  background:var(--accent);
  border-color:var(--accent);
  color:var(--surface);
  font-weight:700;
}
button.primary:hover{background:var(--accent-hover);border-color:var(--accent-hover)}
button.primary:active{background:var(--accent-press);border-color:var(--accent-press)}

button.ghost{background:none;border-color:var(--line);box-shadow:none}
button.ghost:hover{background:var(--surface-3);border-color:var(--line-strong)}

/* destructive: outlined at rest so it never competes with the one primary
   action; the solid fill only appears on hover. --warn carries white at
   5.76:1 and reads on the ground at 5.25:1. */
button.danger,.btn.danger{
  background:none;
  border-color:var(--warn);
  color:var(--warn);
  box-shadow:none;
}
button.danger:hover{background:var(--warn);border-color:var(--warn);color:var(--surface)}

button.back,.back{
  background:none;border:none;box-shadow:none;
  min-height:32px;padding:0;
  margin:0 0 var(--s4);
  color:var(--fg-dim);
  font-size:var(--fs-sm);font-weight:600;
  gap:var(--s1);
}
button.back:hover,.back:hover{background:none;color:var(--fg);transform:none}

button.tdel,.tdel{
  background:none;border:none;box-shadow:none;
  min-height:32px;padding:4px var(--s2);
  color:var(--fg-dim);font-size:var(--fs-meta);
}
button.tdel:hover{background:var(--warm-soft);color:var(--warn);border-radius:var(--r-sm)}

.icon-btn{
  min-width:40px;min-height:40px;
  padding:0 10px;
  font-size:var(--fs-body);line-height:1;
}

/* rows of actions */
.row{display:flex;gap:var(--s2);flex-wrap:wrap;margin:var(--s3) 0}
.row button{flex:1 1 auto;min-width:120px}
.row.tight button{flex:0 0 auto;min-width:0}
.acts{margin:var(--s4) 0 0}
.acts button{
  flex:0 1 auto;min-width:0;
  font-size:var(--fs-meta);
  min-height:36px;padding:8px var(--s3);
}

/* segmented control */
.seg{
  display:flex;gap:2px;
  padding:3px;
  margin-bottom:var(--s4);
  background:var(--surface-3);
  border:1px solid var(--line);
  border-radius:var(--r);
}
.seg button{
  flex:1 1 0;min-width:0;
  min-height:34px;padding:7px var(--s3);
  background:none;border:none;box-shadow:none;
  border-radius:var(--r-sm);
  color:var(--fg-dim);
  font-size:var(--fs-meta);font-weight:700;
  letter-spacing:var(--track-caps);text-transform:uppercase;
}
.seg button:hover{background:var(--surface-2);color:var(--fg)}
.seg button.on{
  background:var(--surface);
  color:var(--fg);
  box-shadow:var(--shadow-1),inset 0 -2px 0 var(--accent);
}

/* inline choice buttons emitted by :::choices */
.choices{display:flex;flex-wrap:wrap;gap:var(--s2);margin:var(--s4) 0 var(--s1)}
.choices button{
  flex:1 1 auto;min-width:140px;
  background:none;
  box-shadow:none;
  border-color:var(--accent);
  color:var(--accent);
  font-size:var(--fs-sm);
  text-align:left;justify-content:flex-start;
}
.choices button:hover{background:var(--accent);border-color:var(--accent);color:var(--surface)}

/* conversation starters (empty chat state) */
.starters{
  display:flex;flex-direction:column;gap:var(--s2);
  max-width:26.25rem;margin:var(--s5) auto 0;
}
.starters button{
  width:100%;
  justify-content:flex-start;text-align:left;
  font-size:var(--fs-sm);font-weight:600;
  padding:13px var(--s4);
  background:var(--surface);
  border-color:var(--line);
  color:var(--fg-dim);
  min-height:48px;
}
.starters button:hover{background:var(--surface);border-color:var(--accent);color:var(--fg)}

/* week switcher on the Ideas tab */
.ideas-week-nav{
  display:flex;gap:var(--s1);align-items:center;flex-wrap:wrap;
  margin-bottom:var(--s5);
}
.ideas-week-nav button{
  flex:1 1 auto;min-width:76px;
  min-height:36px;padding:7px var(--s2);
  font-size:var(--fs-meta);
  background:var(--surface);border-color:var(--line);color:var(--fg-dim);
}
.ideas-week-nav button:hover{color:var(--fg)}
.ideas-week-nav button.on{
  background:var(--surface-3);border-color:var(--accent);color:var(--fg);
}

/* legacy header tab strip (kept working until the sidenav lands) */
.tabs{
  display:flex;flex:none;gap:0;padding:3px;
  background:var(--surface-3);border:1px solid var(--line);border-radius:var(--r);
}
.tabs button{
  background:none;border:none;box-shadow:none;border-radius:var(--r-sm);
  min-height:34px;padding:6px 11px;
  color:var(--fg-dim);font-size:var(--fs-meta);font-weight:700;white-space:nowrap;
}
.tabs button.on{background:var(--surface);color:var(--fg);box-shadow:inset 0 -2px 0 var(--accent)}

/* ============================ FORM FIELDS =============================== */
input,textarea,select{
  font-family:var(--font-body);
  font-size:var(--fs-body);
  color:var(--fg);
  background:var(--surface);
  border:1px solid var(--line-strong);
  border-radius:var(--r);
  padding:11px var(--s3);
}
input:focus,textarea:focus,select:focus{border-color:var(--accent)}

/* The draft editor — a document, not a form field. The whole app is light now,
   so paper can no longer differ by being paler: it reads as paper through
   elevation and edge (white, hairline, a real --shadow-2) on the tinted ground. */
textarea.edit,.draft-preview{
  display:block;width:100%;
  min-height:340px;
  background:var(--paper);
  color:var(--ink);
  border:1px solid var(--line);
  border-radius:var(--r);
  padding:var(--s5);
  font-family:var(--font-body);
  font-size:var(--fs-body);
  line-height:var(--lead-prose);
  resize:vertical;
  box-shadow:var(--shadow-2);
  caret-color:var(--accent);
}
.draft-preview{white-space:pre-wrap;overflow-wrap:anywhere;min-height:0;resize:none}
textarea.edit::placeholder{color:var(--ink-dim)}

/* =============================== CARDS ================================== */
.card{
  position:relative;
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r);
  padding:var(--s4);
  margin-bottom:var(--s2);
  cursor:pointer;
  box-shadow:var(--shadow-1);
  transition:border-color var(--dur-1) var(--ease),
             background var(--dur-1) var(--ease),
             transform var(--dur-1) var(--ease);
}
.card:hover{border-color:var(--line-strong);box-shadow:var(--shadow-2);transform:translateY(-1px)}
.card:active{transform:scale(.985)}
.card h3{
  margin:0 0 var(--s2);
  font-size:var(--fs-lead);
  line-height:var(--lead-h3);
  font-weight:700;
  letter-spacing:var(--track-h3);
}
.card p{color:var(--fg-dim);font-size:var(--fs-sm)}
.card.static{cursor:default}
.card.static:hover{border-color:var(--line);box-shadow:var(--shadow-1);transform:none}

/* conversation rows */
.card.thread h3{font-size:var(--fs-body);margin-bottom:var(--s1);font-weight:600}
.card.thread .meta{justify-content:space-between;width:100%}

.meta{
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2);
  font-size:var(--fs-meta);
  color:var(--fg-dim);
  line-height:var(--lead-body);
  letter-spacing:var(--track-meta);
}
.meta>span{white-space:nowrap}

/* "Needs a decision from you" — the one card state that shouts */
/* Attention state: --warm is the marker, --warm-soft the fill, and the copy on
   it stays --fg (10.84:1). Warm never carries white text anywhere in this file. */
.needs-decision{
  border-color:var(--warm);
  background:var(--warm-soft);
  color:var(--fg);
  box-shadow:var(--shadow-1);
}
.needs-decision p,.needs-decision .meta{color:var(--fg-dim)}
.needs-decision:hover{border-color:var(--warn);box-shadow:var(--shadow-2)}
/* opt-in label, so it never duplicates a string the client already renders */
.needs-decision.with-label::after{
  content:"Needs a decision from you";
  display:block;margin-top:var(--s3);
  font-family:var(--font-display);
  font-size:var(--fs-meta);font-weight:700;
  letter-spacing:var(--track-caps);text-transform:uppercase;
  color:var(--warn);
}
.needs-decision .decision-label{
  display:block;margin-top:var(--s3);
  font-family:var(--font-display);
  font-size:var(--fs-meta);font-weight:700;
  letter-spacing:var(--track-caps);text-transform:uppercase;
  color:var(--warn);
}

/* ============================ IDEA CARDS ================================ */
.idea-card{
  position:relative;
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r);
  padding:var(--s4);
  margin-bottom:var(--s2);
  cursor:pointer;
  box-shadow:var(--shadow-1);
  transition:border-color var(--dur-1) var(--ease),
             background var(--dur-1) var(--ease),
             transform var(--dur-1) var(--ease),
             opacity var(--dur-2) var(--ease);
}
.idea-card:hover{border-color:var(--line-strong);box-shadow:var(--shadow-2);transform:translateY(-1px)}
.idea-card:active{transform:scale(.985)}
.idea-card.picked{
  opacity:.8;
  border-color:var(--ok);
  background:var(--mint-wash);
  box-shadow:none;
}
.idea-card.picked:hover{opacity:1;box-shadow:var(--shadow-1)}
.idea-card.dismissed{opacity:.55;box-shadow:none}
.idea-card.dismissed .idea-title{text-decoration:line-through;text-decoration-color:var(--fg-faint)}

.idea-title{
  font-family:var(--font-display);
  font-size:var(--fs-lead);
  font-weight:700;
  letter-spacing:var(--track-h3);
  line-height:var(--lead-h3);
  margin:var(--s2) 0 var(--s1);
  max-width:var(--measure);
}
.idea-hook{
  font-size:var(--fs-sm);
  color:var(--fg-dim);
  line-height:var(--lead-body);
  margin:var(--s1) 0 0;
  max-width:var(--measure);
}
.idea-star{
  display:inline-flex;align-items:center;
  color:var(--accent);
  font-size:14px;line-height:1;
  margin-left:var(--s1);
}
.idea-star .ic{width:14px;height:14px;fill:var(--accent);stroke:var(--accent)}

/* idea detail sheet internals */
.idea-sheet h3{margin:0 0 var(--s2);font-size:var(--fs-h3)}
.idea-sheet .why,.split-detail .why,.why{margin:var(--s4) 0;line-height:1.6}
.idea-sheet .why strong,.split-detail .why strong,.why>strong{
  display:block;
  font-family:var(--font-display);
  font-size:var(--fs-meta);font-weight:700;
  letter-spacing:var(--track-caps);text-transform:uppercase;
  color:var(--fg-dim);
}
.idea-sheet .why span,.split-detail .why span,.why>span{
  display:block;
  color:var(--fg);
  font-size:var(--fs-sm);
  margin-top:var(--s1);
  max-width:var(--measure);
}
.idea-sheet .sources,.sources{margin:var(--s4) 0 0}
.idea-sheet .sources>strong,.sources>strong{
  display:block;margin-bottom:var(--s2);
  font-family:var(--font-display);
  font-size:var(--fs-meta);font-weight:700;
  letter-spacing:var(--track-caps);text-transform:uppercase;
  color:var(--fg-dim);
}

.idea-source{
  background:var(--ground);
  border:1px solid var(--line);
  border-radius:var(--r-sm);
  padding:var(--s3);
  margin-bottom:var(--s2);
  cursor:pointer;
  transition:border-color var(--dur-1) var(--ease),background var(--dur-1) var(--ease);
}
.idea-source:hover{background:var(--surface-3);border-color:var(--accent)}
.idea-source-author{
  font-size:var(--fs-meta);
  color:var(--fg-dim);
  font-weight:600;
  margin-bottom:var(--s1);
}
.idea-source-quote{
  font-size:var(--fs-sm);
  line-height:var(--lead-body);
  color:var(--fg);
  max-width:var(--measure);
}

/* ============================ FIT LABELS ================================ */
/* Strong fit / Good fit / Worth a look — the score, said in words. */
.fitlabel{
  display:inline-flex;align-items:center;gap:6px;
  padding:3px 10px;
  border-radius:var(--r-pill);
  border:1px solid var(--line-strong);
  background:var(--surface);
  color:var(--fg);
  font-family:var(--font-display);
  font-size:var(--fs-meta);
  font-weight:700;
  letter-spacing:var(--track-meta);
  white-space:nowrap;
}
.fitlabel::before{
  content:"";width:7px;height:7px;border-radius:50%;
  background:currentColor;flex:none;
}
.fitlabel .num,.fitlabel small{
  font-weight:600;
  color:var(--fg-dim);
  font-size:11px;
  letter-spacing:0;
}
.fitlabel.strong{border-color:var(--accent);color:var(--accent);background:var(--surface)}
.fitlabel.strong .num{color:var(--fg-dim)}
.fitlabel.good{border-color:var(--ok);color:var(--fg);background:var(--mint-wash)}
.fitlabel.good::before{background:var(--ok)}
.fitlabel.worth,.fitlabel.maybe{border-color:var(--line-strong);color:var(--fg-dim)}
.fitlabel.worth::before,.fitlabel.maybe::before{background:var(--fg-faint)}

/* ============================== PILLS =================================== */
.pill{
  display:inline-flex;align-items:center;gap:5px;
  padding:2px 10px;
  border:1px solid var(--line-strong);
  border-radius:var(--r-pill);
  background:var(--surface);
  color:var(--fg-dim);
  font-size:var(--fs-meta);
  font-weight:600;
  letter-spacing:var(--track-meta);
  white-space:nowrap;
  line-height:var(--lead-body);
}
/* Five status variants. On a light ground the tint is pale and the ink stays
   dark — every label below is ≥4.5:1 on the fill it actually sits on. */
.pill.draft{background:var(--surface-3);border-color:var(--line-strong);color:var(--fg)}
.pill.approved{background:var(--mint-wash);border-color:var(--ok);color:var(--fg)}
.pill.killed{background:var(--surface-2);border-color:var(--line);color:var(--fg-dim)}
.pill.blocked{background:var(--warm-soft);border-color:var(--warm);color:var(--warn)}
.pill.published{background:var(--surface);border-color:var(--ok);color:var(--fg-dim)}
/* origin variants */
.pill.origin-you{border-color:var(--accent);color:var(--accent);background:var(--surface)}
.pill.origin-auto{border-color:var(--line);color:var(--fg-dim)}
.pill .ic{width:13px;height:13px}

/* source chips on idea cards — six origins, palette-only differentiation */
/* Six origins, told apart with the palette alone: filled vs outlined, cool vs
   warm vs positive. Every one keeps its label ≥4.5:1 on its own fill. */
.idea-chip{
  display:inline-flex;align-items:center;
  padding:2px 8px;
  margin:0 var(--s1) var(--s1) 0;
  border:1px solid var(--line);
  border-radius:var(--r-pill);
  background:var(--surface);
  color:var(--fg-dim);
  font-size:11px;
  font-weight:600;
  letter-spacing:var(--track-meta);
  white-space:nowrap;
  line-height:var(--lead-body);
}
.idea-chip.x{background:var(--surface-3);border-color:var(--line-strong);color:var(--fg)}
.idea-chip.dailydev{background:var(--surface);border-color:var(--accent);color:var(--accent)}
.idea-chip.youtube{background:var(--warm-soft);border-color:var(--warm);color:var(--warn)}
.idea-chip.futuretools{background:var(--surface);border-color:var(--warm);color:var(--warn)}
.idea-chip.recall{background:var(--mint-wash);border-color:var(--line-strong);color:var(--fg)}
.idea-chip.bookmark{background:var(--surface);border-color:var(--ok);color:var(--accent)}

.source-tag{
  display:inline-block;
  background:var(--surface-3);
  border:1px solid var(--line);
  color:var(--fg-dim);
  font-size:var(--fs-meta);
  padding:3px 9px;
  border-radius:var(--r-sm);
  margin:0 var(--s1) var(--s1) 0;
  max-width:100%;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}

/* persistent demo-mode marker */
.demo-badge{
  display:inline-flex;align-items:center;gap:6px;
  padding:3px 10px;
  border:1px solid var(--warm);
  border-radius:var(--r-pill);
  background:var(--warm);
  color:var(--fg);
  font-family:var(--font-display);
  font-size:11px;font-weight:700;
  letter-spacing:var(--track-caps);text-transform:uppercase;
  white-space:nowrap;
}
.demo-badge::before{
  content:"";width:7px;height:7px;border-radius:50%;background:var(--fg);flex:none;
}

/* ============================ GROUP HEADS =============================== */
.grouphead{
  display:flex;align-items:center;gap:var(--s2);
  margin:var(--s6) 0 var(--s3);
  font-family:var(--font-display);
  font-size:var(--fs-meta);
  font-weight:700;
  letter-spacing:var(--track-caps);
  text-transform:uppercase;
  color:var(--fg-dim);
}
.grouphead:first-child{margin-top:0}
.grouphead span{
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r-pill);
  padding:1px 8px;
  font-size:11px;
  font-weight:700;
  letter-spacing:var(--track-meta);
  color:var(--fg);
}
.grouphead::after{
  content:"";flex:1 1 auto;height:1px;background:var(--line);min-width:var(--s5);
}

/* ============================== DETAILS ================================= */
details{
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r);
  padding:var(--s3) var(--s4);
  margin-top:var(--s3);
  box-shadow:var(--shadow-1);
}
details+details{margin-top:var(--s2)}
summary{
  display:flex;align-items:center;gap:var(--s2);
  cursor:pointer;
  list-style:none;
  font-family:var(--font-display);
  font-size:var(--fs-meta);
  color:var(--fg-dim);
  font-weight:700;
  letter-spacing:var(--track-caps);
  text-transform:uppercase;
}
summary::-webkit-details-marker{display:none}
summary::after{
  content:"";margin-left:auto;flex:none;
  width:7px;height:7px;
  border-right:1.75px solid currentColor;
  border-bottom:1.75px solid currentColor;
  transform:rotate(45deg) translate(-2px,-2px);
  transition:transform var(--dur-1) var(--ease);
}
details[open]>summary::after{transform:rotate(-135deg) translate(-2px,-2px)}
summary:hover{color:var(--fg)}
details .body{
  white-space:pre-wrap;
  overflow-wrap:anywhere;
  font-size:var(--fs-sm);
  line-height:var(--lead-prose);
  color:var(--fg-dim);
  margin-top:var(--s3);
  max-width:var(--measure);
}

/* ============================== CHAT ==================================== */
/* THE CHAT COLUMN.
   A transcript laid out across the full 1080px content width scatters: the
   user's turn pins hard right, the assistant's post card strands left, and the
   eye crosses a void between them. Three centred grid tracks give the
   conversation one comfortable reading measure at every window width, and the
   bubbles align inside it — right and left of the same column, not of the
   window. The rule is keyed to what the chat actually renders, so the Ideas,
   Stats and Drafts screens keep the full width they need. */
#main:has(>.msg),#main:has(>.pinbar),#main:has(>.empty>.starters){
  display:grid;
  grid-template-columns:
    [full-start] minmax(0,1fr)
    [col-start] minmax(0,var(--chat-measure)) [col-end]
    minmax(0,1fr) [full-end];
  align-content:start;
  row-gap:var(--s4);
}
#main:has(>.msg)>*,#main:has(>.pinbar)>*,#main:has(>.empty>.starters)>*{
  grid-column:col-start / col-end;
}
/* gaps are the grid's job here, so the children stop carrying their own */
#main:has(>.msg)>.msg,#main:has(>.pinbar)>.msg{margin-block:0}
#main:has(>.pinbar)>.pinbar{margin-bottom:0}
@media (min-width:1024px){
  /* the composer floats over the transcript; leave it a landing strip */
  #main:has(>.msg),#main:has(>.pinbar),#main:has(>.empty>.starters){
    padding-bottom:calc(var(--composer-h) + var(--s5));
  }
}

.msg{
  margin:var(--s3) 0;
  padding:var(--s3) var(--s4);
  border-radius:var(--r-lg);
  white-space:pre-wrap;
  overflow-wrap:anywhere;
  max-width:var(--measure);
  animation:riseIn var(--dur-2) var(--ease) both;
}
/* The user's own turn is the one warm surface in the transcript: a peach fill
   behind dark ink (10.84:1). The assistant's turn is white paper lifting off
   the ground — the two read apart by warmth and elevation, not by weight. */
.msg.me{
  background:var(--warm-soft);
  border:1px solid var(--warm);
  color:var(--fg);
  margin-left:auto;
  border-bottom-right-radius:var(--r-sm);
  max-width:min(var(--measure),86%);
}
.msg.ai{
  background:var(--surface);
  border:1px solid var(--line);
  border-bottom-left-radius:var(--r-sm);
  white-space:normal;      /* AI replies are rendered markdown */
  max-width:100%;          /* the column is already the measure */
  box-shadow:var(--shadow-1);
}
.msg.ai p{margin:0 0 var(--s3)}
.msg.ai p:last-child{margin-bottom:0}
.msg.ai ul,.msg.ai ol{margin:0 0 var(--s3);padding-left:22px}
.msg.ai li{margin:var(--s1) 0}
.msg.ai li::marker{color:var(--fg-dim)}
.msg.ai b,.msg.ai strong{color:var(--fg);font-weight:700}
.msg.ai em{color:var(--fg-dim);font-style:italic}
.msg.ai h1,.msg.ai h2,.msg.ai h3,.msg.ai h4{
  font-size:var(--fs-lead);line-height:var(--lead-h3);letter-spacing:var(--track-h3);
  margin:var(--s4) 0 var(--s2);
}
.msg.ai h1:first-child,.msg.ai h2:first-child,
.msg.ai h3:first-child,.msg.ai h4:first-child{margin-top:0}
.msg.ai a{color:var(--accent);text-decoration:underline;text-underline-offset:2px}
.msg.ai code{
  background:var(--ground);
  border:1px solid var(--line);
  border-radius:var(--r-xs);
  padding:1px 5px;
  color:var(--fg);
}
.msg.ai pre{
  background:var(--ground);
  border:1px solid var(--line);
  border-radius:var(--r-sm);
  padding:var(--s3);
  overflow-x:auto;
  margin:0 0 var(--s3);
}
.msg.ai pre code{background:none;border:0;padding:0}
.msg.ai blockquote{
  margin:0 0 var(--s3);
  padding-left:var(--s4);
  border-left:1px solid var(--line-strong);
  color:var(--fg-dim);
}
.msg.ai hr{margin:var(--s4) 0}
.msg.ai table{border-collapse:collapse;width:100%;font-size:var(--fs-sm);display:block;overflow-x:auto}
.msg.ai th,.msg.ai td{border:1px solid var(--line);padding:6px var(--s2);text-align:left}
.msg.ai th{background:var(--surface-3);font-weight:700}

.ts{
  font-size:var(--fs-meta);
  color:var(--fg-dim);
  letter-spacing:var(--track-meta);
  margin-top:var(--s2);
}
.me .ts{text-align:right}

/* running-job line (compact form; the phase list is the hero form) */
.progress{
  display:flex;align-items:center;gap:var(--s2);
  color:var(--accent);
  font-size:var(--fs-meta);
  font-weight:600;
  letter-spacing:var(--track-meta);
  margin-bottom:var(--s2);
}
.spin{
  width:9px;height:9px;border-radius:50%;
  background:var(--accent);flex:none;
  animation:pulse 1s var(--ease) infinite;
}
.dot{
  width:9px;height:9px;border-radius:50%;
  background:var(--ok);flex:none;
  box-shadow:0 0 0 3px color-mix(in srgb,var(--mint) 45%,transparent);
}
.dot.busy{
  background:var(--warm);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--warm) 40%,transparent);
  animation:pulse 1s var(--ease) infinite;
}
.dot.off{background:var(--line-strong);box-shadow:none}

/* inline warning inside a message (voice check, missing pulse, etc.) */
.warn{
  display:flex;align-items:flex-start;gap:var(--s2);
  background:var(--warm-soft);
  border:1px solid var(--warm);
  border-radius:var(--r-sm);
  color:var(--fg);
  font-size:var(--fs-meta);
  font-weight:600;
  padding:var(--s2) var(--s3);
  margin-bottom:var(--s2);
}
.warn .ic{width:15px;height:15px;margin-top:1px;color:var(--warn)}

/* pinned draft banner */
/* pinned draft banner — a pinned state is an attention state, so it is warm */
.pinbar{
  display:flex;align-items:center;gap:var(--s2);
  background:var(--warm-soft);
  border:1px solid var(--warm);
  border-radius:var(--r-sm);
  padding:var(--s2) var(--s3);
  font-size:var(--fs-meta);
  color:var(--fg);
  margin-bottom:var(--s3);
}
.pinbar button{margin-left:auto}

/* ============ CONTENT AS PAPER — the ready-to-paste post ================
   The app is light now, so a post can no longer say "document" by being paler
   than its surroundings. It says it by elevation and edge instead: pure white,
   one hairline, a real --shadow-2, sitting on the tinted ground. The accent
   lives in the label, never in a bar down one side of the card. */
.post{
  background:var(--paper);
  color:var(--ink);
  border:1px solid var(--line);
  border-radius:var(--r-lg);
  padding:var(--s5) var(--s5) var(--s4);
  margin:var(--s5) 0;
  /* The post always renders inside the white assistant bubble, so white-on-white
     would have nothing to say. The ring of page ground puts the desk back
     underneath it: ground → bubble → desk → document. On the ground itself the
     ring is invisible and --shadow-2 does the work alone. */
  box-shadow:0 0 0 6px var(--ground),var(--shadow-2);
}
.post::before{
  content:"READY TO PASTE";
  display:block;
  font-family:var(--font-display);
  font-size:var(--fs-meta);
  font-weight:700;
  letter-spacing:var(--track-caps);
  color:var(--accent);
  padding-bottom:var(--s2);
  margin-bottom:var(--s3);
  border-bottom:1px solid var(--line);
}
/* The post renders inside the assistant bubble, so `.msg.ai pre` was dressing
   the post body as a code block — a tinted, bordered well in the middle of the
   document. `.msg.ai pre` is two classes deep, so the reset has to match it
   selector-for-selector. The post body is prose; take the costume back off. */
.post pre,.post .body,.msg.ai .post pre,.msg.ai .post .body{
  margin:0 0 var(--s4);
  white-space:pre-wrap;
  overflow-wrap:anywhere;
  font-family:var(--font-body);
  font-size:var(--fs-body);
  line-height:var(--lead-prose);
  color:var(--ink);
  max-width:var(--measure);
  background:none;
  border:0;
  border-radius:0;
  padding:0;
  overflow-x:visible;
}
.post .row{margin:0}
.post .row button{flex:0 0 auto;min-width:0}
.post button{
  font-size:var(--fs-meta);
  min-height:36px;padding:8px var(--s3);
  background:var(--ground);
  border-color:var(--line-strong);
  color:var(--fg);
}
.post button:hover{background:var(--surface-3);border-color:var(--accent)}
.post button.primary{background:var(--accent);border-color:var(--accent);color:var(--surface)}
.post button.primary:hover{background:var(--accent-hover);border-color:var(--accent-hover);color:var(--surface)}
.post button.danger{background:none;border-color:var(--warn);color:var(--warn)}
.post button.danger:hover{background:var(--warn);border-color:var(--warn);color:var(--surface)}
.post a{color:var(--accent-press)}

/* ======================== ASSETS / IMAGES =============================== */
.assets{
  display:grid;
  grid-template-columns:repeat(auto-fill,minmax(104px,1fr));
  gap:var(--s2);
  margin-top:var(--s3);
}
.assets img{
  width:100%;
  aspect-ratio:4/5;
  object-fit:cover;
  border-radius:var(--r-sm);
  border:1px solid var(--line);
  background:var(--mint-wash);
  display:block;
  cursor:zoom-in;
  box-shadow:var(--shadow-1);
  transition:border-color var(--dur-1) var(--ease),transform var(--dur-1) var(--ease);
}
.assets img:hover{border-color:var(--accent);transform:scale(1.015)}

.shots{
  display:flex;gap:var(--s2);
  overflow-x:auto;
  margin:var(--s4) 0;
  padding-bottom:var(--s1);
  scroll-snap-type:x proximity;
}
.shots figure{margin:0;flex:none;width:158px;scroll-snap-align:start}
.shots img{
  width:100%;
  aspect-ratio:4/5;
  object-fit:cover;
  border-radius:var(--r-sm);
  border:1px solid var(--line);
  display:block;
  background:var(--mint-wash);
  cursor:zoom-in;
  box-shadow:var(--shadow-1);
  transition:border-color var(--dur-1) var(--ease);
}
.shots img:hover{border-color:var(--accent)}
.shots figcaption{
  font-size:var(--fs-meta);
  color:var(--fg-dim);
  margin-top:6px;
  text-align:center;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}

/* =========================== STATS SURFACE ============================== */
.stat-tiles{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:var(--s3);
  margin-bottom:var(--s5);
}
.stat-tile{
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r);
  padding:var(--s4);
  box-shadow:var(--shadow-1);
}
.stat-tile .num,.count-up{
  display:block;
  font-family:var(--font-display);
  font-size:var(--fs-h1);
  font-weight:700;
  line-height:var(--lead-display);
  letter-spacing:var(--track-display);
  color:var(--fg);
  font-variant-numeric:tabular-nums;
}
.stat-tile.key .num{color:var(--accent)}
.stat-tile .label{
  font-size:var(--fs-meta);
  color:var(--fg-dim);
  margin-top:var(--s2);
  letter-spacing:var(--track-meta);
}
.stat-tile .delta{
  display:inline-flex;align-items:center;gap:4px;
  margin-top:var(--s2);
  font-size:var(--fs-meta);
  font-weight:600;
  color:var(--fg-dim);
}
.stat-tile .delta.up{color:var(--fg)}
.stat-tile .delta.up::before{content:"▲";font-size:9px;color:var(--ok)}
.stat-tile .delta.down::before{content:"▼";font-size:9px;color:var(--warn)}

.post-item{
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r);
  padding:var(--s4);
  margin-bottom:var(--s2);
  box-shadow:var(--shadow-1);
}
.post-item .title{
  font-size:var(--fs-body);
  font-weight:600;
  margin-bottom:var(--s2);
  line-height:var(--lead-tight);
  max-width:var(--measure);
}
.post-item .meta-row{
  display:flex;flex-wrap:wrap;gap:var(--s4);
  font-size:var(--fs-meta);
  color:var(--fg-dim);
  margin-bottom:var(--s3);
}
.post-bar{
  height:6px;
  background:var(--surface-3);
  border-radius:var(--r-pill);
  overflow:hidden;
}
.post-bar-fill{
  height:100%; width:100%;
  transform:scaleX(var(--fill,0)); transform-origin:left center;
  background:var(--ok);
  border-radius:var(--r-pill);
  transition:transform var(--dur-2) var(--ease);
}

.lessons{
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r);
  padding:var(--s5);
  margin-top:var(--s4);
  box-shadow:var(--shadow-1);
}
.lessons h4{
  margin:0 0 var(--s3);
  font-size:var(--fs-lead);
  color:var(--fg);
}
.lessons .body{
  white-space:pre-wrap;
  overflow-wrap:anywhere;
  font-size:var(--fs-sm);
  line-height:var(--lead-prose);
  color:var(--fg-dim);
  max-width:var(--measure);
}

/* ================= PHASE LIST — the signature panel ===================== */
/* Every phase is listed up front; each lights only when the server says it
   actually started. Markers are drawn with ::before so no extra markup is
   required, and the connector rail is ::after. */
.phase-list{
  list-style:none;
  margin:0;padding:var(--s2) 0;
  display:flex;flex-direction:column;
  position:relative;
}
.phase{
  position:relative;
  display:flex;flex-direction:column;
  gap:2px;
  padding:10px 0 10px 38px;
  color:var(--fg-dim);
  font-size:var(--fs-sm);
  line-height:var(--lead-tight);
  transition:color var(--dur-2) var(--ease);
}
/* marker */
.phase::before{
  content:"";
  position:absolute;left:0;top:11px;
  width:18px;height:18px;
  border-radius:50%;
  border:1.75px solid var(--line-strong);
  background:var(--surface);
  box-sizing:border-box;
  box-shadow:var(--shadow-1);
  transition:border-color var(--dur-2) var(--ease),
             background var(--dur-2) var(--ease),
             box-shadow var(--dur-2) var(--ease);
}
/* connector rail between markers */
.phase::after{
  content:"";
  position:absolute;left:8.25px;top:29px;bottom:-10px;
  width:1.5px;
  background:var(--line);
}
.phase:last-child::after{display:none}

.phase .phase-label,.phase>span:first-child,.phase>strong{
  font-family:var(--font-display);
  font-weight:600;
  letter-spacing:var(--track-h3);
  color:inherit;
}
.phase .phase-detail,.phase small,.phase em{
  font-family:var(--font-body);
  font-style:normal;
  font-size:var(--fs-meta);
  color:var(--fg-dim);
  max-width:var(--measure);
}

/* done */
.phase.done{color:var(--fg-dim)}
.phase.done::before{
  background:var(--ok);
  border-color:var(--ok);
}
.phase.done::after{background:var(--ok)}
/* the tick is drawn into the marker, so no extra markup is needed */
.phase.done::before{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18' fill='none' stroke='%23FFFFFF' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4.6 9.3l2.9 2.9 5.9-6.1'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:center;
  background-size:14px 14px;
}

/* Active — the one animated thing in the app, and the clearest case for the
   attention accent: a warm ring on a warm-soft fill, no text on it at all. */
.phase.active{color:var(--fg)}
.phase.active .phase-label,.phase.active>span:first-child{font-weight:700}
.phase.active::before{
  border-color:var(--warm);
  background:var(--warm-soft);
  animation:phasePulse 1.6s var(--ease) infinite;
}
.phase.active .phase-detail,.phase.active small{color:var(--fg-dim)}

/* skipped */
.phase.skipped{color:var(--fg-dim)}
.phase.skipped::before{
  border-style:dashed;
  border-color:var(--line-strong);
  background:transparent;
  box-shadow:none;
}
.phase.skipped .phase-label,.phase.skipped>span:first-child{font-weight:600}
.phase.skipped .phase-detail,.phase.skipped small{opacity:.85}

/* pending (default) already covered; give it a soft ring only */
.phase-list .phase:not(.done):not(.active):not(.skipped)::before{
  background:var(--surface);
}

/* the panel the phase list lives in */
.job-panel,.phase-panel{
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r-lg);
  padding:var(--s5);
  margin:var(--s4) 0;
  box-shadow:var(--shadow-2);
}
.job-panel h3,.phase-panel h3{
  font-size:var(--fs-lead);
  margin:0 0 var(--s1);
}
.job-panel .elapsed,.phase-panel .elapsed{
  font-size:var(--fs-meta);
  color:var(--fg-dim);
  margin-bottom:var(--s3);
}

/* ============================ SKELETONS ================================= */
.skeleton{
  position:relative;
  overflow:hidden;
  background:var(--surface-3);
  border-radius:var(--r-sm);
  min-height:14px;
  color:transparent!important;
  user-select:none;
  pointer-events:none;
}
.skeleton::after{
  content:"";
  position:absolute;inset:0;
  transform:translateX(-100%);
  /* on a light ground the sweep is a highlight, not a dark band */
  background:linear-gradient(90deg,
    transparent 0%,
    color-mix(in srgb,var(--surface) 55%,transparent) 40%,
    color-mix(in srgb,var(--surface) 95%,transparent) 55%,
    transparent 100%);
  animation:shimmer 1.35s var(--ease) infinite;
}
.skeleton.line{height:14px;margin-bottom:var(--s2)}
.skeleton.line.w60{width:60%}
.skeleton.line.w40{width:40%}
.skeleton.line.w80{width:80%}
.skeleton.title{height:22px;width:70%;margin-bottom:var(--s3);border-radius:var(--r-sm)}
.skeleton.card,.skeleton.block{height:104px;border-radius:var(--r);margin-bottom:var(--s2)}
.skeleton.tile{height:112px;border-radius:var(--r)}
.skeleton.avatar{width:44px;height:44px;border-radius:50%;background:var(--mint-wash)}

/* =========================== EMPTY STATES =============================== */
.empty,.no-ideas{
  color:var(--fg-dim);
  text-align:center;
  padding:var(--s7) var(--s4);
  font-size:var(--fs-sm);
  line-height:var(--lead-prose);
  max-width:44ch;
  margin:0 auto;
}
.empty b,.no-ideas b{color:var(--fg)}
/* The empty state is where a pale app most easily reads as clinical, so the
   glyph sits in a soft peach disc — accent on --warm-soft is 4.78:1. */
.empty .ic,.no-ideas .ic{
  width:34px;height:34px;
  box-sizing:content-box;
  padding:var(--s4);
  margin:0 auto var(--s4);
  color:var(--accent);
  background:var(--warm-soft);
  border-radius:50%;
}
.empty h3{font-size:var(--fs-h3);color:var(--fg);margin-bottom:var(--s2)}

/* ============================= ERROR BOX ================================ */
.errorbox{
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--s3);
  background:var(--warm-soft);
  border:1px solid var(--warm);
  border-radius:var(--r);
  padding:var(--s4);
  margin:var(--s4) 0;
  color:var(--fg);
  font-size:var(--fs-sm);
  box-shadow:var(--shadow-1);
}
.errorbox .ic{width:22px;height:22px;flex:none;color:var(--warn)}
.errorbox .msg-text,.errorbox p{flex:1 1 220px;margin:0;min-width:0}
.errorbox strong{display:block;font-family:var(--font-display);font-weight:700;margin-bottom:2px;color:var(--warn)}
.errorbox button{
  flex:none;
  background:var(--warn);
  border-color:var(--warn);
  color:var(--surface);
  font-weight:700;
}
.errorbox button:hover{background:var(--fg);border-color:var(--fg);color:var(--surface)}

/* ============================== TOAST =================================== */
/* Centring lives on the `translate` property, not on `transform`, so the JS
   spring owns transform outright and never has to re-state the -50%. */
.toast{
  position:fixed;left:50%;
  bottom:calc(var(--navh) + 6rem + var(--safe-b));
  translate:-50% 0;
  z-index:99;
  display:flex;align-items:center;gap:var(--s2);
  background:var(--surface);
  border:1px solid var(--line-strong);
  color:var(--fg);
  padding:10px var(--s4);
  border-radius:var(--r-pill);
  font-family:var(--font-display);
  font-size:var(--fs-sm);
  font-weight:600;
  box-shadow:var(--shadow-2);
  max-width:calc(100vw - var(--s6));
  will-change:transform;
}
.toast::before{
  content:"";width:7px;height:7px;border-radius:50%;background:var(--accent);flex:none;
}
@media (min-width:1024px){
  .toast{bottom:calc(var(--composer-h) + var(--s5))}
}

/* ============================== NETBAR ================================== */
.netbar{
  position:fixed;left:0;right:0;bottom:0;z-index:80;
  background:var(--warn);
  border-top:1px solid var(--warn);
  color:var(--surface);
  font-family:var(--font-display);
  font-size:var(--fs-meta);
  font-weight:700;
  letter-spacing:var(--track-meta);
  text-align:center;
  padding:9px var(--s3) calc(9px + var(--safe-b));
}

/* ========================= SHEET (bottom / dialog) ====================== */
/* GESTURE SURFACES — the CSS keeps its hands off transform here.
   A CSS transition on transform makes an animation uninterruptible: grab a
   closing sheet and it finishes closing before it listens to you. The JS owns
   transform and opacity on these, driven by springs from the live presentation
   value, so CSS only dresses them (SKILL §3, §4). --sheet-blur is the handle
   the JS animates alongside scale so the panel materialises rather than fades. */
.sheet{
  position:fixed;inset:0;z-index:70;
  /* A heavy black wash over a light app looks broken. The veil is a light,
     ink-tinted film with real blur — enough to push the app back, not to
     pretend the lights went out. It is an INK film, which is what lets a
     light panel sit on it: two light translucent layers would cancel. */
  background:color-mix(in srgb,var(--ink) 26%,transparent);
  backdrop-filter:blur(8px) saturate(var(--mat-sat-scrim));
  -webkit-backdrop-filter:blur(8px) saturate(var(--mat-sat-scrim));
  display:flex;flex-direction:column;justify-content:flex-end;
  touch-action:none;
}
.sheet-in{
  background:var(--mat-panel);
  backdrop-filter:blur(var(--sheet-blur,var(--mat-blur-heavy))) saturate(var(--mat-sat-heavy));
  -webkit-backdrop-filter:blur(var(--sheet-blur,var(--mat-blur-heavy))) saturate(var(--mat-sat-heavy));
  border:1px solid var(--line);
  border-top-color:var(--mat-edge);
  border-bottom:0;
  border-top-left-radius:var(--r-lg);
  border-top-right-radius:var(--r-lg);
  max-height:92%;
  overflow-y:auto;
  overscroll-behavior:contain;
  /* the veil swallows touch; the panel keeps vertical pan so its content still
     scrolls, and the JS takes the gesture over at scrollTop 0 */
  touch-action:pan-y;
  padding:var(--s5) var(--s4) calc(var(--s5) + var(--safe-b));
  /* the largest floating surface in the app, so it casts the furthest */
  box-shadow:var(--shadow-glass-heavy);
  will-change:transform;
  transform-origin:50% 100%;
}
@supports not (backdrop-filter:blur(1px)){
  .sheet-in{background:var(--surface)}
}
/* The grab handle: a real affordance for the drag the JS wires up. */
/* The drag handle is a REAL element (.sheet-grab) emitted by the client, because the
   pointer listener needs a hit region — a ::before could be drawn but never grabbed. */
/* While a pointer owns the sheet, nothing inside it may animate on its own —
   the finger is the only thing moving it. */
.sheet[data-dragging],.sheet[data-dragging] *,
.sheet[data-dragging] *::before,.sheet[data-dragging] *::after{
  animation:none!important;
  transition:none!important;
}
.sheet[data-dragging] .sheet-in{cursor:grabbing}
.sheet h4{margin:0 0 var(--s1);font-size:var(--fs-h3)}
.sheet .note{
  color:var(--fg-dim);
  font-size:var(--fs-sm);
  margin-bottom:var(--s4);
  max-width:var(--measure);
}
@media (min-width:1024px){
  .sheet{justify-content:center;align-items:center;padding:var(--s5)}
  .sheet-in{
    width:100%;max-width:620px;
    border:1px solid var(--line);
    border-radius:var(--r-lg);
    padding:var(--s5);
    max-height:86vh;
    transform-origin:50% 50%;
  }
  /* Entering a dialog, the sheet is a card, not a drawer: no drag handle. */
  .sheet-in::before{display:none}
}

/* ============================== MODAL =================================== */
/* Replacement for native confirm(). Always centred, always two explicit acts. */
.modal{
  position:fixed;inset:0;z-index:90;
  background:color-mix(in srgb,var(--ink) 32%,transparent);
  backdrop-filter:blur(10px) saturate(var(--mat-sat-scrim));
  -webkit-backdrop-filter:blur(10px) saturate(var(--mat-sat-scrim));
  display:flex;align-items:center;justify-content:center;
  padding:var(--s4);
}
.modal-panel{
  width:100%;max-width:28.75rem;
  background:var(--mat-panel);
  backdrop-filter:blur(var(--sheet-blur,var(--mat-blur-heavy))) saturate(var(--mat-sat-heavy));
  -webkit-backdrop-filter:blur(var(--sheet-blur,var(--mat-blur-heavy))) saturate(var(--mat-sat-heavy));
  border:1px solid var(--line);
  border-top-color:var(--mat-edge);
  border-radius:var(--r-lg);
  padding:var(--s5);
  box-shadow:var(--shadow-glass-heavy);
  max-height:86vh;overflow-y:auto;
  will-change:transform;
}
@supports not (backdrop-filter:blur(1px)){
  .modal-panel{background:var(--surface)}
}
.modal-panel h3,.modal-panel h4{
  margin:0 0 var(--s2);
  font-size:var(--fs-h3);
}
.modal-panel p{
  color:var(--fg-dim);
  font-size:var(--fs-sm);
  margin:0 0 var(--s3);
  max-width:var(--measure);
}
.modal-panel .danger-note{
  background:var(--warm-soft);
  border:1px solid var(--warm);
  border-radius:var(--r-sm);
  color:var(--fg);
  font-size:var(--fs-meta);
  padding:var(--s2) var(--s3);
  margin-bottom:var(--s3);
}
.modal-actions{
  display:flex;gap:var(--s2);flex-wrap:wrap;
  margin-top:var(--s5);
  justify-content:flex-end;
}
.modal-actions button{flex:1 1 auto;min-width:120px}
@media (min-width:520px){
  .modal-actions button{flex:0 0 auto}
}

/* ============================ LIGHTBOX ================================== */
/* The lightbox is a lit room, not a blackout: the app stays light behind a
   near-opaque ground tint, and the artwork is held by --shadow-2 instead of by
   a black surround. */
.lightbox{
  position:fixed;inset:0;z-index:95;
  background:color-mix(in srgb,var(--ground) 96%,transparent);
  backdrop-filter:blur(24px);
  -webkit-backdrop-filter:blur(24px);
  display:flex;flex-direction:column;
  will-change:transform;
}
.lb-top{
  display:flex;align-items:center;gap:var(--s3);
  padding:var(--s3) var(--s4);
  padding-top:calc(var(--s3) + var(--safe-t));
  color:var(--fg-dim);
  border-bottom:1px solid var(--line);
}
.lb-top span{flex:1 1 auto;min-width:0;font-size:var(--fs-meta);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lb-top button{min-height:40px}
.lb-strip{
  flex:1 1 auto;min-height:0;
  display:flex;
  overflow-x:auto;overflow-y:hidden;
  scroll-snap-type:x mandatory;
  -webkit-overflow-scrolling:touch;
}
.lb-page{
  flex:0 0 100%;
  height:100%;
  scroll-snap-align:center;
  display:flex;align-items:center;justify-content:center;
  overflow:auto;
  padding:var(--s3);
}
.lb-page img{
  max-width:100%;max-height:100%;
  display:block;
  border-radius:var(--r-sm);
  box-shadow:var(--shadow-2);
}
.lb-page img.zoomed{
  max-width:none;max-height:none;
  width:230vw;
  border-radius:0;
  cursor:zoom-out;
}
.lb-hint{
  text-align:center;
  font-size:var(--fs-meta);
  color:var(--fg-dim);
  padding:var(--s2) 0 calc(var(--s3) + var(--safe-b));
}

/* =============================== GATE =================================== */
.gate{
  max-width:26.25rem;
  margin:12vh auto 0;
  text-align:center;
  padding:var(--s6) var(--s5);
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r-lg);
  box-shadow:var(--shadow-2);
}
.gate h2{font-size:var(--fs-h2);margin:0 0 var(--s3)}
.gate p{
  color:var(--fg-dim);
  font-size:var(--fs-sm);
  line-height:var(--lead-prose);
  margin:0 0 var(--s5);
}
.gate input{
  width:100%;
  background:var(--ground);
  border:1px solid var(--line-strong);
  color:var(--fg);
  box-shadow:none;
  border-radius:var(--r);
  padding:13px;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:var(--fs-sm);
  margin-bottom:var(--s3);
  text-align:center;
  letter-spacing:var(--track-caps);
}
.gate button{width:100%;min-height:46px}

/* =================== LINKEDIN PREVIEW — FIDELITY MOCK ===================
   This is a mock of LinkedIn's real UI, not our product. It is isolated:
   the palette tokens are redefined locally so nothing of ours can cascade
   in, and every property is set explicitly. Do NOT restyle to our palette.

   One thing the light rebuild had to fix: our own surfaces are white now, so a
   white LinkedIn card sitting on a white card read as part of the app rather
   than as an embedded preview. It gets a mat — an 8px mint ring drawn from
   --preview-well, a name the local token overrides below deliberately do NOT
   shadow — so it still reads as a screenshot of somewhere else. */
.li{
  --ground:#fff; --surface:#fff; --surface-2:#f3f2ef; --surface-3:#eee;
  --line:#e0e0e0; --line-strong:#d0d0d0;
  --fg:#000; --fg-dim:#666; --fg-faint:#8c8c8c;
  --accent:#0a66c2; --accent-hover:#004182; --accent-press:#004182;
  --paper:#fff; --ink:#000;
  isolation:isolate;
  background:#fff;
  color:#000;
  border:1px solid #e0e0e0;
  border-radius:10px;
  padding:14px;
  box-shadow:0 0 0 8px var(--preview-well,var(--surface-3)),var(--shadow-2);
  margin:var(--s5) 8px;
  font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  font-size:15px;
  line-height:1.45;
  font-variant-numeric:normal;
  letter-spacing:normal;
  text-transform:none;
}
.li *{box-sizing:border-box}
.li-top{display:flex;gap:10px;align-items:center;margin-bottom:10px}
.li-top img{width:44px;height:44px;border-radius:50%;object-fit:cover;background:#eee;flex:none}
.li-name{font-weight:600;font-size:15px;color:#000;letter-spacing:normal}
.li-sub{color:#666;font-size:12px;line-height:1.3;font-weight:400}
.li-body{
  white-space:pre-wrap;
  overflow-wrap:anywhere;
  color:#000;
  font-size:15px;
  line-height:1.45;
}
.li-fold{color:#0a66c2;font-weight:600}
.li-cut{border-top:1px dashed #c00;margin:6px 0 8px;position:relative}
.li-cut span{
  position:absolute;right:0;top:-9px;
  background:#fff;color:#c00;
  font-size:12px;padding:0 4px;letter-spacing:.04em;
  font-family:-apple-system,BlinkMacSystemFont,sans-serif;
}
.li-img{
  width:100%;border-radius:6px;margin-top:10px;display:block;
  border:1px solid #e0e0e0;background:#f3f2ef;
}
.li-acts{
  display:flex;gap:18px;
  color:#666;font-size:12px;
  border-top:1px solid #eee;
  margin-top:12px;padding-top:8px;
}
.li a{color:#0a66c2}
.li button{
  background:#fff;border:1px solid #d0d0d0;color:#000;
  box-shadow:none;
  font-family:inherit;font-weight:600;
}
.li button:hover{background:#f3f2ef;border-color:#0a66c2}

/* ======================== PRESS FEEDBACK (§1) ===========================
   Feedback belongs on pointer-DOWN, never on click. The JS sets [data-press]
   the instant a pointer lands and clears it on up or cancel. Going down is
   instantaneous — the transition duration is zeroed — and coming back up rides
   the element's normal curve, which is what a real button does. :active stays
   as the no-JS fallback and says the same thing. */
[data-press][data-press]{
  transform:scale(.97);
  transition-duration:0s;
}
/* Large surfaces travel less: 3% of a 45rem card is a lurch, 3% of a 6rem
   button is a press. Scaling the scale keeps the movement reading the same. */
.card[data-press][data-press],
.idea-card[data-press][data-press],
.post-item[data-press][data-press],
.idea-source[data-press][data-press],
.starters button[data-press][data-press],
.navitem[data-press][data-press],
.seg button[data-press][data-press],
.assets img[data-press][data-press],
.shots img[data-press][data-press]{transform:scale(.985)}
/* Spring-driven surfaces are the JS's alone; CSS never puts a transform on
   them, pressed or not. */
.sheet-in[data-press],.modal-panel[data-press],
.split-detail[data-press],.lightbox[data-press]{transform:none}

/* The tonal half of the press — every family shifts within its own role. */
button[data-press],.btn[data-press]{background:var(--surface-3);border-color:var(--accent)}
button.primary[data-press]{background:var(--accent-press);border-color:var(--accent-press)}
button.ghost[data-press]{background:var(--surface-3);border-color:var(--line-strong)}
button.danger[data-press]{background:var(--warm-soft);border-color:var(--warn);color:var(--warn)}
.choices button[data-press]{background:var(--accent);border-color:var(--accent);color:var(--surface)}
.card[data-press],.idea-card[data-press],.post-item[data-press],
.starters button[data-press]{
  background:var(--surface-2);
  border-color:var(--line-strong);
  box-shadow:var(--shadow-1);
}
.navitem[data-press]{background:var(--surface-3);color:var(--fg)}
.idea-source[data-press]{background:var(--surface-3);border-color:var(--accent)}
.assets img[data-press],.shots img[data-press]{border-color:var(--accent)}
.post button[data-press]{background:var(--surface-3);border-color:var(--accent)}
/* .li needs no press rule of its own: the block redefines --surface-3 and
   --accent locally, so the generic rule above resolves to LinkedIn's greys. */
/* An icon-only control has no label to shift, so the glyph carries it. */
.icon-btn[data-press] .ic,.composer button[data-press] .ic{opacity:.72}

/* ============================== MOTION ================================== */
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.28}}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
@keyframes riseIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
@keyframes shimmer{100%{transform:translateX(100%)}}
@keyframes phasePulse{
  0%{box-shadow:0 0 0 0 color-mix(in srgb,var(--warm) 70%,transparent)}
  70%{box-shadow:0 0 0 7px color-mix(in srgb,var(--warm) 0%,transparent)}
  100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--warm) 0%,transparent)}
}

/* list items stagger in, ≤200ms total */
.stagger>*{animation:riseIn var(--dur-2) var(--ease) both}
.stagger>*:nth-child(1){animation-delay:0ms}
.stagger>*:nth-child(2){animation-delay:25ms}
.stagger>*:nth-child(3){animation-delay:50ms}
.stagger>*:nth-child(4){animation-delay:75ms}
.stagger>*:nth-child(5){animation-delay:100ms}
.stagger>*:nth-child(6){animation-delay:125ms}
.stagger>*:nth-child(7){animation-delay:150ms}
.stagger>*:nth-child(8){animation-delay:175ms}
.stagger>*:nth-child(n+9){animation-delay:200ms}

/* ========================= ACCESSIBILITY (§14) ==========================
   Three independent signals, not one. Each asks for something different, and
   answering only the first is the usual mistake. */

/* 1. Reduced motion. Not "no feedback" — a gentler, non-vestibular version of
   it. Nothing travels and nothing overshoots; comprehension-bearing changes
   survive as short cross-fades, and every gesture still works, it just does
   not animate afterwards. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    scroll-behavior:auto!important;
    /* travel and scale are struck out; colour and opacity are kept */
    transition-property:opacity,color,background-color,border-color,
                        box-shadow,outline-color,fill,stroke!important;
    transition-duration:var(--dur-1)!important;
  }
  /* looping and sweeping motion stops outright */
  .spin,.dot.busy,.phase.active::before,.post-bar-fill{animation:none!important}
  .skeleton::after{display:none}
  /* arrivals cross-fade in place instead of rising */
  .msg,.stagger>*{animation:fadeIn var(--dur-1) linear both!important}
  .stagger>*{animation-delay:0ms!important}
  /* no hover lift, no press scale — the tonal shift carries the feedback */
  .card:hover,.idea-card:hover,.starters button:hover,
  .assets img:hover,.card:active,.idea-card:active,
  button:active,.btn:active{transform:none}
  [data-press][data-press]{transform:none;box-shadow:none}
}

/* 2. Reduced transparency. The materials become frost: the background opacity
   goes up, the blur comes off, and the scroll edges — which are made of blur —
   go back to being a plain divider. */
@media (prefers-reduced-transparency:reduce){
  #topbar,body>header,.composer,#composer,#sidenav,#sidenav::before,
  .sheet,.sheet-in,.modal,.modal-panel,.lightbox,.scroll-edge{
    backdrop-filter:none;
    -webkit-backdrop-filter:none;
  }
  #topbar,body>header,.composer,#composer{
    background:var(--ground);
    border-color:var(--line);
  }
  #topbar::after,.composer::before,.scroll-edge{display:none}
  #topbar,body>header{border-bottom:1px solid var(--line)}
  #sidenav{background:var(--surface);border-top-color:var(--line)}
  .sheet-in,.modal-panel{background:var(--surface);border-top-color:var(--line)}
  .sheet{background:color-mix(in srgb,var(--ink) 46%,transparent)}
  .modal{background:color-mix(in srgb,var(--ink) 52%,transparent)}
  .lightbox{background:var(--ground)}
}

/* 3. More contrast. Near-solid surfaces and a border that is actually there.
   Every value below is an existing palette token stepped one rung darker —
   no new colour enters the file. */
@media (prefers-contrast:more){
  :root{
    --line:#A9CDD6;          /* was --line-strong */
    --line-strong:#6E8D95;   /* was --fg-faint   */
    --fg-dim:#06323A;        /* secondary copy goes to full ink */
    --fg-faint:#3F636B;      /* hairline ink, still never body text */
    --mat-ground:var(--ground);
    --mat-surface:var(--surface);
    --mat-panel:var(--surface);
    --mat-edge:var(--line-strong);
  }
  #topbar,body>header,.composer,#composer,#sidenav,#sidenav::before,
  .sheet-in,.modal-panel,.scroll-edge{
    backdrop-filter:none;
    -webkit-backdrop-filter:none;
  }
  #topbar::after,.composer::before,.scroll-edge{display:none}
  #topbar,body>header{border-bottom:1px solid var(--line-strong)}
  .card,.idea-card,.post-item,.stat-tile,.lessons,.job-panel,.phase-panel,
  details,.sheet-in,.modal-panel,.split-detail,.post,.toast,.pill,.fitlabel,
  .idea-chip,.source-tag,.gate{border-color:var(--line-strong)}
  button,.btn,input,textarea,select{border-color:var(--line-strong)}
  button.ghost{border-color:var(--line-strong)}
  a,.msg.ai a{text-decoration:underline;text-underline-offset:2px}
  a:focus-visible,button:focus-visible,input:focus-visible,
  textarea:focus-visible,select:focus-visible,summary:focus-visible,
  [tabindex]:focus-visible{outline-width:3px}
  .sheet{background:color-mix(in srgb,var(--ink) 52%,transparent)}
  .modal{background:color-mix(in srgb,var(--ink) 58%,transparent)}
  .skeleton{background:var(--surface-3);border:1px solid var(--line-strong)}
}

/* ============================ RESPONSIVE ================================
   Verified: 360 / 768 / 1280 / 1920. No element sets a fixed width wider
   than its container; every scrolling strip clips inside itself. */

/* 1920 — keep the reading column honest, let the rail breathe */
@media (min-width:1600px){
  :root{--rail:288px}
  /* horizontal only: the vertical padding is the floating chrome's clearance
     and a shorthand here would quietly delete it. */
  #main{padding-left:var(--s7);padding-right:var(--s7)}
  .split{grid-template-columns:minmax(0,1fr) minmax(0,460px)}
}

/* 768 tablet — two-up idea/stat grids, roomier gutters */
@media (min-width:600px) and (max-width:1023.98px){
  #main{padding:var(--s5) var(--s5) calc(var(--navh) + 6.5rem + var(--safe-b))}
  .stat-tiles{grid-template-columns:repeat(2,minmax(0,1fr))}
  .starters{max-width:32.5rem}
}

/* 360 phone — tighten everything that could push the viewport */
@media (max-width:400px){
  :root{--s5:1.25rem;--s6:1.625rem}
  #main{padding-left:var(--s3);padding-right:var(--s3)}
  #topbar,body>header{padding-left:var(--s3);padding-right:154px}
  .composer,#composer{padding-left:var(--s3);padding-right:var(--s3)}
  .navfoot{max-width:56vw;padding-right:var(--s2)}
  .navitem{font-size:10px}
  .stat-tiles{grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s2)}
  .stat-tile{padding:var(--s3)}
  .stat-tile .num{font-size:var(--fs-h2)}
  .row button,.choices button{min-width:0;flex:1 1 46%}
  .msg{padding:var(--s3)}
  .msg.me,.msg.ai{max-width:100%}
  .post{padding:var(--s3) var(--s4)}
  .shots figure{width:132px}
  .assets{grid-template-columns:repeat(auto-fill,minmax(88px,1fr))}
  .idea-title,.card h3{font-size:var(--fs-body)}
  .ideas-week-nav button{min-width:0;flex:1 1 30%}
  textarea.edit{padding:var(--s4)}
}

/* tap targets never drop below 44px on touch pointers */
@media (pointer:coarse){
  button,.btn,.navitem,summary,.tabs button{min-height:44px}
  .acts button,.seg button,.ideas-week-nav button,.tdel{min-height:44px}
  .back{min-height:44px}
}

/* ============================================================================
   INTEGRATION PASS — seams between the parallel builds, found by eyeballing
   the rendered app rather than the code.
   ========================================================================= */

/* The skip-link was in the markup but never styled, so it painted on top of
   every screen. Visible only when keyboard focus reaches it. */
.skiplink{
  position:absolute; left:var(--s3); top:var(--s3); z-index:100;
  padding:var(--s2) var(--s3); border-radius:var(--r-sm);
  background:var(--accent); color:var(--surface);
  font:600 var(--fs-sm)/1 var(--font-display); text-decoration:none;
  transform:translateY(-200%); opacity:0;
}
.skiplink:focus-visible{ transform:none; opacity:1; outline:2px solid var(--fg); outline-offset:2px; }

/* Icon-only rail buttons rendered as full-width bars with a lone glyph. They
   now carry labels and read as a list, matching the nav above them. */
/* Full-width labelled rows belong to the RAIL only. This block sits after the
   mobile rules, so leaving it unscoped overrode them and each icon button became
   164px wide inside the fixed header cluster — spilling left across the page title. */
@media (min-width:1024px){
  .navfoot .icon-btn{
    width:100%; justify-content:flex-start; gap:var(--s3);
    background:none; border-color:transparent; box-shadow:none;
    padding:var(--s2) var(--s3); color:var(--fg-dim);
    font:500 var(--fs-sm)/1 var(--font-display);
  }
  .navfoot .icon-btn .navlabel{ display:inline; }
}
@media (max-width:1023px){
  .navfoot .icon-btn{
    width:40px; min-width:40px; flex:0 0 40px;
    padding:0; justify-content:center;
    background:none; border-color:transparent; box-shadow:none; color:var(--fg-dim);
  }
  .navfoot .icon-btn .navlabel{ display:none; }
}
.navfoot .icon-btn:hover{ color:var(--fg); background:var(--ground); }

/* The demo marker is fixed above the mobile nav; keep it off the last card's text. */
body:has(#demoBadge) #main{ padding-bottom:calc(var(--navh, 4rem) + 3.5rem); }
@media (min-width:1024px){ body:has(#demoBadge) #main{ padding-bottom:var(--s6); } }

/* The chat screen is the first thing a client sees; a 180px column of buttons
   stranded in a 1300px canvas read as unfinished. Give it real composition. */
.empty{ max-width:40rem; margin:0 auto; padding:var(--s7) var(--s4); text-align:center; }
.empty .lead{ font:600 var(--fs-h2)/1.25 var(--font-display); color:var(--fg); margin:0 0 var(--s5); }
.empty .note{ margin-top:var(--s5); color:var(--fg-dim); font-size:var(--fs-sm); }
.starters{ display:grid; gap:var(--s3); }
@media (min-width:720px){ .starters{ grid-template-columns:1fr 1fr; } }
.starters button{
  width:100%; text-align:left; padding:var(--s4);
  min-height:64px; display:flex; align-items:center;
  font:500 var(--fs-body)/1.35 var(--font-display);
  border:1px solid var(--line); border-radius:var(--r);
  background:var(--surface); color:var(--fg);
  box-shadow:var(--shadow-1);
  transition:border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease),
             transform var(--dur-1) var(--ease);
}
.starters button:hover{ border-color:var(--accent); box-shadow:var(--shadow-2); transform:translateY(-1px); }

/* The job panel is the hero. Agent A styled `.progress` as a row for the old inline
   spinner; the phase experience needs head → phases → note stacked, with the list
   wide enough that labels don't wrap mid-phrase. */
.msg.ai .progress,.progress{
  display:flex; flex-direction:column; align-items:stretch; gap:var(--s4);
  width:100%; max-width:35rem;
}
.progress .progress-head{
  width:auto; display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap;
}
.progress .phase-list{ width:100%; max-width:none; }
.progress .progress-note{
  width:auto; max-width:none; margin:0;
  padding-top:var(--s3); border-top:1px solid var(--line);
  color:var(--fg-dim); font-size:var(--fs-sm);
}

/* SAFETY: the page-level demo marker rendered in normal flow at the bottom of the
   document, so a replay could be on screen with nothing saying so. It is the only
   thing standing between a demo and a claim that it ran live — pin it, always visible. */
#demoBadge{
  position:fixed; top:var(--s3); right:var(--s4); z-index:90;
  display:inline-flex; align-items:center; gap:var(--s2);
  padding:var(--s2) var(--s3); border-radius:var(--r-pill);
  background:var(--warm); color:var(--fg);
  border:1px solid var(--warn);
  font:700 var(--fs-meta)/1 var(--font-display);
  letter-spacing:var(--track-caps); text-transform:uppercase;
  box-shadow:var(--shadow-2); pointer-events:none;
}
@media (max-width:1023px){ #demoBadge{ top:auto; bottom:calc(var(--navh) + var(--s3)); right:var(--s3); } }

/* The shell ran ~27px past the viewport, so the whole page scrolled and the title
   slid under the top edge. Pin the shell to the viewport; #main does the scrolling. */
@media (min-width:1024px){
  html,body{ height:100%; overflow:hidden; }
  #shell{ height:100dvh; max-height:100dvh; overflow:hidden; }
  #content{ min-height:0; }
  /* Sticky, not absolute — but still a MATERIAL. An opaque strip here undid the
     glass on every desktop screen: #main is the scroller and passes underneath,
     so the bar has something to be translucent about. */
  #topbar{
    position:sticky; top:0; z-index:30;
    background:var(--mat-ground);
    backdrop-filter:blur(var(--mat-blur)) saturate(var(--mat-sat));
    -webkit-backdrop-filter:blur(var(--mat-blur)) saturate(var(--mat-sat));
    box-shadow:var(--shadow-chrome);
  }
  @supports not (backdrop-filter:blur(1px)){
    #topbar{ background:var(--ground); }
  }
}

/* Removing the side bars took the emphasis with them. It belongs in the label —
   the same resolution this project reached for the callout in slides.css. */
.lessons h2{
  font:700 var(--fs-h3)/1.25 var(--font-display);
  color:var(--accent);
  margin:0 0 var(--s4);
  letter-spacing:var(--track-h3);
}
.idea-source .source-tag,.idea-source b{ color:var(--accent); background:var(--surface); }
.pinbar b{ color:var(--warn); }

/* ============================================================================
   INTEGRATION PASS 3 — seams between the two Apple-design agents, found by
   rendering the app rather than by reading either agent's report.
   ========================================================================= */

/* The grab handle the client actually listens to. >=44px of hit region around a
   28px bar (SKILL §10: real tap targets, generous hysteresis). */
.sheet-grab{
  display:flex; align-items:center; justify-content:center;
  height:2.75rem; margin:calc(var(--s5) * -1) calc(var(--s4) * -1) var(--s2);
  cursor:grab;
}
.sheet-grab:active{cursor:grabbing}
.sheet-grab-bar{
  width:1.75rem; height:4px; border-radius:var(--r-pill);
  background:var(--line-strong);
}
.sheet[data-dragging] .sheet-grab-bar{background:var(--fg-faint)}
@media (min-width:1024px){ .sheet-grab{display:none} }

/* Materialize: blur arrives with the scale. Transform stays untransitioned so the
   spring keeps ownership and the sheet stays grabbable mid-flight (SKILL §3).
   The veil thickens on the same curve, so the whole thing reads as one material
   condensing out of the page rather than as two opacities crossing. */
.sheet-in,.modal-panel{
  transition:backdrop-filter var(--mat-dur) var(--ease),
             -webkit-backdrop-filter var(--mat-dur) var(--ease),
             box-shadow var(--mat-dur) var(--ease);
}
.sheet,.modal{
  transition:backdrop-filter var(--mat-dur) var(--ease),
             -webkit-backdrop-filter var(--mat-dur) var(--ease),
             background-color var(--mat-dur) var(--ease);
}
.sheet[data-dragging],.sheet[data-dragging] .sheet-in{transition:none}

/* The opening screen — leads with what the system has already done.

   These were two big pastel-filled cards: left-aligned labels marooned in a
   centred column, a chevron stranded at the far edge, a lot of dead air in
   between. That shape is a hero, and this is not a hero — it is a status line
   that happens to be tappable. So: no box, no fill, no border. One quiet line
   each, centred under the headline, with the COUNT carrying the whole emphasis
   because the count is the only part that changes day to day. */
.first-run .lead{margin-bottom:var(--s4)}
.first-run-lines{
  display:flex; flex-direction:column; align-items:center;
  gap:var(--s1); margin:0 0 var(--s5);
}
.first-run-line{
  display:inline-flex; align-items:center; gap:var(--s2);
  width:auto; max-width:100%;
  padding:var(--s2) var(--s3);
  background:none; border:0; border-radius:var(--r-pill);
  box-shadow:none; color:var(--fg-dim); text-align:center;
  font:500 var(--fs-body)/1.45 var(--font-display);
  letter-spacing:var(--track-body);
  transition:color var(--dur-1) var(--ease);
}
/* `.empty b` would otherwise claim this, and this block lives inside .empty. */
.first-run-line .first-run-n{
  font-weight:700; color:var(--accent);
  font-size:var(--fs-lead); font-variant-numeric:tabular-nums;
}
/* .empty .ic drops every glyph in an empty state into a soft peach disc. That
   is right for a lone illustrative icon and wrong for a chevron at the end of a
   sentence — it was the last pastel blob left on this screen. Undone here. */
.first-run-line .ic{
  width:15px; height:15px; flex:0 0 auto;
  padding:0; margin:0; background:none; border-radius:0;
  color:var(--fg-faint);
  transition:color var(--dur-1) var(--ease);
}
.first-run-line:hover{color:var(--fg)}
.first-run-line:hover .ic{color:var(--accent)}
/* Something genuinely waiting on you reads warm. With the box gone there is
   nothing left to fill, so the attention accent lands where the eye already
   is — on the number — and the terracotta never carries light text. */
.first-run-line.hot .first-run-n{color:var(--warn)}
.first-run-line.hot .ic{color:var(--warm)}
.first-run-quiet{color:var(--fg-dim); font-size:var(--fs-sm); margin:0 0 var(--s5)}
.first-run-cue{margin:0 0 var(--s4); color:var(--fg-dim); font-size:var(--fs-sm)}

/* ============================================================================
   IDEAS v3 — the taste signal, the engagement it is judged on, and the line
   that makes the learning legible. (V3-IDEAS-CONTRACT.md)
   ========================================================================= */

/* --- What we think you like --------------------------------------------- */
/* One line, not a banner. A taste model nobody can read is a black box; a
   taste model nobody can argue with is worse. So it states its guess and
   offers, quietly, to be told otherwise. */
.taste-line{
  display:flex; align-items:baseline; gap:var(--s2);
  width:100%; margin:0 0 var(--s4); padding:var(--s2) 0;
  background:none; border:0; box-shadow:none; text-align:left;
  color:var(--fg-dim); font-size:var(--fs-sm); line-height:var(--lead-body);
  transition:color var(--dur-1) var(--ease);
}
.taste-line .ic{
  width:15px; height:15px; flex:0 0 auto;
  color:var(--accent); transform:translateY(2px);
}
.taste-line .said{min-width:0; max-width:var(--measure)}
.taste-line .fix{
  flex:0 0 auto; font-style:normal; font-weight:600;
  font-family:var(--font-display); font-size:var(--fs-meta);
  color:var(--accent); white-space:nowrap;
}
.taste-line:hover{color:var(--fg)}
.taste-line:hover .fix{color:var(--accent-hover)}

/* --- Engagement ---------------------------------------------------------- */
/* Saves lead, because "would someone save this" IS the editorial test. Likes,
   views and comments are the supporting cast and are sized like it. A number
   appears only when the data carries it — never a zero, never a placeholder. */
.idea-foot{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--s2) var(--s3); flex-wrap:wrap;
  margin-top:var(--s3);
}
.idea-eng{
  display:flex; align-items:baseline; flex-wrap:wrap;
  gap:var(--s1) var(--s2); min-width:0;
  font-size:var(--fs-meta); color:var(--fg-dim);
  font-variant-numeric:tabular-nums;
}
.idea-eng .eng-lead{color:var(--fg); font-family:var(--font-display); font-weight:600}
.idea-eng .eng-lead b{color:var(--accent); font-weight:700; font-size:var(--fs-sm)}
.idea-eng>span+span::before{
  content:"·"; margin-right:var(--s2); color:var(--line-strong);
}
/* The detail panel can afford the sentence the card cannot. */
.eng-rate{
  margin:var(--s3) 0 0; font-size:var(--fs-sm); color:var(--fg-dim);
  font-variant-numeric:tabular-nums;
}
.eng-rate b{color:var(--accent); font-family:var(--font-display); font-weight:700}

/* --- Like / not-like ----------------------------------------------------- */
/* The first thing in this system that reads a taste back. Quiet on the card:
   it must not compete with the title, and it must never look like the primary
   action, which is still "write this one". */
.idea-taste{display:flex; align-items:center; gap:var(--s1); flex:0 0 auto; margin-left:auto}
.taste-btn{
  display:inline-flex; align-items:center; justify-content:center; gap:var(--s2);
  padding:var(--s1) var(--s3); min-height:1.875rem;
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--r-pill); box-shadow:none; color:var(--fg-dim);
  font:600 var(--fs-meta)/1 var(--font-display);
  letter-spacing:var(--track-meta);
  transition:color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
             background var(--dur-1) var(--ease);
}
.taste-btn .ic{width:15px; height:15px}
.taste-btn .ic.down{transform:rotate(180deg)}
.taste-btn:hover{border-color:var(--line-strong); color:var(--fg); background:var(--surface)}
.taste-btn.down:hover{border-color:var(--warm); color:var(--warn)}
/* Liked: the thumb fills with a wash of the action colour and the label goes
   teal. Nothing shouts — you already know you tapped it. */
.taste-btn.on{border-color:var(--accent); color:var(--accent)}
.taste-btn.on .ic{fill:color-mix(in srgb,var(--accent) 14%,transparent)}
.taste-btn.on:hover{border-color:var(--accent-hover); color:var(--accent-hover)}
/* A liked card keeps its place in the week and wears one hairline. */
.idea-card.liked{border-color:var(--ok)}
.idea-card.liked:hover{border-color:var(--ok)}
/* The detail panel gets the same two controls, under a spoken label. */
.taste-row{
  display:flex; align-items:center; gap:var(--s2); flex-wrap:wrap;
  margin:var(--s4) 0 0; padding-top:var(--s4);
  border-top:1px solid var(--line);
}
/* The label takes the whole line so the pair below it never splits across two. */
.taste-row .taste-q{
  flex:1 0 100%;
  font:700 var(--fs-meta)/1 var(--font-display);
  letter-spacing:var(--track-caps); text-transform:uppercase;
  color:var(--fg-dim); margin-bottom:var(--s1);
}
/* On a card the thumbs go bare. Two labelled buttons plus a metric row wrap to
   a second line in the list column, and a triage control that makes the card
   taller has already lost. The panel below spells them out; the accessible
   name is on the button either way. */
.idea-foot .taste-btn .lbl{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
}
.idea-foot .taste-btn{padding:var(--s1); min-width:1.875rem}
@media (pointer:coarse){
  .taste-btn{min-width:2.75rem}
}

/* --- "Why not?" ---------------------------------------------------------- */
/* Offered after a dislike, never demanded. It answers in one tap, times out on
   its own, and the dislike is already saved before it appears — a demo must
   never stall on a form. */
/* Centred by gutters, not by left:50% — a shrink-to-fit box pinned at 50% can
   only ever be half the viewport wide, which cropped the chips to a column. */
.whybar{
  position:fixed; left:var(--s3); right:var(--s3);
  bottom:calc(var(--navh) + 6rem + var(--safe-b));
  z-index:99;
  display:flex; align-items:center; flex-wrap:wrap; gap:var(--s2);
  width:fit-content; margin-inline:auto; max-width:38rem;
  padding:var(--s2) var(--s3);
  background:var(--surface); border:1px solid var(--line-strong);
  border-radius:var(--r-lg); box-shadow:var(--shadow-2);
  will-change:transform;
}
.whybar-q{
  font:600 var(--fs-sm)/1.2 var(--font-display);
  color:var(--fg); margin-right:var(--s1);
}
.whychip{
  padding:var(--s2) var(--s3); min-height:1.875rem;
  border:1px solid var(--line); border-radius:var(--r-pill);
  background:var(--surface-2); color:var(--fg-dim); box-shadow:none;
  font:600 var(--fs-meta)/1 var(--font-display);
  letter-spacing:var(--track-meta);
  transition:color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
             background var(--dur-1) var(--ease);
}
.whychip:hover{border-color:var(--accent); color:var(--accent); background:var(--surface)}
/* Undo is the one chip that is an action rather than an answer, so it carries a
   glyph and the full ink. */
.whychip.undo{
  display:inline-flex; align-items:center; gap:var(--s2);
  border-color:var(--line-strong); color:var(--fg); background:var(--surface);
}
.whychip .ic{width:14px; height:14px; flex:none}
.whybar-x{
  margin-left:auto; padding:var(--s1); min-height:1.875rem;
  background:none; border:0; box-shadow:none; color:var(--fg-faint);
  transition:color var(--dur-1) var(--ease);
}
.whybar-x:hover{color:var(--fg)}
.whybar-x .ic{width:16px; height:16px}
@media (max-width:1023px){
  .whybar{bottom:calc(var(--navh) + 5rem + var(--safe-b))}
}

/* ============================================================================
   V5 — TODAY'S BAND, AND WHAT HE ACTUALLY POSTED  (V5-TODAY-CONTRACT.md §1–§3)

   Two things arrive in one band and must not read as one thing:
     NEWS       — of the moment, warm marker, the attention accent doing its one
                  job. Terracotta never carries white text, so the tag is dark
                  ink (--warn, 4.53:1) on --warm-soft, exactly as audited.
     VALUE PICK — an evergreen idea from this week's pool, pulled up for today.
                  Teal outline, star, and a sentence saying where else it lives,
                  because it is ALSO in its own group further down and a reader
                  who spots it twice must be told that was on purpose.
   No second heading, no divider: one band, two markers. And no side stripe on
   any of it — a thick coloured edge on a card is the tell this file avoids.
   ========================================================================== */
.today-tag{
  display:inline-flex; align-items:center; gap:5px;
  padding:2px 9px;
  border:1px solid var(--line-strong);
  border-radius:var(--r-pill);
  background:var(--surface);
  color:var(--fg-dim);
  font:700 var(--fs-meta)/var(--lead-body) var(--font-display);
  letter-spacing:var(--track-meta);
  white-space:nowrap;
}
.today-tag .ic{width:13px; height:13px; flex:none}
/* Of the moment. --warn on --warm-soft = 4.53:1. */
.today-tag.news{border-color:var(--warm); background:var(--warm-soft); color:var(--warn)}
/* Surfaced, not breaking. */
.today-tag.pick{border-color:var(--accent); background:var(--surface); color:var(--accent)}
/* The same idea's second appearance, downstairs in its own group. */
.today-tag.echo{border-color:var(--line); background:var(--surface-2); color:var(--fg-dim)}
/* Done. Settled green-in-family, dark ink on the wash. */
.today-tag.done{border-color:var(--ok); background:var(--mint-wash); color:var(--fg)}

/* The band's value pick sits on the quieter paper — white is for what is new. */
.idea-card.pickday{background:var(--surface-2)}
.idea-card.pickday:hover{background:var(--surface-2)}
.today-from{
  margin-top:var(--s2);
  color:var(--fg-dim); font-size:var(--fs-sm); line-height:var(--lead-tight);
  max-width:var(--measure);
}

/* --- Mark as posted ------------------------------------------------------- */
/* The strongest signal on a card: he went all the way and put it out. So it
   keeps its words where the thumbs are bare glyphs, and it sits on a filled
   neutral instead of a hairline. It is still not the primary action — teal
   arrives on hover and on the settled state's border, never as a big fill. */
.posted-btn{
  display:inline-flex; align-items:center; justify-content:center; gap:var(--s2);
  padding:var(--s1) var(--s3); min-height:1.875rem;
  background:var(--surface-3); border:1px solid var(--line-strong);
  border-radius:var(--r-pill); box-shadow:none; color:var(--fg);
  font:700 var(--fs-meta)/1 var(--font-display);
  letter-spacing:var(--track-meta); white-space:nowrap;
  transition:color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
             background var(--dur-1) var(--ease);
}
.posted-btn .ic{width:15px; height:15px; flex:none}
.posted-btn:hover{border-color:var(--accent); color:var(--accent); background:var(--surface)}
.posted-btn.on{background:var(--mint-wash); border-color:var(--ok); color:var(--fg)}
.posted-btn.on:hover{border-color:var(--accent); color:var(--accent); background:var(--surface)}
/* In the panel it can afford the full sentence and a full-width target. */
.posted-btn.wide{
  width:100%; min-height:2.5rem; padding:var(--s2) var(--s4);
  font-size:var(--fs-sm); letter-spacing:var(--track-body);
}
.posted-row{
  display:flex; flex-direction:column; gap:var(--s2);
  margin:var(--s4) 0 0; padding-top:var(--s4);
  border-top:1px solid var(--line);
}
.posted-q{color:var(--fg-dim); font-size:var(--fs-sm); line-height:var(--lead-tight)}
/* Two labelled controls plus the thumbs need room to wrap on a narrow card. */
.idea-taste{flex-wrap:wrap; justify-content:flex-end; row-gap:var(--s1)}

/* --- The posted shelf ----------------------------------------------------- */
/* Quiet, folded, and at the foot — a record, not a working surface. The count
   in the heading answers "what went out this week" without opening it. */
.grouphead.toggle{
  width:100%;
  background:none; border:0; box-shadow:none; padding:0;
  cursor:pointer; text-align:left;
  color:var(--fg-dim);
  font-family:var(--font-display); font-size:var(--fs-meta); font-weight:700;
  letter-spacing:var(--track-caps); text-transform:uppercase;
  transition:color var(--dur-1) var(--ease);
}
.grouphead.toggle:hover{color:var(--fg)}
.grouphead.toggle>.ic{width:15px; height:15px; flex:none; color:var(--ok)}
.grouphead.toggle .ghl{flex:0 0 auto}
/* The hairline belongs between the count and the chevron, so both are ordered
   past it rather than the pseudo-element being pushed to the end. */
.grouphead.toggle::after{order:1}
.grouphead.toggle .chev{order:2; display:inline-flex; flex:none}
.grouphead.toggle .chev .ic{width:16px; height:16px}
/* A static quarter turn, not a transition: the group repaints on toggle, so an
   animated rotation would never get to run anyway (and transforms are the
   spring's alone). */
.grouphead.toggle.open .chev{transform:rotate(90deg)}
/* A full-width heading that scales on press reads as a lurch, so this one takes
   its press as tone alone. */
.grouphead.toggle[data-press][data-press]{
  transform:none; background:none; border:0; color:var(--fg);
}
/* A posted card is settled: it keeps its shape, loses its lift. */
.idea-card.wasposted{
  background:var(--surface-2); box-shadow:none; border-color:var(--line);
}
.idea-card.wasposted:hover{box-shadow:var(--shadow-1); background:var(--surface-2)}

/* --- Saved ---------------------------------------------------------------- */
/* Liked ideas outlive the week that found them. */
.saved-note{margin:calc(var(--s3) * -1) 0 var(--s3); color:var(--fg-dim); font-size:var(--fs-sm)}
/* A source's own numbers sit under its quote, not beside the card's. */
.idea-source .idea-eng{margin-top:var(--s2)}

/* The three accessibility signals reach the new surfaces too (§14). Buttons are
   already covered by the blocks above; the floating bar is not a button. */
@media (prefers-contrast:more){
  .whybar,.taste-btn,.whychip,.today-tag,.posted-btn{border-color:var(--line-strong)}
  .taste-line .fix{text-decoration:underline;text-underline-offset:2px}
}


/* ============================================================================
   V4 — LIQUID GLASS + ATTACHMENTS  (V4-ATTACHMENTS-CONTRACT.md, SKILL §12/§14)

   Three things happen below, in this order, because each depends on the last:
     1. VIBRANCY — type that sits directly on a material.
     2. ATTACHMENTS — the composer's new chips, thumbs and drop state. They live
        in and around the composer, which IS glass, so every one of them is a
        SOLID surface. Two light translucent layers stacked is the one thing
        §12 forbids outright, and it is exactly the mistake this UI invites.
     3. The three accessibility signals reaching all of the above.

   CONTRAST AUDIT FOR THE GLASS (computed against the worst case that can sit
   behind it — a black image scrolling under the bar, not the tinted ground):
     --fg      on --mat-ground  (ground 78% over #000)  = 7.37:1  AA
     --fg-dim  on --mat-ground                          = 3.49:1  FAILS —
               which is why every label on the bars is --fg, not --fg-dim.
     --fg      on --mat-surface (white 88% over #000)   = 9.87:1  AA
     --fg-dim  on --mat-surface                         = 4.97:1  AA
     --fg-dim  on --mat-panel   (white 90% over #000)   = 5.21:1  AA
     --fg-dim  on the lightbox  (ground 96% over #000)  = 5.46:1  AA
     --accent  on the drop wash (mint 24% over white)   = 4.81:1  AA
   The opacities in the token block are set AT those numbers. Lower them and
   the audit stops holding; raise the blur instead — frostier beats unreadable.
   ========================================================================= */

/* ------------------------------- 1. VIBRANCY ---------------------------- */
/* Over a material there is no fixed background, so the usual trick of stepping
   a tone down to say "secondary" stops working — the tone dissolves into
   whatever scrolled underneath. §12's answer: keep the full ink, add a weight
   step and a small tracking bump, and let size carry the hierarchy. Colour
   belongs on the solid layers below, never on the translucent foreground. */
#topbar h1,#topbar .title,body>header h1{
  letter-spacing:calc(var(--track-h3) + .006em);
}
.navitem{letter-spacing:calc(var(--track-h3) + .006em)}
.navfoot .status{font-weight:600;letter-spacing:calc(var(--track-meta) + .006em)}
/* Panel glass is frostier than bar glass, so its secondary copy survives —
   it still gets the weight step so it reads as type on a surface. */
.sheet-in>h4,.modal-panel>h3,.modal-panel>h4{
  letter-spacing:calc(var(--track-h3) + .004em);
}
.sheet .note,.modal-panel>p{font-weight:500;letter-spacing:.004em}

/* --------------------------- 2. ATTACHMENTS ----------------------------- */
/* The row of pending attachments, above what you are typing and inside the
   same reading column. The composer wraps so this takes a full row whether the
   client puts it in .composer or inside .composer-inner. */
.composer,#composer,.composer .composer-inner{flex-wrap:wrap}
.composer-attachments{
  order:-1;
  flex:1 0 100%;
  display:flex;flex-wrap:wrap;align-items:flex-start;
  gap:var(--s2);
  min-width:0;
  max-height:14rem;overflow-y:auto;overscroll-behavior:contain;
  margin-bottom:var(--s2);
}
.composer-attachments:empty{display:none;margin:0}

/* A chip is SOLID. It sits on the composer's glass, and glass on glass is how
   legibility collapses (§12). It gets the light material's cues instead — a
   1px lit top edge and the shallow shadow of a small thing — so it still reads
   as part of the same system without being a second sheet of glass. */
.attach-chip{
  position:relative;
  display:inline-flex;align-items:center;gap:var(--s2);
  min-width:0;max-width:15rem;
  padding:var(--s1) var(--s2) var(--s1) var(--s1);
  background:var(--surface);
  border:1px solid var(--line-strong);
  border-radius:var(--r);
  box-shadow:var(--spec-light),var(--shadow-1);
  color:var(--fg);
  font-size:var(--fs-meta);
  line-height:var(--lead-tight);
  overflow:hidden;
  /* transform is the JS's — removal springs out, and a transition here would
     make that spring uninterruptible (SKILL §3). */
  transition:border-color var(--dur-1) var(--ease),
             background var(--dur-1) var(--ease),
             box-shadow var(--dur-1) var(--ease),
             opacity var(--dur-2) var(--ease);
}
.attach-chip:hover{border-color:var(--accent)}

/* An image chip is the base shape — a thumbnail you can open, and the one kind
   where the picture is the label. */
.attach-chip.image .attach-thumb{cursor:zoom-in}
/* A link has no thumbnail, so it is a tighter chip that leads with its host. */
.attach-chip.link{padding-left:var(--s3);max-width:19rem}
.attach-chip.link .attach-meta b,.attach-chip.link .attach-name{color:var(--accent)}

.attach-thumb{
  flex:0 0 auto;
  width:2.25rem;height:2.25rem;
  border-radius:var(--r-xs);
  object-fit:cover;
  display:block;
  background:var(--mint-wash);
  border:1px solid var(--line);
  color:var(--fg-dim);
}
/* A video says so before it is opened: a filled disc with a play triangle,
   drawn into the thumb so the client needs no extra markup. */
.attach-chip.video .attach-thumb,.msg-attach.video .attach-thumb{
  position:relative;
}
.attach-chip.video .attach-thumb::after,
.msg-attach.video .attach-thumb::after{
  content:"";
  position:absolute;left:50%;top:50%;
  width:14px;height:14px;
  translate:-42% -50%;
  background:var(--surface);
  /* clipped, not built from borders: a play glyph made of border edges is
     indistinguishable from a coloured bar down one side of a card. */
  clip-path:polygon(18% 8%,88% 50%,18% 92%);
  filter:drop-shadow(0 1px 2px rgba(0,64,72,.45));
}

.attach-meta{
  display:flex;flex-direction:column;gap:1px;
  min-width:0;
  padding-right:var(--s1);
}
.attach-meta b,.attach-meta .attach-name,.attach-chip .attach-name{
  font-family:var(--font-display);
  font-weight:600;
  font-size:var(--fs-meta);
  color:var(--fg);
  letter-spacing:var(--track-meta);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  max-width:9.5rem;
}
.attach-meta small,.attach-meta .attach-size{
  font-size:11px;
  color:var(--fg-dim);
  font-variant-numeric:tabular-nums;
  white-space:nowrap;
}

/* Remove: small on purpose — it must never look like the send button. The hit
   region is grown past the glyph rather than the glyph grown past its chip. */
.attach-remove{
  position:relative;
  flex:0 0 auto;
  width:1.5rem;height:1.5rem;min-width:1.5rem;min-height:1.5rem;
  padding:0;
  display:inline-flex;align-items:center;justify-content:center;
  border:0;border-radius:50%;
  background:none;box-shadow:none;
  color:var(--fg-faint);
  cursor:pointer;
  transition:color var(--dur-1) var(--ease),background var(--dur-1) var(--ease);
}
.attach-remove::after{content:"";position:absolute;inset:-9px}
.attach-remove .ic{width:14px;height:14px}
.attach-remove:hover{background:var(--surface-3);color:var(--warn);transform:none}
/* On a finger, 24px is not a target. The chip grows a little so the control can
   be a real 44px disc rather than a glyph you aim at — and the hit expander
   comes off, because the button itself is now the target (SKILL §10). */
@media (pointer:coarse){
  .attach-chip{padding-block:var(--s2)}
  .attach-remove{width:2.75rem;height:2.75rem;min-width:2.75rem;min-height:2.75rem}
  .attach-remove::after{inset:0}
}

/* Upload state. The chip stays readable while it uploads — a greyed-out chip
   with an unreadable filename is worse than no feedback. */
.attach-chip.uploading{background:var(--surface-2);border-color:var(--line)}
.attach-chip.uploading .attach-thumb{opacity:.72}
.attach-chip.uploading .attach-meta small{color:var(--accent);font-weight:600}

/* Progress: a hairline along the chip's own bottom edge. The fill is a scaleX
   on a compositor-friendly transform driven by --p (0–1); the track carries a
   slow sweep so an upload with no byte count still says it is alive. */
.attach-progress{
  position:absolute;left:0;right:0;bottom:0;
  height:3px;
  overflow:hidden;
  background:var(--surface-3);
  pointer-events:none;
}
.attach-progress::before{
  content:"";
  position:absolute;inset:0;
  background:linear-gradient(90deg,transparent,
             color-mix(in srgb,var(--accent) 22%,transparent),transparent);
  animation:attachSweep 1.4s var(--ease) infinite;
}
.attach-progress::after{
  content:"";
  position:absolute;inset:0;
  transform-origin:left center;
  transform:scaleX(var(--p,0));
  background:var(--accent);
  transition:transform var(--dur-2) var(--ease);
}
@keyframes attachSweep{from{translate:-100% 0}to{translate:100% 0}}

/* Failed: the attention state, said the way this app always says it — peach
   fill, terracotta keyline, dark ink. Terracotta never carries light text. */
.attach-chip.failed{
  background:var(--warm-soft);
  border-color:var(--warm);
  color:var(--fg);
}
.attach-chip.failed .attach-meta b,
.attach-chip.failed .attach-name{color:var(--fg)}
.attach-chip.failed .attach-meta small,
.attach-chip.failed .attach-size{color:var(--warn);font-weight:700}
.attach-chip.failed .attach-thumb{background:var(--warm-soft)}
.attach-chip.failed .attach-progress{display:none}
.attach-chip.failed:hover{border-color:var(--warn)}

/* The attach button belongs to the composer's control row and is the quietest
   thing in it — the send button is the one primary action here. */
.composer .attach-btn,.attach-btn{
  flex:none;
  width:44px;min-width:44px;height:44px;
  padding:0;
  background:none;
  border:1px solid transparent;
  box-shadow:none;
  color:var(--fg-dim);
}
.attach-btn .ic{width:20px;height:20px}
.attach-btn:hover{background:var(--surface);border-color:var(--line-strong);color:var(--accent)}
.attach-btn[aria-expanded="true"],.attach-btn.on{
  background:var(--surface);border-color:var(--accent);color:var(--accent);
}

/* Drag & drop over the whole composer. Unmistakable is the requirement, and
   garish is the failure mode, so: no new colour, no flashing, no scale jump.
   One near-solid mint wash, a dashed teal ring, and the instruction said out
   loud. It covers the glass rather than tinting it — a translucent drop state
   over translucent chrome would be the third light layer in a stack of two. */
.dropzone{position:relative}
.dropzone.over::after{
  content:"Drop to attach";
  position:absolute;inset:var(--s2);z-index:3;
  display:flex;align-items:center;justify-content:center;
  border:2px dashed var(--accent);
  border-radius:var(--r-lg);
  background:color-mix(in srgb,var(--mint) 24%,var(--surface));
  color:var(--accent);
  font:700 var(--fs-sm)/1 var(--font-display);
  letter-spacing:var(--track-caps);
  text-transform:uppercase;
  pointer-events:none;
}

/* ---- Attachments as sent, inside the bubble ---------------------------- */
/* The user's bubble is peach; these tiles are white, so they read as things
   carried by the message rather than as part of it. */
.msg-attachments{
  display:flex;flex-wrap:wrap;align-items:flex-start;gap:var(--s2);
  margin-top:var(--s3);
}
.msg-attachments:empty{display:none;margin:0}
.msg-attach{
  display:inline-flex;align-items:center;gap:var(--s2);
  min-width:0;max-width:100%;
  padding:var(--s1) var(--s3) var(--s1) var(--s1);
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r);
  box-shadow:var(--shadow-1);
  color:var(--fg);
  font-size:var(--fs-meta);
  line-height:var(--lead-tight);
  text-decoration:none;
  transition:border-color var(--dur-1) var(--ease),
             box-shadow var(--dur-1) var(--ease);
}
.msg-attach:hover{border-color:var(--accent);box-shadow:var(--shadow-2);text-decoration:none}
/* These are cards that happen to be anchors. Inside a rendered AI reply every
   <a> is underlined teal prose, which turned a link preview into a paragraph
   of underlined fragments — take the prose costume back off. */
.msg.ai .msg-attach,.msg.ai .linkcard,
.msg.ai .msg-attach:hover,.msg.ai .linkcard:hover{
  color:var(--fg);
  text-decoration:none;
}
.msg.ai .linkcard b,.msg.ai .linkcard .linkcard-title{color:var(--fg)}
.msg.ai .linkcard small,.msg.ai .linkcard .linkcard-host,
.msg.ai .linkcard p,.msg.ai .linkcard .linkcard-desc{color:var(--fg-dim)}
.msg-attach .attach-thumb{width:2.5rem;height:2.5rem}
.msg-attach b,.msg-attach .attach-name{
  font-family:var(--font-display);font-weight:600;color:var(--fg);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:11rem;
}
.msg-attach small{color:var(--fg-dim);font-variant-numeric:tabular-nums}
/* A sent image large enough to actually look at, not a filename. */
.msg-attach.image{
  width:13rem;max-width:100%;
  padding:0;border-radius:var(--r);overflow:hidden;cursor:zoom-in;
}
.msg-attach.image img{
  display:block;width:100%;
  aspect-ratio:4/3;object-fit:cover;
  border-radius:var(--r);
}
.msg-attach.link{padding-left:var(--s3)}
.msg-attach.link b,.msg-attach.link .attach-name{color:var(--accent)}

/* ---- Link preview card -------------------------------------------------- */
/* A pasted URL becomes a card, not a wall of query string. Solid white on the
   bubble it sits in, one hairline, and the host line carries the truth of
   where the link actually goes. */







/* On a phone the composer cannot afford to grow a second and third row of
   chips — the keyboard is already taking half the screen. One line that scrolls
   sideways keeps the bar the height it was, and the count is visible either
   way because the first chip is always the one you just added. */
@media (max-width:1023.98px){
  .composer-attachments{
    flex-wrap:nowrap;
    max-height:none;
    overflow-x:auto;overflow-y:hidden;
    scroll-snap-type:x proximity;
    padding-bottom:2px;
  }
  .attach-chip{flex:0 0 auto;max-width:13rem;scroll-snap-align:start}
}
@media (max-width:400px){
  .linkcard img,.linkcard .linkcard-thumb{width:3.25rem;height:3.25rem}
  .attach-chip{max-width:11.5rem}
  .attach-meta b,.attach-meta .attach-name,.attach-chip .attach-name{max-width:7rem}
  .msg-attach.image{width:10rem}
}

/* --------------------- 3. THE THREE A11Y SIGNALS ------------------------ */
/* Reduced motion: the sweep and the fill stop. The chip still says what it is
   doing — it just says it with a number instead of with movement. */
@media (prefers-reduced-motion:reduce){
  .attach-progress::before{display:none}
  .attach-progress::after{transition:none!important}
  .attach-chip,.msg-attach,.linkcard{transform:none!important}
}

/* Reduced transparency: every material goes frost. The specular edge goes with
   it — a lit edge on an opaque panel is a line for no reason. */
@media (prefers-reduced-transparency:reduce){
  :root{
    --spec-hi:inset 0 0 0 0 transparent;
    --spec-lo:inset 0 0 0 0 transparent;
    --spec:inset 0 0 0 0 transparent;
    --spec-light:inset 0 0 0 0 transparent;
    --spec-rail:inset 0 0 0 0 transparent;
  }
  #sidenav{box-shadow:none}
  .sheet-in,.modal-panel{box-shadow:var(--shadow-2)}
  .sheet-in,.modal-panel,.sheet,.modal{transition:none}
}

/* More contrast: near-solid surfaces, borders that are actually there, and the
   glass stops pretending. */
@media (prefers-contrast:more){
  :root{
    --spec-hi:inset 0 0 0 0 transparent;
    --spec-lo:inset 0 0 0 0 transparent;
    --spec:inset 0 0 0 0 transparent;
    --spec-light:inset 0 0 0 0 transparent;
    --spec-rail:inset 0 0 0 0 transparent;
  }
  #sidenav{box-shadow:none;border-right-color:var(--line-strong)}
  .sheet-in,.modal-panel{box-shadow:var(--shadow-2)}
  .attach-chip,.msg-attach,.linkcard,.attach-thumb,.attach-btn{
    border-color:var(--line-strong);
  }
  .attach-btn{background:var(--surface)}
  .attach-meta small,.attach-meta .attach-size,
  .linkcard small,.linkcard .linkcard-host,
  .linkcard p,.linkcard .linkcard-desc,
  .msg-attach small{color:var(--fg)}
  .dropzone.over::after{
    background:var(--surface);
    border-style:solid;
    color:var(--accent-press);
  }
  .attach-progress{background:var(--line-strong)}
}

/* ============================================================================
   INTEGRATION PASS 4 — seams between the three attachment agents.
   ========================================================================= */

/* A chip's own controls inherited the composer's 44px send-button sizing, which made
   "Retry" and "Add the file instead" enormous inside a 15rem chip. */
.attach-retry,.attach-warn-act{
  min-height:0; height:auto; min-width:0; width:auto;
  padding:2px 8px; margin:0;
  border:1px solid var(--line-strong); border-radius:var(--r-sm);
  background:var(--surface); color:var(--accent);
  font:600 var(--fs-meta)/1.35 var(--font-display);
  letter-spacing:var(--track-meta);
  align-self:flex-start; flex:0 0 auto; box-shadow:none;
}
.attach-retry:hover,.attach-warn-act:hover{ background:var(--surface-2); border-color:var(--accent); }
.attach-chip.failed .attach-retry{ color:var(--warn); border-color:var(--warn); }

/* The caution line ("YouTube blocks this…", "it sees the picture, not the words")
   is a sentence, not a fact — let it wrap onto its own full-width row instead of
   being squeezed beside the thumbnail or clipped by nowrap. */
.attach-chip{ flex-wrap:wrap; }
.attach-warn{
  flex:1 0 100%; margin:2px 0 0;
  white-space:normal; overflow:visible; text-overflow:clip;
  font-size:var(--fs-meta); line-height:1.35; color:var(--fg-dim);
}
.attach-chip.failed .attach-warn{ color:var(--warn); }
.attach-chip.link .attach-warn{ color:var(--warn); }

/* ============================================================================
   V5b — REFRESHING TODAY'S BAND  (one control, two speeds)

   The band is the most valuable thing on the page, so the control that rewrites
   it must be reachable without arguing with it. It is an outline pill parked past
   the heading's hairline: --fg-dim on --surface at rest, teal only on hover and
   focus. Teal is the action colour and this is not the action — writing a post is.
   No fill, no shadow, no coloured edge on any one side of anything.
   ========================================================================= */

/* The heading is a flex row whose ::after hairline is its last child. Order it
   ahead of the control so the rule runs BETWEEN the count and the button, and
   the button lands hard right. */
.grouphead.hot::after{order:1}
.grouphead.hot .band-refresh{order:2}
/* `.grouphead span` is the count pill. The button's own label is a span too, and
   inheriting a bordered pill inside a bordered button is two boxes for one word. */
.grouphead .band-refresh .lbl{
  background:none; border:0; padding:0; border-radius:0;
  color:inherit; font-size:inherit; font-weight:inherit; letter-spacing:inherit;
}

.band-refresh{
  display:inline-flex; align-items:center; gap:var(--s2);
  padding:var(--s1) var(--s3); min-height:1.875rem;
  background:var(--surface);
  border:1px solid var(--line-strong);
  border-radius:var(--r-pill);
  box-shadow:none;
  color:var(--fg-dim);
  font:700 var(--fs-meta)/1 var(--font-display);
  letter-spacing:var(--track-meta);
  text-transform:none;
  white-space:nowrap;
  transition:color var(--dur-1) var(--ease),
             border-color var(--dur-1) var(--ease),
             background var(--dur-1) var(--ease);
}
.band-refresh .ic{width:14px; height:14px; flex:none}
.band-refresh:hover{border-color:var(--accent); color:var(--accent); background:var(--surface)}
/* Working, not stuck, and not asking for a second tap: the label says what it is
   doing, the dot says it is still moving, and the control stops taking presses. */
.band-refresh[disabled]{
  background:var(--surface-2);
  border-color:var(--line-strong);
  color:var(--fg);
  cursor:default;
}
.band-refresh[disabled]:hover{border-color:var(--line-strong); color:var(--fg)}
.band-refresh .spin{width:8px; height:8px}
/* A pressed disabled control would be a lie about what the tap will do. */
.band-refresh[disabled][data-press][data-press]{transform:none; background:var(--surface-2)}
@media (pointer:coarse){
  .band-refresh{min-height:2.75rem; padding-inline:var(--s4)}
}

/* What the tap actually did. One line, rewritten in place for every outcome, so
   a quiet news day and a rate-limited sweep read in the same voice as a good run
   rather than as two different kinds of red. */
.band-said{
  display:flex; align-items:flex-start; gap:var(--s2);
  margin:calc(var(--s3) * -1) 0 var(--s3);
  max-width:var(--measure);
  color:var(--fg-dim);
  font-size:var(--fs-sm);
  line-height:var(--lead-tight);
}
.band-said .ic{width:15px; height:15px; flex:none; margin-top:2px; color:var(--accent)}

/* The slow half, in the app's one progress language — the same six phases, the
   same markers, lit only when the server says a phase actually started. Quieter
   and tighter than the chat hero, and parked at the FOOT of the band so the fresh
   picks that already landed never move to make room for it. */
.band-sweep{
  gap:var(--s3);
  margin:var(--s4) 0 0;
  padding:var(--s3) var(--s4);
  max-width:none;
  background:var(--surface-2);
  border:1px solid var(--line);
  border-radius:var(--r-lg);
  box-shadow:none;
  color:var(--fg-dim);
  font-weight:400;
  letter-spacing:var(--track-body);
}
.band-sweep .progress-head{gap:var(--s2)}
.band-sweep .progress-title{
  font:700 var(--fs-meta)/1 var(--font-display);
  letter-spacing:var(--track-caps);
  text-transform:uppercase;
  color:var(--fg-dim);
}
.band-sweep .jobclock{
  color:var(--fg-dim);
  font-size:var(--fs-meta);
  font-variant-numeric:tabular-nums;
}
.band-sweep .phase-list{padding:var(--s1) 0}
.band-sweep .phase{padding:6px 0 6px 26px; font-size:var(--fs-meta)}
.band-sweep .phase::before{
  width:14px; height:14px; top:6px;
  border-width:1.5px;
  background-size:11px 11px;
}
.band-sweep .phase::after{left:6.25px; top:22px; bottom:-6px}
.band-sweep .progress-note{
  padding-top:var(--s2);
  font-size:var(--fs-meta);
  max-width:var(--measure);
}

/* The three accessibility signals reach these too (§14). */
@media (prefers-contrast:more){
  .band-refresh,.band-sweep{border-color:var(--line-strong)}
  .band-said{color:var(--fg)}
}
@media (prefers-reduced-transparency:reduce){
  .band-sweep{background:var(--surface); border-color:var(--line-strong)}
}

/* ======================================================================
   MODEL PICKER — which Claude writes.
   The chip is the quietest thing in the composer's control row after the attach
   button: it reports a setting, it is not an action. The menu is a popover, so it
   takes the heavy glass the material scale reserves for sheets and popovers.
   ====================================================================== */
.composer .model-chip,.model-chip{
  flex:none;
  display:inline-flex;align-items:center;gap:var(--s1);
  height:44px;min-width:0;
  padding:0 var(--s2) 0 var(--s3);
  background:none;
  border:1px solid transparent;
  border-radius:var(--r-pill);
  box-shadow:none;
  color:var(--fg-dim);
  font:600 var(--fs-sm)/1 var(--font-body);
  letter-spacing:var(--track-meta);
  white-space:nowrap;
  transition:background-color var(--dur-1) var(--ease),border-color var(--dur-1) var(--ease),color var(--dur-1) var(--ease);
}
.model-chip[hidden]{display:none}
.model-chip .ic{width:16px;height:16px;opacity:.7}
.model-chip:hover,.model-chip[aria-expanded="true"]{
  background:var(--surface);border-color:var(--line-strong);color:var(--accent);
}
.model-chip:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.model-chip-short{display:none}
@media (max-width:560px){
  /* "Opus" still reads on a phone; "Opus 5.5" costs the text box a word of room. */
  .model-chip-full{display:none}
  .model-chip-short{display:inline}
  .composer .model-chip,.model-chip{padding:0 var(--s1) 0 var(--s2)}
}

/* On a running answer: which model is writing, and the way to switch it. */
.model-pill{
  display:inline-flex;align-items:center;gap:2px;
  height:28px;min-width:0;
  padding:0 var(--s1) 0 var(--s2);
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r-pill);
  box-shadow:none;
  color:var(--fg-dim);
  font:600 var(--fs-meta)/1 var(--font-body);
  letter-spacing:var(--track-meta);
  white-space:nowrap;
  transition:border-color var(--dur-1) var(--ease),color var(--dur-1) var(--ease);
}
.model-pill .ic{width:14px;height:14px;opacity:.7}
button.model-pill:hover{border-color:var(--accent);color:var(--accent)}
button.model-pill:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.model-pill.static{padding:0 var(--s2)}

.model-menu{
  position:fixed;z-index:80;
  width:min(20rem,calc(100vw - 24px));
  padding:var(--s2);
  display:flex;flex-direction:column;gap:2px;
  background:var(--surface);
  background:var(--mat-panel);
  backdrop-filter:blur(var(--mat-blur-heavy)) saturate(var(--mat-sat-heavy));
  -webkit-backdrop-filter:blur(var(--mat-blur-heavy)) saturate(var(--mat-sat-heavy));
  border:1px solid var(--mat-edge);
  border-radius:var(--r-lg);
  box-shadow:var(--shadow-glass-heavy),inset 0 1px 0 var(--spec-hi);
  transform-origin:bottom right;
}
@supports not (backdrop-filter:blur(1px)){
  .model-menu{background:var(--surface);border-color:var(--line)}
}
.model-menu-head{
  padding:var(--s2) var(--s3) var(--s1);
  color:var(--fg-faint);
  font:600 var(--fs-meta)/1.3 var(--font-body);
  letter-spacing:var(--track-meta);
}
.model-opt{
  display:flex;align-items:center;gap:var(--s3);
  width:100%;min-height:44px;
  padding:var(--s2) var(--s3);
  background:none;border:0;box-shadow:none;
  border-radius:var(--r);
  color:var(--fg);text-align:left;
  transition:background-color var(--dur-1) var(--ease);
}
.model-opt:hover,.model-opt:focus-visible{background:color-mix(in srgb,var(--accent) 9%,transparent);outline:none}
.model-opt-text{display:flex;flex-direction:column;gap:2px;flex:1 1 auto;min-width:0}
.model-opt-name{font:600 var(--fs-body)/1.25 var(--font-body)}
.model-opt-note{color:var(--fg-dim);font-size:var(--fs-sm);line-height:1.35}
.model-opt .ic{flex:none;width:18px;height:18px;color:var(--accent)}
.model-opt.on .model-opt-name{color:var(--accent)}

/* The composer wraps (so the attachment strip can take its own row), and with
   `flex-basis:auto` the textarea claimed its natural width first: one more control
   in the row and the send button dropped onto a second line on a phone. From zero,
   the line forms from the fixed controls and the text box fills what is left. */
.composer textarea{flex:1 1 0}

/* Previous / next in the lightbox. They sit in the empty margins either side of
   the slide, not on it, and they are glass because they float over the room.
   Touch screens swipe, so a phone does not get two circles over its artwork. */
.lb-nav{
  position:absolute;top:50%;z-index:2;
  width:52px;height:52px;min-width:0;
  margin-top:-26px;padding:0;
  display:flex;align-items:center;justify-content:center;
  border-radius:50%;
  background:var(--surface);
  background:var(--mat-panel);
  backdrop-filter:blur(var(--mat-blur)) saturate(var(--mat-sat));
  -webkit-backdrop-filter:blur(var(--mat-blur)) saturate(var(--mat-sat));
  border:1px solid var(--mat-edge);
  box-shadow:var(--shadow-glass),inset 0 1px 0 var(--spec-hi);
  color:var(--fg);
  transition:opacity var(--dur-1) var(--ease),color var(--dur-1) var(--ease),border-color var(--dur-1) var(--ease);
}
.lb-nav.prev{left:max(var(--s4),var(--safe-l,0px))}
.lb-nav.next{right:max(var(--s4),var(--safe-r,0px))}
.lb-nav .ic{width:24px;height:24px}
.lb-nav:hover:not(:disabled){color:var(--accent);border-color:var(--accent)}
.lb-nav:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.lb-nav:disabled{opacity:.28;cursor:default;box-shadow:none}
.lightbox[data-dragging] .lb-nav{opacity:0}
@media (hover:none) and (pointer:coarse){
  .lb-nav{display:none}
}

/* ======================= ENGAGE (comment drafts) ========================= */
/* One card per post: who, the post, his comment, four actions. Built for a
   five-minute pass on a phone, so every control is a full 44px target. */
.eng-card{
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r);
  padding:var(--s4);
  margin-bottom:var(--s3);
  box-shadow:var(--shadow-1);
  transition:opacity var(--dur-2) var(--ease);
}
.eng-card.busy{opacity:.72}
.eng-head{display:flex;flex-direction:column;gap:2px;margin-bottom:var(--s3)}
.eng-who{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap}
.eng-name{
  font-family:var(--font-display);font-weight:700;font-size:var(--fs-lead);
  color:var(--fg);text-decoration:none;
}
.eng-name:hover{color:var(--accent);text-decoration:underline}
.eng-group{
  font-size:var(--fs-meta);font-weight:600;letter-spacing:var(--track-meta);
  padding:1px 8px;border-radius:var(--r-pill);
  border:1px solid var(--line-strong);color:var(--fg-dim);background:var(--surface-2);
}
.eng-group.g-icp{border-color:var(--ok);background:var(--mint-wash);color:var(--fg)}
.eng-meta,.eng-headline{font-size:var(--fs-meta);color:var(--fg-dim);letter-spacing:var(--track-meta)}
.eng-headline{color:var(--fg-faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.eng-post{
  font-size:var(--fs-sm);line-height:var(--lead-body);color:var(--fg-dim);
  background:var(--surface-2);border-left:3px solid var(--line-strong);
  border-radius:var(--r-xs);padding:var(--s2) var(--s3);
  white-space:pre-line;max-width:var(--measure);
}
.eng-post.open{color:var(--fg)}
.eng-more{
  background:none;border:0;box-shadow:none;padding:var(--s1) 0;min-height:0;
  color:var(--accent);font-size:var(--fs-meta);font-weight:600;
}
.eng-more:hover{background:none;text-decoration:underline}
.eng-text{
  display:block;width:100%;margin:var(--s3) 0 var(--s2);
  min-height:5.5rem;resize:vertical;line-height:var(--lead-body);
}
.eng-actions{display:flex;flex-wrap:wrap;gap:var(--s2)}
.eng-actions button{min-height:44px}
.eng-actions .primary{flex:1 1 12rem}
.eng-actions .eng-skip{margin-left:auto;color:var(--fg-dim)}
.eng-alts{margin-top:var(--s3);font-size:var(--fs-sm)}
.eng-alts summary{cursor:pointer;color:var(--fg-dim);font-weight:600;min-height:32px;display:flex;align-items:center}
.eng-alt{
  display:flex;gap:var(--s3);align-items:flex-start;justify-content:space-between;
  padding:var(--s2) 0;border-top:1px solid var(--line);
}
.eng-alt p{margin:0;color:var(--fg-dim);line-height:var(--lead-body)}
.eng-alt button{flex:none}
.eng-done{display:flex;flex-direction:column;gap:var(--s1);margin-bottom:var(--s5)}
.eng-done-row{
  display:flex;align-items:center;gap:var(--s3);
  padding:var(--s2) var(--s3);border:1px solid var(--line);border-radius:var(--r);
  background:var(--surface);
}
.eng-done-row a{flex:1;min-width:0;color:var(--fg);font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.eng-done-row button{min-height:36px}
@media (max-width:1023.98px){
  .eng-text{font-size:16px}                /* iOS zooms into anything smaller */
  .eng-actions button span{white-space:nowrap}
}
.eng-passed{margin:var(--s4) 0;font-size:var(--fs-sm)}
.eng-passed summary{cursor:pointer;color:var(--fg-dim);font-weight:600;min-height:36px;display:flex;align-items:center}
.eng-passed .eng-done-row{margin-top:var(--s1)}
.eng-reason{color:var(--fg-faint);font-size:var(--fs-meta);text-align:right;flex:0 1 50%}

/* ======================= HOOK ALTERNATES (draft view) ==================== */
.hooks{margin:var(--s3) 0;font-size:var(--fs-sm)}
.hooks summary{cursor:pointer;color:var(--fg-dim);font-weight:600;min-height:36px;display:flex;align-items:center}
.hook-alt{
  display:flex;gap:var(--s3);align-items:flex-start;justify-content:space-between;
  padding:var(--s2) 0;border-top:1px solid var(--line);
}
.hook-alt p{margin:0;white-space:pre-line;line-height:var(--lead-body);max-width:var(--measure)}
.hook-alt button{flex:none;min-height:36px}
.growth-chip{border-color:var(--accent);color:var(--accent)}

/* ======================= MOTION POSTS (poster + silent loop) ============== */
/* The poster is the frame that is always there; the loop sits exactly on top of it.
   Clicks fall through to the poster, which opens the viewer on the loop. */
.motion{margin:0}
.motion-frame{position:relative;display:block}
.motion-frame video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  border-radius:var(--r-sm);pointer-events:none;background:transparent}
.motion-hero{margin-top:var(--s3);max-width:360px}
.motion-hero img{width:100%;aspect-ratio:4/5;object-fit:cover;border-radius:var(--r-sm);
  border:1px solid var(--line);display:block;cursor:zoom-in;box-shadow:var(--shadow-1);background:var(--surface)}
.lb-page video{max-width:100%;max-height:100%;display:block;border-radius:var(--r-sm);box-shadow:var(--shadow-2)}
.motion-frame.noposter video{position:static;height:auto;aspect-ratio:4/5;pointer-events:auto;
  cursor:zoom-in;border:1px solid var(--line);background:var(--surface)}
.li-pics.many{display:grid;grid-template-columns:1fr 1fr;gap:4px;margin-top:10px}
.li-pics.many .li-img{margin-top:0;aspect-ratio:1;object-fit:cover}
@media (prefers-reduced-motion:reduce){.motion-frame:not(.noposter) video{display:none}}
