/*
  css/components.css — bento cards, rail, prompt box, nav.
  No color/font values here — see tokens.css (§12).

  Breakpoints below are literal px values mirroring the --bp-* tokens in
  tokens.css (480 / 768 / 1024 / 1280) — keep in sync, don't invent new ones (§9).
*/

/* --- Nav / chat toggle --- */
/* Owner/role/tagline (CLAUDE.md §1) — three spans fading from full
   ink to lightest gray, a gradient effect built from existing color
   tokens (--color-text -> --color-text-muted -> --color-text-muted at
   reduced opacity, no new color values) rather than a literal CSS
   gradient on text, which would fight the fixed-weight web fonts at
   this size. Font varies by tier too: headline for the name, body at
   two weights for the rest — not the label font (Geist Pixel), since
   CLAUDE.md §12 reserves that for short all-caps eyebrows, never a
   sentence-like phrase. */
.site-nav-brand {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.site-nav-brand-name {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 1.125rem;
  color: var(--color-text);
}

.site-nav-brand-role {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 0.9375rem;
  color: var(--color-text-muted);
}

.site-nav-brand-detail {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 0.8125rem;
  color: var(--color-text-muted);
  opacity: 0.65;
}

.site-nav-list a {
  font-size: 0.9375rem;
  color: var(--color-text-muted);
  padding: var(--space-1) 0;
}

.site-nav-list a[aria-current="page"] {
  color: var(--color-text);
  font-weight: 600;
}

.chat-toggle {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-2) var(--space-3);
  min-height: 44px;
  font-size: 0.875rem;
  color: var(--color-text-muted);
}

.chat-toggle[aria-expanded="true"] {
  color: var(--color-text);
  background: var(--color-surface);
}

/* --- Ask Tianne side panel (js/shared-ui.js) --------------------------------
   A docked, non-modal panel on the right: no backdrop, the page stays visible
   and scrollable beside it, and it stays open until closed. From 1024px the
   page makes room for it (see the html.tianne-panel-open rules); between
   768px and 1024px it overlays the right edge; below 768px it is a
   full-screen sheet. Same chat backend as everywhere else (CLAUDE.md §6). */
:root {
  --tianne-panel-width: clamp(340px, 30vw, 440px);
}

.tianne-panel {
  position: fixed;
  z-index: 30;
  top: 0;
  right: 0;
  bottom: 0;
  width: var(--tianne-panel-width);
  display: flex;
  flex-direction: column;
  background: var(--color-bg);
  border-left: 1px solid var(--color-border);
  visibility: hidden;
  transform: translateX(100%);
}

.tianne-panel.is-open {
  visibility: visible;
  transform: none;
}

@media (prefers-reduced-motion: no-preference) {
  .tianne-panel {
    transition: transform 0.25s ease, visibility 0s linear 0.25s;
  }

  .tianne-panel.is-open {
    transition: transform 0.25s ease, visibility 0s;
  }
}

.tianne-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
}

.tianne-panel-title {
  font-size: 1rem;
  font-weight: 600;
}

.tianne-panel-actions {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.tianne-panel-newchat {
  min-height: 44px;
  padding: 0 var(--space-2);
  white-space: nowrap;
  font-size: 0.8125rem;
  color: var(--color-text-muted);
}

.tianne-panel-newchat:hover {
  color: var(--color-text);
  text-decoration: underline;
}

.tianne-panel-close {
  min-width: 44px;
  min-height: 44px;
  font-size: 1.25rem;
  line-height: 1;
  color: var(--color-text-muted);
}

/* The thread. */
.tianne-panel-body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
  overflow-y: auto;
  color: var(--color-text);
}

.tianne-panel-placeholder {
  color: var(--color-text-muted);
}

.tianne-msg {
  margin: 0;
  max-width: 100%;
  font-size: 0.9375rem;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.tianne-msg--user {
  align-self: flex-end;
  max-width: 85%;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius);
  background: var(--color-surface);
}

.tianne-msg--pending {
  color: var(--color-text-muted);
}

.tianne-msg--error {
  padding-left: var(--space-3);
  border-left: 2px solid var(--color-border);
  color: var(--color-text-muted);
}

