:root {
  color-scheme: light;
  --lf-bg: #f6f7f9;
  --lf-surface: #ffffff;
  --lf-surface-2: #f0f2f6;
  --lf-surface-3: #e4e7ee;
  /* Recessed well for the login folder tabs: the band must sit clearly below the panel colour in
   * BOTH themes or the selected tab stops reading as cut out of the band (bwgzt login.vue uses
   * #eee on #fff for exactly this). */
  --lf-sunk: #e4e7ee;
  --lf-sidebar: #ffffff;
  --lf-border: #e4e7ee;
  --lf-border-strong: #d3d8e2;
  --lf-fg: #1b1f27;
  --lf-muted: #5b6270;
  --lf-faint: #8a90a0;
  --lf-accent: #4f5ef7;
  --lf-accent-soft: #eef0ff;
  --lf-badge: linear-gradient(135deg, #4f5ef7, #7a5cf7);
  --lf-cover: linear-gradient(135deg, #c9d3ff, #7a5cf7);
  --lf-danger: #e5484d;
  --lf-success: #16a085;
  --lf-success-soft: #e2f3ee;
  --lf-warning: #b26a00;
  --lf-warning-soft: #fff4e0;
  --lf-shadow-sm: 0 1px 2px rgba(20, 25, 50, 0.06);
  --lf-shadow: 0 8px 28px rgba(20, 25, 50, 0.1);
  --lf-shadow-lg: 0 14px 40px rgba(20, 25, 50, 0.18);
  --lf-r-sm: 9px;
  --lf-r-md: 12px;
  --lf-r-lg: 16px;
  --lf-r-pill: 999px;
  /* One height for the two rows under the window strip — the sidebar's brand head and the top bar —
   * so their bottom rules land on the same pixel instead of one sitting above the other. */
  --lf-head-h: 46px;
}

html.dark {
  color-scheme: dark;
  --lf-bg: #0f1217;
  --lf-surface: #181c24;
  --lf-surface-2: #20242e;
  --lf-surface-3: #2a2f3a;
  --lf-sunk: #0b0e13;
  --lf-sidebar: #141820;
  --lf-border: #2a2f3a;
  --lf-border-strong: #3a4150;
  --lf-fg: #e8eaf0;
  --lf-muted: #aab0c0;
  --lf-faint: #6f7685;
  --lf-accent: #7b88ff;
  --lf-accent-soft: #232a4d;
  --lf-badge: linear-gradient(135deg, #6470ff, #8f6cff);
  --lf-cover: linear-gradient(135deg, #4a5aa8, #7a5cf7);
  --lf-danger: #ff6369;
  --lf-success: #2fd3ae;
  --lf-success-soft: #12342c;
  --lf-warning: #e0a55f;
  --lf-warning-soft: #2c2417;
  --lf-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --lf-shadow: 0 8px 28px rgba(0, 0, 0, 0.45);
  --lf-shadow-lg: 0 16px 44px rgba(0, 0, 0, 0.6);
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body,
#app {
  margin: 0;
  padding: 0;
  height: 100%;
}

body {
  background: var(--lf-bg);
  color: var(--lf-fg);
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC',
    system-ui, sans-serif;
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  user-select: none;
  overflow: hidden;
}

textarea,
input,
.markdown-body {
  user-select: text;
}

button {
  font-family: inherit;
}

svg {
  display: block;
}

* {
  scrollbar-width: thin;
  scrollbar-color: var(--lf-border-strong) transparent;
}

*::-webkit-scrollbar {
  width: 9px;
  height: 9px;
}

*::-webkit-scrollbar-thumb {
  background: var(--lf-border-strong);
  border: 3px solid transparent;
  border-radius: var(--lf-r-pill);
  background-clip: content-box;
}

.muted {
  color: var(--lf-faint);
}

.card {
  background: var(--lf-surface);
  border: 1px solid var(--lf-border);
  border-radius: var(--lf-r-lg);
  box-shadow: var(--lf-shadow);
}

.row {
  display: flex;
  align-items: center;
  gap: 8px;
}
/* Hand-written primitives, tuned to the lexForge/client shell: flat accent, 9-16px radii, accent-soft hovers. */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 34px;
  padding: 0 13px;
  border: 1px solid var(--lf-border);
  border-radius: var(--lf-r-sm);
  background: var(--lf-surface);
  color: var(--lf-muted);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.16s ease, border-color 0.16s ease, color 0.16s ease, filter 0.16s ease;
  white-space: nowrap;
}

.btn:hover {
  color: var(--lf-fg);
  background: var(--lf-surface-2);
}

.btn:focus-visible {
  outline: 2px solid var(--lf-accent);
  outline-offset: 2px;
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn--primary {
  border-color: var(--lf-accent);
  background: var(--lf-accent);
  color: #fff;
}

.btn--primary:hover {
  background: var(--lf-accent);
  color: #fff;
  filter: brightness(1.08);
}

.btn--primary:disabled {
  background: var(--lf-surface-3);
  border-color: var(--lf-surface-3);
  color: var(--lf-faint);
  filter: none;
}

/*  destructive action on a solid red face — a tinted one reads as a hint, not as the thing that
    actually deletes. Same disabled treatment as the primary so a dead button never looks live. */
.btn--danger {
  border-color: var(--lf-danger);
  background: var(--lf-danger);
  color: #fff;
}

.btn--danger:hover {
  background: var(--lf-danger);
  color: #fff;
  filter: brightness(1.08);
}

.btn--danger:disabled {
  background: var(--lf-surface-3);
  border-color: var(--lf-surface-3);
  color: var(--lf-faint);
  filter: none;
}

.btn--ghost {
  border-color: transparent;
  background: transparent;
  color: var(--lf-muted);
}

.btn--ghost:hover {
  background: var(--lf-surface-2);
  color: var(--lf-fg);
}

.btn--sm {
  min-height: 28px;
  padding: 0 10px;
  font-size: 12px;
}

.btn--icon {
  width: 34px;
  min-height: 34px;
  padding: 0;
  border-color: transparent;
  background: transparent;
  color: var(--lf-muted);
}

.btn--icon:hover {
  background: var(--lf-surface-2);
  color: var(--lf-fg);
}

.btn--round {
  border-radius: var(--lf-r-pill);
}

.btn--block {
  width: 100%;
}

.tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border: 1px solid var(--lf-border);
  border-radius: var(--lf-r-pill);
  background: var(--lf-surface-2);
  color: var(--lf-muted);
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.7;
  white-space: nowrap;
}

.tag--accent {
  border-color: transparent;
  background: var(--lf-accent);
  color: #fff;
}

.tag--soft {
  border-color: transparent;
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
}

.tag--warning {
  border-color: transparent;
  background: var(--lf-warning-soft);
  color: var(--lf-warning);
}

.tag--danger {
  border-color: transparent;
  background: var(--lf-danger);
  color: #fff;
}

/*
 * The skill citation pill: what `@[技能名](skill://id)` looks like inside the running text. This stylesheet is the only copy of it —
 * in a message it is a `<span class="skill-tag">` stitched together with v-html, in the composer a component with the same class,
 * so the icon goes through a mask (it follows the text colour, a dark theme needs no second drawing) and does not rely on the AppIcon component.
 */
.skill-tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: 100%;
  height: 24px;
  padding: 0 9px;
  border-radius: var(--lf-r-pill);
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1;
  vertical-align: middle;
  white-space: nowrap;
  overflow: hidden;
}

/* The pale-purple background behind your own line is the very colour the pill defaults to, so without a restyle it turns
   invisible: here it becomes a white pill with a border that lifts it off the ground. The pill is stitched together with v-html,
   so a scoped style cannot reach it — that is why the override lives in this file. */
.msg--user .bubble .skill-tag {
  border: 1px solid var(--lf-border);
  background: var(--lf-surface);
  color: var(--lf-accent);
}

.skill-tag::before {
  content: '';
  flex: 0 0 auto;
  width: 12px;
  height: 12px;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round' d='M12 6.5C10.2 5 7.6 4.4 4 4.4v13.2c3.6 0 6.2.6 8 2 1.8-1.4 4.4-2 8-2V4.4c-3.6 0-6.2.6-8 2.1z'/%3E%3Cpath fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' d='M12 6.5v13.1'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round' d='M12 6.5C10.2 5 7.6 4.4 4 4.4v13.2c3.6 0 6.2.6 8 2 1.8-1.4 4.4-2 8-2V4.4c-3.6 0-6.2.6-8 2.1z'/%3E%3Cpath fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' d='M12 6.5v13.1'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* The ones in the composer carry an extra cross to remove them, so room is kept on the right. */
.skill-tag--pick {
  padding-right: 4px;
}

.skill-tag__x {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  opacity: 0.6;
  cursor: pointer;
}

.skill-tag__x:hover {
  opacity: 1;
  background: var(--lf-surface);
}

.field {
  width: 100%;
  min-height: 38px;
  padding: 9px 12px;
  border: 1px solid var(--lf-border);
  border-radius: 10px;
  background: var(--lf-surface-2);
  color: var(--lf-fg);
  font: inherit;
  font-size: 13.5px;
  transition: border-color 0.16s ease, background 0.16s ease;
  box-sizing: border-box;
}

.field::placeholder {
  color: var(--lf-faint);
}

.field:focus {
  outline: none;
  border-color: var(--lf-accent);
  background: var(--lf-surface);
}

.field--invalid,
.field--invalid:focus {
  border-color: var(--lf-danger);
}

.label {
  display: block;
  margin: 0 0 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--lf-muted);
}

.error {
  margin: 7px 0 0;
  font-size: 12px;
  color: var(--lf-danger);
}

/* Topbar language and theme toggles. */
.seg {
  display: flex;
  align-items: center;
  gap: 0;
  border: 1px solid var(--lf-border);
  border-radius: var(--lf-r-sm);
  background: var(--lf-surface);
  overflow: hidden;
  flex: 0 0 auto;
}

.seg button {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 11px;
  border: 0;
  background: transparent;
  color: var(--lf-muted);
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
}

.seg button + button {
  border-left: 1px solid var(--lf-border);
}

.seg button:hover {
  color: var(--lf-fg);
}

.seg button[aria-pressed='true'] {
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
  font-weight: 600;
}

/* Sidebar navigation rows. */
.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 10px;
  border: 0;
  border-radius: var(--lf-r-sm);
  background: transparent;
  color: var(--lf-muted);
  font-size: 13.5px;
  font-weight: 500;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.14s ease, color 0.14s ease;
}

.nav-item:hover {
  background: var(--lf-surface-2);
  color: var(--lf-fg);
}

.nav-item--active {
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
  font-weight: 600;
}

.nav-item--primary {
  justify-content: center;
  background: var(--lf-accent);
  color: #fff;
  font-weight: 600;
}

.nav-item--primary:hover {
  background: var(--lf-accent);
  color: #fff;
  filter: brightness(1.08);
}

/* Group headings, not rows: smaller, wider-tracked and faint, so a label can never be mistaken for
   the 13px clickable entry under it. The line that separates groups belongs to the panel, not here. */
.sb-label {
  margin: 12px 0 4px;
  padding: 0 10px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--lf-faint);
}

