/* ==========================================================
   TOKENS
   ========================================================== */
:root {
  /* Main panel */
  --bg-main: #ffffff;
  --text-main: #14171a;
  --text-muted: #5b6169;
  --border: #e6e6e1;

  /* Chat panel */
  --bg-chat: #f5f0e1;
  --chat-accent: #1f4d2c;
  --chat-accent-soft: #bfe0c4;
  --chat-text: #2c3b2f;
  --chat-text-muted: #6f7d70;
  --chat-border: #e3dcc6;

  /* Fonts */
  --font-display: 'Lora', Georgia, serif;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  --radius-md: 12px;
  --radius-lg: 20px;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--text-main);
  background: var(--bg-main);
  -webkit-font-smoothing: antialiased;
}

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

/* Focus visible for keyboard accessibility */
a:focus-visible,
button:focus-visible,
input:focus-visible {
  outline: 2px solid var(--chat-accent);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { animation: none !important; transition: none !important; }
}

/* ==========================================================
   LAYOUT — portfolio pleine largeur + chat en couche
   ========================================================== */
/* The gutter stays reserved when .is-chat-open hides the scrollbar, so the
   viewport width does not change: scroll-experience.js recomputes the
   horizontal track on resize and would otherwise jump. */
html { scrollbar-gutter: stable; }
html.is-chat-open { overflow: hidden; }

.page {
  min-height: 100vh;
  width: 100%;
}

.main-panel {
  width: 100%;
  max-width: none;
  padding: 64px 72px 48px;
}

.chat-backdrop {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(0, 0, 0, 0.45);
}

/* Closed means invisible, never display:none: #turnstile-widget is rendered
   once and must keep its iframe across open/close (only captcha.hide() in
   main.js removes it). visibility:hidden also takes the closed window's
   controls out of the Tab order; it flips once the fade-out has ended. */
.chat-panel {
  position: fixed;
  top: 50%;
  right: 32px;
  z-index: 61;
  width: min(420px, calc(100vw - 32px));
  height: min(620px, calc(100vh - 64px));
  height: min(620px, calc(100dvh - 64px));
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--bg-chat);
  border: 1px solid var(--chat-border);
  border-radius: var(--radius-lg);
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.25);
  color: var(--chat-text);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-50%) scale(0.96);
  transform-origin: bottom right;
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s linear 0.2s;
}

.chat-panel.is-open {
  opacity: 1;
  visibility: visible;
  transform: translateY(-50%);
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s;
}

.chat-close {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  margin-left: auto;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--chat-text);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.chat-close:hover { background: var(--chat-accent-soft); }
.chat-close svg { width: 18px; height: 18px; }

.chat-bubble {
  position: fixed;
  right: 24px;
  bottom: 24px;
  z-index: 50;
  width: 60px;
  height: 60px;
  border: none;
  border-radius: 50%;
  background: var(--chat-accent);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
}
.chat-bubble:hover { background: #163d22; }
.chat-bubble svg { width: 30px; height: 30px; }

/* Pulsing halo. Stops once the chat has been opened (.was-opened): past that
   point the visitor knows where Wags is. The global reduced-motion rule turns
   it off too. */
.chat-bubble::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--chat-accent);
  opacity: 0.35;
  z-index: -1;
  animation: chat-bubble-pulse 2s ease-out infinite;
}
.chat-bubble.was-opened::before { animation: none; opacity: 0; }
@keyframes chat-bubble-pulse {
  to { transform: scale(1.6); opacity: 0; }
}

.chat-bubble-hint {
  position: absolute;
  bottom: calc(100% + 10px);
  right: 0;
  padding: 6px 10px;
  border-radius: 8px;
  background: #1c1f22;
  color: #fff;
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 500;
  white-space: nowrap;
  pointer-events: none;
}

html.is-chat-open .chat-bubble { visibility: hidden; }

@media (max-width: 560px) {
  .chat-panel,
  .chat-panel.is-open {
    top: 0;
    right: 0;
    width: 100%;
    height: 100vh;
    height: 100dvh;
    border: none;
    border-radius: 0;
  }
  .chat-panel { transform: scale(0.96); }
  .chat-panel.is-open { transform: none; }

  .chat-bubble {
    right: 16px;
    bottom: 16px;
  }
}

/* ==========================================================
   PANNEAU PRINCIPAL
   ========================================================== */
