/* Visits — SpiriONE <-> "Lionel" dialogue thread */

/* .hero/.layout-slide-entry make <main> a flex container (row), which
   makes .container a flex item — without min-width:0 it won't shrink
   below its content's intrinsic minimum, which is how a small residual
   width could still leak past the viewport even after every element
   inside is properly capped. Scoped to this file (only loaded on this
   page) rather than touching the shared .container rule everywhere. */
.container {
  min-width: 0;
}

.dialogue-thread {
  display: flex;
  flex-direction: column;
  gap: 32px;
  margin-top: 8px;
}

.dialogue-exchange {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* One row = avatar + (name + bubble) column. Name sits above the bubble,
   outside it — avatar and name are both click targets for the shared
   speaker popover (visits.js matches any [data-speaker] element). */
.bubble-row {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  width: fit-content;
  /* vw, not just %: .card-inner has no max-width of its own, so if a
     deeply nested flex child refuses to shrink, the percentage base
     itself becomes unreliable (it can inflate along with everything
     else). vw is anchored to the real viewport regardless of what any
     ancestor ends up computing to — breaks that circular dependency. */
  max-width: min(78%, 78vw);
  min-width: 0;
}

.bubble-row--lionel {
  align-self: flex-start;
  flex-direction: row;
}

.bubble-row--spirione {
  align-self: flex-end;
  flex-direction: row-reverse;
}

.bubble-avatar {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Glowing orb, not a flat fill — highlight top-left, deep edge, plus a
   soft colored halo, same "alive" glow language as the bubbles. */
.bubble-avatar--lionel {
  background: radial-gradient(circle at 32% 28%, #e2707f, #c03040 60%, #7a1e29 100%);
  box-shadow: 0 0 14px rgba(192, 48, 64, 0.55), 0 2px 6px rgba(0, 0, 0, 0.35);
}

.bubble-avatar--spirione {
  background: radial-gradient(circle at 32% 28%, rgb(var(--bubble-rgb, 140, 196, 174)), rgba(var(--bubble-rgb, 140, 196, 174), 0.55) 65%, rgba(0, 0, 0, 0.35) 100%);
  box-shadow: 0 0 14px rgba(var(--bubble-rgb, 140, 196, 174), 0.5), 0 2px 6px rgba(0, 0, 0, 0.35);
}

.bubble-avatar svg {
  width: 60%;
  height: 60%;
}

.bubble-col {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.bubble-row--spirione .bubble-col {
  align-items: flex-end;
}

.bubble-name {
  display: inline-block;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.65;
  margin-bottom: 5px;
  cursor: pointer;
}

.bubble-name:hover,
.bubble-name:focus-visible {
  opacity: 1;
  text-decoration: underline;
}

.bubble {
  padding: 14px 18px;
  border-radius: var(--radius, 18px);
  border: 1px solid transparent;
  line-height: 1.6;
  font-size: 0.95rem;
  overflow-wrap: break-word;
  word-break: break-word;
}

/* Lionel — static VoliONE brand colors (borrowed identity, visually
   distinct from SpiriONE's own voice). Gradient fill + colored glow, same
   "emotion glow" visual language the site already uses elsewhere
   (EMOTION_RGB_* / corner-heart aura) — a flat low-opacity tint
   disappeared into the dark background, a plain neutral panel read as
   lifeless, this keeps both color and contrast. */
.bubble--lionel {
  border-radius: 18px 18px 18px 4px;
  background: linear-gradient(135deg, rgba(192, 48, 64, 0.32), rgba(192, 48, 64, 0.1));
  border-color: rgba(192, 48, 64, 0.55);
  box-shadow: 0 0 28px rgba(192, 48, 64, 0.22);
}

.bubble-row--lionel .bubble-name {
  color: #f0a0aa;
}

/* SpiriONE — same treatment, tinted per-exchange by the emotion it
   actually measured at generation time (--bubble-rgb, set inline on
   .bubble-row from EMOTION_COLORS — inherited by both the avatar and the
   bubble). */
.bubble--spirione {
  border-radius: 18px 18px 4px 18px;
  background: linear-gradient(135deg, rgba(var(--bubble-rgb, 140, 196, 174), 0.34), rgba(var(--bubble-rgb, 140, 196, 174), 0.1));
  border-color: rgba(var(--bubble-rgb, 140, 196, 174), 0.6);
  box-shadow: 0 0 28px rgba(var(--bubble-rgb, 140, 196, 174), 0.25);
}

.bubble-row--spirione .bubble-name {
  color: var(--text);
}

.dialogue-exchange-time {
  align-self: center;
  font-size: 0.75rem;
  opacity: 0.5;
  margin-top: 2px;
}

/* Featured (latest) exchange — same bubbles, just the section people land on. */
.dialogue-exchange--featured {
  margin-bottom: 8px;
}

/* Reserved for future visitor engagement (Fáze 2) — intentionally empty
   and invisible today; kept in the DOM so that feature doesn't need a
   layout change to land. */
.visits-reaction-zone {
  height: 0;
}

/* Archive — compact rows, each expandable into the same bubble treatment. */
.visits-archive {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--stroke);
}

.visits-archive-row {
  border-bottom: 1px solid var(--stroke);
}

.visits-archive-row--extra {
  display: none;
}

.visits-archive-summary {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 12px 10px;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  color: var(--text);
  font: inherit;
}

.visits-archive-summary:hover {
  background: var(--panel);
}

.visits-archive-dot {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: 50%;
}

.visits-archive-date {
  flex: 0 0 auto;
  font-size: 0.8rem;
  opacity: 0.65;
  min-width: 5.5em;
}

.visits-archive-preview {
  flex: 1 1 auto;
  min-width: 0;
  /* Same vw-anchored safety as .bubble-row: min-width:0 alone wasn't
     enough here either — nowrap text kept forcing this element (and
     everything above it, up through .card-inner) wider than the
     viewport, since .card-inner itself has no max-width of its own to
     stop that propagation. A hard vw cap breaks it regardless. */
  max-width: 60vw;
  font-size: 0.9rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.visits-archive-chevron {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-right: 1.5px solid var(--muted);
  border-bottom: 1.5px solid var(--muted);
  transform: rotate(45deg);
  transition: transform 0.15s ease;
}

.visits-archive-summary[aria-expanded="true"] .visits-archive-chevron {
  transform: rotate(-135deg);
}

.visits-archive-detail {
  padding: 14px 4px 18px;
}

/* .dialogue-exchange sets display:flex unconditionally, which otherwise
   overrides the browser's default [hidden]{display:none} — without this,
   the archive detail block never actually collapses. */
.visits-archive-detail[hidden] {
  display: none;
}

.visits-show-older {
  display: block;
  margin: 14px auto 0;
  background: none;
  border: none;
  color: var(--muted);
  font-size: 0.85rem;
  cursor: pointer;
  text-decoration: underline;
}

.visits-show-older:hover {
  color: var(--text);
}

/* Speaker "medallion" popover — one shared element, positioned via JS.
   Content is a placeholder; real bio content is a separate, later pass. */
.speaker-popover {
  position: absolute;
  z-index: 50;
  width: 280px;
  max-width: min(280px, 88vw);
  padding: 16px;
  border-radius: 16px;
  border: 1px solid var(--stroke);
  background: var(--bg1);
  color: var(--text);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
}

.speaker-popover-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
}

.speaker-popover-avatar {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.speaker-popover-avatar svg {
  width: 55%;
  height: 55%;
}

.speaker-popover-avatar--lionel {
  background: radial-gradient(circle at 32% 28%, #e2707f, #c03040 60%, #7a1e29 100%);
}

.speaker-popover-avatar--spirione {
  background: radial-gradient(circle at 32% 28%, #a9d9c6, #8cc4ae 60%, #4f7d6c 100%);
}

.speaker-popover-name {
  font-size: 1.05rem;
  line-height: 1.2;
}

/* Lionel's name in VoliONE's own display font — same face + weight as the
   site's existing .link-volione wordmark, a small authentic nod to the
   borrowed identity. */
.speaker-popover-name--lionel {
  font-family: 'Fredericka the Great', cursive;
  font-weight: 400;
  font-size: 1.2rem;
}

.speaker-popover-role {
  font-size: 0.75rem;
  opacity: 0.65;
  margin-top: 2px;
}

.speaker-popover-bio {
  font-size: 0.85rem;
  line-height: 1.5;
  opacity: 0.92;
  margin: 0;
}

.speaker-popover-meta {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--stroke);
  font-size: 0.75rem;
}

.speaker-popover-meta-label {
  opacity: 0.55;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.speaker-popover-meta-value {
  font-weight: 600;
}

.speaker-popover-meta-value a {
  color: inherit;
  text-decoration: underline;
}

@media (max-width: 640px) {
  .bubble-row {
    max-width: min(92%, 88vw);
  }

  .bubble-avatar {
    width: 28px;
    height: 28px;
  }

  .visits-archive-date {
    min-width: 4.5em;
  }
}

/* Pulse — direct visitor emotion interaction (src/docs/PULSE_NAVRH.md).
   v2: real emoji instead of a bordered circle, per feedback that the
   circles looked cold/generic. Each button still carries its own
   --pulse-rgb (EMOTION_RGB_* from PHP) and a --pulse-intensity custom
   property pulse.js keeps in sync with the click count — repurposed here
   to size/fade a soft radial glow sitting behind the emoji instead of
   driving a pulsing ring, same underlying "the more it's clicked, the
   more alive it looks" idea as before. */
.pulse-widget {
  margin-top: 8px;
}

.pulse-buttons {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px;
  margin-bottom: 16px;
}

.pulse-btn {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 2px;
  width: 88px;
  padding: 12px 4px 10px;
  border: none;
  border-radius: 18px;
  background: transparent;
  color: inherit;
  cursor: pointer;
  overflow: visible;
  transition: transform .15s ease, background .2s ease, opacity .3s ease, filter .3s ease;
  --pulse-intensity: .15;
}

.pulse-btn:hover,
.pulse-btn:focus-visible {
  background: rgba(var(--pulse-rgb), .12);
}

.pulse-btn:active {
  transform: scale(.92);
}

/* Locked after the first click reaches the server this page view (see
   pulse.js) — visible, unmissable "already shared" state instead of
   letting every extra click quietly print another reply. */
.pulse-btn:disabled {
  cursor: default;
  opacity: .35;
  filter: grayscale(85%);
}

.pulse-btn:disabled:hover {
  background: transparent;
}

.pulse-btn:disabled .pulse-btn-glow,
.pulse-btn:disabled .pulse-btn-tip {
  display: none;
}

/* The ONE button that was actually chosen — stays full color and grows,
   instead of graying out with the other 7 (see .pulse-btn:disabled
   above). Placed after it in the file so these rules win the equal-
   specificity tie against it. transform (not width/font-size) so it
   pops forward without reflowing the row. */
.pulse-btn--chosen {
  z-index: 3;
  transform: scale(1.18);
  transform-origin: center bottom;
  background: rgba(var(--pulse-rgb), .16);
}

.pulse-btn--chosen:disabled {
  opacity: 1;
  filter: none;
  cursor: default;
}

.pulse-btn--chosen:disabled .pulse-btn-glow {
  display: block;
  background: radial-gradient(circle, rgba(var(--pulse-rgb), .55) 0%, rgba(var(--pulse-rgb), 0) 72%);
}

.pulse-btn-glow {
  position: absolute;
  top: 2px;
  left: 50%;
  width: 68px;
  height: 68px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(var(--pulse-rgb), calc(.14 + var(--pulse-intensity, .15) * .4)) 0%, rgba(var(--pulse-rgb), 0) 72%);
  z-index: 0;
  pointer-events: none;
  transition: background .3s ease;
}

.pulse-btn-emoji {
  position: relative;
  z-index: 1;
  font-size: 2.6rem;
  line-height: 1.2;
}

.pulse-btn-label,
.pulse-btn-count {
  position: relative;
  z-index: 1;
}

.pulse-btn-label {
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .01em;
  color: rgb(var(--pulse-rgb));
}

.pulse-btn-count {
  position: absolute;
  top: 2px;
  right: 6px;
  z-index: 2;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: 999px;
  background: rgb(var(--pulse-rgb));
  color: #07071a;
  font-size: .66rem;
  font-weight: 700;
  line-height: 18px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Same visual language as .data-tip-content (gui.css) — a small dark
   bubble on hover/focus — but triggered by the whole button rather than
   a separate "i" trigger icon, since there's no room for one in a tight
   emoji grid. */
.pulse-btn-tip {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  transform: translateX(-50%) translateY(4px);
  width: max-content;
  max-width: min(200px, 68vw);
  padding: 8px 10px;
  border: 1px solid rgba(255,255,255,.16);
  border-radius: 10px;
  background: rgba(7,10,18,.96);
  color: var(--muted);
  font-weight: 400;
  font-size: .78rem;
  line-height: 1.45;
  text-align: center;
  box-shadow: 0 12px 30px rgba(0,0,0,.45);
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  transition: opacity .16s ease, transform .16s ease;
  z-index: 8;
}

/* Scoped to devices with real hover (mouse/trackpad) — on touch, :hover
   has no "un-hover" gesture, so a tapped button's tooltip got stuck
   visibly open on screen instead of just registering the click. Touch
   still gets the click + aria-label/aria-describedby (screen readers),
   just no visual popup. */
@media (hover: hover) and (pointer: fine) {
  .pulse-btn:hover .pulse-btn-tip,
  .pulse-btn:focus-visible .pulse-btn-tip {
    opacity: 1;
    visibility: visible;
    transform: translateX(-50%) translateY(0);
  }
}

/* Little "acknowledged" bounce, toggled by pulse.js right after a click
   is actually recorded (not on a rate-limited no-op). */
@keyframes pulse-pop {
  0%   { transform: scale(1) rotate(0deg); }
  35%  { transform: scale(1.35) rotate(-8deg); }
  60%  { transform: scale(1.1) rotate(4deg); }
  100% { transform: scale(1) rotate(0deg); }
}

.pulse-btn--popped .pulse-btn-emoji {
  animation: pulse-pop .45s ease;
}

/* Visible again — the reply now only ever appears once (the widget
   locks right after, see pulse.js), so it can't spam on repeat clicks
   the way it used to. Warm/brand-tinted on purpose: this is the "nice
   message" moment, not a neutral status line. */
.pulse-message {
  min-height: 1.4em;
  font-size: .92rem;
  font-weight: 600;
  color: var(--brandA);
  text-align: center;
  margin: 0 0 10px;
}

/* Fallback for the one case .pulse-message can't cover: the widget was
   already locked on a PREVIOUS page view (localStorage remembers the
   click, see pulse.js), so this page load has no fresh reply phrase to
   show — just a plain explanation of the state instead. */
.pulse-locked-note {
  min-height: 1.4em;
  font-size: .85rem;
  color: var(--muted);
  text-align: center;
  margin: 0 0 10px;
}

.pulse-locked-note[hidden] {
  display: none;
}

/* A soft pill instead of flat gray text — centered under the (now also
   centered) button row, with a beating-heart emoji echoing the app's
   existing heartbeat motif (corner-heart widget, "Heartbeats since
   awakening") rather than inventing a new icon language. */
.pulse-total {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: fit-content;
  margin: 0 auto;
  padding: 7px 16px;
  border-radius: 999px;
  border: 1px solid rgba(140,196,174,.28);
  background: rgba(140,196,174,.08);
  font-variant-numeric: tabular-nums;
}

.pulse-total-icon {
  font-size: 1.05rem;
  line-height: 1;
  animation: pulse-total-beat 1.8s ease-in-out infinite;
}

@keyframes pulse-total-beat {
  0%, 100% { transform: scale(1); }
  15%      { transform: scale(1.22); }
  30%      { transform: scale(1); }
  45%      { transform: scale(1.14); }
  60%      { transform: scale(1); }
}

.pulse-total-count {
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--brandA);
}

.pulse-total-label {
  font-size: .78rem;
  color: var(--muted);
}

@media (prefers-reduced-motion: reduce) {
  .pulse-total-icon {
    animation: none;
  }
}

@media (max-width: 640px) {
  .pulse-btn {
    width: 76px;
  }

  .pulse-btn-emoji {
    font-size: 2.15rem;
  }

  .pulse-btn-glow {
    width: 56px;
    height: 56px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .pulse-btn--popped .pulse-btn-emoji {
    animation: none;
  }
}