.anchor {
  position: relative;
}

.modal-mask {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(10, 12, 20, 0.5);
}

.modal {
  /* Anchor for the corner close button: without this `.modal__x` positions against `.modal-mask`
   * and lands in the window corner instead of the dialog's. */
  position: relative;
  width: min(440px, 94vw);
  padding: 22px;
  border: 1px solid var(--lf-border);
  border-radius: var(--lf-r-lg);
  background: var(--lf-surface);
  box-shadow: var(--lf-shadow-lg);
  animation: lf-pop 0.18s ease;
}

.modal__title {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
}

.modal__body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-height: min(64vh, 520px);
  overflow-y: auto;
}

.modal__foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 20px;
}

@keyframes lf-pop {
  from {
    opacity: 0;
    transform: translateY(8px) scale(0.985);
  }
}

/* One artifact per message: cover head + tabbed body. */
.result-card {
  border: 1px solid var(--lf-border);
  border-radius: var(--lf-r-lg);
  background: var(--lf-surface);
  box-shadow: var(--lf-shadow);
  overflow: hidden;
}

.rc-head {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 18px 20px;
  border-bottom: 1px solid var(--lf-border);
}

.rc-cover {
  flex: 0 0 auto;
  width: 54px;
  height: 74px;
  border-radius: 8px;
  background: var(--lf-cover);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-align: center;
  padding: 6px;
}

.rc-title {
  font-size: 18px;
  font-weight: 700;
  overflow-wrap: anywhere;
}

.rc-tabs {
  display: flex;
  gap: 4px;
  padding: 0 12px;
  border-bottom: 1px solid var(--lf-border);
  background: var(--lf-surface-2);
}

.rc-tabs button {
  padding: 12px 14px;
  border: 0;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--lf-muted);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}

.rc-tabs button:hover {
  color: var(--lf-fg);
}

.rc-tabs button[aria-selected='true'] {
  border-bottom-color: var(--lf-accent);
  color: var(--lf-accent);
  font-weight: 600;
}

.rc-body {
  padding: 18px 20px;
}

.outline-list {
  margin: 0;
  padding-left: 20px;
}

.outline-list li {
  margin: 7px 0;
  line-height: 1.65;
}

.outline-list li::marker {
  color: var(--lf-accent);
  font-weight: 600;
}

/* Study plan sits in an accent box with key / value rows. */
.study-plan {
  border: 1px solid var(--lf-accent);
  border-radius: var(--lf-r-md);
  background: var(--lf-accent-soft);
  padding: 14px 16px;
}

.sp-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 0;
  border-bottom: 1px dashed var(--lf-border);
  font-size: 13px;
}

.sp-row:last-of-type {
  border-bottom: none;
}

.sp-row .k {
  color: var(--lf-muted);
}

.sp-note {
  margin: 10px 0 0;
  font-size: 12px;
  color: var(--lf-muted);
}

/* Ingestion progress: one row per stage with a spinner dot. */
.prog-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 0;
  border-bottom: 1px dashed var(--lf-border);
  font-size: 13px;
}

.prog-item:last-child {
  border-bottom: none;
}

.prog-dot {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2px solid var(--lf-border);
}

.prog-dot--now {
  border-color: var(--lf-accent);
  border-top-color: transparent;
  animation: lf-spin 0.8s linear infinite;
}

.prog-dot--done {
  border-color: var(--lf-success);
  background: var(--lf-success);
}

.prog-state {
  margin-left: auto;
  font-size: 12px;
  color: var(--lf-faint);
}

@keyframes lf-spin {
  to {
    transform: rotate(360deg);
  }
}

/* Recommendation and project rows. */
.proj-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  border: 1px solid var(--lf-border);
  border-radius: 13px;
  background: var(--lf-surface);
  margin-bottom: 12px;
}

.proj-item:last-child {
  margin-bottom: 0;
}

.proj-ic {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
}

.dl-btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  border: 1px solid var(--lf-accent);
  border-radius: var(--lf-r-sm);
  background: var(--lf-accent);
  color: #fff;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.dl-btn:hover:not(:disabled) {
  filter: brightness(1.08);
}

.dl-btn:disabled {
  border-color: var(--lf-border);
  background: var(--lf-surface-2);
  color: var(--lf-faint);
  cursor: not-allowed;
}

.alert {
  display: flex;
  gap: 10px;
  padding: 11px 13px;
  border: 1px solid var(--lf-border);
  border-radius: 10px;
  background: var(--lf-surface-2);
  font-size: 12.5px;
}

.alert__mark {
  flex: 0 0 auto;
  margin-top: 1px;
  color: var(--lf-accent);
}

.alert__title {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
}

.alert__body {
  margin: 3px 0 0;
  line-height: 1.65;
  color: var(--lf-muted);
}

.alert--warning {
  border-color: var(--lf-warning);
  background: var(--lf-warning-soft);
}

.alert--warning .alert__mark,
.alert--warning .alert__title {
  color: var(--lf-warning);
}

.stepper {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--lf-border);
  border-radius: var(--lf-r-sm);
  background: var(--lf-surface);
  overflow: hidden;
}

.stepper button {
  width: 26px;
  height: 26px;
  border: 0;
  background: transparent;
  color: var(--lf-muted);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}

.stepper button:hover {
  background: var(--lf-surface-2);
  color: var(--lf-accent);
}

.stepper input {
  width: 48px;
  height: 26px;
  border: 0;
  border-left: 1px solid var(--lf-border);
  border-right: 1px solid var(--lf-border);
  background: transparent;
  color: var(--lf-fg);
  font: inherit;
  font-size: 12.5px;
  text-align: center;
}