.hero {
  margin-bottom: 48px;
}

.eyebrow {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--chat-accent);
  margin: 0 0 12px;
}

.hero-name {
  font-family: var(--font-display);
  font-size: 44px;
  font-weight: 700;
  line-height: 1.15;
  margin: 0 0 6px;
  color: var(--text-main);
}

.hero-title {
  font-size: 18px;
  color: var(--text-muted);
  margin: 0 0 20px;
}

.hero-bio {
  font-size: 16px;
  line-height: 1.7;
  color: var(--text-main);
  max-width: 60ch;
  margin: 0;
}

.section-nav {
  display: flex;
  /* Six labels plus their gaps overflow narrow screens, so the links wrap. */
  flex-wrap: wrap;
  gap: 12px 24px;
  padding: 16px 0;
  margin-bottom: 40px;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.section-nav a {
  font-size: 14px;
  font-weight: 500;
  color: var(--text-muted);
}
.section-nav a:hover {
  color: var(--chat-accent);
  text-decoration: none;
}

/* Set by src/js/section-nav.js on the link of the section being read. */
.section-nav a[aria-current="true"] {
  color: var(--chat-accent);
  font-weight: 600;
}

/* From 901px the nav becomes a fixed column of links on a vertical rail, left
   of the content; below, it stays a horizontal bar under the hero (see the
   max-width: 900px block). */
@media (min-width: 901px) {
  /* The empty space right of the content matches the nav column on its
     left: 24px offset + 144px nav + 32px gap. */
  .main-panel {
    padding-left: 200px;
    padding-right: 200px;
  }

  /* z-index stays under the chat bubble (50) and the chat backdrop (60). */
  .section-nav {
    position: fixed;
    top: 180px;
    left: 24px;
    z-index: 40;
    width: 144px;
    flex-direction: column;
    /* Without it the links stretch and the focus outline spans the column. */
    align-items: flex-start;
    flex-wrap: nowrap;
    gap: 6px;
    padding: 0;
    margin: 0;
    border: none;
  }

  .section-nav a {
    padding: 6px 0 6px 12px;
    border-left: 2px solid var(--border);
  }

  .section-nav a[aria-current="true"] {
    border-left-color: var(--chat-accent);
  }
}

.section {
  margin-bottom: 40px;
  scroll-margin-top: 24px;
}

/* Sections listing items (projects, education...) are framed; the items keep
   their own padding and their border-bottom runs from edge to edge.
   Plain-text sections (.section-text) share the frame but carry their own
   padding, since they have no item children to carry it instead. */
.section-list,
.section-text {
  border: 1px solid var(--border);
  border-radius: 8px;
}

.section-list > .project-card,
.section-list > .experience-item {
  padding: 20px 24px;
}

.section-text {
  padding: 32px;
}
.section-text > :last-child {
  margin-bottom: 0;
}

.section-title {
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: 600;
  margin: 0 0 20px;
  color: var(--text-main);
}

.section-body p {
  font-size: 15px;
  line-height: 1.75;
  color: var(--text-main);
  margin: 0 0 12px;
}

/* Projects */
.project-card {
  padding: 20px 0;
  border-bottom: 1px solid var(--border);
}
.project-card:last-child { border-bottom: none; }

.project-card-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 6px;
}

.project-card-header h3 {
  font-size: 17px;
  font-weight: 600;
  margin: 0;
}

.project-link {
  font-size: 13px;
  white-space: nowrap;
}

.project-bullets {
  margin: 0 0 8px 18px;
  padding: 0;
}

.project-bullets li {
  font-size: 15px;
  line-height: 1.75;
  color: var(--text-main);
  margin: 0 0 8px;
}

.project-bullets li:last-child {
  margin-bottom: 0;
}

.tag-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  list-style: none;
  padding: 0;
  margin: 10px 0 0;
}

.tag-list li {
  font-size: 12px;
  font-weight: 500;
  color: var(--chat-accent);
  background: var(--chat-accent-soft);
  padding: 4px 10px;
  border-radius: 999px;
}

/* Experience */
.experience-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 20px 0;
  border-bottom: 1px solid var(--border);
}
.experience-item:last-child { border-bottom: none; }

.experience-content {
  flex: 1;
  min-width: 0;
}