.tianne-panel-links {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.tianne-panel-links a {
  display: block;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  color: var(--color-text);
  font-size: 0.9375rem;
}

.tianne-panel-links a:hover {
  border-color: var(--color-text-muted);
}

/* Follow-up input, pinned to the bottom of the panel. */
.tianne-panel-form {
  margin: 0;
  border-radius: 0;
  border: none;
  border-top: 1px solid var(--color-border);
}

/* Room for the panel: from 1024px the page and the fixed nav both narrow to
   sit beside it instead of running underneath. */
@media (min-width: 1024px) {
  html.tianne-panel-open body {
    padding-right: var(--tianne-panel-width);
  }

  html.tianne-panel-open .site-nav {
    right: var(--tianne-panel-width);
  }
}

@media (prefers-reduced-motion: no-preference) {
  body,
  .site-nav {
    transition: padding-right 0.25s ease, right 0.25s ease;
  }
}

/* Full-screen sheet below the tablet breakpoint. Scrolling is locked on the
   page behind it, since it is fully covered. */
@media (max-width: 767px) {
  .tianne-panel {
    width: 100%;
    border-left: none;
    transform: translateY(100%);
  }

  .tianne-panel.is-open {
    transform: none;
  }

  html.tianne-panel-open,
  html.tianne-panel-open body {
    overflow: hidden;
  }
}

/* --- Prompt box — visible on load, sticky once scrolled (js/home.js).
   Submit calls the real Tianne LLM backend; see CLAUDE.md §5/§6. --- */
.prompt-box-wrap {
  margin-bottom: var(--space-5);
}

.prompt-box {
  display: flex;
  gap: var(--space-2);
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-surface);
}

.prompt-box-input {
  flex: 1;
  min-width: 0;
  min-height: 44px;
  border: none;
  background: transparent;
  padding: var(--space-2);
  font-size: 1rem;
}

.prompt-box-input::placeholder {
  color: var(--color-text-muted);
}

.prompt-box-input:disabled,
.prompt-box-submit:disabled,
.prompt-box-clear:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.prompt-box-submit {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-2) var(--space-4);
  min-height: 44px;
  min-width: 44px;
  font-weight: 600;
}

.prompt-box-clear {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-2) var(--space-4);
  min-height: 44px;
  color: var(--color-text-muted);
}

/* Faded example questions under the empty hero bar (js/home.js). Muted like
   a search engine's suggestions; each is a real button and submits when
   clicked. */
.prompt-suggestions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-4);
  margin: var(--space-3) 0 0;
  padding: 0 var(--space-3);
  list-style: none;
  transition: opacity 0.25s ease;
}

.prompt-suggestions[hidden] {
  display: none;
}

.prompt-suggestions.is-swapping {
  opacity: 0;
}

.prompt-suggestion {
  min-height: 44px;
  padding: 0;
  text-align: left;
  font-size: 0.9375rem;
  color: var(--color-text-muted);
  opacity: 0.7;
  transition: opacity 0.15s ease, color 0.15s ease;
}

.prompt-suggestion:hover,
.prompt-suggestion:focus-visible {
  opacity: 1;
  color: var(--color-text);
  text-decoration: underline;
}

@media (prefers-reduced-motion: reduce) {
  .prompt-suggestions,
  .prompt-suggestion {
    transition: none;
  }
}

/* --- Homepage project grid — two-column masonry (2026-09-21). js/home.js
   places each card, in HOME_ORDER, into whichever column is currently
   shorter (not CSS multi-column, which would read down one column before
   starting the next). One gap value, --space-5 (24px), is used both
   between the columns and between stacked cards, so the whitespace is
   uniform in both directions. Cards carry no border, shadow or
   background: the media sits directly on the page. Full curated list by default, or just the current
   query's matches within that same set (js/home.js, CLAUDE.md §5/§6).
   One grid, two states — never a separate results panel. Columns
   stack to one below --bp-sm (480px), matching the site's named-
   breakpoint convention (§9/§11). --- */