.stepper input:focus {
  outline: none;
  background: var(--lf-accent-soft);
}

.check {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 12.5px;
  cursor: pointer;
  color: var(--lf-fg);
}

.check__box {
  flex: 0 0 auto;
  width: 17px;
  height: 17px;
  margin-top: 2px;
  border: 1.5px solid var(--lf-border-strong);
  border-radius: 5px;
  background: var(--lf-surface);
  color: #fff;
  display: grid;
  place-items: center;
  transition: background 0.16s ease, border-color 0.16s ease;
}

.check__box[data-checked='true'] {
  background: var(--lf-accent);
  border-color: var(--lf-accent);
}

/* The notice is pinned dead centre in the window: a strip at the bottom gets squeezed between the composer and the
   message body, and you cannot tell which reply it is answering. The whole layer is pointer-events: none, so pressing
   down on the content never blocks a click. */
.toast-layer {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 120;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  pointer-events: none;
}

.toast {
  /* `svg { display: block }` is global, so the mark needs a flex row to sit beside the text. */
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 18px;
  border-radius: 10px;
  background: var(--lf-fg);
  color: var(--lf-bg);
  box-shadow: var(--lf-shadow);
  font-size: 13px;
  max-width: 90vw;
  animation: lf-pop 0.18s ease;
}

.toast__mark {
  flex: 0 0 auto;
}

@media (max-width: 820px) {
  .seg .lbl {
    display: none;
  }
}

/* ===== Prototype layout shells ===== */
/* One centered column for every non-chat screen, so the header and the composer stay aligned. */
.view-wrap {
  width: 100%;
  max-width: 760px;
  margin: 0 auto;
  padding: 24px 20px 32px;
}

.view-wrap--narrow {
  max-width: 640px;
}

.view-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 18px;
}

.view-head h2 {
  margin: 0;
  font-size: 20px;
  font-weight: 700;
}

.view-empty {
  padding: 46px 20px;
  text-align: center;
  font-size: 13.5px;
  color: var(--lf-faint);
}

/* An empty list gets a block of its own rather than a bare sentence: a solid badge to give it weight,
   a title, and one line saying what fills it. No border — the panel already has edges. */
.empty-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 10px;
  padding: 22px 14px;
  text-align: center;
}

.empty-card__badge {
  width: 38px;
  height: 38px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 10px;
  border-radius: 50%;
  background: var(--lf-accent);
  color: #fff;
}

.empty-card__title {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--lf-muted);
}

.empty-card__hint {
  margin: 4px 0 0;
  font-size: 11.5px;
  line-height: 1.6;
  color: var(--lf-faint);
}

/* ===== Modal primitives ===== */
.modal__sub {
  margin: -6px 0 14px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--lf-faint);
}

.m-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.m-field .label {
  margin: 0;
}

.m-legal {
  padding: 12px;
  border: 1px solid var(--lf-border);
  border-radius: 10px;
  background: var(--lf-surface-2);
  font-size: 12px;
  line-height: 1.7;
  color: var(--lf-muted);
}

.m-btns .btn {
  min-height: 38px;
  padding: 0 16px;
  border-radius: 10px;
}

/* ===== Search results ===== */
.sr-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.sr-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px;
  border: 1px solid var(--lf-border);
  border-radius: 11px;
  background: var(--lf-surface);
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.sr-item:hover {
  border-color: var(--lf-accent);
}

.sr-item--sel {
  border-color: var(--lf-accent);
  background: var(--lf-accent-soft);
}

.sr-item input {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  accent-color: var(--lf-accent);
}

.sr-info {
  flex: 1 1 auto;
  min-width: 0;
}

.sr-title {
  font-size: 13px;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.sr-meta {
  margin-top: 2px;
  font-size: 12px;
  color: var(--lf-faint);
}

.sr-format {
  flex: 0 0 auto;
  padding: 2px 6px;
  border-radius: 6px;
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
  font-size: 10px;
}

/* ===== Ingestion progress ===== */
.prog-name {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
  font-size: 14px;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.prog-name svg {
  flex: 0 0 auto;
  color: var(--lf-accent);
}

.prog-bar {
  height: 6px;
  margin: 12px 0 6px;
  border-radius: 6px;
  background: var(--lf-surface-2);
  overflow: hidden;
}

.prog-bar i {
  display: block;
  height: 100%;
  border-radius: 6px;
  background: var(--lf-accent);
  transition: width 0.3s ease;
}

.prog-dot--done {
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
}

/* ===== Similar titles ===== */
.rec-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.rec-item {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px;
  border: 1px solid var(--lf-border);
  border-radius: var(--lf-r-md);
  background: var(--lf-surface);
}

.rec-num {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--lf-surface-2);
  color: var(--lf-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
}

.rec-info {
  flex: 1 1 auto;
  min-width: 0;
}

.rec-name {
  font-size: 14px;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.rec-author {
  margin-top: 2px;
  font-size: 12px;
  color: var(--lf-faint);
}

.rec-desc {
  margin-top: 3px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--lf-muted);
}

.dl-btn--done,
.dl-btn--done:disabled {
  border-color: var(--lf-success);
  background: var(--lf-success);
  color: #fff;
  cursor: default;
}

/* ===== Plus popover ===== */
.pp-title {
  margin: 0 0 10px;
  font-size: 14px;
  font-weight: 600;
}

.pp-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.pp-row + .pp-row {
  margin-top: 12px;
}

.pp-rowlabel {
  padding: 0 2px;
  font-size: 12px;
  font-weight: 600;
  color: var(--lf-faint);
}

.search-row {
  display: flex;
  gap: 8px;
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--lf-border);
}

.search-row .field {
  min-height: 40px;
  border-radius: 10px;
}

.search-row .btn {
  min-height: 40px;
  padding: 0 14px;
  border-radius: 10px;
}

/* ===== Markdown editor ===== */
/* Look only. How tall this box is belongs to the page that owns it (the skill editor makes it fill the
   body beside the preview), so no height lives here. */
.md-editor {
  width: 100%;
  padding: 16px;
  border: 1px solid var(--lf-border);
  border-radius: var(--lf-r-md);
  background: var(--lf-surface);
  color: var(--lf-fg);
  font-family: 'JetBrains Mono', 'Cascadia Code', Consolas, monospace;
  font-size: 13px;
  line-height: 1.7;
}

.md-editor:focus {
  outline: none;
  border-color: var(--lf-accent);
}

/* Typography only: this class serves both the skill editor's preview and the answer bubbles in a conversation, so the
   outer frame (padding / border / background) is drawn by whichever page uses it — painted here it would become a box
   inside a box in the chat. */
.md-preview {
  font-size: 14px;
  line-height: 1.8;
  overflow-wrap: anywhere;
}

.md-preview h1 {
  margin: 0 0 10px;
  font-size: 20px;
}

.md-preview h2 {
  margin: 14px 0 6px;
  font-size: 16px;
}

.md-preview h3 {
  margin: 12px 0 6px;
  font-size: 15px;
}

/* A generated skill.md goes deeper than three levels, and the UA default sizes there are barely
   above the body text — so they get an explicit step instead of reading as a paragraph. */
.md-preview h4,
.md-preview h5,
.md-preview h6 {
  margin: 10px 0 4px;
  font-size: 14px;
}

.md-preview p {
  margin: 6px 0;
}

.md-preview ul,
.md-preview ol {
  margin: 6px 0;
  padding-left: 20px;
}

.md-preview li + li {
  margin-top: 3px;
}

.md-preview strong {
  color: var(--lf-accent);
}

.md-preview code {
  padding: 1px 6px;
  border-radius: 6px;
  background: var(--lf-surface-2);
  font-family: 'JetBrains Mono', 'Cascadia Code', Consolas, monospace;
  font-size: 12.5px;
}

/* A fenced block already paints its own plate; without this the chip style wraps the whole sample. */
.md-preview pre {
  margin: 8px 0;
  padding: 10px 12px;
  border-radius: var(--lf-r-sm);
  background: var(--lf-surface-2);
  overflow-x: auto;
}

.md-preview pre code {
  padding: 0;
  border-radius: 0;
  background: transparent;
}

.md-preview a {
  color: var(--lf-accent);
}

.md-preview blockquote {
  margin: 8px 0;
  padding: 2px 0 2px 12px;
  border-left: 3px solid var(--lf-border-strong);
  color: var(--lf-muted);
}

.md-preview hr {
  margin: 14px 0;
  border: 0;
  border-top: 1px solid var(--lf-border);
}

.md-preview table {
  border-collapse: collapse;
}

.md-preview th,
.md-preview td {
  border: 1px solid var(--lf-border);
  padding: 6px 10px;
}

/* ===== Settings rows ===== */
.setting-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 14px 0;
  border-bottom: 1px solid var(--lf-border);
}