.experience-dates {
  font-size: 13px;
  color: var(--text-muted);
  white-space: nowrap;
  padding-top: 2px;
}

.experience-content h3 {
  font-size: 16px;
  font-weight: 600;
  margin: 0 0 2px;
}

.experience-org {
  font-size: 14px;
  color: var(--text-muted);
  margin: 0 0 8px;
}

.experience-bullets {
  margin: 8px 0 0 18px;
  padding: 0;
}

.experience-bullets li {
  font-size: 15px;
  line-height: 1.75;
  color: var(--text-main);
  margin: 0 0 8px;
}

.experience-bullets li:last-child {
  margin-bottom: 0;
}

@media (max-width: 560px) {
  .experience-item {
    flex-direction: column;
    gap: 4px;
  }

  .project-card-header {
    flex-direction: column;
    gap: 4px;
  }

  .section-list > .project-card,
  .section-list > .experience-item {
    padding: 16px 18px;
  }
}

/* Certifications */
.cert-item {
  padding: 16px 0;
  border-bottom: 1px solid var(--border);
}
.cert-item:last-child { border-bottom: none; }

.cert-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.cert-name {
  flex: 1;
  min-width: 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--text-main);
}

.cert-org {
  display: block;
  font-size: 13px;
  color: var(--text-muted);
  margin-top: 4px;
}

.cert-link {
  font-size: 13px;
  white-space: nowrap;
}

/* Contact */
.contact-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.contact-list a {
  font-size: 15px;
  font-weight: 500;
}

.contact-bullets {
  margin: 0 0 0 18px;
  padding: 0;
}

.contact-bullets li {
  font-size: 15px;
  line-height: 1.75;
  color: var(--text-main);
  margin: 0 0 8px;
}

.contact-bullets li:last-child {
  margin-bottom: 0;
}

/* ==========================================================
   PANNEAU CHAT
   ========================================================== */
.chat-header {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-shrink: 0;
  padding: 16px 16px 16px 20px;
  border-bottom: 1px solid var(--chat-border);
}

.chat-avatar {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--chat-accent);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.chat-avatar svg { width: 26px; height: 26px; }

.chat-header-text h2 {
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 600;
  margin: 0 0 4px;
  color: var(--text-main);
}

.chat-header-text p {
  font-size: 12.5px;
  color: var(--chat-text-muted);
  margin: 0;
  display: flex;
  align-items: center;
  gap: 6px;
}

.status-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #4caf6b;
  display: inline-block;
}

.chat-body {
  flex: 1;
  overflow-y: auto;
  padding: 8px 24px;
  display: flex;
  flex-direction: column;
}

.chat-empty-state {
  width: 100%;
  max-width: none;
  margin: 16px 0 0;
  text-align: left;
  padding: 8px 0 24px;
}

.chat-empty-state h3 {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 600;
  margin: 0 0 10px;
  color: var(--text-main);
}

.chat-empty-state p {
  font-size: 14px;
  line-height: 1.65;
  color: var(--chat-text-muted);
  margin: 0 0 12px;
}
.chat-empty-state p:last-child { margin-bottom: 0; }

/* Messages (dynamically generated by main.js) */
.chat-message {
  max-width: 88%;
  padding: 10px 14px;
  border-radius: var(--radius-md);
  font-size: 14px;
  line-height: 1.5;
  margin-bottom: 10px;
}

.chat-message.user {
  align-self: flex-end;
  background: var(--chat-accent);
  color: #fff;
  border-bottom-right-radius: 4px;
}

.chat-message.agent {
  align-self: flex-start;
  background: #ffffff;
  border: 1px solid var(--chat-border);
  color: var(--text-main);
  border-bottom-left-radius: 4px;
}

/* Markdown rendered inside an agent bubble (main.js renderMarkdown). Element
   default margins are reset on the first/last child, or they stack on top of
   the bubble's own padding. */
.chat-message.agent > *:first-child { margin-top: 0; }
.chat-message.agent > *:last-child { margin-bottom: 0; }

.chat-message.agent p {
  margin: 0 0 8px;
}

.chat-message.agent h1,
.chat-message.agent h2,
.chat-message.agent h3,
.chat-message.agent h4,
.chat-message.agent h5,
.chat-message.agent h6 {
  font-size: 14px;
  font-weight: 600;
  margin: 10px 0 6px;
}