.bento-grid {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

/* Was --bp-sm (480px) — raised to --bp-lg (1024px) so each column
   gets enough width for the enlarged card title/meta text (1.65rem/
   1.125rem, typography-v2.css) to stay on one line. Below 1024px a
   single full-width column gives that same text plenty of room; two
   narrower columns didn't, down to as low as 480px. */
@media (min-width: 1024px) {
  .bento-grid {
    flex-direction: row;
    align-items: flex-start;
  }
}

.bento-grid-column {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  flex: 1;
  min-width: 0;
}

.bento-grid-column:empty {
  display: none; /* single-column view leaves the right one empty: no phantom gap */
}

.bento-empty {
  padding: var(--space-6);
  text-align: center;
  color: var(--color-text-muted);
}

.bento-card {
  /* Lets the caption size itself off the card's own width, so title +
     company + status always fit on one line (typography-v2.css). */
  container-type: inline-size;
}

.bento-card-link {
  display: block;
  min-height: 44px;
  border-radius: 0;
  transition: opacity 0.15s ease;
}

.bento-card-link:hover {
  opacity: 0.85;
}

/* Two-step tap preview state (touch devices only, see js/home.js §5).
   Hover-capable devices never set data-previewed — they preview on
   hover/focus instead and navigate on the first click. */
.bento-card[data-previewed="true"] .bento-card-link {
  outline: 2px solid var(--color-text);
  outline-offset: 2px;
}

.bento-card-thumb-wrap {
  position: relative;
  overflow: hidden;
}

.bento-card-thumb {
  display: block;
  width: 100%;
  height: auto;
}

/* Reuses .media-placeholder (layout.css) for projects with no real
   thumbnail yet — this modifier removes its standalone margin (doesn't
   apply inside a thumb slot with its own caption spacing below) and
   swaps its fixed 220px min-height for a 16:9 aspect-ratio, so a
   placeholder card matches the ~16:9 real image/video thumbnails next
   to it in the grid instead of rendering visibly shorter. */
.bento-card-thumb-placeholder {
  margin-bottom: 0;
  border: 0;
  border-radius: 0;
  min-height: 0;
  aspect-ratio: 16 / 9;
}

/* Title, then company / status, on one baseline row (2026-09-21). Font sizes
   scale with the card (typography-v2.css) so it fits; the meta never shrinks,
   and if a row is ever still too narrow the title gets the ellipsis (its
   full text stays in the native title tooltip). */
.bento-card-caption {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: baseline;
  gap: 2px var(--space-3);
  min-width: 0;
  padding-top: var(--space-2);
}

.bento-card-tagline {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 1rem;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: var(--color-text);
}

.bento-card-meta {
  flex: none;
  overflow: hidden;
  font-family: var(--font-label);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: var(--color-text-muted);
}

.bento-card-hint {
  display: none;
  font-size: 0.75rem;
  color: var(--color-text-muted);
  margin-top: var(--space-1);
}

.bento-card[data-previewed="true"] .bento-card-hint {
  display: block;
}

@media (hover: hover) and (pointer: fine) {
  .bento-card-hint {
    display: none !important;
  }
}

.prompt-box-input.is-preview {
  color: var(--color-text-muted);
}

/* --- Focus visibility (Quality Floor, §11) --- */
.prompt-box-submit:focus-visible,
.prompt-box-input:focus-visible,
.prompt-box-clear:focus-visible,
.bento-card-link:focus-visible,
.chat-toggle:focus-visible,
.tianne-panel-newchat:focus-visible,
.tianne-panel-body:focus-visible,
.prompt-suggestion:focus-visible,
.tianne-panel-close:focus-visible,
.tianne-panel-links a:focus-visible,
.case-study-section a:focus-visible,
.case-study-sidenav a:focus-visible,
.case-study-sidenav summary:focus-visible {
  outline: 2px solid var(--color-text);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .bento-card-link {
    transition: none;
  }
}

/* --- Site footer: contact links (js/shared-ui.js) --------------------------
   Quiet on purpose: a hairline, small label-font links, generous margins
   that follow the homepage's side padding. */
.site-footer {
  margin-top: var(--space-8);
  padding: var(--space-5) var(--space-4) var(--space-7);
  border-top: 1px solid var(--color-border);
}

@media (min-width: 768px) {
  .site-footer {
    padding-inline: var(--space-7);
  }
}

@media (min-width: 1024px) {
  .site-footer {
    padding-inline: var(--space-8);
  }
}

.site-footer-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-footer-list a {
  display: inline-block;
  padding-block: var(--space-2); /* keeps the touch target tall enough */
  font-family: var(--font-label);
  font-size: 0.8125rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
  text-decoration: none;
}

.site-footer-list a:hover {
  color: var(--color-text);
  text-decoration: underline;
}

.site-footer-list a:focus-visible {
  outline: 2px solid var(--color-text);
  outline-offset: 2px;
}