.setting-row .k {
  font-weight: 500;
}

.setting-row .v {
  font-size: 13px;
  color: var(--lf-faint);
  text-align: right;
}

.setting-row .v b {
  color: var(--lf-accent);
}

/* A state readout, deliberately not text-coloured like the link beside it: 已开启 and 设置 sitting in
   the same accent blue on one line reads as one thing you can click. The dot carries the colour so the
   word itself stays in the normal ink, and the tinted ground is what says on / off. */
.state-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: var(--lf-r-pill);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
}

.state-pill::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

.state-pill--on {
  background: var(--lf-success-soft);
  color: var(--lf-success);
}

.state-pill--off {
  background: var(--lf-sunk);
  color: var(--lf-muted);
}

/* ===== Landing section headings ===== */
/* Shared by the four blocks under `views/LandingView.vue`; each one is a title and one line under it. */
.sec-title {
  margin: 0;
  /* Scales with the window up to 48px: a 32px heading over a 1720px column reads as a caption. */
  font-size: clamp(24px, 2.1vw, 48px);
  line-height: 1.22;
  font-weight: 700;
  letter-spacing: -0.028em;
  /* The wipe: the composable clips this box open from the left, so it needs its own paint layer. */
  will-change: clip-path;
}

.sec-lead {
  margin: 12px 0 0;
  max-width: 62ch;
  font-size: clamp(14.5px, 0.9vw, 18px);
  line-height: 1.85;
  color: var(--lf-muted);
}

.topbar[data-v-2e652c54] {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  /* Same height as the sidebar's brand row (`--lf-head-h`): the two bottom rules must line up. */
  min-height: var(--lf-head-h);
  padding: 0 20px;
  border-bottom: 1px solid var(--lf-border);
  /* Same token as `.sidebar` / `.titlebar` (App.vue): one colour for the whole chrome column. */
  background: var(--lf-sidebar);
}
.topbar__left[data-v-2e652c54],
.topbar__right[data-v-2e652c54] {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.topbar__right[data-v-2e652c54] {
  flex: 0 0 auto;
}
.top-tagline[data-v-2e652c54] {
  flex: 0 1 auto;
  min-width: 0;
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--lf-faint);
}
@media (max-width: 900px) {
.top-tagline[data-v-2e652c54] {
    display: none;
}
}
@media (max-width: 820px) {
.topbar__right .btn .lbl[data-v-2e652c54] {
    display: none;
}
}

.dd[data-v-ab962b3d] {
  position: absolute;
  top: var(--dd-top, 28px);
  right: var(--dd-right, 6px);
  z-index: 30;
  /* Menu labels are fixed strings, never user data: a long one widens the panel instead of wrapping. */
  width: max-content;
  min-width: 148px;
  padding: 6px;
  border: 1px solid var(--lf-border);
  border-radius: 10px;
  background: var(--lf-surface);
  box-shadow: var(--lf-shadow);
}

/* Anchored to the left edge instead: the title bar's 查看 menu hangs under its own button. */
.dd--left[data-v-ab962b3d] {
  right: auto;
  left: var(--dd-left, 10px);
}
.dd button[data-v-ab962b3d] {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 9px 10px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--lf-fg);
  font: inherit;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}
.dd button[data-v-ab962b3d]:hover {
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
}
.dd button span[data-v-ab962b3d] {
  white-space: nowrap;
}
.dd__key[data-v-ab962b3d] {
  margin-left: auto;
  padding-left: 18px;
  color: var(--lf-faint);
  font-size: 12px;
  white-space: nowrap;
}
.dd .dd--danger[data-v-ab962b3d] {
  color: var(--lf-danger);
}
.dd .dd--danger[data-v-ab962b3d]:hover {
  background: var(--lf-danger);
  color: #fff;
}

.sb-row[data-v-7a531e8f] {
  position: relative;
}
.chat-item[data-v-7a531e8f] {
  /* Full panel width like the nav items above: the action button floats over the row's right end
     instead of eating 26px out of the label, which is what made the selected pill look short.
     Flex so the checkbox added by 多选 sits on the label's shoulder instead of above it. */
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 34px 8px calc(10px + var(--row-indent, 0px));
  border: 0;
  border-radius: var(--lf-r-sm);
  background: transparent;
  color: var(--lf-muted);
  font: inherit;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}

/* The label is the only thing that truncates, so it needs its own min-width:0 — a flex item
   defaults to max-content and would push the box out of the panel instead of ending in ellipsis. */
.chat-item__text[data-v-7a531e8f] {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chat-item[data-v-7a531e8f]:hover {
  background: var(--lf-surface-2);
  color: var(--lf-fg);
}
.chat-item--active[data-v-7a531e8f] {
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
  font-weight: 600;
}

/* No dots to dodge in 多选, so the row gets its right padding back. */
.chat-item--pick[data-v-7a531e8f] {
  padding-right: 10px;
}
.chat-item--picked[data-v-7a531e8f] {
  background: var(--lf-surface-2);
  color: var(--lf-fg);
}
.pick-box[data-v-7a531e8f] {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  border: 1.5px solid var(--lf-border-strong);
  border-radius: 5px;
  background: var(--lf-surface);
  color: #fff;
  display: grid;
  place-items: center;
  transition: background 0.16s ease, border-color 0.16s ease;
}
.pick-box[data-checked='true'][data-v-7a531e8f] {
  background: var(--lf-accent);
  border-color: var(--lf-accent);
}
.row-dots[data-v-7a531e8f] {
  position: absolute;
  right: 3px;
  top: 50%;
  transform: translateY(-50%);
  width: 26px;
  height: 26px;
  border: 0;
  border-radius: var(--lf-r-sm);
  background: transparent;
  color: var(--lf-faint);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s ease;
}

/* Hover is the only reveal. Clicking a row leaves focus on its label, and a row-wide
 * `:focus-within` used to pin the dots open on whatever is on screen. */
.sb-row:hover .row-dots[data-v-7a531e8f],
.row-dots[data-v-7a531e8f]:focus {
  opacity: 1;
}
.row-dots svg[data-v-7a531e8f] {
  transform: rotate(90deg);
}
.row-dots[data-v-7a531e8f]:hover {
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
}

/* No hover on a narrow window: the action button has to be visible, not discovered. */
@media (max-width: 820px) {
.row-dots[data-v-7a531e8f] {
    opacity: 1;
}
}

.folder-more[data-v-a5403a2e] {
  position: absolute;
  right: 2px;
  top: 50%;
  transform: translateY(-50%);
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: var(--lf-r-sm);
  background: transparent;
  color: var(--lf-faint);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s ease;
}

/* Shares a project row's .folder-line hover: the pointer has to reach that row before these dots show themselves. */
.folder-line:hover .folder-more[data-v-a5403a2e],
.folder-more[data-v-a5403a2e]:focus {
  opacity: 1;
}
.folder-more[data-v-a5403a2e]:hover,
.folder-more[aria-expanded='true'][data-v-a5403a2e] {
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
}
.folder-more[data-v-a5403a2e] svg {
  transform: rotate(90deg);
}

/* A narrow window has no hover: the menu entry has to be visible, not discovered. */
@media (max-width: 820px) {
.folder-more[data-v-a5403a2e] {
    opacity: 1;
}
}

.modal__x[data-v-18343df6] {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--lf-r-sm);
  background: transparent;
  color: var(--lf-faint);
  cursor: pointer;
}
.modal__x[data-v-18343df6]:hover {
  background: var(--lf-surface-2);
  color: var(--lf-fg);
}
.modal__title[data-v-18343df6] {
  margin: 0 0 6px;
  padding-right: 30px;
  font-size: 16px;
  font-weight: 700;
}
.modal__body[data-v-18343df6] {
  gap: 14px;
}