.chat-message.agent ul,
.chat-message.agent ol {
  margin: 0 0 8px;
  padding-left: 20px;
}

.chat-message.agent code {
  font-family: monospace;
  background: var(--chat-accent-soft);
  border-radius: 4px;
  padding: 1px 4px;
  font-size: 0.9em;
}

.chat-message.agent pre {
  /* A code block wider than the chat window must scroll, not overflow it. */
  overflow-x: auto;
  margin: 0 0 8px;
  /* Without a ground of its own a fenced block reads as an ordinary
     paragraph: only the font would set it apart. */
  background: var(--chat-accent-soft);
  border-radius: var(--radius-md);
  padding: 8px 10px;
}

.chat-message.agent pre code {
  background: none;
  padding: 0;
}

.chat-message.agent a {
  text-decoration: underline;
  /* A long, unbroken URL otherwise pushes past the bubble. */
  word-break: break-word;
}

.chat-message.agent table {
  display: block;
  overflow-x: auto;
  margin: 0 0 8px;
}

.chat-message.agent th,
.chat-message.agent td {
  border: 1px solid var(--chat-border);
  padding: 4px 8px;
  font-size: 13px;
}

.chat-message.agent blockquote {
  margin: 0 0 8px;
  padding-left: 10px;
  border-left: 2px solid var(--chat-border);
}

/* Blinking caret shown at the end of a bubble while it streams. A CSS
   pseudo-element, not a character appended to the Markdown string: injecting
   one there would break whatever syntax is mid-parse. */
.chat-message.is-streaming:not(.is-waiting)::after {
  content: '';
  display: inline-block;
  width: 2px;
  height: 1em;
  background: currentColor;
  margin-left: 2px;
  vertical-align: text-bottom;
  animation: chat-caret 1s step-end infinite;
}
@keyframes chat-caret { 50% { opacity: 0; } }

/* Three dots while the agent has sent nothing yet. The width is animated rather
   than `content`, which browsers do not animate reliably; `overflow: hidden`
   reveals the dots one at a time. */
.chat-message.is-waiting::after {
  content: '...';
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  width: 0;
  animation: chat-dots 1.2s steps(4, jump-none) infinite;
}
@keyframes chat-dots { to { width: 3ch; } }

.chat-input-bar {
  padding: 16px 24px 24px;
}

.chat-captcha {
  margin-bottom: 8px;
}
.chat-captcha:empty { display: none; }

#chat-form {
  display: flex;
  align-items: center;
  gap: 8px;
  background: #ffffff;
  border: 1px solid var(--chat-border);
  border-radius: var(--radius-lg);
  padding: 6px 6px 6px 18px;
}

#chat-input {
  flex: 1;
  /* Without this the field refuses to shrink below its placeholder width and
     pushes the send button out of the panel on narrow viewports. */
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  font-family: var(--font-body);
  /* Safari iOS zooms the page in when a focused field is under 16px. */
  font-size: 16px;
  color: var(--text-main);
  padding: 10px 0;
}
#chat-input::placeholder { color: var(--chat-text-muted); }

#chat-form button {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: none;
  background: var(--chat-accent);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.15s ease;
}
#chat-form button:not(:disabled):hover { background: #163d22; }
#chat-form button svg { width: 16px; height: 16px; }

/* Locked while a reply streams: one request in flight at a time. */
#chat-input:disabled { opacity: 0.6; cursor: not-allowed; }
#chat-form button:disabled { opacity: 0.5; cursor: not-allowed; }

.chat-disclaimer {
  text-align: center;
  font-size: 11.5px;
  color: var(--chat-text-muted);
  margin: 12px 0 0;
}

/* ==========================================================
   RESPONSIF — portfolio plus serré sous 900px
   ========================================================== */
/* The chat layer sizes itself (see LAYOUT); this block only tightens the
   portfolio. The literal cannot be a token: custom properties do not work
   inside a media condition. It sits last because a media query carries no
   specificity bonus — it only wins on source order. */
@media (max-width: 900px) {
  .main-panel {
    padding: 32px 20px 40px;
  }

  .hero {
    margin-bottom: 32px;
  }

  .hero-name {
    font-size: 32px;
  }

  .section-nav {
    gap: 10px 16px;
    margin-bottom: 28px;
  }

  .section {
    margin-bottom: 32px;
  }

  .section-text {
    padding: 24px;
  }
}