.sb[data-v-0933eb39] {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.sb-nav[data-v-0933eb39] {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px;
  border-bottom: 1px solid var(--lf-border);
}

/* The action button is not a nav entry, so it keeps its own air above the list. */
.sb-nav .nav-item--primary[data-v-0933eb39] {
  margin-bottom: 8px;
}
.sb-body[data-v-0933eb39] {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 0 10px 10px;
}

/* The 最近对话 heading and its 多选 toggle share one line: the label keeps its own left padding,
   so the toggle only has to line up with the right edge of the rows below it. */
.sb-list-head[data-v-0933eb39] {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: 12px 0 4px;
}
.sb-list-head .sb-label[data-v-0933eb39] {
  margin: 0;
}

/* Same shape as the gear at the bottom of this panel: a flat icon button, no border, and the
   accent-soft face is what says "this one is on". */
.sb-mode[data-v-0933eb39] {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  margin-right: 2px;
  padding: 0;
  border: 0;
  border-radius: var(--lf-r-sm);
  background: transparent;
  color: var(--lf-faint);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.16s ease, color 0.16s ease;
}
.sb-mode[data-v-0933eb39]:hover {
  background: var(--lf-surface-2);
  color: var(--lf-fg);
}
.sb-mode--on[data-v-0933eb39] {
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
}
.sb-mode--on[data-v-0933eb39]:hover {
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
}
.sb-folder[data-v-0933eb39] {
  margin-bottom: 2px;
}

/* The drop target on the drag-out side: while a row is being dragged over it the whole block gets a dashed outline, gone as soon as the button is released. */
.sb-loose--over[data-v-0933eb39] {
  border-radius: var(--lf-r-sm);
  outline: 1.5px dashed var(--lf-accent);
  outline-offset: 2px;
  background: var(--lf-accent-soft);
}

/* Project row: caret first (so folded or not is readable), the name is the click to fold, the row count sits on the right. */
.folder-row[data-v-0933eb39] {
  display: flex;
  align-items: center;
  gap: 7px;
  width: 100%;
  padding: 7px 34px 7px 2px;
  border: 1px solid transparent;
  border-radius: var(--lf-r-sm);
  background: transparent;
  color: var(--lf-fg);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.folder-row[data-v-0933eb39]:hover {
  background: var(--lf-surface-2);
}

/* Only the one actually being dragged over gets an outline: if the whole column lit up nobody could see where the drop would land. */
.sb-folder--over .folder-row[data-v-0933eb39] {
  border-color: var(--lf-accent);
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
}
.folder-caret[data-v-0933eb39] {
  flex: 0 0 auto;
  color: var(--lf-faint);
  transition: transform 0.16s ease;
}
.folder-caret--open[data-v-0933eb39] {
  transform: rotate(90deg);
}
.folder-name[data-v-0933eb39] {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* The project-name line is a div: the whole row is the fold button and the three-dot menu entry sits next to it (a button cannot nest a button).
   That button's styling and its hover reveal both live in ProjectMenu.vue; the row keeps 34px at its right end for it. */
.folder-line[data-v-0933eb39] {
  position: relative;
}

/* The create / rename row: the input eats the remaining width, the check button hugs the right edge. */
.sb-edit[data-v-0933eb39] {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 2px 0 4px;
}
.sb-edit__input[data-v-0933eb39] {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 30px;
  padding: 4px 8px;
  font-size: 12.5px;
}
.sb-hint[data-v-0933eb39] {
  margin: 0 10px 6px;
  font-size: 11.5px;
  color: var(--lf-faint);
}

/* The empty state when there is not a single project: the hint plus that 「新建项目」 button. Once projects exist this button moves back to the right of the heading row. */
.sb-empty-projects[data-v-0933eb39] {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 6px 10px 10px;
  text-align: center;
}
.sb-empty-projects .sb-hint[data-v-0933eb39] {
  margin: 0;
}

/* Name clashes, name empty — that sentence is written by the backend per the `Lang` header and lands as-is under the input. */
.sb-error[data-v-0933eb39] {
  margin: 0 10px 6px;
  font-size: 11.5px;
  color: var(--lf-danger);
}

/* Pagination is server-side, so this is one "another page" line rather than a number. */
.sb-more[data-v-0933eb39] {
  display: block;
  width: 100%;
  margin-top: 2px;
  padding: 7px 10px;
  border: 0;
  border-radius: var(--lf-r-sm);
  background: transparent;
  color: var(--lf-accent);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

/* The "show more" inside a project is indented along with the conversation rows, otherwise it reads as a button for the whole list. */
.sb-more--in[data-v-0933eb39] {
  padding-left: 30px;
}
.sb-more[data-v-0933eb39]:hover {
  background: var(--lf-accent-soft);
}

/* Batch bar: pinned under the scrolling list, so it stays reachable however long the list gets.
   The tinted face keeps it reading as a tray for the list above, not as another account row. */
.sb-batch[data-v-0933eb39] {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border-top: 1px solid var(--lf-border);
  background: var(--lf-surface-2);
}
.sb-batch__all[data-v-0933eb39] {
  flex: 0 0 auto;
  padding: 3px 7px;
  border: 0;
  border-radius: var(--lf-r-sm);
  background: transparent;
  color: var(--lf-accent);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.sb-batch__all[data-v-0933eb39]:hover {
  background: var(--lf-accent-soft);
}
.sb-batch__count[data-v-0933eb39] {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: var(--lf-faint);
  font-size: 11.5px;
}

/* The count disappears with an empty selection, so the button holds the right edge by itself. */
.sb-batch .btn[data-v-0933eb39] {
  margin-left: auto;
}
.sb-foot[data-v-0933eb39] {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 14px 12px;
  border-top: 1px solid var(--lf-border);
  font-size: 12px;
  color: var(--lf-faint);
}

/* The account line and the 设置 button share the bottom row. */
.sb-acct[data-v-0933eb39] {
  display: flex;
  align-items: center;
  gap: 6px;
}
.acct[data-v-0933eb39] {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 6px 8px;
  border: 0;
  border-radius: var(--lf-r-sm);
  background: transparent;
  color: var(--lf-muted);
  font: inherit;
  font-size: 12px;
  text-align: left;
  cursor: pointer;
}
.acct[data-v-0933eb39]:hover {
  background: var(--lf-surface-2);
  color: var(--lf-fg);
}
.acct-gear[data-v-0933eb39] {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--lf-r-sm);
  background: transparent;
  color: var(--lf-muted);
  cursor: pointer;
}
.acct-gear[data-v-0933eb39]:hover {
  background: var(--lf-surface-2);
  color: var(--lf-fg);
}
.acct-gear--on[data-v-0933eb39] {
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
}
.acct-signin[data-v-0933eb39] {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 34px;
  padding: 0 12px;
  border: 0;
  border-radius: var(--lf-r-sm);
  background: var(--lf-accent);
  color: #fff;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.acct-signin[data-v-0933eb39]:hover {
  filter: brightness(1.08);
}
.acct-mail[data-v-0933eb39] {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.sb-plan[data-v-0933eb39] {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 8px;
}
.lim[data-v-0933eb39] {
  padding: 3px 8px;
  border-radius: var(--lf-r-pill);
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
  font-size: 11px;
  font-weight: 600;
}

.current-mail[data-v-769d548c] {
  margin: 0 0 12px;
  padding: 8px 10px;
  border-radius: var(--lf-r-sm);
  background: var(--lf-surface-2);
  color: var(--lf-muted);
  font-size: 12px;
  overflow-wrap: anywhere;
}
.legal-title[data-v-769d548c] {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0;
  font-weight: 600;
  color: var(--lf-warning);
}
.legal-title svg[data-v-769d548c] {
  flex: 0 0 auto;
  margin-top: 2px;
}
.check[data-v-769d548c] {
  position: relative;
}
.check__native[data-v-769d548c] {
  position: absolute;
  opacity: 0;
  width: 17px;
  height: 17px;
  margin: 0;
  cursor: pointer;
}
.check__native:focus-visible + .check__box[data-v-769d548c] {
  outline: 2px solid var(--lf-accent);
  outline-offset: 2px;
}

/* `.modal-mask` already paints the translucent backdrop and centers the card; this only adds
   scrolling so the form stays reachable on a short window. */
.login-mask[data-v-1d673a2d] {
  overflow-y: auto;
}
.login__card[data-v-1d673a2d] {
  position: relative;
  width: 100%;
  max-width: 380px;
  padding: 26px 26px 22px;
  border: 1px solid var(--lf-border);
  border-radius: var(--lf-r-lg);
  background: var(--lf-surface);
  box-shadow: var(--lf-shadow);
}
.login__close[data-v-1d673a2d] {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--lf-muted);
  cursor: pointer;
}
.login__close[data-v-1d673a2d]:hover {
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
}
.login__head[data-v-1d673a2d] {
  text-align: center;
  margin-bottom: 18px;
}

/* Both themes keep the chip's space so the card lays out identically; dark mode only paints the
   ground the navy mark needs. `content-box` keeps the mark at its full 96px. */
.login__logo[data-v-1d673a2d] {
  box-sizing: content-box;
  width: 96px;
  height: auto;
  padding: 10px 14px;
  border-radius: 18px;
}
html.dark .login__logo[data-v-1d673a2d] {
  background: #eef0f8;
}
.login__head h1[data-v-1d673a2d] {
  margin: 8px 0 2px;
  font-size: 20px;
  font-weight: 700;
}
.login__head p[data-v-1d673a2d] {
  margin: 0;
  font-size: 12.5px;
  color: var(--lf-faint);
}

/* Folder tabs, taken from bwgzt/uweb/front-v3 `src/views/login.vue` `.tab-list`: the selected tab
 * takes the panel colour and the grey band curves into it. Divergences, each marked below: colours
 * are LexForge tokens instead of the reference's #fff / #eee / #0170FE so dark mode works, the
 * state hook is `aria-selected` instead of `.tab-selected` / `.not-selected`, the sizes are scaled
 * to this 380px card, and the joint is a radial-gradient concave fillet — the reference's joint is
 * a full-height skewX slab that only reads as a curve because the FOUR neighbouring tabs paint
 * their own grey slabs over it (z-index: 1); with two tabs the same slab renders as a bare
 * diagonal, so the fillet below does that job self-contained. */
.login__tabs[data-v-1d673a2d] {
  --tab-h: 40px;
  --tab-curve: var(--lf-r-md);
  --tab-active: var(--lf-surface);
  --tab-band: var(--lf-sunk);
  display: flex;
  position: relative;
  z-index: 2;
  margin-bottom: 16px;
  border-radius: var(--lf-r-sm) var(--lf-r-sm) 0 0;
  background-color: var(--tab-band);
  overflow: hidden;
}
.login__tabs button[data-v-1d673a2d] {
  flex: 1;
  height: var(--tab-h);
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  padding: 0;
  border: 0;
  background: transparent;
  /* The faint end of the scale on purpose: `--lf-muted` is lighter than the accent, so on the dark
     band the unselected label out-shouts the selected one and nobody can tell which tab is on. */
  color: var(--lf-faint);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.6;
  cursor: pointer;
  user-select: none;
}
.login__tabs button[data-v-1d673a2d]:not([aria-selected='true']):hover {
  color: var(--lf-fg);
}
.login__tabs button[aria-selected='true'][data-v-1d673a2d] {
  background: var(--tab-active);
  border-radius: var(--tab-curve) var(--tab-curve) 0 0;
  color: var(--lf-accent);
  font-weight: 700;
  /* The tab fill is the panel colour, so the selected sheet has no edge of its own — this bar is
     what says "this one". */
  box-shadow: inset 0 3px 0 var(--lf-accent);
}

/* The band clips at 9px but the tab rounds at 12, so an edge tab's outer top corner leaks a grey
   crescent between the two curves. Squaring it lets the panel colour fill the band's own corner. */
.login__tabs button:first-child[aria-selected='true'][data-v-1d673a2d] {
  border-top-left-radius: 0;
}
.login__tabs button:last-child[aria-selected='true'][data-v-1d673a2d] {
  border-top-right-radius: 0;
}

/* 12×12 concave fillets at the base of the selected tab: the gradient keeps the band colour inside
 * a quarter circle anchored at the outer top corner and lets the panel colour flare into the
 * bottom, so the tab base widens into the band the way the reference's joint reads. */
.login__tabs button[aria-selected='true'][data-v-1d673a2d]::before {
  content: '';
  position: absolute;
  left: calc(-1 * var(--tab-curve));
  bottom: 0;
  width: var(--tab-curve);
  height: var(--tab-curve);
  background: radial-gradient(
    circle var(--tab-curve) at 0 0,
    var(--tab-band) calc(var(--tab-curve) - 0.5px),
    var(--tab-active) var(--tab-curve)
  );
}
.login__tabs button[aria-selected='true'][data-v-1d673a2d]::after {
  content: '';
  position: absolute;
  right: calc(-1 * var(--tab-curve));
  bottom: 0;
  width: var(--tab-curve);
  height: var(--tab-curve);
  background: radial-gradient(
    circle var(--tab-curve) at 100% 0,
    var(--tab-band) calc(var(--tab-curve) - 0.5px),
    var(--tab-active) var(--tab-curve)
  );
}
.login__form[data-v-1d673a2d] {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* Google-style outlined text field, taken from ai-agent/demo `src/views/login/LoginPage.vue`
 * `.field-box` / `.field-label` / `.field-input`: a leading icon, a borderless input, and the label
 * absolutely positioned inside the box so it floats up onto the border and cuts a notch in it
 * (`background: var(--lf-surface)` = the card colour) once the field is focused or holds text. The
 * placeholder stays transparent until focus, because the resting label already sits in its place.
 * Divergences, each marked below: plain CSS instead of SCSS nesting (this project has no preprocessor)
 * with LexForge tokens instead of the demo's `--okNN` oklch set, `html.dark` instead of
 * `[data-theme="dark"]`, and the floated label takes the accent colour only while focused, which is
 * what Google does and the demo flattens to one colour. */
.login__box[data-v-1d673a2d] {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 46px;
  padding: 0 12px;
  border: 1px solid var(--lf-border-strong);
  border-radius: var(--lf-r-md);
  /* Transparent fill: the floated label must paint in the card colour to break the border. */
  background: transparent;
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
}
.login__box[data-v-1d673a2d]:hover {
  border-color: var(--lf-muted);
}
.login__box[data-v-1d673a2d]:focus-within {
  border-color: var(--lf-accent);
  box-shadow: 0 0 0 4px var(--lf-accent-soft);
}
.login__label[data-v-1d673a2d] {
  position: absolute;
  /* 12px box padding + 18px icon + 8px gap, minus the label's own 4px padding, so the text lines up
     with where the input's text starts. */
  left: 34px;
  top: 50%;
  padding: 0 4px;
  transform: translateY(-50%) scale(1);
  transform-origin: left center;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--lf-muted);
  background: transparent;
  pointer-events: none;
  transition: transform 0.16s ease, top 0.16s ease, color 0.16s ease, background-color 0.16s ease;
}
.login__box:has(.login__input:not(:placeholder-shown)) .login__label[data-v-1d673a2d] {
  top: 0;
  transform: translateY(-50%) scale(0.8);
  color: var(--lf-muted);
  background: var(--lf-surface);
}
.login__box:focus-within .login__label[data-v-1d673a2d] {
  top: 0;
  transform: translateY(-50%) scale(0.8);
  color: var(--lf-accent);
  background: var(--lf-surface);
}
.login__icon[data-v-1d673a2d] {
  flex-shrink: 0;
  color: var(--lf-faint);
  transition: color 0.16s ease;
}
.login__box:focus-within .login__icon[data-v-1d673a2d] {
  color: var(--lf-accent);
}
.login__input[data-v-1d673a2d] {
  flex: 1;
  /* Without this the input is a flex item with min-width:auto and long values push the box open. */
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--lf-fg);
  font: inherit;
  font-size: 13.5px;
}
.login__input[data-v-1d673a2d]:focus {
  outline: none;
}
.login__input[data-v-1d673a2d]::placeholder {
  color: transparent;
  transition: color 0.16s ease;
}
.login__box:focus-within .login__input[data-v-1d673a2d]::placeholder {
  color: var(--lf-faint);
}
.login__reveal[data-v-1d673a2d] {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  padding: 2px;
  border: 0;
  background: none;
  color: var(--lf-faint);
  cursor: pointer;
  transition: color 0.16s ease;
}
.login__reveal[data-v-1d673a2d]:hover,
.login__reveal[aria-pressed='true'][data-v-1d673a2d] {
  color: var(--lf-accent);
}

/* The hook every in-box notice and the rule card hangs off. */
.login__field[data-v-1d673a2d] {
  position: relative;
}

/* One line, inside the box, right-aligned: the box already has a right edge, so the verdict lives
   there rather than under the field where it would shove the rest of the form down. */
.login__state[data-v-1d673a2d] {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  gap: 3px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--lf-faint);
}
.login__state--bad[data-v-1d673a2d] {
  color: var(--lf-danger);
}

/* Focus-gated, and hung outside the card on the empty side rather than over the next field: the
   window is wide, and a card that parks on top of 确认密码 / 邮箱 reads as a bug even when it does not
   move anything. `+ 42px` is the card's own 26px padding plus a 16px gap off its right edge, and the
   caret points back at the box the hint belongs to. Still no flow impact — five rows appearing and
   disappearing would shove the rest of the form down on every focus change. */
/* One card shape, two contents: the five password rules, and the one-line complaint that the two boxes
   do not match. Both hang to the right of the box they belong to, so neither pushes a field down the
   way an in-flow hint would. */
.login__rules[data-v-1d673a2d],
.login__aside[data-v-1d673a2d] {
  position: absolute;
  z-index: 3;
  top: 0;
  left: calc(100% + 42px);
  width: 190px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 0;
  padding: 9px 12px;
  list-style: none;
  border: 1px solid var(--lf-border);
  border-radius: var(--lf-r-md);
  background: var(--lf-surface);
  box-shadow: var(--lf-shadow);
  font-size: 11.5px;
  line-height: 1.4;
  /* It is a hint, not a control: clicks have to fall through to whatever is underneath. */
  pointer-events: none;
}

/* The diamond that ties the card back to the password box: two of its four edges carry the border,
   so after the 45° turn it reads as a left-pointing tip with the card's outline running through it. */
.login__rules[data-v-1d673a2d]::before,
.login__aside[data-v-1d673a2d]::before {
  content: '';
  position: absolute;
  top: 16px;
  left: -5px;
  width: 8px;
  height: 8px;
  background: var(--lf-surface);
  border-left: 1px solid var(--lf-border);
  border-bottom: 1px solid var(--lf-border);
  transform: rotate(45deg);
}

/* Not enough room beside the card any more (the card is centered, so it needs its own half plus the
   panel's 232px on the other side): fall back to overlaying it under the field, where it can only
   cover what the user is not looking at. */
@media (max-width: 900px) {
.login__rules[data-v-1d673a2d],
  .login__aside[data-v-1d673a2d] {
    top: calc(100% + 6px);
    left: 0;
    width: auto;
    right: 0;
}
.login__rules[data-v-1d673a2d]::before,
  .login__aside[data-v-1d673a2d]::before {
    display: none;
}
}

/* The complaint is the only thing in this card, so it wears the danger colour and nothing else. */
.login__aside[data-v-1d673a2d] {
  color: var(--lf-danger);
  font-weight: 600;
}
.login__rules li[data-v-1d673a2d] {
  display: flex;
  align-items: center;
  gap: 6px;
}
.login__rules .is-ok[data-v-1d673a2d] {
  color: var(--lf-accent);
}
.login__rules .is-wait[data-v-1d673a2d] {
  color: var(--lf-faint);
}

/* The unmet rows keep their line: what is still missing is the point of the card. */
.login__dot[data-v-1d673a2d] {
  width: 11px;
  height: 11px;
  display: inline-flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--lf-border-strong);
  border-radius: 50%;
}

/* The send button is part of the field it acts on, so it sits in the box instead of beside it. */
.login__send[data-v-1d673a2d] {
  flex-shrink: 0;
  padding: 5px 9px;
  border: 0;
  border-radius: var(--lf-r-pill);
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
  font: inherit;
  font-size: 11.5px;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
}
.login__send[data-v-1d673a2d]:hover:not(:disabled) {
  background: var(--lf-accent);
  color: #fff;
}
.login__send[data-v-1d673a2d]:disabled {
  background: var(--lf-sunk);
  color: var(--lf-faint);
  cursor: not-allowed;
}
.login__field--invalid .login__box[data-v-1d673a2d] {
  border-color: var(--lf-danger);
}
.login__field--invalid .login__box[data-v-1d673a2d]:focus-within {
  box-shadow: 0 0 0 4px rgba(229, 72, 77, 0.18);
}
html.dark .login__field--invalid .login__box[data-v-1d673a2d]:focus-within {
  box-shadow: 0 0 0 4px rgba(255, 99, 105, 0.22);
}
.login__field--invalid .login__label[data-v-1d673a2d] {
  color: var(--lf-danger);
}
.login__submit[data-v-1d673a2d] {
  width: 100%;
  min-height: 40px;
  margin-top: 2px;
}

/* The way out at the bottom right of the password box. Its width is only the text, right-aligned so it forms its own line and pushes no other field down. */
.login__forgot[data-v-1d673a2d] {
  display: block;
  width: fit-content;
  margin: 0 0 4px auto;
  padding: 2px 0;
  border: 0;
  background: transparent;
  color: var(--lf-accent);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.login__forgot[data-v-1d673a2d]:hover {
  text-decoration: underline;
}

/* The second stage / forgot-password has no tabs, so these two lines fill the title slot themselves. */
.login__panel-title[data-v-1d673a2d] {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  color: var(--lf-fg);
}
.login__panel-hint[data-v-1d673a2d] {
  margin: 0 0 6px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--lf-muted);
}

/* The attempts left reported by the server: a bit heavier than the hint text, because it is warning you to stop guessing. */
.login__attempts[data-v-1d673a2d] {
  margin: 0 0 6px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--lf-warning);
}

/* The sentence after sending is this cell's conclusion, not a hint: give it its own ground instead of letting it mix into a placeholder. */
.login__sent[data-v-1d673a2d] {
  margin: 0 0 4px;
  padding: 10px 12px;
  border-radius: var(--lf-r-sm);
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
  font-size: 13px;
  line-height: 1.5;
}
.login__undo[data-v-1d673a2d] {
  display: block;
  width: fit-content;
  margin: 8px auto 0;
  padding: 4px 8px;
  border: 0;
  background: transparent;
  color: var(--lf-muted);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.login__undo[data-v-1d673a2d]:hover {
  color: var(--lf-accent);
}

.sr-item[data-v-395203c4] {
  cursor: pointer;
}
.sr-title[data-v-395203c4],
.sr-meta[data-v-395203c4] {
  display: block;
}

/* Retrieval is a round trip, so the wait and its failure need the same weight as the list. */
.sr-state[data-v-395203c4] {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 22px 4px;
  font-size: 13px;
  color: var(--lf-muted);
}
.sr-state svg[data-v-395203c4] {
  color: var(--lf-warning);
}
.view-empty[data-v-395203c4] {
  padding: 20px 0;
}

.conflict-meta[data-v-1eb59e9e] {
  margin: 0;
  font-size: 12.5px;
  color: var(--lf-muted);
  overflow-wrap: anywhere;
}

.label[data-v-1d7c0ffc] {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}

/* The count is a ceiling marker, not a readout to read: it only earns attention near the limit. */
.counter[data-v-1d7c0ffc] {
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  color: var(--lf-faint);
}

.setting-row .k[data-v-4e0d5984] {
  flex: 0 0 auto;
}

/* A pill and a link on one line: inline boxes would sit their baselines apart and the pair would look
   like two rows that collided. */
.v-pair[data-v-4e0d5984] {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
}
.link[data-v-4e0d5984] {
  margin-left: 6px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--lf-accent);
  font: inherit;
  cursor: pointer;
}
.link--solo[data-v-4e0d5984] {
  margin-left: 0;
}
.mail[data-v-4e0d5984] {
  max-width: 200px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  vertical-align: bottom;
}

/* Taller than a row control and separated from the list, so it reads as its own action. */
.signout[data-v-4e0d5984] {
  min-height: 42px;
  margin-top: 18px;
}

/* This dialog is a list of rows, not a document: an inner scroll area cut 退出登录 off half a row
   deep, which is the worst place for the one control a user comes looking for. Let the card grow with
   its content and hand the overflow to the mask (same deal `LoginDialog` makes with `.login-mask`).
   Auto margins rather than `align-items: center`: a centered flex item taller than its container gets
   its top clipped off with no way to scroll back to it, `margin: auto` does not do that. */
.settings-modal[data-v-4e0d5984] {
  overflow-y: auto;
  align-items: flex-start;
}
.settings-modal[data-v-4e0d5984] .modal {
  margin: auto;
}
.settings-modal[data-v-4e0d5984] .modal__body {
  max-height: none;
  overflow: visible;
}

.sec-lead[data-v-c9f3d73d] {
  margin: 0;
  font-size: 13px;
  line-height: 1.55;
  color: var(--lf-fg);
}
.sec-hint[data-v-c9f3d73d] {
  margin: 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--lf-muted);
}

/* A "shown only once" kind of sentence needs more weight than body text, but red is not its turn yet — that is kept for failures. */
.sec-warn[data-v-c9f3d73d] {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  padding: 8px 10px;
  border-radius: var(--lf-r-sm);
  background: var(--lf-warning-soft);
  color: var(--lf-warning);
  font-size: 12px;
  line-height: 1.5;
}

/* Two columns, one entry per row: the layout copies the recovery-code way of one thing per line, so reading them needs no wrapping. */
.sec-codes[data-v-c9f3d73d] {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px 14px;
  margin: 0;
  padding: 12px 14px 12px 30px;
  border: 1px solid var(--lf-border);
  border-radius: var(--lf-r-sm);
  background: var(--lf-surface-2);
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  font-size: 13px;
  color: var(--lf-fg);
}
.sec-qr[data-v-c9f3d73d] {
  width: 176px;
  height: 176px;
  align-self: center;
  border-radius: var(--lf-r-sm);
  background: #fff;
}

/* The secret is there for a person to copy: selectable, and never wrapped down to half a character. */
.sec-secret[data-v-c9f3d73d] {
  margin: 0;
  padding: 8px 10px;
  border: 1px dashed var(--lf-border-strong);
  border-radius: var(--lf-r-sm);
  background: var(--lf-sunk);
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  font-size: 12.5px;
  color: var(--lf-fg);
  overflow-wrap: anywhere;
  user-select: all;
}
.sec-mail[data-v-c9f3d73d] {
  max-width: 240px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  vertical-align: bottom;
}
.sec-actions[data-v-c9f3d73d] {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 2px;
}
.sec-box[data-v-c9f3d73d] {
  position: relative;
}
.sec-box .field[data-v-c9f3d73d] {
  padding-right: 40px;
}
.sec-reveal[data-v-c9f3d73d] {
  position: absolute;
  top: 50%;
  right: 6px;
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--lf-r-sm);
  background: transparent;
  color: var(--lf-faint);
  cursor: pointer;
  transform: translateY(-50%);
}
.sec-reveal[data-v-c9f3d73d]:hover,
.sec-reveal[aria-pressed='true'][data-v-c9f3d73d] {
  color: var(--lf-accent);
}

.app[data-v-6cc95cc1] {
  /* The strip's height doubles as the Windows caption-button height (App.vue `.titlebar__wins`). */
  --titlebar-h: 40px;
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}

/* No strip in a browser tab, so the floating panel below starts at the very top of the page. */
.app--framed[data-v-6cc95cc1] {
  --titlebar-h: 0px;
}
.titlebar[data-v-6cc95cc1] {
  flex: 0 0 auto;
  /* The 查看 dropdown hangs out of this strip and over the sidebar, which paints later in the DOM;
   * 50 keeps the strip and its menu above that (40) and below a modal mask (90) and toasts (120). */
  position: relative;
  z-index: 50;
  height: var(--titlebar-h);
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 0 0 10px;
  border-bottom: 1px solid var(--lf-border);
  background: var(--lf-sidebar);
  -webkit-app-region: drag;
}
.titlebar button[data-v-6cc95cc1] {
  -webkit-app-region: no-drag;
}

/* Electron inherits the drag region down the DOM, and this scoped rule can only reach elements that
 * carry App.vue's own id — the dropdown belongs to AppMenu, so it needs `:deep()` to opt out, or the
 * window drag handler eats every click in it. */
.titlebar__menu[data-v-6cc95cc1] .dd,
.titlebar__menu[data-v-6cc95cc1] .dd button {
  -webkit-app-region: no-drag;
}
.titlebar__menu[data-v-6cc95cc1] {
  position: relative;
  display: flex;
  align-items: center;
}
.titlebar__menu-btn[data-v-6cc95cc1] {
  padding: 5px 10px;
  border: 0;
  border-radius: var(--lf-r-sm);
  background: transparent;
  color: var(--lf-muted);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.titlebar__menu-btn[data-v-6cc95cc1]:hover,
.titlebar__menu-btn[aria-expanded='true'][data-v-6cc95cc1] {
  background: var(--lf-accent-soft);
  color: var(--lf-accent);
}

/* Electron composites a drag region above normal content, so a `z-index` mask cannot cover the
 * strip while it is draggable. Every dialog renders a `.modal-mask`, so the drag region steps
 * aside for as long as one is in the tree — nobody drags a window that has a modal open. */
html:has(.modal-mask) .titlebar[data-v-6cc95cc1] {
  -webkit-app-region: no-drag;
}
.sb-head[data-v-6cc95cc1] {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: var(--lf-head-h);
  padding: 0 12px;
  border-bottom: 1px solid var(--lf-border);
}

/* The mark as one button: it inherits the row's look and only tints on hover, so it still reads as a
   brand rather than a second toolbar control. */
.sb-home[data-v-6cc95cc1] {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.sb-home:hover .brand[data-v-6cc95cc1] {
  color: var(--lf-accent);
}

/* An image inside a button starts a native drag on mousedown in Electron and the click never lands. */
.sb-home img[data-v-6cc95cc1] {
  -webkit-user-drag: none;
}
.sb-home[data-v-6cc95cc1] {
  user-select: none;
}
.sb-home[data-v-6cc95cc1]:focus-visible {
  outline: 2px solid var(--lf-accent);
  outline-offset: 2px;
  border-radius: var(--lf-r-sm);
}

/* The light ground the navy mark needs in dark mode is reserved in both themes, so the brand row
   measures the same either way; `content-box` keeps the mark at its full 30px. */
.logo[data-v-6cc95cc1] {
  box-sizing: content-box;
  flex: 0 0 auto;
  height: 30px;
  width: auto;
  display: block;
  padding: 3px 5px;
  border-radius: 8px;
}
html.dark .logo[data-v-6cc95cc1] {
  background: #eef0f8;
}
.brand[data-v-6cc95cc1] {
  font-size: 14px;
  font-weight: 700;
  line-height: 1.25;
  white-space: nowrap;
}
.brand small[data-v-6cc95cc1] {
  display: block;
  font-size: 10px;
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: 0.4px;
  color: var(--lf-faint);
}
.titlebar__btn[data-v-6cc95cc1] {
  margin-left: 2px;
}

/* Windows caption buttons, drawn here because the window is frameless: 46px wide like the native
   ones, full strip height, and the close button takes the usual red hover. */
.titlebar__wins[data-v-6cc95cc1] {
  margin-left: auto;
  display: flex;
  align-self: stretch;
}
.titlebar__win[data-v-6cc95cc1] {
  width: 46px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--lf-muted);
  cursor: pointer;
}
.titlebar__win[data-v-6cc95cc1]:hover {
  background: var(--lf-surface-2);
  color: var(--lf-fg);
}
.titlebar__win--close[data-v-6cc95cc1]:hover {
  background: var(--lf-danger);
  color: #fff;
}
.body[data-v-6cc95cc1] {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
}
.sidebar[data-v-6cc95cc1] {
  flex-shrink: 0;
  width: 272px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-right: 1px solid var(--lf-border);
  background: var(--lf-sidebar);
  transition: width 0.22s ease, margin-left 0.22s ease;
}
.sidebar--collapsed[data-v-6cc95cc1] {
  width: 0;
  margin-left: -1px;
  border-right: none;
}
.main[data-v-6cc95cc1] {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.screen[data-v-6cc95cc1] {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
@media (max-width: 820px) {
  /* The floating panel starts under the window strip, so the brand stays reachable. */
.sidebar[data-v-6cc95cc1]:not(.sidebar--collapsed) {
    position: fixed;
    inset: var(--titlebar-h) auto 0 0;
    z-index: 40;
    box-shadow: var(--lf-shadow-lg);
}
}
