/* FileExplorer — unscoped stylesheet (lib consumers apply once). */

/* ⚠⚠ This block belongs at the TOP of the file, not the bottom. Component
   rules of the same specificity (`.fe *` = 0,1,0) win the cascade when they
   sit BELOW it; while it sat at the bottom it overrode the tab strip's own
   "hide the bar" rule and dropped a scrollbar under the tabs (2026-08-12,
   reported). The general default comes first, the component writes its own
   exception afterwards. */

/* === RTL — the one sign every direction-bearing MOTION reads ==========
   Layout here is written in logical properties (margin-inline-start,
   inset-inline-end, text-align: start …), and those turn by themselves under
   `dir="rtl"`. What does not turn by itself is anything written as an
   x-OFFSET: a translateX, a box-shadow's first length. Those multiply by this
   sign instead — `translateX(calc(8px * var(--filex-dir-x, 1)))` — so the
   inspector still slides in from ITS edge, a switch's knob still travels
   toward "on", and a pinned column's edge still falls on the side the rows
   scroll under. In LTR it is `* 1`: the same pixels as before.

   ⚠ Set on the element that CARRIES `dir` and inherited from there, so the
   NEAREST `dir` wins — which is the nesting rule (an English explorer inside
   an Arabic page keeps +1; see lib/direction).

   ⚠⚠ Never in document space. A PDF page, an image, a drawing, a signature
   box has its own coordinates; a box at the left of a page is at the left of
   the page in every language (lib/pdfFieldsGeom). */
[dir='ltr'] { --filex-dir-x: 1; }
[dir='rtl'] { --filex-dir-x: -1; }

/* ⚠ RTL — MACHINE text reads left to right in every language: a command, a
   path, a URL, an address, a key. The connection guides print
   `sftp -P 2022 {user}@host` with the placeholder translated; laid out right
   to left the command's words swap places and the line cannot be typed back.
   Isolated, so the Arabic around it stays right to left and an Arabic word
   inside it (a translated placeholder) stays a readable word.
   Keyed on `[dir='rtl']` so an LTR page is untouched — this rule does not
   exist for English or Turkish. */
[dir='rtl'] :is(pre, code, kbd, samp, .font-mono, .tbl-mono, .fe-share__url, .fe-inspector__share-url) {
  direction: ltr;
  unicode-bidi: isolate;
}
/* A plain-text file and a notebook's raw markdown are the person's PROSE in a
   <pre>: each line decides its own direction (declared after the rule above,
   which it overrides for exactly these two). */
[dir='rtl'] :is(.fe-preview__pre, .filex-viewer-ipynb__md-raw) {
  unicode-bidi: plaintext;
}
/* An app's own sentences are in the APP's language, which is not always the
   interface's: an English hint inside an Arabic wizard keeps its full stop at
   its end. `plaintext` lets each paragraph take the direction of its own first
   letter — for English or Turkish in an LTR page that is exactly what it was. */
:is(.fe-surface__text, .fe-surface__error) {
  unicode-bidi: plaintext;
}
/* …and what a person TYPES decides its own direction: an e-mail, a URL, a
   file name or `tag:invoice` in a right-to-left form reads left to right,
   Arabic reads right to left, and a placeholder follows its own words.
   `plaintext` is `dir="auto"` for every field at once, without touching the
   markup of each. RTL only, so no English or Turkish field changes. */
[dir='rtl'] :is(input:not([type]), input[type='text'], input[type='search'], input[type='email'], input[type='url'], input[type='tel'], textarea) {
  unicode-bidi: plaintext;
}

/* ⚠ RTL — icons that MEAN a direction, mirrored in one place. The set is
   decided where the glyph is drawn: lib/actionIcons marks its directional
   keys `fe-aicon--dir`; the few glyphs drawn inline are listed here — the
   viewer's previous / next chevrons (which now sit at the start / end edges)
   and the destination picker's "go into" chevron.
   `:dir(rtl)` on the ICON, never `[dir='rtl'] .icon`: an English explorer
   inside an Arabic page keeps its arrows. Text arrows need nothing — the `›`
   of a breadcrumb and the `‹ ›` of a pager are bidi-MIRRORED characters and
   the browser turns them itself. */
:is(.fe-aicon--dir, .fe-viewer__chev > svg, .fe-destpick__rowinto):dir(rtl) {
  transform: scaleX(-1);
}
/* A sideways disclosure caret points the way the line reads while closed
   (left, in RTL) and DOWN when open in both directions. Turned rather than
   mirrored so the open state's `rotate(90deg)` (declared later, same weight)
   animates the short way round. */
:is(.fe-share__caret, .fe-pinsp__chev):dir(rtl) {
  transform: rotate(180deg);
}

/* === ui-fix — scrollbars ==============================================
   The platform scrollbar ignores the theme completely: on Terminal Green or
   any dark variant it paints a wide light-grey slab down the edge of the
   panel. Chromium/WebKit honour the -webkit pseudo-elements and Firefox the
   two standard properties, so both are declared — the standard pair first, so
   the rules survive an engine that drops the prefixed set.

   ⚠ SCOPED TO `.fe`, never to `*`. base.css is loaded by embedders (work,
   fishapp, the admin SPA), and a bare `*` rule would repaint the HOST page's
   scrollbars from inside an embedded file browser. The two teleported
   surfaces are listed explicitly because they are children of <body>, not of
   the explorer root.

   Colours come from the existing tokens rather than from new ones, so every
   theme — including ones added later — gets a matching bar for free. */
.fe,
.fe *,
.fe-ctx-backdrop,
.fe-ctx-backdrop *,
.fe-tour,
.fe-tour * {
  scrollbar-width: thin;
  scrollbar-color: var(--fe-border-strong) transparent;
}
.fe ::-webkit-scrollbar,
.fe-ctx-backdrop ::-webkit-scrollbar,
.fe-tour ::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
.fe ::-webkit-scrollbar-track,
.fe-ctx-backdrop ::-webkit-scrollbar-track,
.fe-tour ::-webkit-scrollbar-track {
  background: transparent;
}
.fe ::-webkit-scrollbar-thumb,
.fe-ctx-backdrop ::-webkit-scrollbar-thumb,
.fe-tour ::-webkit-scrollbar-thumb {
  background: var(--fe-border-strong);
  /* Inset with a transparent border instead of a narrower width: a thin thumb
     inside a wide gutter drifts away from the content edge. */
  border: 2px solid transparent;
  background-clip: padding-box;
  border-radius: 8px;
}
.fe ::-webkit-scrollbar-thumb:hover,
.fe-ctx-backdrop ::-webkit-scrollbar-thumb:hover,
.fe-tour ::-webkit-scrollbar-thumb:hover {
  background: var(--fe-text-muted);
  background-clip: padding-box;
}
/* Unstyled, the meeting point of a vertical and a horizontal bar paints as a
   white square in the corner of a dark panel. */
.fe ::-webkit-scrollbar-corner,
.fe-ctx-backdrop ::-webkit-scrollbar-corner,
.fe-tour ::-webkit-scrollbar-corner {
  background: transparent;
}

.fe {
  position: relative;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  min-height: 420px;
  height: 100%;
  background: var(--fe-bg);
  color: var(--fe-text);
  font-family: var(--fe-font);
  /* gorunum:v1 — 13px, not 14. A row carries a filename, a size, a date and a
   * menu; at 14 that wraps to two lines at any realistic column width, which
   * is why every dense file manager sits at 13. */
  font-size: var(--fe-text-md);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  overflow: hidden;
  outline: none;
}

.fe * {
  box-sizing: border-box;
}

.fe__body {
  flex: 1 1 auto;
  overflow: auto;
  /* Flexbox's default min-height:auto stops a flex item shrinking below its
     content height — in a height-constrained embed the body would stay at
     content height, its overflow:auto never engages and everything past the
     fold is clipped (unreachable) by .fe's overflow:hidden. min-height:0
     caps the body to the remaining space so internal (touch) scroll works. */
  min-height: 0;
  background: var(--fe-bg);
}

/* Web-component host default: custom elements are display:inline, which
   collapses the height chain. Declared here so embedders don't need inline
   styles on <filex-explorer> — an inline style on the host falls through to
   the .fe root (Vue attr fallthrough) where display:block would kill the
   flex column and with it the internal scroll. */
filex-explorer {
  display: block;
  height: 100%;
  /* ⚠⚠ The host is almost always a FLEX ITEM (the desktop shell, work, the
   * admin SPA all put it in a flex column/row). A flex item's default
   * `min-width: auto` floors it at its content's min-content width, so a wide
   * row of tabs pushed the host — and the whole page — sideways instead of
   * letting the tab strip scroll: 63 tabs made a 3256px host inside a 1264px
   * window (2026-08-12). Every embedder would otherwise have to know to write
   * this themselves, which is the opposite of what shipping one component is
   * for. Same reason for the width: a block that cannot exceed its box. */
  min-width: 0;
  max-width: 100%;
}

/* Live-presence strip (who else is in this folder) — shown under the breadcrumb. */
.fe__presence {
  flex: 0 0 auto;
  padding: 0.25rem 0.75rem;
  border-bottom: 1px solid var(--fe-border, rgba(0, 0, 0, 0.06));
}

/* "More results than shown" — a search answer the server cut (FileExplorer's
 * banners slot). Quiet on purpose: it qualifies the list, it is not an error. */
.fe-search-cut {
  flex: 0 0 auto;
  padding: 0.375rem 0.75rem;
  border-bottom: 1px solid var(--fe-border, rgba(0, 0, 0, 0.06));
  color: var(--fe-text-muted);
  font-size: 0.8125rem;
}

/* The catalog does not cover all of this storage (lib/catalogCoverage): the
   same quiet strip as a cut search, with room for one action. */
.fe-coverage {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.375rem 0.75rem;
  border-bottom: 1px solid var(--fe-border, rgba(0, 0, 0, 0.06));
  color: var(--fe-text-muted);
  font-size: 0.8125rem;
}
.fe-coverage__text {
  flex: 1 1 16rem;
  min-width: 0;
}
.fe-coverage__action {
  flex: 0 0 auto;
  padding: 0.125rem 0.625rem;
  border: 1px solid var(--fe-border-strong);
  border-radius: var(--fe-radius-sm);
  background: var(--fe-bg-elev);
  color: var(--fe-text);
  font: inherit;
  cursor: pointer;
}
.fe-coverage__action:hover:not(:disabled) {
  background: var(--fe-bg-hover);
}
.fe-coverage__action:disabled {
  opacity: 0.6;
  cursor: default;
}

/* Initial-load spinner (shown until the first listing arrives). */
.fe__loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  min-height: 240px;
  height: 100%;
  color: var(--fe-text-muted);
}
.fe__spinner {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 3px solid color-mix(in srgb, var(--fe-text-muted) 30%, transparent);
  border-top-color: var(--fe-primary);
  animation: fe-spin 0.7s linear infinite;
}
.fe__loading-text {
  margin: 0;
  font-size: 13px;
}
@keyframes fe-spin {
  to { transform: rotate(360deg); }
}

/* Dead deep-link state (404 / RBAC-hidden 403). */
.fe__notfound {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 240px;
  height: 100%;
  color: var(--fe-text-muted);
  text-align: center;
  padding: 24px;
}
.fe__notfound-icon {
  font-size: 40px;
  filter: grayscale(1);
  opacity: 0.6;
}
.fe__notfound-title {
  margin: 4px 0 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--fe-text);
}
.fe__notfound-path {
  margin: 0;
  font-size: 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  word-break: break-all;
  max-width: 480px;
}
.fe__notfound-desc {
  margin: 0 0 10px;
  font-size: 13px;
}

.fe__file-input {
  position: absolute;
  inset-inline-start: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
}

/* ---------- Buttons ---------- */

.fe-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  background: var(--fe-bg-elev);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  color: var(--fe-text);
  font: inherit;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
  white-space: nowrap;
}
.fe-btn:hover:not(:disabled) {
  background: var(--fe-bg-hover);
  border-color: var(--fe-border-strong);
}
.fe-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.fe-btn--primary {
  background: var(--fe-primary);
  border-color: var(--fe-primary);
  color: var(--fe-text-on-primary);
}
.fe-btn--primary:hover:not(:disabled) {
  background: var(--fe-primary-hover);
  border-color: var(--fe-primary-hover);
}
.fe-btn--danger {
  background: var(--fe-danger);
  border-color: var(--fe-danger);
  color: var(--fe-text-on-primary);
}
.fe-btn--danger:hover:not(:disabled) {
  background: var(--fe-danger-hover);
  border-color: var(--fe-danger-hover);
}
.fe-btn--icon-only {
  padding: 6px 8px;
}
.fe-btn.is-active {
  background: var(--fe-bg-selected);
  border-color: var(--fe-primary);
}
.fe-btn__label {
  line-height: 1;
}

.fe-icon {
  font-size: 1.05em;
  line-height: 1;
}

/* ---------- Toolbar ---------- */

.fe-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  background: var(--fe-bg-elev);
  border-bottom: 1px solid var(--fe-border);
  flex-wrap: wrap;
}
.fe-toolbar__primary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.fe-toolbar__spacer {
  flex: 1 1 auto;
}
/* Search + Upload + Refresh pinned together so the cluster doesn't get
 * orphaned by the wrap point on narrow widths. */
.fe-toolbar__search-group {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--fe-bg);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  padding: 2px;
}
.fe-toolbar__search-group .fe-search__input {
  border: 0;
  background: transparent;
  padding: 4px 8px;
}
.fe-toolbar__search-group .fe-btn {
  border: 0;
  background: transparent;
  padding: 4px 6px;
}
.fe-toolbar__search-group .fe-btn:hover:not(:disabled) {
  background: var(--fe-bg-hover);
}
.fe-toolbar__view {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  background: var(--fe-bg);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
}
.fe-toolbar__view .fe-btn {
  border: 0;
  background: transparent;
}
.fe-toolbar__view .fe-btn.is-active {
  background: var(--fe-bg-selected);
}

/* ---------- Search ---------- */

.fe-search {
  position: relative;
}
.fe-search__input {
  background: var(--fe-bg);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  padding: 6px 10px;
  font: inherit;
  color: var(--fe-text);
  min-width: 200px;
}
.fe-search__input:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}

/* ---------- Breadcrumb ---------- */

.fe-breadcrumb {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  padding: 6px 12px;
  /* ui-fix — same height as the side pane's .fe-split__crumbs in split mode
     (so both rows start at the same y even without the main pane's "Yola Git"
     ✏ button). */
  min-height: 37px;
  background: var(--fe-bg);
  border-bottom: 1px solid var(--fe-border);
  font-size: 13px;
  color: var(--fe-text-muted);
}
.fe-breadcrumb__crumb {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: transparent;
  border: 0;
  color: inherit;
  padding: 2px 6px;
  border-radius: var(--fe-radius-sm);
  cursor: pointer;
  font: inherit;
}
.fe-breadcrumb__crumb:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-breadcrumb__crumb.is-last {
  color: var(--fe-text);
  font-weight: 600;
}
.fe-breadcrumb__sep {
  color: var(--fe-text-muted);
}
/* Edit pencil button — V1.5, verbatim (translated): "let me type a path and go" */
.fe-breadcrumb__edit {
  margin-inline-start: auto;
  background: none;
  border: 1px solid transparent;
  color: var(--fe-text-muted);
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  padding: 4px 8px;
  border-radius: 4px;
}
.fe-breadcrumb__edit:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
  border-color: var(--fe-border);
}
/* Inline path input — replaces the crumbs while editing */
.fe-breadcrumb__input {
  flex: 1;
  background: var(--fe-bg);
  color: var(--fe-text);
  border: 1px solid var(--fe-primary);
  border-radius: 4px;
  padding: 4px 8px;
  font-family: var(--fe-font-mono);
  font-size: 13px;
  outline: none;
}

/* ---------- List view ---------- */

.fe-list {
  display: flex;
  flex-direction: column;
}
.fe-list__head {
  display: grid;
  grid-template-columns: 32px minmax(0, 1fr) 120px 180px;
  gap: 8px;
  padding: 8px 12px;
  font-weight: 600;
  color: var(--fe-text-muted);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: var(--fe-bg-elev);
  border-bottom: 1px solid var(--fe-border);
}
.fe-list__body {
  display: flex;
  flex-direction: column;
}
.fe-list__row {
  display: grid;
  grid-template-columns: 32px minmax(0, 1fr) 120px 180px;
  gap: 8px;
  padding: 8px 12px;
  align-items: center;
  cursor: pointer;
  border-bottom: 1px solid var(--fe-border);
  transition: background 0.08s;
  user-select: none;
}
.fe-list__row:hover {
  background: var(--fe-bg-hover);
}
.fe-list__row.is-selected {
  background: var(--fe-bg-selected);
}
.fe-list__row.is-trash {
  opacity: 0.7;
}
.fe-list__row.is-clipped,
.fe-grid__card.is-clipped {
  opacity: 0.45;
  font-style: italic;
}
.fe-list__row:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}
.fe-list__col--star {
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.12s;
}
/* issue #26, third round — a reveal ON HOVER only where hovering exists.
   iOS WebKit treats a tap whose :hover styles reveal content (an opacity
   transition from 0 counts) as "the user wanted to see the hover state": it
   applies the hover and swallows the click, so the first tap on a row or card
   only highlighted it and a second tap was needed to open. `(hover: hover)`
   keeps these reveals off touch screens, where they could never be hovered on
   purpose anyway. `:focus-within` and a starred item stay visible everywhere. */
.fe-list__row:focus-within .fe-list__col--star,
.fe-list__col--star:has(.is-starred) {
  opacity: 1;
}
@media (hover: hover) {
  .fe-list__row:hover .fe-list__col--star {
    opacity: 1;
  }
}
.fe-list__col--star .filex-star-btn.is-starred {
  color: var(--fe-warning, #f59e0b);
}
.fe-list__col--name {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.fe-list__icon {
  flex-shrink: 0;
  font-size: 16px;
}
.fe-list__name-wrap {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1 1 auto;
}
.fe-list__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-list__parent {
  font-size: 11px;
  color: var(--fe-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-top: 2px;
}
.fe-list__col--size,
.fe-list__col--mod {
  color: var(--fe-text-muted);
  font-size: 13px;
}
.fe-list__empty {
  padding: 40px 20px;
  text-align: center;
  color: var(--fe-text-muted);
}

/* ---------- Grid view ---------- */

.fe-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: var(--fe-gap);
  padding: var(--fe-gap);
}
.fe-grid__card {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding: 8px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg-elev);
  cursor: pointer;
  transition: background 0.08s, border-color 0.08s;
  user-select: none;
}
.fe-grid__card:hover {
  background: var(--fe-bg-hover);
  border-color: var(--fe-border-strong);
}
.fe-grid__card.is-selected {
  background: var(--fe-bg-selected);
  border-color: var(--fe-primary);
}
.fe-grid__card.is-trash {
  opacity: 0.7;
}
.fe-grid__thumb {
  /* yildiz:s1 — positioning context for the star chip in the corner. The
     gallery's thumb already had one; this one did not, so an absolutely
     placed chip would have resolved against whatever ancestor happened to be
     positioned (the grid, or the explorer root) and landed in the wrong tile. */
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1 / 1;
  background: var(--fe-bg);
  border-radius: var(--fe-radius-sm);
  margin-bottom: 8px;
  overflow: hidden;
}
.fe-grid__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.fe-grid__icon {
  font-size: 32px;
}
.fe-grid__label {
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-grid__meta {
  font-size: 11px;
  color: var(--fe-text-muted);
  margin-top: 2px;
}
.fe-grid__parent {
  font-size: 11px;
  color: var(--fe-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-top: 2px;
  font-style: italic;
}
/* bul:s3 — search-result content snippet on grid cards (clamped to two
 * lines; the full plain text lives in the title attribute). */
.fe-grid__snippet {
  margin-top: 2px;
  font-size: 11px;
  line-height: 1.35;
  color: var(--fe-text-muted);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
  text-align: start;
}
.fe-grid__empty {
  grid-column: 1 / -1;
  padding: 40px 20px;
  text-align: center;
  color: var(--fe-text-muted);
}

/* ---------- Drag overlay ---------- */

.fe__dragover {
  position: absolute;
  inset: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(59, 130, 246, 0.12);
  border: 3px dashed var(--fe-primary);
  pointer-events: none;
}
.fe__dragover-card {
  background: var(--fe-bg);
  border-radius: var(--fe-radius-lg);
  padding: 24px 40px;
  box-shadow: var(--fe-shadow);
  text-align: center;
}
.fe__dragover-card .fe-icon {
  font-size: 32px;
  display: block;
  margin-bottom: 8px;
}

/* ---------- Upload progress ---------- */

.fe-upload {
  position: absolute;
  inset-inline-end: 16px;
  bottom: 16px;
  width: min(360px, calc(100% - 32px));
  background: var(--fe-bg);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  box-shadow: var(--fe-shadow);
  z-index: 40;
  max-height: 40vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.fe-upload__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  background: var(--fe-bg-elev);
  border-bottom: 1px solid var(--fe-border);
  font-size: 13px;
}
.fe-upload__count {
  background: var(--fe-primary);
  color: var(--fe-text-on-primary);
  font-size: 12px;
  padding: 1px 7px;
  border-radius: 999px;
}
.fe-upload__list {
  list-style: none;
  padding: 0;
  margin: 0;
  overflow: auto;
}
.fe-upload__item {
  padding: 10px 14px;
  border-bottom: 1px solid var(--fe-border);
}
.fe-upload__item:last-child {
  border-bottom: 0;
}
.fe-upload__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 13px;
  margin-bottom: 4px;
}
.fe-upload__bar {
  background: var(--fe-bg-elev);
  border-radius: 99px;
  height: 6px;
  overflow: hidden;
  margin-bottom: 4px;
}
.fe-upload__bar-fill {
  background: var(--fe-primary);
  height: 100%;
  transition: width 0.2s;
}
.fe-upload__item.is-error .fe-upload__bar-fill,
.fe-upload__item.is-aborted .fe-upload__bar-fill {
  background: var(--fe-danger);
}
.fe-upload__item.is-done .fe-upload__bar-fill {
  background: #16a34a;
}
.fe-upload__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  font-size: 11px;
  color: var(--fe-text-muted);
}
.fe-upload__cancel {
  background: transparent;
  border: 0;
  color: var(--fe-primary);
  cursor: pointer;
  font: inherit;
}

.fe-upload-slide-enter-active,
.fe-upload-slide-leave-active {
  transition: transform 0.2s, opacity 0.2s;
}
.fe-upload-slide-enter-from,
.fe-upload-slide-leave-to {
  opacity: 0;
  transform: translateY(10px);
}

/* ---------- Pending ops tray ---------- */

/* Sits to the left of UploadProgress (which uses right: 16px) so the
 * two stacks don't overlap when both are active. */
.fe-ops {
  position: absolute;
  inset-inline-start: 16px;
  bottom: 16px;
  width: min(320px, calc(100% - 32px));
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 40;
}
.fe-ops__item {
  background: var(--fe-bg);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  box-shadow: var(--fe-shadow);
  padding: 10px 14px;
  font-size: 13px;
}
.fe-ops__item.is-done {
  border-color: #16a34a;
}
.fe-ops__item.is-error {
  border-color: var(--fe-danger);
}
.fe-ops__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 6px;
}
.fe-ops__title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1 1 auto;
}
.fe-ops__dismiss {
  background: transparent;
  border: 0;
  color: var(--fe-text-muted);
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
  padding: 0 4px;
}
.fe-ops__bar {
  background: var(--fe-bg-elev);
  border-radius: 99px;
  height: 6px;
  overflow: hidden;
}
.fe-ops__bar-fill {
  background: var(--fe-primary);
  height: 100%;
  transition: width 0.25s;
}
.fe-ops__item.is-done .fe-ops__bar-fill {
  background: #16a34a;
}
.fe-ops__item.is-error .fe-ops__bar-fill {
  background: var(--fe-danger);
}
.fe-ops__error {
  margin-top: 6px;
  font-size: 11px;
  color: var(--fe-danger);
}
.fe-ops-slide-enter-active,
.fe-ops-slide-leave-active {
  transition: transform 0.2s, opacity 0.2s;
}
.fe-ops-slide-enter-from,
.fe-ops-slide-leave-to {
  opacity: 0;
  transform: translateY(10px);
}

/* ---------- Context menu ---------- */

.fe-ctx-backdrop {
  position: fixed;
  inset: 0;
  z-index: 80;
  /* Make the backdrop itself the variable scope. After teleporting under
   * <body> we lose the `.fe` parent, so without explicit `color` here
   * the menu inherits Filament's body color (white on dark) and the
   * labels disappear against `--fe-bg` (white). V1.1 bug list update. */
  color: var(--fe-text);
  font-family: var(--fe-font);
  font-size: 14px;
}
.fe-ctx {
  position: absolute;
  min-width: 180px;
  background: var(--fe-bg);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  box-shadow: var(--fe-shadow);
  padding: 4px;
  z-index: 90;
  color: var(--fe-text);
}
.fe-ctx__item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  border-radius: var(--fe-radius-sm);
  border: 0;
  background: transparent;
  color: var(--fe-text);
  font: inherit;
  width: 100%;
  text-align: start;
  cursor: pointer;
}
.fe-ctx__item:hover:not(.is-disabled) {
  background: var(--fe-bg-hover);
}
.fe-ctx__item.is-danger {
  color: var(--fe-danger);
}
.fe-ctx__item.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.fe-ctx__icon {
  width: 1.2em;
  text-align: center;
}
/* tus:t1 — the key that runs this row, pushed to the trailing edge. Muted and
 * smaller: it is a reminder, not a second label, and it must never win the
 * row's width from the label (hence auto margin + no shrink on the label). */
.fe-ctx__label {
  flex: 1 1 auto;
  min-width: 0;
}
.fe-ctx__key {
  flex: 0 0 auto;
  margin-inline-start: auto;
  padding-inline-start: 16px;
  color: var(--fe-text-muted);
  font-size: 11px;
  font-family: var(--fe-font-mono);
  white-space: nowrap;
}
/* A key on a touch screen is an instruction nobody holding it can follow:
 * the phone's row sheet listed "Enter", "Ctrl+X", "F2" beside every verb (QA,
 * 2026-09-21). The same test the explorer uses to open the menu as a sheet
 * (`(pointer: coarse)`), so every menu — the sheet, the New menu, the
 * toolbar's — answers alike. */
@media (pointer: coarse) {
  .fe-ctx__key {
    display: none;
  }
}
.fe-ctx__sep {
  height: 1px;
  background: var(--fe-border);
  margin: 4px 0;
}
.fe-ctx-enter-active,
.fe-ctx-leave-active {
  transition: opacity 0.1s;
}
.fe-ctx-enter-from,
.fe-ctx-leave-to {
  opacity: 0;
}

/* ---------- Modal ---------- */

/* ⚠⚠ `--fe-overlay-top` — where every window-sized layer the explorer draws
 * BEGINS. A host that keeps a strip of its own at the top of the window sets
 * it to that strip's height; nothing else does, and it is 0 everywhere else.
 *
 * The desktop app is that host: its window is frameless, so its title bar
 * (minimise, maximise, close, and the strip the OS drags the window by) is
 * page content. A dialog's backdrop drawn over it took those clicks — the
 * first click on "minimise" closed the dialog instead — and the owner's rule
 * (2026-09-27) is that the title bar works ALWAYS. So the layers stop short
 * of it rather than out-number it in z-index: a stacking order can always be
 * beaten by the next layer somebody writes at 10000, a layer that does not
 * reach the strip cannot.
 *
 * Every layer that covers the window (this backdrop — i.e. every dialog built
 * on Modal, the user settings dialog included — the share dialog's scrim,
 * the connections overlay, the notifications screen, the column menu's click
 * catcher) starts at it; web/tests/api/overlayTop.test.ts holds the list, and
 * names the two that do not and why. */
.fe-modal__backdrop {
  position: fixed;
  inset: var(--fe-overlay-top, 0px) 0 0;
  /* `.fe` (this element's other class) says height:100%; with a top of its
     own that would run the backdrop past the bottom of the window. It also
     draws a 1px border with a radius — on a scrim that is a light hairline
     round the whole window while any dialog is open (measured on the web
     page the day the user settings dialog moved onto this frame; the
     full-bleed variant below had already dropped it for the same reason). */
  height: auto;
  border: 0;
  border-radius: 0;
  z-index: 70;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(13, 19, 27, 0.48);
  padding: 16px;
}
.fe-modal__card {
  background: var(--fe-bg);
  border-radius: var(--fe-radius);
  box-shadow: var(--fe-shadow);
  width: 100%;
  max-height: calc(100vh - 32px - var(--fe-overlay-top, 0px));
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.fe-modal__card--sm { max-width: 420px; }
.fe-modal__card--md { max-width: 560px; }
.fe-modal__card--lg { max-width: 800px; }
.fe-modal__card--xl { max-width: 1080px; height: calc(100vh - 64px - var(--fe-overlay-top, 0px)); }
/* bare — the slot draws the whole card (Modal `bare`: the user settings
   dialog, with its own head, rail and size). The frame is only the dialog. */
.fe-modal__card--bare {
  width: auto;
  max-width: none;
  max-height: none;
  background: transparent;
  border-radius: 0;
  box-shadow: none;
  overflow: visible;
}
.fe-modal__card--bare > .fe-modal__body {
  padding: 0;
  overflow: visible;
}

/* Recently-opened tray — anchored modal that drops down from the
 * toolbar. Re-uses fe-modal__backdrop, just pins the panel to the top. */
.fe-recents__backdrop {
  align-items: flex-start !important;
  padding-top: 60px;
}
.fe-recents__panel {
  background: var(--fe-bg, #fff);
  border-radius: var(--fe-radius, 8px);
  box-shadow: var(--fe-shadow, 0 6px 24px rgba(0, 0, 0, 0.18));
  width: min(480px, calc(100vw - 32px));
  max-height: calc(100vh - 96px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.fe-recents__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  border-bottom: 1px solid var(--fe-border, #e2e6ed);
}
.fe-recents__close {
  appearance: none;
  background: transparent;
  border: 0;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  color: var(--fe-text-muted, #6b7280);
  padding: 0 4px;
}
.fe-recents__close:hover {
  color: var(--fe-text, #111);
}

/* Markdown split editor — textarea (raw) | rendered HTML preview.
 * The chromeless modal's full viewport makes the side-by-side feasible;
 * on narrower screens it collapses to a vertical stack. */
.fe-preview__md-split {
  display: flex;
  flex-direction: column;
  height: 100%;
  width: 100%;
  min-height: 0;
}
.fe-preview__md-split-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 14px;
  background: var(--fe-bg-elev, #f6f7f9);
  border-bottom: 1px solid var(--fe-border, #e2e6ed);
  flex: 0 0 auto;
}
.fe-preview__md-split-label {
  font-family: var(--fe-font-mono, ui-monospace, monospace);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--fe-text-muted, #6b7280);
}
.fe-preview__md-split-error {
  flex: 1 1 auto;
  font-size: 12px;
  color: var(--fe-danger, #dc2626);
  text-align: center;
}
.fe-preview__md-split-body {
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  min-height: 0;
}
.fe-preview__md-split-input {
  resize: none;
  border: 0;
  outline: 0;
  padding: 16px 20px;
  font-family: var(--fe-font-mono, ui-monospace, monospace);
  font-size: 13px;
  line-height: 1.6;
  background: var(--fe-bg, #fff);
  color: var(--fe-text, #111);
  border-inline-end: 1px solid var(--fe-border, #e2e6ed);
  overflow: auto;
  tab-size: 2;
}
.fe-preview__md-split-output {
  overflow: auto;
  padding: 16px 24px;
  background: var(--fe-bg, #fff);
}
/* The split-output element carries BOTH `fe-preview__md-split-output` and
 * `fe-preview__md`. The shared `.fe-preview__md` rule caps height at 70vh
 * (right for the standalone modal preview, wrong here) and — being later in
 * source order at equal specificity — wins over a plain
 * `.fe-preview__md-split-output { max-height: none }`. The compound selector
 * below raises specificity so the cap is dropped regardless of order, letting
 * the rendered pane fill the grid row (tall docs scroll inside it) instead of
 * leaving a dead gap below the content. */
.fe-preview__md-split-output.fe-preview__md {
  max-height: none;
  min-height: 0;
  height: 100%;
}
@media (max-width: 900px) {
  .fe-preview__md-split-body {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr 1fr;
  }
  .fe-preview__md-split-input {
    border-inline-end: 0;
    border-bottom: 1px solid var(--fe-border, #e2e6ed);
  }
}

/* Chromeless — used by the standalone /files/edit route. Drops the
 * dialog frame entirely so the viewport belongs to the viewer.
 * The viewport less the host's strips: `--fe-overlay-top` (the desktop's
 * document-window bar) and `--fe-overlay-bottom` (its open-with note) - the
 * host states them on `:root` and the viewer fits between (variables.css). */
.fe-modal__backdrop--chromeless {
  background: transparent;
  padding: 0;
  bottom: var(--fe-overlay-bottom, 0px);
}
.fe-modal__card--chromeless {
  max-width: none;
  width: 100vw;
  height: calc(100vh - var(--fe-overlay-top, 0px) - var(--fe-overlay-bottom, 0px));
  max-height: calc(100vh - var(--fe-overlay-top, 0px) - var(--fe-overlay-bottom, 0px));
  border-radius: 0;
  box-shadow: none;
}
.fe-modal__card--chromeless > .fe-modal__body {
  flex: 1 1 auto;
  min-height: 0;
  padding: 0;
}
.fe-modal__card--chromeless .fe-preview {
  /* Override center-alignment so split editors + iframes fill the
   * full chromeless viewport instead of floating in the middle. */
  align-items: stretch;
  justify-content: stretch;
  min-height: 0;
  flex: 1 1 auto;
}
.fe-modal__card--chromeless .fe-preview > * {
  width: 100%;
  height: 100%;
}

.fe-modal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--fe-border);
}
.fe-modal__title {
  font-size: 16px;
  font-weight: 600;
  margin: 0;
}
.fe-modal__close {
  background: transparent;
  border: 0;
  color: var(--fe-text-muted);
  font-size: 22px;
  cursor: pointer;
  padding: 0 4px;
  line-height: 1;
}
.fe-modal__body {
  padding: 16px;
  overflow: auto;
  flex: 1 1 auto;
}
.fe-modal__actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 16px;
  border-top: 1px solid var(--fe-border);
  background: var(--fe-bg-elev);
}
.fe-modal__close:disabled {
  cursor: default;
  opacity: 0.35;
}
/* Modal `busyClose`: the question a busy dialog asks before closing would end
   its work, over the footer — in the dialog, never the browser's confirm(). */
.fe-modal__ask {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 12px;
  padding: 12px 16px;
  border-top: 1px solid var(--fe-border);
  background: var(--fe-bg-elev);
}
.fe-modal__ask-text {
  margin: 0;
  flex: 1 1 220px;
  font-size: 13px;
  color: var(--fe-text);
}
.fe-modal__ask-actions {
  display: flex;
  gap: 8px;
  margin-inline-start: auto;
}

.fe-modal-enter-active,
.fe-modal-leave-active {
  transition: opacity 0.15s;
}
.fe-modal-enter-from,
.fe-modal-leave-to {
  opacity: 0;
}

/* ---------- Form ---------- */

.fe-input {
  width: 100%;
  padding: 8px 10px;
  background: var(--fe-bg);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  font: inherit;
  color: var(--fe-text);
}
.fe-input:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}
.fe-input--mono {
  font-family: var(--fe-font-mono);
  letter-spacing: 0.1em;
}
.fe-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.fe-form__row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.fe-form__row--stack {
  flex-direction: column;
  align-items: stretch;
}
.fe-form__hint { font-size: 12px; color: var(--fe-text-muted); }
.fe-share-result__row--note small { color: var(--fe-text-muted); }
.fe-form__error {
  color: var(--fe-danger);
  font-size: 12px;
  margin: 6px 0 0;
}

/* Share result */
.fe-share-result {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.fe-share-result__row label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--fe-text-muted);
  margin-bottom: 4px;
}
.fe-share-result__copy {
  display: flex;
  gap: 6px;
}

/* ---------- Preview ---------- */

.fe-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  min-height: 50vh;
  background: var(--fe-bg-elev);
}
.fe-preview__image {
  max-width: 100%;
  max-height: 70vh;
  object-fit: contain;
}
.fe-preview__video,
.fe-preview__audio {
  max-width: 100%;
  max-height: 70vh;
}
.fe-preview__iframe {
  width: 100%;
  height: 70vh;
  border: 0;
  background: #fff;
}
.fe-preview__office {
  width: 100%;
  height: 78vh;
  background: var(--fe-bg);
}
/* Task #92: the editor's frame on the interface origin fills the mount (the
   page in it gives api.js's own frame all of its room). */
.fe-preview__officeframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}
/* CodeMirror 6 editor surface — V1.5 follow-up #4: the Monaco editor did
   not work, so CodeMirror replaced it. */
.fe-preview__code-wrap {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 78vh;
}
.fe-preview__code-toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  background: var(--fe-bg-hover, #f3f4f6);
  border-bottom: 1px solid var(--fe-border, #e5e7eb);
  font-size: 12px;
}
.fe-preview__code-lang {
  font-family: var(--fe-font-mono, ui-monospace, monospace);
  color: var(--fe-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.fe-preview__code-toolbar > .fe-btn {
  margin-inline-start: auto;
}
.fe-preview__code-status {
  font-size: 12px;
  font-weight: 500;
}
.fe-preview__code-status--ok { color: #059669; }
.fe-preview__code-status--err { color: #dc2626; cursor: help; }
.fe-preview__code-editor {
  flex: 1;
  min-height: 0;
  overflow: hidden;
}
.fe-preview__code-editor .cm-editor {
  height: 100%;
  font-size: 13px;
}
.fe-preview__code-editor .cm-scroller {
  font-family: var(--fe-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
}
/* An administrator's second line under a viewer failure — the raw words
   behind the sentence (lib/errorWords). Nobody else is given it. */
.fe-preview__error-detail {
  font-family: var(--fe-font-mono, ui-monospace, monospace);
  font-size: 11px;
  color: var(--fe-text-muted);
  overflow-wrap: anywhere;
  max-width: 60ch;
}
.fe-preview__fallback {
  text-align: center;
  color: var(--fe-text-muted);
  padding: 40px 20px;
}
.fe-preview__fallback-icon {
  font-size: 48px;
  display: block;
  margin-bottom: 12px;
}
.fe-preview__pre {
  margin: 0;
  padding: 16px;
  width: 100%;
  max-height: 70vh;
  overflow: auto;
  background: var(--fe-bg);
  color: var(--fe-text);
  font-family: var(--fe-font-mono);
  font-size: 13px;
  line-height: 1.5;
  white-space: pre-wrap;
  word-wrap: break-word;
}
.fe-preview__code {
  white-space: pre;
  word-wrap: normal;
}
/* Minimal highlight.js theme — host can override by importing one of
 * highlight.js's stylesheets ahead of file-explorer.css. We ship
 * something usable so the unstyled colour-class spans don't render as
 * raw text in the wild. */
.hljs { background: var(--fe-bg); color: var(--fe-text); }
.hljs-comment, .hljs-quote { color: var(--fe-text-muted); font-style: italic; }
.hljs-keyword, .hljs-selector-tag, .hljs-built_in, .hljs-section,
.hljs-meta { color: #c678dd; }
.hljs-string, .hljs-attr, .hljs-symbol, .hljs-bullet, .hljs-addition,
.hljs-link { color: #98c379; }
.hljs-number, .hljs-literal, .hljs-deletion { color: #d19a66; }
.hljs-title, .hljs-name, .hljs-selector-id, .hljs-selector-class,
.hljs-template-tag, .hljs-template-variable { color: #61aeee; }
.hljs-type, .hljs-tag { color: #e06c75; }
.hljs-variable, .hljs-attribute { color: #d19a66; }
.hljs-emphasis { font-style: italic; }
.hljs-strong { font-weight: bold; }
.fe-preview__md {
  width: 100%;
  max-height: 70vh;
  overflow: auto;
  padding: 24px 32px;
  background: var(--fe-bg);
  color: var(--fe-text);
  line-height: 1.6;
  font-size: 15px;
}
.fe-preview__md h1,
.fe-preview__md h2,
.fe-preview__md h3,
.fe-preview__md h4 {
  margin: 1.2em 0 0.6em;
  font-weight: 600;
}
.fe-preview__md h1 { font-size: 1.8em; border-bottom: 1px solid var(--fe-border); padding-bottom: 0.3em; }
.fe-preview__md h2 { font-size: 1.4em; border-bottom: 1px solid var(--fe-border); padding-bottom: 0.3em; }
.fe-preview__md h3 { font-size: 1.2em; }
.fe-preview__md p { margin: 0.8em 0; }
.fe-preview__md a { color: var(--fe-primary); text-decoration: underline; }
.fe-preview__md code {
  font-family: var(--fe-font-mono);
  background: var(--fe-bg-elev);
  padding: 1px 5px;
  border-radius: 3px;
  font-size: 0.92em;
}
.fe-preview__md pre {
  background: var(--fe-bg-elev);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  padding: 12px 16px;
  overflow: auto;
  font-size: 0.9em;
}
.fe-preview__md pre code { background: transparent; padding: 0; }
.fe-preview__md ul,
.fe-preview__md ol { padding-inline-start: 2em; margin: 0.8em 0; }
.fe-preview__md li { margin: 0.3em 0; }
.fe-preview__md blockquote {
  border-inline-start: 3px solid var(--fe-border-strong);
  padding: 0 1em;
  color: var(--fe-text-muted);
  margin: 0.8em 0;
}
.fe-preview__md table {
  border-collapse: collapse;
  margin: 1em 0;
  width: 100%;
}
.fe-preview__md th,
.fe-preview__md td {
  border: 1px solid var(--fe-border);
  padding: 6px 10px;
  text-align: start;
}
.fe-preview__md th { background: var(--fe-bg-elev); }
.fe-preview__md img { max-width: 100%; }

/* ---------- Toast ---------- */

.fe-toast {
  position: absolute;
  bottom: 24px;
  left: 50%; /* rtl-physical: centred by translate(-50%) — the same place in both directions */
  transform: translateX(-50%);
  background: var(--fe-text);
  color: var(--fe-bg);
  padding: 8px 14px;
  border-radius: var(--fe-radius);
  font-size: 13px;
  box-shadow: var(--fe-shadow-sm);
  z-index: 95;
}
.fe-toast-enter-active,
.fe-toast-leave-active {
  transition: opacity 0.2s, transform 0.2s;
}
.fe-toast-enter-from,
.fe-toast-leave-to {
  opacity: 0;
  transform: translate(-50%, 10px); /* rtl-physical: centred (see .fe-toast) */
}

/* ---------- Responsive ---------- */

@media (max-width: 640px) {
  .fe-list__head,
  .fe-list__row {
    grid-template-columns: 28px minmax(0, 1fr) 90px;
  }
  .fe-list__col--mod {
    display: none;
  }
  .fe-toolbar {
    padding: 8px;
  }
  .fe-search__input {
    min-width: 120px;
  }
}

/* === cila:a — file-type SVG icons + date group headers + density mode === */

/* SVG icon set (lib/fileIcons) — sized by the wrapper's font-size so the
 * existing 32px grid / 16px list scales keep working. */
.fe-ficon {
  width: 1em;
  height: 1em;
  display: block;
  flex-shrink: 0;
}
.fe-ficon--folder { color: var(--fe-icon-folder); }
.fe-ficon--image { color: var(--fe-icon-image); }
.fe-ficon--video { color: var(--fe-icon-video); }
.fe-ficon--audio { color: var(--fe-icon-audio); }
.fe-ficon--pdf { color: var(--fe-icon-pdf); }
.fe-ficon--doc { color: var(--fe-icon-doc); }
.fe-ficon--sheet { color: var(--fe-icon-sheet); }
.fe-ficon--slides { color: var(--fe-icon-slides); }
.fe-ficon--archive { color: var(--fe-icon-archive); }
.fe-ficon--code { color: var(--fe-icon-code); }
.fe-ficon--text { color: var(--fe-icon-text); }
.fe-ficon--unknown { color: var(--fe-icon-unknown); }

/* gorunum:v1 — the listing badge: a filled rounded square in the family's
 * colour with the glyph knocked out in white. Sized from the wrapper's
 * font-size like .fe-ficon, so the existing 32px grid / 16px list scales keep
 * working: the chip is the full em box and the glyph sits at 60% of it.
 * The folder keeps its own colour and no chip — see lib/fileIcons.iconTile. */
.fe-ftile {
  width: 1em;
  height: 1em;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 0.25em;
  background: var(--fe-icon-unknown);
  color: #ffffff;
}
.fe-ftile .fe-ficon {
  width: 0.6em;
  height: 0.6em;
  color: inherit;
}
.fe-ftile--folder {
  background: none;
  border-radius: 0;
  color: var(--fe-icon-folder);
}
.fe-ftile--folder .fe-ficon {
  width: 1em;
  height: 1em;
}
.fe-ftile--image { background: var(--fe-icon-image); }
.fe-ftile--video { background: var(--fe-icon-video); }
.fe-ftile--audio { background: var(--fe-icon-audio); }
.fe-ftile--pdf { background: var(--fe-icon-pdf); }
.fe-ftile--doc { background: var(--fe-icon-doc); }
.fe-ftile--sheet { background: var(--fe-icon-sheet); }
.fe-ftile--slides { background: var(--fe-icon-slides); }
.fe-ftile--archive { background: var(--fe-icon-archive); }
.fe-ftile--code { background: var(--fe-icon-code); }
.fe-ftile--text { background: var(--fe-icon-text); }
.fe-ftile--unknown { background: var(--fe-icon-unknown); }

.fe-grid__icon--svg {
  display: flex;
  align-items: center;
  justify-content: center;
}
.fe-list__icon--svg {
  display: inline-flex;
  align-items: center;
}

/* Sortable list column headers — plain-looking buttons, arrow only when
 * the column drives the order. */
.fe-list__sort {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: transparent;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
  cursor: pointer;
}
.fe-list__sort:hover {
  color: var(--fe-text);
}
.fe-list__sort:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 2px;
  border-radius: 2px;
}
.fe-list__sort-arrow {
  font-size: 11px;
  color: var(--fe-primary);
}

/* Date group headers (only while sorted by modified date) — thin, muted,
 * non-interactive. */
.fe-list__group {
  padding: 12px 12px 4px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--fe-text-muted);
  user-select: none;
}

/* Density: compact — tighter rows/cards, smaller grid tiles. Toggled from
 * the toolbar; class lives on the `.fe` root. */
.fe--density-compact .fe-toolbar {
  padding: 6px 8px;
  gap: 6px;
}
.fe--density-compact .fe-breadcrumb {
  padding: 4px 12px;
}
.fe--density-compact .fe-list__head {
  padding: 4px 12px;
}
.fe--density-compact .fe-list__row {
  padding: 3px 12px;
}
.fe--density-compact .fe-list__icon {
  font-size: 14px;
}
.fe--density-compact .fe-list__group {
  padding: 8px 12px 2px;
}
.fe--density-compact .fe-grid {
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: 8px;
  padding: 8px;
}
.fe--density-compact .fe-grid__card {
  padding: 6px;
}
.fe--density-compact .fe-grid__icon {
  font-size: 24px;
}


/* === cila:c — command palette + shortcuts help + breadcrumb overflow === */

/* Command palette (Ctrl/Cmd+K) — top-anchored launcher card. */
.fe-cmdp__backdrop {
  align-items: flex-start;
  padding-top: min(18vh, 160px);
}
.fe-cmdp {
  background: var(--fe-bg);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-lg);
  box-shadow: var(--fe-shadow);
  width: min(560px, calc(100vw - 32px));
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.fe-cmdp__inputwrap {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--fe-border);
  color: var(--fe-text-muted);
}
.fe-cmdp__glyph {
  flex: 0 0 auto;
}
.fe-cmdp__input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  outline: none;
  background: transparent;
  color: var(--fe-text);
  font: inherit;
  font-size: 15px;
}
.fe-cmdp__input::placeholder {
  color: var(--fe-text-muted);
}
.fe-cmdp__list {
  max-height: min(48vh, 380px);
  overflow-y: auto;
  padding: 6px;
}
.fe-cmdp__group {
  padding: 8px 10px 4px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--fe-text-muted);
}
.fe-cmdp__item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: start;
  background: transparent;
  border: 0;
  border-radius: var(--fe-radius-sm);
  padding: 8px 10px;
  color: var(--fe-text);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}
.fe-cmdp__item.is-active {
  background: var(--fe-bg-selected);
}
.fe-cmdp__item:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}
.fe-cmdp__icon {
  flex: 0 0 auto;
  width: 20px;
  text-align: center;
}
.fe-cmdp__label {
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-cmdp__empty {
  padding: 20px 12px;
  text-align: center;
  color: var(--fe-text-muted);
  font-size: 13px;
}
.fe-cmdp__hint {
  padding: 8px 14px;
  border-top: 1px solid var(--fe-border);
  background: var(--fe-bg-elev);
  font-size: 11px;
  color: var(--fe-text-muted);
}

/* Shortcuts help ("?") — grouped cheat-sheet inside the standard modal. */
.fe-shortcuts {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.fe-shortcuts__heading {
  margin: 0 0 6px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--fe-text-muted);
}
.fe-shortcuts__table {
  display: flex;
  flex-direction: column;
}
.fe-shortcuts__row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 4px 0;
}
.fe-shortcuts__keys {
  flex: 0 0 40%;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.fe-shortcuts__desc {
  flex: 1 1 auto;
  font-size: 13px;
  color: var(--fe-text);
}
.fe-shortcuts__or {
  color: var(--fe-text-muted);
  font-size: 11px;
}
.fe-kbd {
  display: inline-block;
  padding: 2px 7px;
  border: 1px solid var(--fe-border-strong);
  border-bottom-width: 2px;
  border-radius: var(--fe-radius-sm);
  background: var(--fe-bg-elev);
  color: var(--fe-text);
  font-family: var(--fe-font-mono);
  font-size: 11.5px;
  line-height: 1.4;
}

/* Breadcrumb overflow — "…" trigger + dropdown for the collapsed middle. */
.fe-breadcrumb__more-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.fe-breadcrumb__more {
  background: transparent;
  border: 0;
  color: inherit;
  padding: 2px 8px;
  border-radius: var(--fe-radius-sm);
  cursor: pointer;
  font: inherit;
  line-height: 1.2;
}
.fe-breadcrumb__more:hover,
.fe-breadcrumb__more[aria-expanded='true'] {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-breadcrumb__more:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}
.fe-breadcrumb__menu {
  position: absolute;
  top: calc(100% + 4px);
  inset-inline-start: 0;
  z-index: 80;
  min-width: 160px;
  max-width: 280px;
  max-height: 40vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  padding: 4px;
  background: var(--fe-bg);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  box-shadow: var(--fe-shadow);
}
.fe-breadcrumb__menu-item {
  text-align: start;
  background: transparent;
  border: 0;
  padding: 6px 10px;
  border-radius: var(--fe-radius-sm);
  color: var(--fe-text);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-breadcrumb__menu-item:hover,
.fe-breadcrumb__menu-item:focus-visible {
  background: var(--fe-bg-hover);
}

/* Long folder names shouldn't blow up the crumb row — ellipsize labels. */
.fe-breadcrumb__crumb > span:first-child {
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
@media (max-width: 640px) {
  .fe-breadcrumb__crumb > span:first-child {
    max-width: 110px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .fe-modal-enter-active,
  .fe-modal-leave-active {
    transition: none !important;
  }
}


/* === cila:b — skeleton loading, state screens, undo snackbar, connection badge === */

/* Screen-reader-only helper (skeleton announces "loading" without visuals). */
.fe-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Skeleton (initial load) ---------- */

.fe__skeleton {
  min-height: 240px;
}
.fe-skel {
  position: relative;
  overflow: hidden;
  border-radius: var(--fe-radius-sm);
  background: color-mix(in srgb, var(--fe-text-muted) 14%, transparent);
}
.fe-skel::after {
  content: '';
  position: absolute;
  inset: 0;
  /* ⚠ RTL: the sweep runs the way the line reads (--filex-dir-x, top of file). */
  transform: translateX(calc(-100% * var(--filex-dir-x, 1)));
  background: linear-gradient(
    90deg,
    transparent,
    color-mix(in srgb, var(--fe-text-muted) 12%, transparent),
    transparent
  );
  animation: fe-skel-shimmer 1.4s ease-in-out infinite;
}
@keyframes fe-skel-shimmer {
  to { transform: translateX(calc(100% * var(--filex-dir-x, 1))); }
}
@media (prefers-reduced-motion: reduce) {
  /* Static faint blocks — no sweep. */
  .fe-skel::after {
    animation: none;
    content: none;
  }
}

/* List-mode ghosts mirror .fe-list__row's grid so the swap to real rows
   doesn't shift the layout. */
.fe-skel-list {
  padding-top: 2px;
}
.fe-skel-row {
  display: grid;
  grid-template-columns: 32px minmax(0, 1fr) 120px 180px;
  gap: 8px;
  align-items: center;
  padding: 12px;
  border-bottom: 1px solid var(--fe-border);
}
.fe-skel-row .fe-skel {
  height: 12px;
}
.fe-skel-row .fe-skel--icon {
  width: 20px;
  height: 20px;
  border-radius: 6px;
}
.fe-skel-row .fe-skel--name { width: 55%; }
.fe-skel-row:nth-child(2n) .fe-skel--name { width: 38%; }
.fe-skel-row:nth-child(3n) .fe-skel--name { width: 70%; }
.fe-skel-row .fe-skel--size { width: 56px; justify-self: start; }
.fe-skel-row .fe-skel--date { width: 110px; justify-self: start; }

/* Grid-mode ghosts mirror .fe-grid / .fe-grid__card. */
.fe-skel-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: var(--fe-gap);
  padding: var(--fe-gap);
}
.fe-skel-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg-elev);
}
.fe-skel-card .fe-skel--thumb {
  aspect-ratio: 1 / 1;
}
.fe-skel-card .fe-skel--label {
  height: 12px;
  width: 70%;
}
.fe-skel-card:nth-child(2n) .fe-skel--label { width: 50%; }
.fe-skel-card:nth-child(3n) .fe-skel--label { width: 85%; }

/* ---------- State screens (empty / search-empty / not-found / error) ---------- */

.fe-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 240px;
  height: 100%;
  padding: 24px;
  text-align: center;
  color: var(--fe-text-muted);
}
.fe-state__art {
  color: var(--fe-text-muted);
  opacity: 0.7;
  margin-bottom: 4px;
  flex: 0 0 auto;
}
.fe-state__title {
  margin: 2px 0 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--fe-text);
}
.fe-state__hint {
  margin: 0;
  font-size: 13px;
  max-width: 420px;
}
.fe-state__path {
  margin: 0;
  font-size: 12px;
  font-family: var(--fe-font-mono);
  word-break: break-all;
  max-width: 480px;
}
.fe-state__actions {
  margin-top: 12px;
  display: flex;
  gap: 8px;
}

/* ---------- Undo snackbar (action-capable toast) ---------- */

.fe-toast--action {
  display: flex;
  align-items: center;
  gap: 14px;
  padding-block: 8px; padding-inline: 14px 10px;
  cursor: pointer;
  max-width: min(480px, calc(100% - 32px));
}
.fe-toast__msg {
  min-width: 0;
}
.fe-toast__action {
  flex: 0 0 auto;
  background: transparent;
  border: 0;
  padding: 4px 8px;
  border-radius: var(--fe-radius-sm);
  font: inherit;
  font-weight: 600;
  color: color-mix(in srgb, var(--fe-primary) 70%, var(--fe-bg));
  cursor: pointer;
}
.fe-toast__action:hover {
  background: color-mix(in srgb, var(--fe-bg) 14%, transparent);
}
.fe-toast__action:focus-visible {
  outline: 2px solid var(--fe-bg);
  outline-offset: 1px;
}

/* ---------- Connection badge (degraded realtime only) ---------- */

.fe-connbadge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  line-height: 1.4;
  color: var(--fe-text-muted);
  background: var(--fe-bg-elev);
  border: 1px solid var(--fe-border);
  border-radius: 999px;
  padding: 2px 10px;
  cursor: help;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.fe-connbadge__dot {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--fe-warning, #f59e0b);
}

/* The one line a page shows while the SERVER cannot be reached at all —
   components/ConnectionNotice.vue, drawn from the shared folding rule in
   lib/connection instead of a toast per failed request.
   ⚠⚠ Here and not in the component's own `<style>`: a single-file component's
   styles are emitted only into a bundle that IMPORTS it, and the web-component
   distribution mounts the explorer without this one — so the sheet it ships
   lost these rules while core's kept them, and
   web/tests/deploy/packageLook.test.ts ("the two shipped stylesheets have
   drifted") caught it. base.css is in both.
   ⚠ Logical properties only; web/tests/quality/rtlLogical.test.ts refuses the
   physical spellings, and this strip is drawn in Arabic too.
   ⚠ Only tokens declared in styles/variables.css — a `--fe-*` invented at a
   call site is a phantom to every other reader of the theme
   (web/tests/api/themeTokens.test.ts). The amber is carried by the border and
   the dot, the way the connection badge above says it. */
.fe-connlost {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 6px 12px;
  font-size: 13px;
  line-height: 1.4;
  text-align: center;
  color: var(--fe-text);
  background: var(--fe-bg-elev);
  border-block-end: 1px solid var(--fe-warning);
  /* A statement, not a control: it must never take a click from what is
     underneath it. */
  pointer-events: none;
}
.fe-connlost__dot {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--fe-warning);
}
.fe-connlost__text {
  min-width: 0;
}

/* `filex 0.43.0` — which filex this is, at the foot of an account menu and in
   the user settings dialog's head (components/ProductVersion.vue). A quiet
   line: the muted colour and the smallest size, selectable so it can be
   copied into a bug report. ⚠ Here, not in the component: see `.fe-connlost`
   above for why (lesson #416). */
.fe-version {
  margin: 0;
  font-size: var(--fe-text-xs);
  line-height: 1.4;
  color: var(--fe-text-muted);
  font-variant-numeric: tabular-nums;
  user-select: text;
  white-space: nowrap;
  /* A string longer than the menu is cut, never scrolled: the release alone
     fits, and a menu must not grow a sideways scroll bar (2026-09-26). */
  overflow: hidden;
  text-overflow: ellipsis;
}

/* === bul:s3 === */

/* Palette — "Everywhere" hit rows (name + crumb + snippet stack). */
.fe-cmdp__item--hit {
  align-items: flex-start;
}
.fe-cmdp__item--hit .fe-cmdp__icon {
  margin-top: 1px;
}
.fe-cmdp__hitbody {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.fe-cmdp__hitline {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.fe-cmdp__badge,
.fe-list__badge {
  flex: 0 0 auto;
  display: inline-block;
  padding: 1px 6px;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--fe-primary) 35%, transparent);
  background: color-mix(in srgb, var(--fe-primary) 12%, transparent);
  color: var(--fe-primary);
  font-size: 10px;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
  vertical-align: middle;
}
.fe-cmdp__crumb {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 11.5px;
  color: var(--fe-text-muted);
}
.fe-cmdp__snippet,
.fe-list__snippet {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
  color: var(--fe-text-muted);
}
.fe-cmdp__mark,
.fe-list__mark,
.fe-grid__mark {
  background: color-mix(in srgb, var(--fe-primary) 22%, transparent);
  color: var(--fe-text);
  border-radius: 2px;
  padding: 0 1px;
  font-weight: 600;
}
.fe-cmdp__loading {
  padding: 8px 12px;
  font-size: 13px;
  color: var(--fe-text-muted);
}

/* Palette — saved-search rows (div-based so the delete button nests legally). */
.fe-cmdp__item--saved {
  user-select: none;
}
.fe-cmdp__del {
  flex: 0 0 auto;
  background: transparent;
  border: 0;
  border-radius: var(--fe-radius-sm);
  padding: 2px 6px;
  font: inherit;
  font-size: 13px;
  color: var(--fe-text-muted);
  cursor: pointer;
  opacity: 0.55;
}
.fe-cmdp__item--saved:hover .fe-cmdp__del,
.fe-cmdp__item--saved.is-active .fe-cmdp__del {
  opacity: 1;
}
.fe-cmdp__del:hover {
  background: var(--fe-bg-elev);
  color: var(--fe-danger, #dc2626);
}
.fe-cmdp__del:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}

/* #47 — a search hit's own verbs: a Download button on the row, the row
   itself draggable, and one badge per signed-in account above its rows. */
.fe-cmdp__item--hit {
  user-select: none;
}
.fe-cmdp__hitact {
  flex: 0 0 auto;
  align-self: center;
  background: transparent;
  border: 0;
  border-radius: var(--fe-radius-sm);
  padding: 2px 6px;
  font: inherit;
  color: var(--fe-text-muted);
  cursor: pointer;
  opacity: 0.55;
}
.fe-cmdp__item--hit:hover .fe-cmdp__hitact,
.fe-cmdp__item--hit.is-active .fe-cmdp__hitact {
  opacity: 1;
}
.fe-cmdp__hitact:hover {
  background: var(--fe-bg-elev);
  color: var(--fe-primary);
}
.fe-cmdp__hitact:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
  opacity: 1;
}
.fe-cmdp__hitact .fe-aicon {
  width: 16px;
  height: 16px;
}
.fe-cmdp__acct {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 8px 10px 2px;
  min-width: 0;
  font-size: 12px;
  color: var(--fe-text);
}
.fe-cmdp__acct-dot {
  flex: 0 0 auto;
  align-self: center;
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: var(--fe-primary);
}
.fe-cmdp__acct-label {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-cmdp__acct-detail {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--fe-text-muted);
}

/* List view — search-result snippet line under the filename. */
.fe-list__snippet {
  max-width: 100%;
}

/* === bag:b4 — narrow/embed mini mode === */
/* Everything below is scoped to `.fe--narrow`, `.fe-toolbar--narrow`,
 * `.fe-fab` or `.fe-sheet` — none of which exist in the wide desktop
 * layout, so the classic look is untouched when the mode is off. */

/* Narrow toolbar: a single non-wrapping row of icon buttons. */
.fe-toolbar--narrow {
  flex-wrap: nowrap;
  gap: 6px;
}
.fe-toolbar--narrow .fe-btn.is-active {
  background: var(--fe-bg-selected);
}
/* Expanded search: the input takes the whole row. */
.fe-toolbar--narrow .fe-search--full {
  flex: 1 1 auto;
  min-width: 0;
}
.fe-toolbar--narrow .fe-search--full .fe-search__input {
  width: 100%;
  min-width: 0;
}

/* Upload FAB — bottom-right corner of the explorer (narrow mode only). */
.fe-fab {
  position: absolute;
  inset-inline-end: 16px;
  bottom: 16px;
  width: 56px;
  height: 56px;
  border: 0;
  border-radius: 50%;
  background: var(--fe-primary);
  color: var(--fe-text-on-primary);
  font-size: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--fe-shadow);
  cursor: pointer;
  z-index: 45;
  transition: background 0.15s, transform 0.15s;
}
.fe-fab:hover {
  background: var(--fe-primary-hover);
}
.fe-fab:active {
  transform: scale(0.94);
}
.fe-fab:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 3px;
}
/* Keep the corner trays clear of the FAB while narrow. */
.fe--narrow .fe-upload,
.fe--narrow .fe-ops {
  bottom: 84px;
}

/* Bottom sheet (ContextMenu sheet variant, coarse-pointer devices). */
.fe-ctx-backdrop--sheet {
  background: rgba(13, 19, 27, 0.42);
}
.fe-sheet {
  position: fixed;
  inset-inline-start: 0;
  inset-inline-end: 0;
  bottom: 0;
  max-height: 70vh;
  display: flex;
  flex-direction: column;
  background: var(--fe-bg);
  color: var(--fe-text);
  border: 1px solid var(--fe-border);
  border-bottom: 0;
  border-start-start-radius: var(--fe-radius-lg);
  border-start-end-radius: var(--fe-radius-lg);
  box-shadow: 0 -10px 32px rgba(15, 23, 42, 0.28);
  padding: 0 8px calc(8px + env(safe-area-inset-bottom, 0px));
  outline: none;
  z-index: 90;
}
.fe-sheet__handle {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px 0 8px;
  cursor: grab;
  touch-action: none;
}
.fe-sheet__handle::before {
  content: '';
  width: 40px;
  height: 4px;
  border-radius: 99px;
  background: var(--fe-border-strong);
}
.fe-sheet__items {
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
/* Big touch targets (≥44px) on sheet entries. */
.fe-sheet__item {
  min-height: 44px;
  padding: 10px 14px;
  font-size: 15px;
  gap: 12px;
}
/* Sheet transitions: backdrop fades, panel slides up from the bottom. */
.fe-sheet-enter-active,
.fe-sheet-leave-active {
  transition: opacity 0.18s ease;
}
.fe-sheet-enter-active .fe-sheet,
.fe-sheet-leave-active .fe-sheet {
  transition: transform 0.22s ease;
}
.fe-sheet-enter-from,
.fe-sheet-leave-to {
  opacity: 0;
}
.fe-sheet-enter-from .fe-sheet,
.fe-sheet-leave-to .fe-sheet {
  transform: translateY(100%);
}
@media (prefers-reduced-motion: reduce) {
  .fe-sheet-enter-active,
  .fe-sheet-leave-active,
  .fe-sheet-enter-active .fe-sheet,
  .fe-sheet-leave-active .fe-sheet,
  .fe-fab {
    transition: none;
  }
}

/* === koru:k1 — inspector (details) panel === */

/* fe__main — horizontal flex wrapper: listing body + inspector side panel.
   Takes over fe__body's old role as .fe's flexible middle region; fe__body
   keeps its own rules (flex:1 1 auto now distributes WIDTH inside the row). */
.fe__main {
  display: flex;
  flex-direction: row;
  flex: 1 1 auto;
  min-height: 0;
}
.fe__main > .fe__body {
  min-width: 0;
  height: 100%;
}

.fe-inspector {
  flex: 0 0 300px;
  width: 300px;
  min-height: 0;
  display: flex;
  flex-direction: column;
  border-inline-start: 1px solid var(--fe-border);
  background: var(--fe-bg);
  color: var(--fe-text);
}
/* Narrow embeds: full-size overlay on top of the explorer (below modals and
   context menus, above the listing + FAB). */
.fe-inspector--overlay {
  position: absolute;
  inset: 0;
  z-index: 60;
  width: auto;
  border-inline-start: none;
}

.fe-inspector__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--fe-border);
  flex: 0 0 auto;
}
.fe-inspector__title {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.fe-inspector__close {
  border: none;
  background: none;
  color: var(--fe-text-muted);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  padding: 2px 6px;
  border-radius: 6px;
}
.fe-inspector__close:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}

.fe-inspector__scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 12px;
}

.fe-inspector__section {
  margin-bottom: 18px;
}
.fe-inspector__section:last-child {
  margin-bottom: 0;
}
.fe-inspector__heading {
  margin: 0 0 8px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--fe-text-muted);
}

.fe-inspector__hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  text-align: center;
  padding: 6px 0 4px;
}
.fe-inspector__bigicon {
  display: inline-flex;
}
.fe-inspector__bigicon .fe-ficon {
  width: 56px;
  height: 56px;
}
.fe-inspector__thumb {
  width: 96px;
  height: 96px;
  object-fit: cover;
  border-radius: var(--fe-radius);
  border: 1px solid var(--fe-border);
}
.fe-inspector__name {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  word-break: break-word;
  max-width: 100%;
}
.fe-inspector__sub {
  margin: 0;
  font-size: 12px;
  color: var(--fe-text-muted);
}

.fe-inspector__meta {
  margin: 8px 0 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.fe-inspector__row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 12px;
}
.fe-inspector__row dt {
  flex: 0 0 84px;
  color: var(--fe-text-muted);
}
.fe-inspector__row dd {
  margin: 0;
  min-width: 0;
  flex: 1 1 auto;
  word-break: break-word;
}
.fe-inspector__pathcell {
  display: flex;
  align-items: center;
  gap: 4px;
}
.fe-inspector__path {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  direction: rtl; /* keep the tail (basename) visible when truncated */
  text-align: start;
}
.fe-inspector__mime {
  font-family: var(--fe-font-mono);
  font-size: 11px;
}
.fe-inspector__copy {
  flex: 0 0 auto;
  border: none;
  background: none;
  color: var(--fe-text-muted);
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  padding: 2px 4px;
  border-radius: 4px;
}
.fe-inspector__copy:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}

.fe-inspector__empty {
  margin: 0;
  font-size: 12px;
  color: var(--fe-text-muted);
}

/* Small buttons used inside the panel rows. */
.fe-btn--sm {
  padding: 3px 8px;
  font-size: 12px;
}

.fe-inspector__versions {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.fe-inspector__version {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
}
.fe-inspector__version-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.fe-inspector__version-n {
  font-size: 12px;
  font-weight: 600;
}
.fe-inspector__version-meta {
  font-size: 11px;
  color: var(--fe-text-muted);
}
.fe-inspector__confirm {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.fe-inspector__confirm-q {
  margin: 0;
  font-size: 12px;
}
.fe-inspector__check {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: var(--fe-text-muted);
  cursor: pointer;
}
.fe-inspector__confirm-actions {
  display: flex;
  gap: 6px;
}
.fe-inspector__snapshot {
  margin-top: 8px;
  width: 100%;
}

.fe-inspector__permrow {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.fe-inspector__badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 99px;
  font-size: 11px;
  font-weight: 600;
  border: 1px solid var(--fe-border-strong);
  color: var(--fe-text);
  background: var(--fe-bg-hover);
}
.fe-inspector__badge--owner {
  border-color: var(--fe-primary);
  color: var(--fe-primary);
  background: transparent;
}
.fe-inspector__badge--none {
  color: var(--fe-text-muted);
}
.fe-inspector__badge--pin {
  border-color: var(--fe-warning, #f59e0b);
  color: var(--fe-warning, #f59e0b);
  background: transparent;
}

.fe-inspector__shares {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.fe-inspector__share {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  min-width: 0;
}
.fe-inspector__share-url {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-inspector__share-exp {
  flex: 0 0 auto;
  font-size: 11px;
  color: var(--fe-text-muted);
}

/* Slide-in is subtle and honors prefers-reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
  .fe-inspector {
    animation: fe-inspector-in 0.16s ease;
  }
  @keyframes fe-inspector-in {
    from {
      opacity: 0;
      /* From the inspector's own edge — the inline END, left in RTL. */
      transform: translateX(calc(8px * var(--filex-dir-x, 1)));
    }
    to {
      opacity: 1;
      transform: none;
    }
  }
}

/* wiring:c2 — shortcut settings modal + quick-look hint bar */

.fe-shortcuts__unbound {
  font-size: 12px;
  font-style: italic;
  color: var(--fe-text-muted);
}
.fe-shortcuts__custom {
  margin-inline-start: 4px;
  font-size: 9px;
  vertical-align: 2px;
  color: var(--fe-primary);
}

.fe-shortset {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.fe-shortset__topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.fe-shortset__hint {
  margin: 0;
  font-size: 12px;
  color: var(--fe-text-muted);
  flex: 1 1 auto;
}
.fe-shortset__reset-all {
  flex: 0 0 auto;
}
.fe-shortset__group {
  display: flex;
  flex-direction: column;
}
.fe-shortset__item {
  border-radius: var(--fe-radius-sm);
  padding: 2px 6px;
  margin: 0 -6px;
}
.fe-shortset__item.is-capturing {
  background: var(--fe-bg-hover);
  outline: 1px dashed var(--fe-primary);
  outline-offset: -1px;
}
.fe-shortset__item.is-conflict {
  background: color-mix(in srgb, var(--fe-danger) 8%, transparent);
  outline: 1px solid var(--fe-danger);
  outline-offset: -1px;
}
.fe-shortset__row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 4px 0;
  min-height: 34px;
}
.fe-shortset__label {
  flex: 1 1 auto;
  font-size: 13px;
  color: var(--fe-text);
}
.fe-shortset__keys {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  justify-content: flex-end;
}
.fe-shortset__capture {
  font-size: 12px;
  color: var(--fe-primary);
  animation: fe-shortset-blink 1.1s ease-in-out infinite;
}
@keyframes fe-shortset-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}
.fe-shortset__actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 118px;
  justify-content: flex-end;
}
.fe-shortset__btn {
  padding: 3px 9px;
  font-size: 12px;
}
/* tus:t1 — "Desktop app only" on a combo the browser keeps for itself. */
.fe-shortset__reserved {
  margin-inline-start: 6px;
  font-size: 10.5px;
  color: var(--fe-text-muted);
  border: 1px dashed var(--fe-border);
  border-radius: var(--fe-radius-sm);
  padding: 1px 6px;
  white-space: nowrap;
}
.fe-shortset__fixed {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fe-text-muted);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  padding: 2px 7px;
}
.fe-shortset__conflict {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 6px 2px 8px;
  font-size: 12.5px;
  color: var(--fe-danger);
}
.fe-shortset__conflict-msg {
  flex: 1 1 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 200px;
}
.fe-shortset__conflict-actions {
  flex: 0 0 auto;
  display: flex;
  gap: 6px;
}

/* Quick-look hint bar — floats above the PreviewModal backdrop (z 70).
 * Carries the `.fe` class for the theme-variable cascade, so the `.fe`
 * root layout defaults (column flex, min-height 420px, 100% height)
 * must be explicitly neutralised here.
 *
 * Written as `.fe.fe-ql-hint` on purpose: a single class ties with the
 * `.fe` root rule and would be decided by source order alone, which is
 * not something a host embed can be trusted to preserve. The hint is
 * also Teleported under <body> (QuickLook.vue) so a host container's
 * own `.fe` override cannot reach it by descendant — issue #22, where
 * the pill grew to the full viewport height inside the admin UI. */
.fe.fe-ql-hint {
  position: fixed;
  left: 50%; /* rtl-physical: centred by translate(-50%) — the same place in both directions */
  bottom: 14px;
  transform: translateX(-50%);
  z-index: 72;
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  width: auto;
  height: auto;
  min-height: 0;
  gap: 6px;
  padding: 6px 12px;
  border-radius: 999px;
  border: 1px solid var(--fe-border);
  background: var(--fe-bg-elev);
  color: var(--fe-text-muted);
  font-size: 12px;
  box-shadow: var(--fe-shadow);
  pointer-events: none;
  white-space: nowrap;
}
.fe-ql-hint .fe-kbd {
  font-size: 10.5px;
  padding: 1px 5px;
}
.fe-ql-hint__sep {
  opacity: 0.5;
}
/* One key-cap group plus its label, kept together so the pill can only
 * break between segments. */
.fe-ql-hint__seg {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
/* /wiring:c2 */
/* =========================================================
 * wiring:c3 — Operations center (unified upload + ops-queue surface)
 * Badge bottom-right (count + aggregate progress ring), expanding
 * panel with active ops + session history. Replaces the old
 * .fe-upload / .fe-ops corner trays (those components are now
 * renderless publishers — their CSS above is retained but unused).
 * ================================================================ */

.fe-opc {
  position: absolute;
  inset-inline-end: 16px;
  bottom: 16px;
  z-index: 45;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
  max-width: calc(100% - 32px);
}

.fe-opc__badge {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-block: 8px; padding-inline: 10px 14px;
  border-radius: 999px;
  border: 1px solid var(--fe-border);
  background: var(--fe-bg);
  color: var(--fe-text);
  box-shadow: var(--fe-shadow);
  cursor: pointer;
  font: inherit;
  font-size: 13px;
  line-height: 1;
}
.fe-opc__badge:hover {
  background: var(--fe-bg-hover);
}
.fe-opc__badge:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 2px;
}
.fe-opc__badge.is-error {
  border-color: var(--fe-danger);
}
.fe-opc__badge.is-error .fe-opc__count {
  color: var(--fe-danger);
}

.fe-opc__ring {
  width: 22px;
  height: 22px;
  flex: 0 0 auto;
}
.fe-opc__ring-track {
  stroke: var(--fe-border-strong);
}
.fe-opc__ring-fill {
  stroke: var(--fe-primary);
  transition: stroke-dashoffset 0.25s;
}
.fe-opc__badge.is-error .fe-opc__ring-fill {
  stroke: var(--fe-danger);
}
.fe-opc__ring--spin {
  animation: fe-opc-spin 1s linear infinite;
}
@keyframes fe-opc-spin {
  to {
    transform: rotate(360deg);
  }
}
.fe-opc__count {
  font-weight: 600;
}
.fe-opc__pct {
  color: var(--fe-text-muted);
  font-size: 12px;
}

.fe-opc__panel {
  width: min(380px, calc(100vw - 32px));
  max-width: 100%;
  max-height: min(420px, 60vh);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--fe-bg);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-lg);
  box-shadow: var(--fe-shadow);
}
.fe-opc__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  background: var(--fe-bg-elev);
  border-bottom: 1px solid var(--fe-border);
  font-size: 13px;
}
.fe-opc__body {
  overflow: auto;
  padding: 6px 0 8px;
}
.fe-opc__section + .fe-opc__section {
  border-top: 1px solid var(--fe-border);
  margin-top: 6px;
}
.fe-opc__section-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 0;
  padding: 8px 14px 4px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fe-text-muted);
}
.fe-opc__list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.fe-opc__item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 8px 14px;
}
.fe-opc__item + .fe-opc__item {
  border-top: 1px solid var(--fe-border);
}
.fe-opc__item.is-error {
  background: color-mix(in srgb, var(--fe-danger) 7%, transparent);
}
.fe-opc__kicon {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  margin-top: 2px;
  color: var(--fe-text-muted);
}
.fe-opc__item.is-error .fe-opc__kicon {
  color: var(--fe-danger);
}
.fe-opc__mid {
  flex: 1 1 auto;
  min-width: 0;
}
.fe-opc__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 13px;
}
.fe-opc__sub {
  display: flex;
  align-items: baseline;
  gap: 5px;
  font-size: 11px;
  color: var(--fe-text-muted);
  margin-top: 1px;
  min-width: 0;
}
.fe-opc__sub--err,
.fe-opc__item.is-error .fe-opc__state {
  color: var(--fe-danger);
}
/* An administrator's second line under a failure: the raw words behind the
   sentence (lib/errorWords). Muted and monospace — it is evidence, not the
   message. */
.fe-opc__sub--detail {
  font-family: var(--fe-font-mono, ui-monospace, monospace);
  font-size: 10.5px;
  overflow-wrap: anywhere;
}
/* An app job's output mode ("new file" / "new version") — the one fact a
   finished row cannot show any other way, because in `version` mode "Open"
   lands on the file the job started from. */
.fe-opc__outmode {
  flex: 0 0 auto;
  padding: 0 5px;
  border: 1px solid var(--fe-border);
  border-radius: 8px;
  line-height: 15px;
  white-space: nowrap;
  cursor: help;
}
.fe-opc__state {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-opc__bar {
  background: var(--fe-bg-elev);
  border-radius: 99px;
  height: 5px;
  overflow: hidden;
  margin-top: 5px;
}
.fe-opc__bar-fill {
  background: var(--fe-primary);
  height: 100%;
  transition: width 0.2s;
}
.fe-opc__spin {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  margin-top: 4px;
  border-radius: 50%;
  border: 2px solid var(--fe-border-strong);
  border-top-color: var(--fe-primary);
  animation: fe-opc-spin 0.8s linear infinite;
}
.fe-opc__acts {
  display: flex;
  align-items: center;
  gap: 2px;
  flex: 0 0 auto;
}
.fe-opc__act {
  background: transparent;
  border: 0;
  color: var(--fe-primary);
  cursor: pointer;
  font: inherit;
  font-size: 12px;
  padding: 2px 4px;
  border-radius: var(--fe-radius-sm);
}
.fe-opc__act:hover {
  background: var(--fe-bg-hover);
}
.fe-opc__act--retry {
  font-weight: 600;
}
.fe-opc__x {
  background: transparent;
  border: 0;
  color: var(--fe-text-muted);
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  padding: 0 4px;
  border-radius: var(--fe-radius-sm);
}
.fe-opc__x:hover {
  color: var(--fe-text);
}
.fe-opc__item--history .fe-opc__name {
  font-size: 12px;
  color: var(--fe-text-muted);
}
.fe-opc__chip {
  flex: 0 0 auto;
  font-size: 10px;
  font-weight: 600;
  padding: 2px 7px;
  border-radius: 999px;
  background: var(--fe-bg-elev);
  color: var(--fe-text-muted);
}
.fe-opc__chip.is-done {
  color: var(--fe-ok, #16a34a);
  background: color-mix(in srgb, var(--fe-ok, #16a34a) 12%, transparent);
}
.fe-opc__chip.is-error {
  color: var(--fe-danger);
  background: color-mix(in srgb, var(--fe-danger) 12%, transparent);
}
.fe-opc__item.is-done .fe-opc__bar-fill {
  background: var(--fe-ok, #16a34a);
}
.fe-opc__empty {
  margin: 0;
  padding: 16px 14px;
  font-size: 12px;
  color: var(--fe-text-muted);
  text-align: center;
}

.fe-opc-pop-enter-active,
.fe-opc-pop-leave-active {
  transition: transform 0.16s ease, opacity 0.16s ease;
}
.fe-opc-pop-enter-from,
.fe-opc-pop-leave-to {
  opacity: 0;
  transform: translateY(8px) scale(0.98);
}

/* Narrow / embedded: clear the FAB and behave like a bottom sheet —
 * badge stays compact at the right, panel stretches edge to edge. */
.fe--narrow .fe-opc {
  inset-inline-start: 12px;
  inset-inline-end: 12px;
  bottom: 84px;
  max-width: none;
}
.fe--narrow .fe-opc__panel {
  width: 100%;
  max-height: 50vh;
}

@media (prefers-reduced-motion: reduce) {
  .fe-opc__ring--spin,
  .fe-opc__spin {
    animation-duration: 3s;
  }
  .fe-opc-pop-enter-active,
  .fe-opc-pop-leave-active {
    transition: none;
  }
}
/* /wiring:c3 */
/* === wiring:c4 — onboarding tour, accessibility and interaction polish === */

/* ---------- Focus ring: grid cards (list rows already had one) ---------- */
.fe-grid__card:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 2px;
}
.fe-ctx__item:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
  background: var(--fe-bg-hover);
}

/* ---------- Drag & drop feedback ---------- */

/* The folder currently hovered by an internal drag. Dashed ring + tinted
   background so it reads differently from plain selection. */
.fe-list__row.is-droptarget,
.fe-grid__card.is-droptarget {
  outline: 2px dashed var(--fe-primary);
  outline-offset: -2px;
  background: var(--fe-bg-selected);
}
.fe-grid__card.is-droptarget {
  border-color: var(--fe-primary);
}

/* Custom drag ghost (lib/dragGhost.ts) — appended to <body>, themed via the
   :root/.dark token publication. */
.fe-dragghost {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: 260px;
  padding: 6px 10px;
  border: 1px solid var(--fe-border-strong);
  border-radius: var(--fe-radius);
  background: var(--fe-bg-elev);
  color: var(--fe-text);
  font-family: var(--fe-font);
  font-size: 13px;
  box-shadow: var(--fe-shadow-sm);
  pointer-events: none;
  z-index: 96;
}
.fe-dragghost__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-dragghost__badge {
  flex: 0 0 auto;
  min-width: 20px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--fe-primary);
  color: var(--fe-text-on-primary);
  font-size: 12px;
  font-weight: 600;
  text-align: center;
}

/* ---------- Error state: technical detail disclosure ---------- */
.fe-state__details {
  margin-top: 10px;
  max-width: 480px;
  font-size: 12px;
  text-align: start;
}
.fe-state__details-summary {
  cursor: pointer;
  text-align: center;
  color: var(--fe-text-muted);
  user-select: none;
}
.fe-state__details-summary:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 2px;
}
.fe-state__details-pre {
  margin: 8px 0 0;
  padding: 8px 10px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  background: var(--fe-bg-elev);
  font-family: var(--fe-font-mono);
  font-size: 11px;
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 160px;
  overflow: auto;
}

/* ---------- Onboarding tour ---------- */

.fe-tour {
  position: fixed;
  inset: 0;
  z-index: 96; /* above the command palette (95) */
  color: var(--fe-text);
  font-family: var(--fe-font);
  font-size: 14px;
}
/* Spotlight cutout: the element itself is transparent; the dim layer is the
   giant box-shadow around it. */
.fe-tour__spot {
  position: fixed;
  border-radius: 10px;
  box-shadow: 0 0 0 9999px rgba(13, 19, 27, 0.55);
  border: 2px solid var(--fe-primary);
  pointer-events: none;
}
.fe-tour__dim {
  position: fixed;
  inset: 0;
  background: rgba(13, 19, 27, 0.55);
  pointer-events: none;
}
.fe-tour__card {
  position: fixed;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 14px 16px;
  border: 1px solid var(--fe-border-strong);
  border-radius: var(--fe-radius-lg);
  background: var(--fe-bg);
  color: var(--fe-text);
  box-shadow: var(--fe-shadow);
  outline: none;
}
.fe-tour__card--center {
  top: 50%;
  left: 50%; /* rtl-physical: centred by translate(-50%) — the same place in both directions */
  transform: translate(-50%, -50%);
  width: min(360px, calc(100vw - 24px));
}
.fe-tour__progress {
  margin: 0;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--fe-text-muted);
}
.fe-tour__title {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
}
.fe-tour__desc {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--fe-text-muted);
}
.fe-tour__dots {
  display: flex;
  gap: 5px;
  margin-top: 4px;
}
.fe-tour__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--fe-border-strong);
  transition: background 0.15s;
}
.fe-tour__dot.is-active {
  background: var(--fe-primary);
}
.fe-tour__actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
}
.fe-tour__actions-spacer {
  flex: 1 1 auto;
}
.fe-tour__skip {
  color: var(--fe-text-muted);
}
.fe-tour-enter-active,
.fe-tour-leave-active {
  transition: opacity 0.16s ease;
}
.fe-tour-enter-from,
.fe-tour-leave-to {
  opacity: 0;
}

/* ---------- prefers-reduced-motion: quiet the remaining movers ---------- */
@media (prefers-reduced-motion: reduce) {
  .fe-tour-enter-active,
  .fe-tour-leave-active,
  .fe-ctx-enter-active,
  .fe-ctx-leave-active,
  .fe-sheet-enter-active,
  .fe-sheet-leave-active,
  .fe-toast-enter-active,
  .fe-toast-leave-active {
    transition: none !important;
  }
  .fe-list__row,
  .fe-grid__card,
  .fe-tour__dot,
  .fe-list__col--star {
    transition: none !important;
  }
}
/* === /wiring:c4 === */

/* === wiring:d1 — tabs (tab strip) + split pane === */

/* Tab strip — thin row ABOVE the toolbar. The host renders it only with
   2+ tabs, so the default single-tab embed stays pixel-identical. */
.fe-tabs {
  display: flex;
  align-items: stretch;
  gap: 4px;
  flex: 0 0 auto;
  /* ⚠⚠ Without this the strip never scrolls — it GROWS. A flex item's default
   * `min-width: auto` floors it at its min-content width, so a row of tabs
   * wider than the window pushed the whole explorer sideways instead of
   * overflowing into `.fe-tabs__scroll`: measured 63 tabs → a 4903px strip in
   * a 1264px window, the layout running off the right edge with no scrollbar
   * anywhere (2026-08-12, reported as "it overflowed the screen and keeps sliding"). Allowing the
   * strip to shrink is what hands the overflow to the scroller inside it. */
  min-width: 0;
  padding: 4px 6px 0;
  background: var(--fe-bg-elev);
  border-bottom: 1px solid var(--fe-border);
  user-select: none;
}
.fe-tabs__scroll {
  display: flex;
  align-items: stretch;
  gap: 2px;
  flex: 1 1 auto;
  min-width: 0;
  overflow-x: auto;
  /* ⚠ EXPLICIT `hidden`, not left to default. `overflow-x: auto` alone makes
   * the other axis compute to `auto` too, so the strip grew a VERTICAL
   * scrollbar with nothing to scroll — a bar down the side of a row of tabs
   * (2026-08-12, reported). */
  overflow-y: hidden;
  /* Enough tabs and they run off the edge; a strip that scrolls without
   * saying so is a strip whose overflow is invisible. Thin, on its own axis,
   * and only when there is something to scroll. */
  scrollbar-width: thin;
  scrollbar-color: var(--fe-border-strong) transparent;
}
.fe-tabs__scroll::-webkit-scrollbar {
  height: 6px;
  width: 0;
}
.fe-tabs__scroll::-webkit-scrollbar-track {
  background: transparent;
}
.fe-tabs__scroll::-webkit-scrollbar-thumb {
  background: var(--fe-border-strong);
  border-radius: 6px;
}
.fe-tabs__scroll::-webkit-scrollbar-thumb:hover {
  background: var(--fe-text-muted);
}
.fe-tabs__tab {
  display: flex;
  align-items: center;
  gap: 6px;
  max-width: 180px;
  min-width: 72px;
  padding-block: 4px; padding-inline: 10px 6px;
  font-size: 12px;
  color: var(--fe-text-muted);
  background: transparent;
  border: 1px solid transparent;
  border-bottom: none;
  border-radius: 8px 8px 0 0;
  cursor: pointer;
  white-space: nowrap;
  position: relative;
}
.fe-tabs__tab:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-tabs__tab.is-active {
  background: var(--fe-bg);
  color: var(--fe-text);
  border-color: var(--fe-border);
}
/* Mask the strip's bottom border under the active tab (connected look). */
.fe-tabs__tab.is-active::after {
  content: '';
  position: absolute;
  inset-inline-start: 0;
  inset-inline-end: 0;
  bottom: -1px;
  height: 1px;
  background: var(--fe-bg);
}
.fe-tabs__tab.is-dragover {
  outline: 1px dashed var(--fe-primary);
  outline-offset: -2px;
}
.fe-tabs__tab:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}
.fe-tabs__label {
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1 1 auto;
  min-width: 0;
}
.fe-tabs__splitdot {
  width: 6px;
  height: 6px;
  border-radius: 2px;
  background: var(--fe-primary);
  flex: 0 0 auto;
}
.fe-tabs__close {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 4px;
  background: transparent;
  color: inherit;
  font-size: 13px;
  cursor: pointer;
  padding: 0;
}
.fe-tabs__close:hover {
  background: var(--fe-bg-hover);
}
.fe-tabs__new,
.fe-tabs__split {
  flex: 0 0 auto;
  align-self: center;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: var(--fe-text-muted);
  font-size: 15px;
  cursor: pointer;
  padding: 0;
}
.fe-tabs__split .fe-ficon {
  width: 14px;
  height: 14px;
}
.fe-tabs__new:hover,
.fe-tabs__split:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-tabs__split.is-active {
  background: var(--fe-bg-selected);
  color: var(--fe-primary);
  border-color: var(--fe-border);
}
/* Narrow embeds: split disabled — the host already hides the button via
   prop; this is belt & braces. */
.fe--narrow .fe-tabs__split {
  display: none;
}

/* ui-fix — left pane wrapper: breadcrumb + status strips + body in one
   flex-column. Full width without split; the left half once split (so the
   breadcrumb spans its own pane only, symmetric with SecondaryPane). */
.fe__primary {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
}
.fe__primary > .fe__body {
  min-width: 0;
}

/* Split layout — equal halves inside fe__main; the inspector (koru:k1)
   keeps its fixed width to the right of both. */
.fe__main--split > .fe__primary {
  flex: 1 1 50%;
}
.fe-split {
  flex: 1 1 50%;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  border-inline-start: 1px solid var(--fe-border);
  background: var(--fe-bg);
  color: var(--fe-text);
}
/* Active-panel accent: a thin top line on whichever pane owns the keyboard. */
.fe__main--split > .fe__primary,
.fe-split {
  box-shadow: inset 0 2px 0 transparent;
}
.fe__main--split > .fe__primary.fe-pane--focus,
.fe-split.fe-pane--focus {
  box-shadow: inset 0 2px 0 var(--fe-primary);
}

.fe-split__crumbs {
  display: flex;
  align-items: center;
  /* ui-fix — EXACTLY the same metrics as the main pane's .fe-breadcrumb
     (gap/padding/font) → in split mode the two breadcrumbs line up perfectly,
     with no hairline drift. */
  gap: 4px;
  flex: 0 0 auto;
  padding: 6px 12px;
  min-height: 37px; /* ui-fix — same height as the main pane's .fe-breadcrumb */
  border-bottom: 1px solid var(--fe-border);
  font-size: 13px;
  color: var(--fe-text-muted);
  overflow: hidden;
  white-space: nowrap;
}
.fe-split__crumb {
  border: none;
  background: transparent;
  color: inherit;
  font: inherit;
  padding: 2px 6px; /* ui-fix — same as .fe-breadcrumb__crumb */
  border-radius: var(--fe-radius-sm);
  cursor: pointer;
  max-width: 140px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-split__crumb:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-split__crumb.is-last {
  color: var(--fe-text);
  font-weight: 600;
}
.fe-split__sep {
  opacity: 0.6;
  flex: 0 0 auto;
}
.fe-split__spacer {
  flex: 1 1 auto;
}
.fe-split__closebtn {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 5px;
  background: transparent;
  color: var(--fe-text-muted);
  font-size: 14px;
  cursor: pointer;
  padding: 0;
}
.fe-split__closebtn:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}

.fe-split__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  position: relative;
}
.fe-split__body.is-dropover {
  outline: 2px dashed var(--fe-primary);
  outline-offset: -4px;
}
.fe-split__list {
  padding: 4px 0;
}
.fe-split__row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 10px;
  font-size: 13px;
  cursor: default;
}
.fe-split__row:hover {
  background: var(--fe-bg-hover);
}
.fe-split__row.is-selected {
  background: var(--fe-bg-selected);
}
.fe-split__row.is-droptarget {
  outline: 1.5px dashed var(--fe-primary);
  outline-offset: -2px;
  border-radius: 6px;
}
.fe-split__row:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}
.fe-split__icon {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
}
.fe-split__icon .fe-ficon {
  width: 16px;
  height: 16px;
}
.fe-split__name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-split__size {
  flex: 0 0 auto;
  color: var(--fe-text-muted);
  font-size: 11.5px;
}
.fe-split__state {
  padding: 24px 12px;
  text-align: center;
  color: var(--fe-text-muted);
  font-size: 12.5px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: center;
}
/* === /wiring:d1 === */

/* === wiring:d2 — gallery view (GalleryView.vue) === */

/* Large thumbnail grid: square cards, name below, size+date on hover. */
.fe-gal {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: var(--fe-gap);
  padding: var(--fe-gap);
}
.fe-gal__card {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg-elev);
  cursor: pointer;
  overflow: hidden;
  transition: background 0.08s, border-color 0.08s;
  user-select: none;
}
.fe-gal__card:hover {
  background: var(--fe-bg-hover);
  border-color: var(--fe-border-strong);
}
.fe-gal__card.is-selected {
  background: var(--fe-bg-selected);
  border-color: var(--fe-primary);
}
.fe-gal__card.is-trash {
  opacity: 0.7;
}
.fe-gal__card.is-clipped {
  opacity: 0.55;
  border-style: dashed;
}
.fe-gal__card:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 2px;
}
.fe-gal__card.is-droptarget {
  outline: 2px dashed var(--fe-primary);
  outline-offset: -2px;
  background: var(--fe-bg-selected);
  border-color: var(--fe-primary);
}
.fe-gal__thumb {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1 / 1;
  background: var(--fe-bg);
  overflow: hidden;
}
.fe-gal__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.fe-gal__icon {
  font-size: 44px;
}
.fe-gal__icon--svg svg {
  width: 64px;
  height: 64px;
}
/* Hover meta band — a translucent strip sitting at the bottom of the
   thumbnail; visible only on hover/keyboard focus (on touch the card's
   title is enough). */
.fe-gal__meta {
  position: absolute;
  inset-inline-start: 0;
  inset-inline-end: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 6px 8px;
  font-size: 11px;
  line-height: 1.35;
  color: var(--fe-text);
  background: color-mix(in srgb, var(--fe-bg-elev) 88%, transparent);
  backdrop-filter: blur(2px);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.12s ease, transform 0.12s ease;
  pointer-events: none;
}
.fe-gal__card:focus-visible .fe-gal__meta {
  opacity: 1;
  transform: translateY(0);
}
/* issue #26 — see the note on .fe-list__col--star: no hover reveal on touch.
   A screen that cannot hover shows the caption outright instead, since it
   would otherwise never be seen there. */
@media (hover: hover) {
  .fe-gal__card:hover .fe-gal__meta {
    opacity: 1;
    transform: translateY(0);
  }
}
@media (hover: none) {
  .fe-gal__meta {
    opacity: 1;
    transform: none;
  }
}
.fe-gal__meta-line {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-gal__meta-line--path {
  color: var(--fe-text-muted);
  font-style: italic;
}
.fe-gal__label {
  padding: 7px 9px;
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: center;
}
.fe-gal__empty {
  grid-column: 1 / -1;
  padding: 40px 20px;
  text-align: center;
  color: var(--fe-text-muted);
}
/* Smaller tiles in narrow/embed mini mode (2-3 columns fit). */
.fe--narrow .fe-gal {
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
}
/* Density: tiles shrink under the compact preference (same logic as grid). */
.fe--density-compact .fe-gal {
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: calc(var(--fe-gap) * 0.6);
}
.fe--density-compact .fe-gal__label {
  padding: 5px 7px;
  font-size: 12px;
}
@media (prefers-reduced-motion: reduce) {
  .fe-gal__card,
  .fe-gal__meta {
    transition: none !important;
    transform: none;
  }
}
/* === /wiring:d2 === */

/* === wiring:d3 — inspector Yorumlar (node comments) === */
.fe-inspector__countbadge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  margin-inline-start: 6px;
  border-radius: 8px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0;
  background: var(--fe-bg-hover);
  color: var(--fe-text);
  vertical-align: middle;
}

.fe-inspector__comments {
  list-style: none;
  margin: 0 0 8px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.fe-inspector__comment {
  border: 1px solid var(--fe-border);
  border-radius: 8px;
  padding: 6px 8px;
  background: var(--fe-bg);
}
.fe-inspector__comment-top {
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
}
.fe-inspector__comment-author {
  font-size: 12px;
  font-weight: 600;
  color: var(--fe-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.fe-inspector__comment-time {
  font-size: 11px;
  color: var(--fe-text-muted);
  flex: 1 1 auto;
  white-space: nowrap;
}
.fe-inspector__comment-del {
  border: none;
  background: none;
  color: var(--fe-text-muted);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
  border-radius: 4px;
  flex: 0 0 auto;
}
.fe-inspector__comment-del:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-danger);
}
.fe-inspector__comment-del:disabled {
  opacity: 0.5;
  cursor: default;
}
.fe-inspector__comment-body {
  margin: 4px 0 0;
  font-size: 12px;
  line-height: 1.45;
  color: var(--fe-text);
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}

.fe-inspector__comment-form {
  display: flex;
  gap: 6px;
  align-items: center;
}
.fe-inspector__comment-input {
  flex: 1 1 auto;
  min-width: 0;
  border: 1px solid var(--fe-border);
  border-radius: 8px;
  background: var(--fe-bg);
  color: var(--fe-text);
  font: inherit;
  font-size: 12px;
  padding: 6px 8px;
}
.fe-inspector__comment-input:focus {
  outline: none;
  border-color: var(--fe-primary);
}
.fe-inspector__comment-input:disabled {
  opacity: 0.6;
}
/* === /wiring:d3 === */

/* === wiring:e2 — end-to-end encrypted folders === */
/* Unlocked strip (same horizontal language as the presence strip). */
.fe-e2e-strip {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 12px;
  font-size: 12.5px;
  color: var(--fe-text-muted);
  border-bottom: 1px solid var(--fe-border);
  background: var(--fe-bg-elev);
}
.fe-e2e-strip__icon {
  font-size: 13px;
  line-height: 1;
}
.fe-e2e-strip__label {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-e2e-strip__btn {
  padding: 2px 10px;
  font-size: 12px;
}
/* wiring:e2 names — "names encrypted" / "names readable": the fact people
   ask about, in the strip rather than behind a dialog. */
.fe-e2e-strip__names {
  flex: none;
  padding: 1px 8px;
  border: 1px solid var(--fe-border);
  border-radius: 999px;
  font-size: 11.5px;
  white-space: nowrap;
}
.fe-e2e-upgrade__progress {
  font-variant-numeric: tabular-nums;
}
/* wiring:e2 vault — the vault strip (E2eVaultStrip): who writes, and the
   two clocks. It wraps on a narrow screen: the sentence is the point, and a
   one-line strip would cut it off at 390 px. */
.fe-vault-strip {
  flex-wrap: wrap;
  row-gap: 4px;
  border-inline-start: 3px solid transparent;
}
.fe-vault-strip__text {
  flex: 1 1 240px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  color: var(--fe-text);
}
.fe-vault-strip__sentence {
  overflow-wrap: anywhere;
}
.fe-vault-strip__lock,
.fe-vault-strip__unsaved {
  font-size: 12px;
  color: var(--fe-text-muted);
  font-variant-numeric: tabular-nums;
}
.fe-vault-strip__actions {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-inline-start: auto;
}
.fe-vault-strip--write {
  border-inline-start-color: var(--fe-primary);
}
.fe-vault-strip--warn {
  border-inline-start-color: var(--fe-warning, #f59e0b);
}
.fe-vault-strip--error {
  border-inline-start-color: var(--fe-danger);
}
.fe-vault-strip--error .fe-vault-strip__unsaved {
  color: var(--fe-danger);
}
/* The vault card's cost line in the level picker. */
.fe-e2e-level__cost {
  font-size: 12px;
  color: var(--fe-text-muted);
}

/* Lock screen: a passphrase form on top of the fe-state language. */
.fe-e2e-lock__form {
  display: flex;
  gap: 8px;
  align-items: stretch;
  margin-top: 12px;
  width: min(340px, 100%);
}
.fe-e2e-lock__input {
  flex: 1;
  min-width: 0;
}
/* The split pane's lock screen: the same screen, narrower. */
.fe-e2e-lock--compact .fe-state__hint {
  max-width: 34ch;
}
.fe-e2e-lock--compact .fe-e2e-lock__form {
  flex-wrap: wrap;
}

/* Encrypted-folder creation modal. */
.fe-e2e-names {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.fe-e2e-names__hint {
  margin: 0;
  font-size: 12.5px;
  color: var(--fe-text-muted);
}
/* wiring:e2 — the level picker (E2eLevelPicker): one card per level. */
.fe-e2e-levels {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0;
  padding: 0;
  border: 0;
  min-width: 0;
}
.fe-e2e-levels legend {
  padding: 0;
  margin-bottom: 4px;
}
.fe-e2e-level {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  padding: 8px 10px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  cursor: pointer;
}
.fe-e2e-level--on {
  border-color: var(--fe-primary);
  background: var(--fe-primary-soft);
}
.fe-e2e-level input {
  margin-top: 3px;
  flex: none;
}
.fe-e2e-level__text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.fe-e2e-level__name {
  font-weight: 600;
}
.fe-e2e-level__hint {
  font-size: 12.5px;
  color: var(--fe-text-muted);
}
/* wiring:e2 — the folder's encryption settings (E2eSettingsModal). */
.fe-e2e-settings {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.fe-e2e-settings__section {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
}
.fe-e2e-settings__section + .fe-e2e-settings__section {
  border-top: 1px solid var(--fe-border);
  padding-top: 12px;
}
.fe-e2e-settings__head {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
}
.fe-e2e-settings__value {
  margin: 0;
}
.fe-e2e-settings__hint {
  margin: 0;
  font-size: 12.5px;
  color: var(--fe-text-muted);
}
.fe-e2e-settings__row {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 6px;
}
.fe-e2e-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* wiring:e2 convert — what the server already has, in the dialog. */
.fe-e2e-convert-past {
  display: flex;
  flex-direction: column;
  gap: 6px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  padding: 8px 10px;
  font-size: 12.5px;
}
.fe-e2e-convert-past p {
  margin: 0;
}
.fe-e2e-warn {
  border: 1px solid var(--fe-danger);
  border-radius: var(--fe-radius-sm);
  padding: 8px 10px;
  font-size: 12.5px;
  color: var(--fe-text);
  background: color-mix(in srgb, var(--fe-danger) 8%, transparent);
}
.fe-e2e-warn strong {
  display: block;
  color: var(--fe-danger);
  margin-bottom: 2px;
}
.fe-e2e-warn p {
  margin: 0;
  color: var(--fe-text-muted);
}
.fe-e2e-ack {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 12.5px;
  color: var(--fe-text);
  cursor: pointer;
}
.fe-e2e-ack input {
  margin-top: 2px;
  accent-color: var(--fe-primary);
}

/* The "şifreli klasör" (encrypted folder) entry link inside NewFolderModal. */
.fe-e2e-optlink {
  margin-top: 10px;
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  font-size: 12.5px;
  color: var(--fe-primary);
  cursor: pointer;
  text-align: start;
}
.fe-e2e-optlink:hover {
  text-decoration: underline;
}

/* --- wiring:e2 fxe — "what the server has already seen", in the dialog
   that encrypts a single file: a note, not a warning — it is a fact about
   the past, not a risk being taken. */
.fe-e2e-seen {
  border: 1px solid var(--fe-border);
  border-inline-start: 3px solid var(--fe-text-muted);
  border-radius: var(--fe-radius-sm);
  padding: 8px 10px;
  font-size: 12.5px;
}
.fe-e2e-seen strong {
  display: block;
  margin-bottom: 2px;
  color: var(--fe-text);
}
.fe-e2e-seen p {
  margin: 0 0 4px;
  color: var(--fe-text-muted);
}
.fe-e2e-seen ul {
  margin: 0;
  padding-inline-start: 18px;
  color: var(--fe-text-muted);
}
.fe-e2e-seen li + li {
  margin-top: 2px;
}
/* The too-big dialog (E2eTooBigModal): the steps and the command to copy. */
.fe-e2e-big__steps {
  margin: 0;
  padding-inline-start: 20px;
  list-style: decimal;
  display: grid;
  gap: 8px;
}
.fe-e2e-big__cmd {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 4px;
}
.fe-e2e-big__cmd code {
  padding: 4px 8px;
  border-radius: var(--fe-radius-sm);
  background: var(--fe-bg-hover);
  font-family: var(--fe-font-mono);
  font-size: 12.5px;
  overflow-wrap: anywhere;
}
/* --- /wiring:e2 fxe */

/* --- wiring:e2 recovery ---------------------------------------------
   Unscoped and fe-prefixed on purpose: a scoped <style> in an SFC does not
   survive the web-component build, so every class a core component uses has
   to live here. */

/* The shown-once key. Monospace, big, wrapping — it exists to be read off
   the screen and typed somewhere else, so legibility beats compactness. */
.fe-e2e-rk {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.fe-e2e-rk__lead {
  margin: 0;
  font-size: 13px;
  color: var(--fe-text-muted);
}
.fe-e2e-rk__key {
  display: block;
  padding: 12px 14px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  background: var(--fe-bg-elev);
  color: var(--fe-text);
  font-family: var(--fe-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 15px;
  letter-spacing: 0.04em;
  line-height: 1.7;
  word-break: break-all;
  user-select: all;
  text-align: center;
}
.fe-e2e-rk__actions {
  display: flex;
  gap: 8px;
}
.fe-e2e-rk__escrow {
  border: 1px solid var(--fe-border);
  border-inline-start: 3px solid var(--fe-warning, var(--fe-primary));
  border-radius: var(--fe-radius-sm);
  padding: 8px 10px;
  font-size: 12.5px;
}
.fe-e2e-rk__escrow strong {
  display: block;
  margin-bottom: 2px;
  color: var(--fe-text);
}
.fe-e2e-rk__escrow p {
  margin: 0;
  color: var(--fe-text-muted);
}
.fe-e2e-rk__kid code {
  font-size: 11.5px;
}

/* Unlock-without-the-password dialog. */
.fe-e2e-recover {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.fe-e2e-recover__none {
  margin: 0;
  font-size: 13px;
  color: var(--fe-text-muted);
}
.fe-e2e-recover__hint {
  margin: 0;
  font-size: 12.5px;
  color: var(--fe-text-muted);
}
/* Why the escrow key does not apply to THIS folder. Deliberately not muted
   to the point of invisibility: it is the answer to a question the reader
   is already asking, and the pre-adoption case has no other surface. */
.fe-e2e-recover__note {
  margin: 0;
  padding: 8px 10px;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--fe-text);
  border: 1px solid var(--fe-border);
  border-inline-start: 3px solid var(--fe-warning, #f59e0b);
  border-radius: var(--fe-radius);
  background: var(--fe-bg-elev);
}
.fe-e2e-recover__tabs {
  display: flex;
  gap: 4px;
  padding: 3px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-lg);
  background: var(--fe-bg-elev);
}
.fe-e2e-recover__tab {
  flex: 1;
  padding: 6px 10px;
  border: 0;
  border-radius: calc(var(--fe-radius-lg) - 4px);
  background: transparent;
  color: var(--fe-text-muted);
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
}
.fe-e2e-recover__tab--on {
  background: var(--fe-bg);
  color: var(--fe-text);
  font-weight: 600;
}
.fe-e2e-recover__key {
  font-family: var(--fe-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  letter-spacing: 0.04em;
  text-align: center;
}
.fe-e2e-recover__escrow {
  font-family: var(--fe-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 11.5px;
  resize: vertical;
  min-height: 90px;
}

/* The offer to give a pre-recovery folder a recovery key. A strip, not a
   modal: it must be visible and dismissible, never something that blocks
   the folder the user came here to open. */
.fe-e2e-upgrade {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  margin: 0 0 10px;
  padding: 10px 12px;
  border: 1px solid var(--fe-border);
  border-inline-start: 3px solid var(--fe-primary);
  border-radius: var(--fe-radius-sm);
  background: color-mix(in srgb, var(--fe-primary) 6%, transparent);
}
.fe-e2e-upgrade__text {
  flex: 1 1 320px;
  min-width: 0;
}
.fe-e2e-upgrade__text strong {
  display: block;
  font-size: 13px;
  color: var(--fe-text);
}
.fe-e2e-upgrade__text p {
  margin: 4px 0 0;
  font-size: 12.5px;
  color: var(--fe-text-muted);
}
.fe-e2e-upgrade__escrow {
  color: var(--fe-danger) !important;
}
.fe-e2e-upgrade__actions {
  display: flex;
  gap: 8px;
  flex: none;
}
/* wiring:e2 escrow-offer — the same strip, asking a heavier question:
   accepting hands the operator a permanent second key. Amber rather than
   the recovery offer's primary tint, so it does not read as routine. */
.fe-e2e-upgrade--escrow {
  border-inline-start-color: var(--fe-warning, #f59e0b);
  background: color-mix(in srgb, var(--fe-warning, #f59e0b) 7%, transparent);
}
/* The link to the honest long version has to LOOK like a link: it inherits
   the warning colour, and without this it read as ordinary sentence text —
   somebody being asked to hand over a key should be able to see the way to
   the page that explains what they are handing over. */
.fe-e2e-upgrade__escrow a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  font-weight: 600;
}
.fe-e2e-upgrade__escrow a:hover,
.fe-e2e-upgrade__escrow a:focus-visible {
  text-decoration-thickness: 2px;
}
.fe-e2e-upgrade__pw {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 8px;
  max-width: 320px;
  font-size: 12.5px;
  color: var(--fe-text-muted);
}
/* === /wiring:e2 === */

/* === ui-fix — toolbar single row + folding + measure strip ============ */
.fe:not(.fe--narrow) .fe-toolbar {
  flex-wrap: nowrap;
}
.fe:not(.fe--narrow) .fe-toolbar__primary {
  flex: 1 1 auto;
  min-width: 0;
  flex-wrap: nowrap;
  overflow: hidden;
  position: relative;
}
.fe-toolbar__measure {
  position: absolute;
  visibility: hidden;
  pointer-events: none;
  height: 0;
  overflow: hidden;
  display: flex;
  gap: 8px;
  white-space: nowrap;
}
.fe-toolbar__measure .fe-btn {
  flex: none;
}
/* === /ui-fix ========================================================= */


/* === wiring:c1 — day / night / auto ====================================
   Lives at the top of the theme gallery: "night mode" is what people open the
   theme button looking for, and a palette picker that cannot answer that
   question is the wrong place to stop. */
.fe-thememode {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.fe-thememode__label {
  font-size: 13px;
  font-weight: 600;
  color: var(--fe-text-muted);
}
.fe-thememode__seg {
  display: inline-flex;
  padding: 3px;
  gap: 2px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-lg);
  background: var(--fe-bg-elev);
}
.fe-thememode__opt {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border: 0;
  border-radius: calc(var(--fe-radius-lg) - 4px);
  background: transparent;
  color: var(--fe-text-muted);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  transition: background 0.14s ease, color 0.14s ease;
}
.fe-thememode__opt:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-thememode__opt.is-active {
  background: var(--fe-bg);
  color: var(--fe-text);
  box-shadow: var(--fe-shadow-sm);
}
.fe-thememode__opt .fe-ficon {
  width: 15px;
  height: 15px;
  flex: none;
}
/* Under ~360px the label and the three options cannot share a line; the strip
   takes the width and the options divide it rather than overflowing. */
@media (max-width: 420px) {
  .fe-thememode__seg {
    flex: 1 1 100%;
  }
  .fe-thememode__opt {
    flex: 1;
    justify-content: center;
  }
}
/* === /ui-fix ========================================================= */

/* ── selective sync (desktop shell only): availability badges + strip ──
   The glyphs read in the OneDrive/Drive grammar: ✓ on this computer,
   ⟳ being synced right now, ☁ online-only. Rendered only when the host
   passes keepBadgeFor — the web admin and the embeds never show them. */
.fe-keepbadge {
  flex: 0 0 auto;
  display: inline-block;
  margin-inline-start: 4px;
  font-size: 11px;
  line-height: 1;
  vertical-align: middle;
}
.fe-keepbadge--kept {
  color: var(--fe-keep-ok, #16a34a);
}
.fe-keepbadge--cloud {
  color: var(--fe-text-muted);
  opacity: 0.7;
}
.fe-keepbadge--partial {
  color: var(--fe-keep-ok, #16a34a);
  opacity: 0.85;
}
.fe-keepbadge--syncing {
  color: var(--fe-primary);
  animation: fe-keep-spin 1.2s linear infinite;
}
@keyframes fe-keep-spin {
  to {
    transform: rotate(360deg);
  }
}

/* Bottom overlay strip: progress of the folder being synced right now.
   pointer-events none — it reports, it never blocks a click. */
.fe-keep-strip {
  position: absolute;
  inset-inline-start: 12px;
  inset-inline-end: 12px;
  bottom: 10px;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border: 1px solid var(--fe-border);
  border-radius: 10px;
  background: var(--fe-bg-elev);
  box-shadow: var(--fe-shadow-sm, 0 2px 6px rgba(0, 0, 0, 0.18));
  font-size: 12.5px;
  color: var(--fe-text-muted);
  pointer-events: none;
}
.fe-keep-strip__icon {
  font-size: 13px;
  line-height: 1;
  color: var(--fe-primary);
  animation: fe-keep-spin 1.2s linear infinite;
}
.fe-keep-strip__label {
  flex: 0 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-keep-strip__bar {
  flex: 1 1 80px;
  min-width: 60px;
  height: 4px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--fe-primary) 18%, transparent);
  overflow: hidden;
}
.fe-keep-strip__fill {
  height: 100%;
  border-radius: 999px;
  background: var(--fe-primary);
  transition: width 0.3s ease;
}

/* === gezinti:g1 — SideNav (navigation panel) ==========================
   A left column inside .fe__main, the mirror of .fe-inspector on the right:
   same flex slot, same overlay trick when the explorer is narrow. .fe__primary
   already carries `flex: 1 1 auto; min-width: 0`, so it absorbs the width
   change with no rule of its own. */
.fe-sidenav {
  flex: 0 0 232px;
  width: 232px;
  min-height: 0;
  display: flex;
  flex-direction: column;
  border-inline-end: 1px solid var(--fe-border);
  /* No `background: var(--fe-bg)` here on purpose — see .fe-conn. This one IS
     inside the full-screen explorer, whose root already paints --fe-bg, so a
     second identical fill would only be a second chance to disagree with it. */
  color: var(--fe-text);
  transition: flex-basis 0.14s ease, width 0.14s ease;
}
/* Collapsed: an icon rail, not nothing. A panel that disappears takes its own
   re-open affordance with it. Width is one 32px control plus its padding. */
.fe-sidenav--rail {
  flex: 0 0 56px;
  width: 56px;
}
.fe-sidenav__head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  /* Same height as .fe-breadcrumb (37px) so the panel head and the crumb bar
     sit on one line across the top of .fe__main. */
  min-height: 37px;
  padding: 4px 8px;
  border-bottom: 1px solid var(--fe-border);
}
.fe-sidenav--rail .fe-sidenav__head {
  justify-content: center;
  padding: 4px;
}
.fe-sidenav__toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 28px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text-muted);
  font: inherit;
  font-size: 17px;
  cursor: pointer;
}
.fe-sidenav__toggle:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-sidenav__toggle:focus-visible,
.fe-sidenav__item:focus-visible,
.fe-sidenav__upload:focus-visible,
.fe-sidenav__secondary:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}

/* Primary action block. */
.fe-sidenav__primary {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 10px 6px;
}
.fe-sidenav--rail .fe-sidenav__primary {
  padding: 10px 8px 6px;
  align-items: center;
}
.fe-sidenav__upload,
.fe-sidenav__secondary {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 12px;
  border-radius: 999px;
  border: 1px solid transparent;
  font: inherit;
  font-size: 13.5px;
  cursor: pointer;
  text-align: start;
  white-space: nowrap;
  overflow: hidden;
}
.fe-sidenav__upload {
  background: var(--fe-primary);
  border-color: var(--fe-primary);
  color: var(--fe-text-on-primary);
  font-weight: 600;
  box-shadow: var(--fe-shadow-sm);
}
.fe-sidenav__upload:hover:not(:disabled) {
  background: var(--fe-primary-hover);
  border-color: var(--fe-primary-hover);
}
.fe-sidenav__secondary {
  background: transparent;
  border-color: var(--fe-border);
  color: var(--fe-text);
}
.fe-sidenav__secondary:hover:not(:disabled) {
  background: var(--fe-bg-hover);
  border-color: var(--fe-border-strong);
}
.fe-sidenav__upload:disabled,
.fe-sidenav__secondary:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.fe-sidenav__upload .fe-ficon,
.fe-sidenav__secondary .fe-ficon,
.fe-sidenav__item .fe-ficon {
  font-size: 17px;
}
/* On the rail the two buttons become squares — a pill with no label reads as
   a stretched blob. */
.fe-sidenav--rail .fe-sidenav__upload,
.fe-sidenav--rail .fe-sidenav__secondary {
  width: 38px;
  height: 38px;
  padding: 0;
  justify-content: center;
  border-radius: var(--fe-radius-md);
}

.fe-sidenav__scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 4px 8px 10px;
}
.fe-sidenav__group {
  list-style: none;
  margin: 0;
  padding: 0;
}
.fe-sidenav__item {
  display: flex;
  align-items: center;
  /* ⚠ Said out loud, not left to the default: the explorer is not in a shadow
     root, so a host page's own `button { justify-content: center }` reached
     these buttons — the desktop app has exactly that rule for its dialogs,
     and every sidebar entry sat in the middle of its row (#53). */
  justify-content: flex-start;
  gap: 10px;
  width: 100%;
  padding: 7px 10px;
  margin: 1px 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--fe-text);
  font: inherit;
  font-size: 13.5px;
  cursor: pointer;
  text-align: start;
  white-space: nowrap;
  overflow: hidden;
}
.fe-sidenav__item:hover {
  background: var(--fe-bg-hover);
}
.fe-sidenav__item.is-active {
  background: var(--fe-bg-selected);
  font-weight: 600;
}
.fe-sidenav--rail .fe-sidenav__item {
  justify-content: center;
  padding: 8px 0;
  border-radius: var(--fe-radius-md);
}
.fe-sidenav__text {
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Drafts (issue #71): the row's count. A count, not an alarm — the quiet
   primary tint the listing's badges wear, pushed to the row's end. */
.fe-sidenav__count {
  flex: 0 0 auto;
  margin-inline-start: auto;
  min-width: 20px;
  padding: 0 6px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--fe-primary) 14%, transparent);
  color: var(--fe-primary);
  font-size: 11px;
  font-weight: 600;
  line-height: 18px;
  text-align: center;
}
/* On the collapsed rail there is no row end: the count sits on the icon's
   corner. */
.fe-sidenav--rail .fe-sidenav__item {
  position: relative;
}
.fe-sidenav__count--rail {
  position: absolute;
  top: 1px;
  inset-inline-end: 4px;
  min-width: 16px;
  padding: 0 4px;
  font-size: 10px;
  line-height: 16px;
}
.fe-sidenav__section {
  margin-top: 10px;
}
.fe-sidenav__heading {
  margin: 4px 0 2px;
  padding: 0 10px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fe-text-muted);
}
.fe-sidenav__rule {
  margin: 8px 6px;
  border: 0;
  border-top: 1px solid var(--fe-border);
}
/* "Shared" marker on a storage the caller only reaches through a grant. */
.fe-sidenav__tag {
  margin-inline-start: auto;
  flex: 0 0 auto;
  padding: 1px 6px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--fe-primary) 16%, transparent);
  color: var(--fe-primary);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.02em;
}
/* The read-only mark (StorageTags, the one tag for that fact on every
   surface): at the row's end in the side panel; on a Home card, under the
   size line, from the start edge, and never wider than the card. */
.fe-sidenav__stags {
  margin-inline-start: auto;
  flex: 0 0 auto;
}
.fe-home__storage-ro {
  align-self: flex-start;
  margin-top: 3px;
  max-width: 100%;
}
.fe-home__storage-ro .fe-stag {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* === #57 — reordering the storages ===================================
   A storage row can be dragged (SideNav: pointer events, a long press for a
   finger). Nothing here changes a row that is not being dragged. */
.fe-sidenav__item--storage {
  /* A finger's long press lifts the row; without these it would select the
     label (Android) or open the system's link callout (iOS) instead. */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
/* The row in hand: still in its place (the drop marker says where it goes),
   faded so the marker is what the eye follows. The same on a table row the
   admin Storages page drags by its handle (`useReorderDrag`, one gesture). */
.fe-sidenav__item--storage.is-dragging,
.fe-list__row--dragging {
  opacity: 0.45;
}
.fe-sidenav--reordering,
.fe-sidenav--reordering .fe-sidenav__item {
  cursor: grabbing;
}
/* The drop marker: a 2px primary line in the gap the row would land in —
   above the row carrying `is-drop-before`, below the last row for
   `is-drop-after`. Positioned on the <li> (a table: on the row) so it takes no
   layout space and the rows do not jump while the pointer moves; logical
   properties, so it is the same line in a right-to-left panel. On a table row
   it sits above the frozen Name / Actions cells, which paint an opaque ground
   at z-index 2. */
.fe-sidenav__group > li.is-drop-before,
.fe-sidenav__group > li.is-drop-after,
.fe-list__row--drop-before,
.fe-list__row--drop-after {
  position: relative;
}
.fe-sidenav__group > li.is-drop-before::before,
.fe-sidenav__group > li.is-drop-after::after,
.fe-list__row--drop-before::before,
.fe-list__row--drop-after::after {
  content: '';
  position: absolute;
  inset-inline: 6px;
  height: 2px;
  border-radius: 2px;
  background: var(--fe-primary);
  pointer-events: none;
  z-index: 3;
}
.fe-list__row--drop-before::before,
.fe-list__row--drop-after::after {
  inset-inline: 0;
}
.fe-sidenav__group > li.is-drop-before::before,
.fe-list__row--drop-before::before {
  inset-block-start: -1px;
}
.fe-sidenav__group > li.is-drop-after::after,
.fe-list__row--drop-after::after {
  inset-block-end: -1px;
}
/* A table row's handle (the admin Storages page). ⚠ `touch-action: none`:
   a finger on the handle drags at once and never scrolls the page instead —
   which is why the handle, and not the whole row, is what a finger holds. */
.fe-reorder-handle {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text-muted);
  cursor: grab;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}
.fe-reorder-handle:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-reorder-handle:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}

/* === etiket:t1 — the Tags section ==================================== */
/* A tag row is an ordinary panel item; only its label wraps differently,
   because a tag is user text and can be long. */
.fe-sidenav__item--tag .fe-sidenav__text {
  text-transform: none;
}
/* etiket:k2 — "Personal" / "Team" under the Tags caption: quieter than the
   caption itself (no capitals, no tracking) and carrying the kind's glyph, the
   same one on every row below it and on every chip on a file. Logical
   properties, so a right-to-left language mirrors it for free. */
.fe-sidenav__subheading {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 6px 0 0;
  padding-inline: 18px;
  font-size: 11px;
  font-weight: 600;
  color: var(--fe-text-muted);
}
.fe-sidenav__subheading .fe-tagkind {
  flex: none;
}
/* "Show all (12)" / "Show fewer" — a link, not a button: it changes what this
   section shows, it is not a destination. */
.fe-sidenav__more {
  display: block;
  width: 100%;
  margin: 2px 0 0;
  padding: 5px 10px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--fe-primary);
  font: inherit;
  font-size: 12.5px;
  text-align: start;
  cursor: pointer;
}
.fe-sidenav__more:hover {
  background: var(--fe-bg-hover);
}
.fe-sidenav__more:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}
/* "No tags yet — add them from a file's menu." */
.fe-sidenav__hint {
  margin: 2px 0 4px;
  padding: 0 10px;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--fe-text-muted);
}

/* === yildiz:s1 — the star chip on a grid / gallery tile =============== */
/* Positioned by the card, painted by StarButton (.filex-star-btn.is-card).
   ⚠ The rules live HERE and not in the SFC: the panel's styles do too, and
   these are about the CARD's geometry, which is this file's business. */
.fe-grid__star,
.fe-gal__star {
  position: absolute;
  top: 6px;
  inset-inline-end: 6px;
  z-index: 2;
  /* Hidden until the tile is hovered or something inside it is focused —
     EXCEPT when the file is starred, which is the whole point: the mark has
     to be visible without hovering every tile to find it (see .is-starred
     below). */
  opacity: 0;
  transition: opacity 120ms ease;
}
.fe-grid__card:focus-within .fe-grid__star,
.fe-gal__card:focus-within .fe-gal__star,
.fe-grid__star:focus-within,
.fe-gal__star:focus-within,
.fe-grid__star:has(.is-starred),
.fe-gal__star:has(.is-starred) {
  opacity: 1;
}
/* issue #26 — see the note on .fe-list__col--star: no hover reveal on touch. */
@media (hover: hover) {
  .fe-grid__card:hover .fe-grid__star,
  .fe-gal__card:hover .fe-gal__star {
    opacity: 1;
  }
}
/* `:has()` is the modern half of the rule above; this is the same thing for
   engines without it — a starred chip paints itself. Both are needed: the
   parent wrapper carries the opacity, the child cannot raise it on its own. */
.filex-star-btn.is-card.is-starred {
  opacity: 1;
}
/* === issue #26 — the checkbox on a grid / gallery card ================
   The one click on a card that selects; a click anywhere else opens it. The
   box itself is ItemCheck (.fe-list__check, the list's own tick). It shows
   when the card is hovered (where hovering exists — see the star note), when
   something inside the card is focused, when the card is selected, and on
   every card once anything is selected — which is how a finger, with no
   hover, finds the next box to tick after a long press selected the first.
   ⚠ Hidden means `pointer-events: none` as well: an invisible box would take
   the tap meant to open the card under it. */
.fe-item-check {
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms ease;
}
.fe-grid__card.is-selected .fe-item-check,
.fe-gal__card.is-selected .fe-item-check,
.fe-grid.has-selection .fe-item-check,
.fe-gal.has-selection .fe-item-check,
.fe-grid__card:focus-within .fe-item-check,
.fe-gal__card:focus-within .fe-item-check {
  opacity: 1;
  pointer-events: auto;
}
@media (hover: hover) {
  .fe-grid__card:hover .fe-item-check,
  .fe-gal__card:hover .fe-item-check {
    opacity: 1;
    pointer-events: auto;
  }
}
/* On a grid card the box takes the type tile's 24px slot in the card's row,
   so the name does not move when it appears; the tile steps aside. */
.fe-grid__lead {
  position: relative;
  flex: 0 0 24px;
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.fe-grid__lead > .fe-item-check {
  position: absolute;
  inset: 0;
}
.fe-grid__lead > .fe-grid__tile {
  transition: opacity 120ms ease;
}
.fe-grid__card.is-selected .fe-grid__lead--pick > .fe-grid__tile,
.fe-grid.has-selection .fe-grid__lead--pick > .fe-grid__tile,
.fe-grid__card:focus-within .fe-grid__lead--pick > .fe-grid__tile {
  opacity: 0;
}
@media (hover: hover) {
  .fe-grid__card:hover .fe-grid__lead--pick > .fe-grid__tile {
    opacity: 0;
  }
}
/* On a gallery card it is the thumbnail's top-left corner, opposite the star. */
.fe-gal__check {
  position: absolute;
  top: 6px;
  inset-inline-start: 6px;
  z-index: 2;
}

/* The chip itself: a round translucent puck, legible over a photo thumbnail
   as well as over a flat icon tile. */
.filex-star-btn.is-card {
  width: 26px;
  height: 26px;
  padding: 0;
  justify-content: center;
  border-radius: 999px;
  background: rgba(17, 24, 39, 0.55);
  color: #fff;
  backdrop-filter: blur(2px);
}
.filex-star-btn.is-card:hover {
  background: rgba(17, 24, 39, 0.78);
  color: var(--filex-accent-amber, #f59e0b);
}
.filex-star-btn.is-card.is-starred {
  color: var(--filex-accent-amber, #f59e0b);
}
.filex-star-btn.is-card:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}
/* The gallery's meta strip covers the tile's foot on hover and is
   aria-hidden; the star sits above it and stays clickable. */
.fe-gal__star {
  z-index: 3;
}

/* Narrow (<560px): a drawer over the listing, never a column. At 390px a
   232px column leaves 158px for the files, and a 56px rail still takes an
   eighth of the screen from a listing that is already tight. */
.fe-sidenav--drawer {
  position: absolute;
  top: 0;
  bottom: 0;
  inset-inline-start: 0;
  width: min(280px, 84%);
  flex: none;
  /* Same layer as .fe-inspector--overlay: above the listing and the FAB (45),
     below the context menus (90) and the onboarding tour (96), which are
     appended to <body> and would paint over anything lower anyway. */
  z-index: 60;
  background: var(--fe-bg);
  border-inline-end: 1px solid var(--fe-border);
  box-shadow: var(--fe-shadow);
}
.fe-sidenav__scrim {
  position: absolute;
  inset: 0;
  z-index: 59;
  border: 0;
  padding: 0;
  background: rgba(13, 19, 27, 0.42);
  cursor: pointer;
}
/* === /gezinti:g1 === */

/* --- gezinti:g1 — the panel's Connections / API-keys overlay --- */
/* ⚠ z-index 130. The explorer's onboarding tour (96) and its teleported menus
   (90) are appended to <body> and are `fixed`, so they paint over anything in
   the normal stacking order — measured in the web app, where the tour card
   landed on top of this very surface and swallowed its clicks; that page went
   to z-[120] for the same reason and this has to clear it. */
.fe-overlay {
  position: fixed;
  inset: var(--fe-overlay-top, 0px) 0 0; /* the host's own strip stays reachable — see .fe-modal__backdrop */
  z-index: 130;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 32px 16px;
  overflow: auto;
  background: rgba(13, 19, 27, 0.45);
}
.fe-overlay__card {
  width: min(880px, 100%);
  max-height: calc(100vh - 64px - var(--fe-overlay-top, 0px));
  overflow: auto;
  padding: 18px;
  border-radius: var(--fe-radius-lg);
  border: 1px solid var(--fe-border);
  background: var(--fe-bg);
  color: var(--fe-text);
  box-shadow: var(--fe-shadow);
}
.fe-overlay__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 10px;
}
.fe-overlay__title {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
}
.fe-overlay__close {
  border: 0;
  background: transparent;
  color: var(--fe-text-muted);
  font-size: 20px;
  line-height: 1;
  padding: 2px 6px;
  border-radius: var(--fe-radius-sm);
  cursor: pointer;
}
.fe-overlay__close:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-overlay__close:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}
@media (max-width: 560px) {
  .fe-overlay {
    padding: 12px 8px;
  }
  .fe-overlay__card {
    padding: 12px;
    max-height: calc(100vh - 24px);
  }
}

/* The full API-key form (TokensPanel `full`). */
.fe-tokform {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 8px 0 4px;
}
.fe-tokform__scopes {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin: 0;
  padding: 8px 10px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
}
.fe-tokform__legend {
  padding: 0 4px;
  font-size: 12px;
  color: var(--fe-text-muted);
}
.fe-tokform__scope {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 13px;
  font-family: var(--fe-font-mono);
  cursor: pointer;
}
.fe-tokform__field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.fe-tokform__field--narrow {
  flex: 0 0 180px;
}
.fe-tokform__label {
  font-size: 12px;
  color: var(--fe-text-muted);
}
.fe-tokform__row {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  flex-wrap: wrap;
}
.fe-tokform__row .fe-s3keys__btn {
  margin-inline-start: auto;
}
/* --- /gezinti:g1 --- */

/* === surucu:d1 — the shell (GitHub #14) ===============================
   ⚠ This block used to be additive and gated on a class only `uiProfile:
   'drive'` carried. It is not a variant any more: `.fe-toolbar--drive` is on
   every header (Toolbar.vue says why the modifier survives), and every profile
   renders what is below. See ExplorerConfig.uiProfile.

   ⚠ Unscoped and prefixed, like the rest of this file. A `<style scoped>` in
   an SFC compiles to `.cls[data-v-HASH]`, and the hash does not match in the
   web-component build — the rules silently stop applying in every embed, which
   is how the share dialog once shipped as raw unstyled HTML.
   ---------------------------------------------------------------------- */

/* ── the header ───────────────────────────────────────────────────────── */
.fe-toolbar--drive {
  gap: 10px;
  padding: 8px 12px;
}
/* The field takes the row. `min-width: 0` because a flex item's default `auto`
   minimum is its content width and an <input> reports a wide one — without it
   the header refuses to shrink and the whole explorer grows a horizontal
   scrollbar at 390px. */
.fe-toolbar__search-group--drive {
  flex: 1 1 auto;
  min-width: 0;
  gap: 8px;
}
.fe-drivesearch {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1 auto;
  min-width: 0;
  max-width: 720px;
  padding-block: 0; padding-inline: 12px 10px;
  height: 40px;
  border-radius: 999px;
  border: 1px solid transparent;
  background: var(--fe-bg-elev);
  color: var(--fe-text);
}
.fe-drivesearch:focus-within {
  background: var(--fe-bg);
  border-color: var(--fe-border-strong);
  box-shadow: var(--fe-shadow-sm);
}
.fe-drivesearch__glass {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  color: var(--fe-text-muted);
}
.fe-drivesearch__input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 14px;
  outline: none;
}
.fe-drivesearch__input::placeholder {
  color: var(--fe-text-muted);
}
/* The ⌘K chip. A real button, not decoration: it is the one visible door to
   the palette in this shell, so it has to be clickable and tab-reachable —
   printing a shortcut nobody can press with a mouse is a hint, not a control. */
.fe-drivesearch__kbd {
  flex: 0 0 auto;
  padding: 3px 8px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  background: var(--fe-bg);
  color: var(--fe-text-muted);
  font: inherit;
  font-size: 11.5px;
  letter-spacing: 0.02em;
  cursor: pointer;
  white-space: nowrap;
}
.fe-drivesearch__kbd:hover {
  color: var(--fe-text);
  border-color: var(--fe-border-strong);
}
.fe-drivesearch__kbd:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}

/* ── the "+ New" button ───────────────────────────────────────────────── */
.fe-sidenav__new {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 11px 18px;
  border-radius: 999px;
  border: 1px solid var(--fe-primary);
  background: var(--fe-primary);
  color: var(--fe-text-on-primary);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  text-align: start;
  white-space: nowrap;
  overflow: hidden;
  cursor: pointer;
  box-shadow: var(--fe-shadow-sm);
}
.fe-sidenav__new:hover {
  background: var(--fe-primary-hover);
  border-color: var(--fe-primary-hover);
}
.fe-sidenav__new:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 2px;
}
.fe-sidenav__new .fe-ficon {
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
}
/* On the rail it becomes a circle: a pill with no label is a stretched blob. */
.fe-sidenav--rail .fe-sidenav__new {
  width: 40px;
  height: 40px;
  padding: 0;
  justify-content: center;
  border-radius: 50%;
}

/* ── the storage line ─────────────────────────────────────────────────── */
.fe-sidenav__quota {
  flex: 0 0 auto;
  padding: 10px 12px 12px;
  border-top: 1px solid var(--fe-border);
}
.fe-sidenav--rail .fe-sidenav__quota {
  padding: 10px 8px;
}
.fe-sidenav__quota-label {
  margin: 0 0 6px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fe-text-muted);
}
.fe-sidenav__quota-bar {
  height: 5px;
  border-radius: 999px;
  background: var(--fe-bg-hover);
  overflow: hidden;
}
.fe-sidenav__quota-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--fe-primary);
  transition: width 0.2s ease;
}
.fe-sidenav__quota-text {
  margin: 6px 0 0;
  font-size: 12px;
  color: var(--fe-text-muted);
}

/* ── the breadcrumb row ───────────────────────────────────────────────── */
/* Not a drive shell: the wrapper leaves layout entirely, so the breadcrumb is
   the same flex child of .fe__primary it has always been. */
.fe-subhead--plain {
  display: contents;
}
.fe-subhead {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.fe-subhead > .fe-breadcrumb {
  flex: 1 1 auto;
  min-width: 0;
}
.fe-subhead__actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
  padding-inline-end: 8px;
}

/* ── the filter row ───────────────────────────────────────────────────── */
.fe-filterbar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  /* Scrolls sideways ON ITSELF at 390px rather than widening the explorer:
     three chips and a clear button do not fit a phone, and the alternative is
     a horizontal scrollbar on everything. */
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
}
.fe-filterbar::-webkit-scrollbar {
  display: none;
}
.fe-filterbar__chip {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 32px;
  padding-block: 0; padding-inline: 12px 10px;
  border: 1px solid var(--fe-border-strong);
  border-radius: 999px;
  background: transparent;
  color: var(--fe-text);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  white-space: nowrap;
}
.fe-filterbar__chip:hover {
  background: var(--fe-bg-hover);
}
.fe-filterbar__chip.is-set {
  background: var(--fe-bg-selected);
  border-color: var(--fe-primary);
  font-weight: 600;
}
.fe-filterbar__chip.is-open {
  border-color: var(--fe-primary);
}
.fe-filterbar__chip:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}
.fe-filterbar__caret {
  width: 15px;
  height: 15px;
}
.fe-filterbar__clear {
  flex: 0 0 auto;
  border: 0;
  background: transparent;
  color: var(--fe-primary);
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
  padding: 4px 6px;
  border-radius: var(--fe-radius-sm);
  white-space: nowrap;
}
.fe-filterbar__clear:hover {
  background: var(--fe-bg-hover);
}
.fe-filterbar__count {
  flex: 0 0 auto;
  margin-inline-start: auto;
  padding-inline-start: 8px;
  font-size: 12px;
  color: var(--fe-text-muted);
  white-space: nowrap;
}
/* Teleported to <body>: z-index 90, the layer the context menus already use.
   The onboarding tour sits above at 96 on purpose — a tour step must never end
   up behind a filter popover. */
.fe-filterpop {
  position: fixed;
  z-index: 90;
  min-width: 190px;
  max-height: 60vh;
  overflow-y: auto;
  padding: 6px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-md);
  background: var(--fe-bg);
  color: var(--fe-text);
  box-shadow: var(--fe-shadow);
  font-family: var(--fe-font);
}
.fe-filterpop__item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 7px 10px;
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 13px;
  text-align: start;
  cursor: pointer;
}
.fe-filterpop__item:hover {
  background: var(--fe-bg-hover);
}
.fe-filterpop__item:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}
.fe-filterpop__tick {
  flex: 0 0 14px;
  color: var(--fe-primary);
  font-size: 12px;
}

/* ── Folders / Files sections in the grid ─────────────────────────────── */
/* A grid item spanning every column, so the cards keep flowing under it.
 * ⚠ The gallery's date headings share this rule rather than copying it: the
 * two are the same object in two grids, and a second declaration block is how
 * one of them ends up a different size after a design pass. */
.fe-grid__heading,
.fe-gal__heading {
  grid-column: 1 / -1;
  margin: 6px 2px 2px;
  font-size: 13px;
  font-weight: 600;
  color: var(--fe-text);
}
.fe-grid__heading:first-child,
.fe-gal__heading:first-child {
  margin-top: 0;
}

/* ── the details panel: tabs, people, link row ────────────────────────── */
.fe-inspector__tabs {
  flex: 0 0 auto;
  display: flex;
  gap: 2px;
  padding: 0 10px;
  border-bottom: 1px solid var(--fe-border);
}
.fe-inspector__tab {
  padding: 9px 12px;
  border: 0;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--fe-text-muted);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.fe-inspector__tab:hover {
  color: var(--fe-text);
}
.fe-inspector__tab.is-active {
  color: var(--fe-primary);
  border-bottom-color: var(--fe-primary);
  font-weight: 600;
}
.fe-inspector__tab:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}
.fe-inspector__people {
  list-style: none;
  margin: 0 0 8px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.fe-inspector__person {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.fe-inspector__avatar {
  flex: 0 0 28px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--fe-primary);
  color: var(--fe-text-on-primary);
  font-size: 12px;
  font-weight: 600;
}
.fe-inspector__person-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.fe-inspector__person-name {
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-inspector__person-sub {
  font-size: 11.5px;
  color: var(--fe-text-muted);
}
.fe-inspector__linkrow {
  display: flex;
  align-items: center;
  gap: 8px;
}
.fe-inspector__linkicon {
  flex: 0 0 auto;
  font-size: 14px;
}
.fe-inspector__linknone {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13px;
  color: var(--fe-text-muted);
}
.fe-inspector__hint {
  margin: 6px 0 0;
  font-size: 12px;
  color: var(--fe-text-muted);
}

/* ── narrow ───────────────────────────────────────────────────────────── */
/* At 390px the chip is the only thing that has to go: the shortcut it prints
   cannot be typed on a phone anyway, and the palette stays reachable from the
   "⋯" menu. The field keeps the whole row. */
.fe--narrow .fe-drivesearch__kbd {
  display: none;
}
.fe--narrow .fe-drivesearch {
  height: 36px;
  max-width: none;
}
.fe--narrow .fe-subhead__actions {
  padding-inline-end: 4px;
}
.fe--narrow .fe-filterbar {
  padding: 6px 8px;
}
/* The narrow DRIVE header is three items: toggle · field · "⋯". The field is
   the only one that grows, and `min-width: 0` is what lets it shrink past its
   own content width — without it the row is wider than the phone. */
.fe-toolbar--narrow.fe-toolbar--drive {
  gap: 6px;
  padding: 6px 8px;
}
.fe-toolbar--narrow .fe-drivesearch {
  flex: 1 1 auto;
  min-width: 0;
}
/* === /surucu:d1 === */

/* === gorunum:v1-icons — appended by the menu-icon slice === */
/*
 * One stroked set for every action row, replacing the emoji the menus used to
 * print. Emoji are not an icon set: the OS picks the font, so two rows of the
 * same menu came out at different weights, different optical sizes and — worst
 * of the three — different colours, which meant the row's own colour never
 * reached its glyph. These draw on `currentColor`, so the menu's one coloured
 * entry, "Çöpe taşı", is the only coloured icon in it.
 *
 * The markup comes from lib/actionIcons.ts (static strings, injected with
 * v-html); this block only sizes and colours it.
 */
.fe-aicon {
  /* 16px, not 1em: the glyph is drawn on a 24-unit grid and reads as a smudge
     below 15. Fixed so a menu, a toolbar button and a bottom sheet — three
     different font sizes — get the same icon. */
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  display: block;
  /* Muted by default so the LABEL stays the thing being read; the row's own
     colour takes over for the states that mean something (below). */
  color: var(--fe-text-muted);
}

/* The context menu / bottom sheet. `.fe-ctx__icon` keeps the 1.2em column it
   already had — this only centres the SVG inside it, so rows with an icon and
   rows without still line their labels up. */
.fe-ctx__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* The destructive entry, and only it, is coloured — the whole point of moving
   off emoji was that this could be true. */
.fe-ctx__item.is-danger .fe-aicon {
  color: var(--fe-danger);
}
/* Under the pointer the icon joins the label at full strength. */
.fe-ctx__item:hover:not(.is-disabled) .fe-aicon,
.fe-ctx__item:focus-visible .fe-aicon {
  color: var(--fe-text);
}

/* Toolbar buttons. `.fe-icon` was a text span; when it carries an SVG it is a
   box. The button already sets its own colour (primary, danger, active), so
   the icon inherits it rather than staying muted. */
.fe-icon > .fe-aicon {
  color: inherit;
}
.fe-btn--danger .fe-aicon {
  color: var(--fe-danger);
}
/* === /gorunum:v1-icons === */

/* === gorunum:v1-grid === */
/* The grid card's anatomy. Two shapes, one shared row:
 *
 *   .fe-grid__card--folder   186×56    the row IS the card
 *   .fe-grid__card--file     186×166   184×108 preview + the row as a footer
 *
 * Heights are min-heights on purpose: a search result adds a parent path and
 * a content snippet under the caption, and a card that clipped them would be
 * hiding the very thing the search found.
 */

.fe-grid {
  /* A fixed track, not a stretching one: the card's 186px is part of the
     spec, and a grid that stretches the last row's cards to 240px makes the
     same folder a different size in two folders. */
  grid-template-columns: repeat(auto-fill, 186px);
  gap: var(--fe-gap);
  padding: var(--fe-gap);
  align-content: start;
  /* ⚠ Without this a folder card that shares a row with file cards is
     stretched to 166px by the grid's default `stretch`: the 56px card exists
     in the markup and measures 166 on screen, and only in a row that happens
     to hold both. Measured, not guessed — the first browser pass read 186×166
     for a folder card. */
  align-items: start;
}
/* Compact density keeps its tighter gap and padding, but not a narrower card:
   at ~104px the name column is ~30px and every card reads as an ellipsis. */
.fe--density-compact .fe-grid {
  grid-template-columns: repeat(auto-fill, 186px);
}

.fe-grid__card {
  padding: 0;
  border-radius: var(--fe-radius-md);
  background: var(--fe-bg);
  overflow: hidden;
}
.fe--density-compact .fe-grid__card {
  padding: 0;
}
.fe-grid__card--folder {
  min-height: 56px;
}
.fe-grid__card--file {
  min-height: 166px;
}

/* The preview: files only. A thumbnail fills it; without one the type tile
   sits centred on --fe-bg-elev, which is also the mat behind a thumbnail that
   does not cover the whole box. Rounded to the card's TOP corners only —
   inner radius, so the curve follows the border rather than crossing it. */
.fe-grid__card--file .fe-grid__thumb {
  flex: 0 0 108px;
  height: 108px;
  aspect-ratio: auto;
  margin: 0;
  border-radius: calc(var(--fe-radius-md) - 1px) calc(var(--fe-radius-md) - 1px) 0 0;
  background: var(--fe-bg-elev);
}

/* The shared row. On a file it is the footer under the preview; on a folder
   it is the whole card, which is why the padding differs: the folder's glyph
   needs the 12px inset the file's tile does not. */
.fe-grid__foot {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  min-width: 0;
}
.fe-grid__card--folder .fe-grid__foot {
  min-height: 54px;
  padding-block: 0; padding-inline: var(--fe-gap) var(--fe-gap-xs);
  gap: var(--fe-gap);
}
.fe-grid__card--file .fe-grid__foot {
  min-height: 56px;
  padding-block: 0; padding-inline: var(--fe-gap-sm) var(--fe-gap-xs);
  gap: var(--fe-gap-sm);
}

/* Folder previews (0.50, docs/thumbnails.md): a folder card grows the file
   card's 184x108 box when any folder in the view has pictures inside
   (GridView `has-folder-previews`), so a row of folders stays one height. The
   box holds the folder drawn large (FolderMosaic): its back, up to three of
   its pictures rising out of it like prints, each turned a little, and its
   front over their lower half; with no pictures, the same folder, empty. A
   view with no pictured folder, or folder previews turned off (Settings),
   keeps the compact 56px cards. */
.fe-grid.has-folder-previews .fe-grid__card--folder {
  min-height: 166px;
}
.fe-grid__card--folder .fe-grid__thumb--folder {
  flex: 0 0 108px;
  height: 108px;
  margin: 0;
  overflow: hidden;
  border-radius: calc(var(--fe-radius-md) - 1px) calc(var(--fe-radius-md) - 1px) 0 0;
  background: var(--fe-bg-elev);
}
.fe-fmosaic {
  /* The folder's two tones: its own colour in front, a deeper one behind. */
  --fmosaic-front: var(--fe-icon-folder);
  --fmosaic-back: color-mix(in srgb, var(--fe-icon-folder) 70%, #6b3f00);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
}
.fe-fmosaic__stage {
  position: relative;
  flex: none;
}
.fe-fmosaic__back,
.fe-fmosaic__front {
  position: absolute;
  display: block;
  inset-inline: 0;
  width: 100%;
}
.fe-fmosaic__back {
  inset-block: 0;
  height: 100%;
  fill: var(--fmosaic-back);
  /* The tab sits on the inline-start side, mirrored for right-to-left. */
  transform: scaleX(var(--filex-dir-x, 1));
}
.fe-fmosaic__front {
  z-index: 2;
  inset-block-end: 0;
  fill: var(--fmosaic-front);
  /* It casts a little shadow up onto the prints, which is what puts them
     inside the folder rather than on top of it. */
  filter: drop-shadow(0 -1px 2px rgba(0, 0, 0, 0.22));
}
/* A print: one file from inside, its picture or its type icon on white, in a
   white border. Its place and turn come from --x / --y / --r (by count and
   slot, below), mirrored for right-to-left. The newest is the middle one. */
.fe-fmosaic__print {
  --x: 0px;
  --y: 0px;
  --r: 0deg;
  --lift: 0px;
  position: absolute;
  inset-inline-start: 50%;
  /* A type icon (ThumbTile's slot, no wrapper) sits in the middle. */
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  box-sizing: border-box;
  border: 2px solid #fff;
  border-radius: 3px;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.28);
  transform: translate(calc(var(--x) * var(--filex-dir-x, 1)), calc(var(--y) + var(--lift))) /* rtl-physical: mirrored by --filex-dir-x */
    rotate(calc(var(--r) * var(--filex-dir-x, 1)));
  transition: transform 160ms ease;
}
.fe-fmosaic--1 .fe-fmosaic__print--1 { --r: -4deg; }
.fe-fmosaic--2 .fe-fmosaic__print--1 { --x: -15px; --y: 2px; --r: -8deg; }
.fe-fmosaic--2 .fe-fmosaic__print--2 { --x: 15px; --r: 6deg; }
.fe-fmosaic--3 .fe-fmosaic__print--1 { --x: -25px; --y: 5px; --r: -11deg; }
.fe-fmosaic--3 .fe-fmosaic__print--2 { --y: -1px; --r: -1deg; z-index: 1; }
.fe-fmosaic--3 .fe-fmosaic__print--3 { --x: 25px; --y: 4px; --r: 10deg; }
@media (prefers-reduced-motion: reduce) {
  .fe-fmosaic__print { transition: none; }
}
.fe-fmosaic__img {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* A file with no picture: its type icon, small, on the white print. */
.fe-fmosaic__icon {
  display: inline-flex;
  font-size: 20px;
  line-height: 0;
}
.fe-fmosaic__glyph {
  align-self: center;
  width: 100%;
  height: 100%;
  font-size: 44px;
}

/* peek: the grid card's 184x108 box, the folder 120x88 at its foot. */
.fe-fmosaic--peek {
  padding-block-end: 8px;
}
.fe-fmosaic--peek .fe-fmosaic__stage {
  width: 120px;
  height: 88px;
}
.fe-fmosaic--peek .fe-fmosaic__front {
  height: 50px;
}
.fe-fmosaic--peek .fe-fmosaic__print {
  inset-block-start: 4px;
  width: 64px;
  height: 48px;
  margin-inline-start: -32px;
}
/* Pointing at the folder lifts its files a little out of it. */
.fe-grid__card--folder:hover .fe-fmosaic--peek .fe-fmosaic__print {
  --lift: -4px;
}

/* fan: the gallery's square tile. The folder drawn large behind, and up to
   three prints fanned out from their foot in front of it, the newest in
   the middle and on top. */
.fe-fmosaic--fan {
  align-items: center;
}
.fe-fmosaic--fan .fe-fmosaic__stage {
  width: 132px;
  height: 97px;
  margin-block-start: 12px;
}
.fe-fmosaic--fan .fe-fmosaic__front {
  z-index: 0;
  height: 55px;
}
.fe-fmosaic--fan .fe-fmosaic__print {
  z-index: 1;
  inset-block-end: 14px;
  width: 92px;
  height: 69px;
  margin-inline-start: -46px;
  border-width: 3px;
  border-radius: 4px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
  transform-origin: 50% 100%;
}
.fe-fmosaic--fan .fe-fmosaic__icon {
  font-size: 30px;
}
.fe-fmosaic--fan.fe-fmosaic--1 .fe-fmosaic__print--1 { --r: -3deg; }
.fe-fmosaic--fan.fe-fmosaic--2 .fe-fmosaic__print--1 { --x: -12px; --y: 0px; --r: -9deg; }
.fe-fmosaic--fan.fe-fmosaic--2 .fe-fmosaic__print--2 { --x: 12px; --r: 7deg; z-index: 2; }
.fe-fmosaic--fan.fe-fmosaic--3 .fe-fmosaic__print--1 { --x: -16px; --y: 0px; --r: -14deg; }
.fe-fmosaic--fan.fe-fmosaic--3 .fe-fmosaic__print--3 { --x: 16px; --y: 0px; --r: 12deg; }
.fe-fmosaic--fan.fe-fmosaic--3 .fe-fmosaic__print--2 { --y: 0px; --r: -1deg; z-index: 2; }
.fe-gal__card:hover .fe-fmosaic--fan .fe-fmosaic__print {
  --lift: -3px;
}

/* mini: the list row's 24px icon, the newest file rising out of the folder. */
.fe-fmosaic--mini {
  width: 24px;
  height: 24px;
  flex: 0 0 auto;
}
.fe-fmosaic--mini .fe-fmosaic__stage {
  width: 24px;
  height: 19px;
}
.fe-fmosaic--mini .fe-fmosaic__front {
  height: 11px;
  filter: drop-shadow(0 -0.5px 1px rgba(0, 0, 0, 0.25));
}
.fe-fmosaic--mini .fe-fmosaic__print {
  inset-block-start: 0;
  width: 15px;
  height: 11px;
  margin-inline-start: -7.5px;
  border-width: 1px;
  border-radius: 1.5px;
  box-shadow: 0 0.5px 1px rgba(0, 0, 0, 0.3);
}
.fe-fmosaic--mini .fe-fmosaic__icon {
  font-size: 8px;
}

/* The folder peek (FolderPeek): a glance, not a menu. It takes no pointer, so
   it can never catch the click meant for the folder under it. Placed and
   layered by the component (el.style), sized here. */
.fe-fpeek {
  position: fixed;
  width: 264px;
  max-width: calc(100vw - 16px);
  padding: var(--fe-gap-sm);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-md);
  background: var(--fe-bg);
  box-shadow: var(--fe-shadow);
  color: var(--fe-text);
  font-family: var(--fe-font);
  font-size: var(--fe-text-sm);
  pointer-events: none;
}
.fe-fpeek__head {
  display: flex;
  align-items: baseline;
  gap: var(--fe-gap-sm);
  margin: 0 0 var(--fe-gap-xs);
  min-width: 0;
}
.fe-fpeek__name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
}
.fe-fpeek__count,
.fe-fpeek__loading,
.fe-fpeek__more {
  color: var(--fe-text-muted);
  font-size: var(--fe-text-xs);
  white-space: nowrap;
}
.fe-fpeek__loading,
.fe-fpeek__more {
  margin: var(--fe-gap-xs) 0 0;
}
.fe-fpeek__list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.fe-fpeek__row {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  min-width: 0;
  padding-block: 2px;
}
.fe-fpeek__tile {
  /* The corner a page's type badge sits in (ThumbTile, .fe-thumb__type). */
  position: relative;
  flex: 0 0 24px;
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: var(--fe-radius-sm);
  font-size: 18px;
}
.fe-fpeek__img {
  width: 24px;
  height: 24px;
  object-fit: cover;
}
.fe-fpeek__label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The 24px type tile in the row (lib/fileIcons sizes itself from font-size).
   A folder gets the same box and the untiled solid glyph inside it. */
.fe-grid__tile {
  flex: 0 0 24px;
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  line-height: 1;
}

.fe-grid__main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.fe-grid__label {
  font-size: var(--fe-text-sm);
  font-weight: 500;
  line-height: 1.3;
  color: var(--fe-text);
}
/* The caption: "1.7 MB • Sep 9, 2026" on a file, "Folder" on a folder. */
.fe-grid__meta {
  margin-top: 2px;
  font-size: 11px;
  font-weight: 400;
  line-height: 1.3;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The ⋮ on every card. It opens the context menu — the same one the right
   click opens, built from the explorer's single action list — so it carries
   no styling that would suggest a menu of its own. */
.fe-grid__menu {
  flex: 0 0 var(--fe-h-sm);
  width: var(--fe-h-sm);
  height: var(--fe-h-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--fe-radius-md);
  background: transparent;
  color: var(--fe-text-muted);
  font-family: inherit;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
.fe-grid__menu:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-grid__menu:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}

/* The section labels — and, while the rows are in date order, the date
   labels that replace "Files" (lib/dateGroups). ~20px of air above each, none
   above the first: the grid's own padding already provides it.
   ⚠ The gallery rides the same rule; see the note at the first block. */
.fe-grid__heading,
.fe-gal__heading {
  grid-column: 1 / -1;
  margin: 20px 2px var(--fe-gap-sm);
  font-size: var(--fe-text-md);
  font-weight: 600;
  line-height: 1.3;
  color: var(--fe-text);
}
.fe-grid__heading:first-child,
.fe-gal__heading:first-child {
  margin-top: 0;
}
/* === /gorunum:v1-grid === */

/* === gorunum:v1-chrome — the shell: sidebar · header · crumbs · filters ===
   Measured off the reference at 1440×900, light and dark (the spec's "Chrome"
   section). Only declared --fe-* tokens are used: a raw value here cannot be
   reached by a theme, and web/tests/api/themeTokens.test.ts fails the build on
   a phantom one.

   ⚠ Last block in the file on purpose — sibling agents append their own, and
   nobody edits the middle. Everything here is a RESTYLE of markup that already
   exists; no behaviour (the toolbar fold, the narrow layout, the drawer, the
   drive shell, the uiProfile variants) is gated on any of it.
   ---------------------------------------------------------------------- */

/* ── the sidebar ──────────────────────────────────────────────────────── */
/* An elevated ground with one hairline against the listing, which is what
   separates "the places" from "the files" without a second border or a
   shadow. 255px: the widest storage label in the fixtures fits on one line. */
.fe-sidenav {
  flex: 0 0 var(--fe-sidenav-w);
  width: var(--fe-sidenav-w);
  background: var(--fe-bg-elev);
}
/* The rail and the drawer keep their own widths — only the ground follows. */
.fe-sidenav--rail {
  flex: 0 0 56px;
  width: 56px;
}
/* ⚠ The width HAS to be restated here. `.fe-sidenav` above is a later rule of
   equal specificity, so without this the drawer inherits the docked column's
   255px and stops being `min(280px, 84%)` — measured at 390px, where the
   drawer came back 25px narrow. */
.fe-sidenav--drawer {
  flex: none;
  width: min(280px, 84%);
  background: var(--fe-bg-elev);
}
.fe-sidenav__head {
  background: var(--fe-bg-elev);
}
.fe-sidenav__scroll {
  padding: 6px 8px 12px;
}
/* One row = one destination: a 34px box, the token height every other control
   in the shell picks, with the glyph and the label 8px apart. */
.fe-sidenav__item {
  height: var(--fe-h-md);
  padding: 0 var(--fe-gap);
  gap: var(--fe-gap-sm);
  margin: 1px 0;
  border-radius: var(--fe-radius-md);
  font-size: var(--fe-text-md);
  font-weight: 500;
}
.fe-sidenav__item .fe-ficon {
  flex: 0 0 18px;
  width: 18px;
  height: 18px;
  font-size: 18px;
}
/* The active row is the soft primary wash with primary ink. ⚠ Weight stays
   500: bolding the active row re-measures its text and every row below it
   shifts by a pixel as you click down the list. */
.fe-sidenav__item.is-active {
  background: var(--fe-primary-soft);
  color: var(--fe-primary-ink);
  font-weight: 500;
}
.fe-sidenav__item.is-active:hover {
  background: var(--fe-primary-soft);
}
.fe-sidenav--rail .fe-sidenav__item {
  padding: 0;
  justify-content: center;
  border-radius: var(--fe-radius-md);
}
/* Section captions: small, spaced and quiet — a label for a group, not a row
   you can click. The 18px inset is measured, and it is deliberately deeper
   than the items' 12px: the caption aligns with the item TEXT, not with the
   glyph column. */
.fe-sidenav__heading {
  margin: 12px 0 2px;
  padding: 0 18px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--fe-text-muted);
}
.fe-sidenav__section {
  margin-top: var(--fe-gap-sm);
}
/* The footer. Two muted lines — the name of the thing over the figure — so it
   reads as a status and never competes with a destination above it.
   ⚠ "Storage" is the ACCOUNT's line (quotaMe), not the selected drive's, so
   the label stays the generic word rather than the storage's name. */
.fe-sidenav__quota {
  background: var(--fe-bg-elev);
}
.fe-sidenav__quota-label {
  margin: 0 0 6px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: normal;
  text-transform: none;
  color: var(--fe-text-muted);
}
.fe-sidenav__quota-text {
  margin: 6px 0 0;
  font-size: 11px;
  color: var(--fe-text-muted);
}

/* ── the header's search field ────────────────────────────────────────── */
/* ONE field in every shell (Toolbar.vue renders the same markup for both, the
   classic one carrying the legacy `fe-search*` hooks as well). A wide box with
   the magnifier inside the leading edge and the ⌘K chip inside the trailing
   one; the chip's text comes from the shortcut registry, never a literal. */
.fe-drivesearch {
  height: var(--fe-h-lg);
  max-width: 560px;
  padding-block: 0; padding-inline: var(--fe-gap) var(--fe-gap-sm);
  gap: var(--fe-gap-sm);
  border-radius: var(--fe-radius);
  border: 1px solid var(--fe-border);
  background: var(--fe-bg);
}
.fe-drivesearch:focus-within {
  border-color: var(--fe-primary);
  box-shadow: none;
  background: var(--fe-bg);
}
.fe-drivesearch__input {
  font-size: var(--fe-text-md);
  /* ⚠ The classic field also carries .fe-search__input, whose own rule sets a
     border, a ground and a 200px minimum — the field around it draws all three
     now, so they are cleared here rather than in the middle of the file. */
  min-width: 0;
  padding: 0;
  border: 0;
  background: transparent;
}
.fe-drivesearch__input:focus-visible {
  outline: none;
}
.fe-drivesearch__kbd {
  font-size: 11px;
  border-radius: var(--fe-radius-sm);
  background: var(--fe-bg-elev);
}
/* The classic header's search cluster used to be a bordered pill holding a
   bare input plus Upload and Refresh. The field draws its own box now, so the
   wrapper goes back to being pure layout — and it is what grows. */
.fe-toolbar__search-group {
  flex: 1 1 auto;
  min-width: 0;
  gap: var(--fe-gap-sm);
  padding: 0;
  background: transparent;
  border-color: transparent;
}
.fe-toolbar__search-group .fe-btn {
  height: var(--fe-h-md);
  padding: 0 var(--fe-gap-sm);
  border-radius: var(--fe-radius-md);
}
.fe-search {
  flex: 1 1 auto;
  min-width: 0;
}
/* Narrow, classic: the row-wide field the magnifier expands into keeps the
   same box as the wide one, one size down. */
.fe-toolbar--narrow .fe-search--full .fe-search__input {
  height: var(--fe-h-md);
  padding: 0 var(--fe-gap-sm);
  border-radius: var(--fe-radius);
  border: 1px solid var(--fe-border);
  background: var(--fe-bg);
  font-size: var(--fe-text-md);
}

/* ── the breadcrumb row ───────────────────────────────────────────────── */
/* Crumbs lead; the view switcher and the details toggle sit at the trailing
   edge (the drive shell renders them there — everywhere else the wrapper is
   `display: contents` and this row is the breadcrumb alone).
   ⚠ The rule under the row moves to the WRAPPER: while it lived on the
   breadcrumb itself it stopped under the crumbs and left the actions hanging
   over nothing. */
.fe-subhead {
  gap: var(--fe-gap-sm);
  background: var(--fe-bg);
  border-bottom: 1px solid var(--fe-border);
}
.fe-subhead > .fe-breadcrumb {
  border-bottom: 0;
  background: transparent;
}
.fe-subhead__actions {
  gap: var(--fe-gap-sm);
  padding-inline-end: var(--fe-gap);
}
.fe-breadcrumb {
  gap: var(--fe-gap-xs);
  padding: 6px var(--fe-gap);
  font-size: var(--fe-text-md);
}
/* "Go to path" — an icon button now (the ✏ emoji it used to print came from
   whatever emoji font the machine had), so it needs a box rather than a
   font-size. */
/* ⚠ 24px, not --fe-h-sm. The crumb bar is pinned at 37px because the panel's
   head (.fe-sidenav__head) and the split pane's own crumbs (.fe-split__crumbs)
   are 37px too, and the three sit on one line across the top; a 28px button
   inside 6px padding makes this row 40px and only this row. */
.fe-breadcrumb__edit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border-radius: var(--fe-radius-sm);
}
.fe-breadcrumb__edit .fe-ficon {
  width: 15px;
  height: 15px;
}
/* The segmented view switcher: one box, one active cell, no gaps between the
   cells — three separate buttons in a row read as three controls. */
.fe-toolbar__view {
  gap: 0;
  padding: 2px;
  border-radius: var(--fe-radius);
  background: var(--fe-bg-elev);
}
.fe-toolbar__view .fe-btn {
  height: var(--fe-h-sm);
  padding: 0 var(--fe-gap-sm);
  border-radius: var(--fe-radius-sm);
  color: var(--fe-text-muted);
}
.fe-toolbar__view .fe-btn.is-active {
  background: var(--fe-bg);
  color: var(--fe-primary);
}

/* ── the filter row ───────────────────────────────────────────────────── */
.fe-filterbar {
  gap: var(--fe-gap-sm);
  padding: var(--fe-gap-sm) var(--fe-gap);
}
/* A pill is a control, not a tag: 28px, the small token height, with the
   caret that says it opens something. */
.fe-filterbar__chip {
  height: var(--fe-h-sm);
  padding-block: 0; padding-inline: var(--fe-gap) var(--fe-gap-sm);
  gap: var(--fe-gap-xs);
  border-radius: var(--fe-radius);
  border-color: var(--fe-border);
  font-size: var(--fe-text-xs);
}
.fe-filterbar__chip.is-set {
  background: var(--fe-primary-soft);
  border-color: var(--fe-primary);
  color: var(--fe-primary-ink);
  font-weight: 600;
}
.fe-filterbar__caret {
  width: 14px;
  height: 14px;
}
/* "Filter in this folder…" — the same 28px box as the pills beside it, so the
   row has one edge top and bottom. */
.fe-filterbar__find {
  flex: 0 1 240px;
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 140px;
  height: var(--fe-h-sm);
  padding: 0 var(--fe-gap-sm);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg);
  color: var(--fe-text);
}
.fe-filterbar__find:focus-within {
  border-color: var(--fe-primary);
}
.fe-filterbar__find-glass {
  flex: 0 0 14px;
  width: 14px;
  height: 14px;
  color: var(--fe-text-muted);
}
.fe-filterbar__find-input {
  flex: 1 1 auto;
  min-width: 0;
  height: 100%;
  border: 0;
  padding: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: var(--fe-text-xs);
  outline: none;
}
.fe-filterbar__find-input::placeholder {
  color: var(--fe-text-muted);
}
.fe-filterbar__clear {
  font-size: var(--fe-text-xs);
}
/* At 390px the row scrolls sideways on itself (surucu:d1); the name box must
   not eat the width the pills need to be reachable in that scroll. */
.fe--narrow .fe-filterbar__find {
  flex: 0 0 160px;
  min-width: 160px;
}
/* === /gorunum:v1-chrome === */

/* === gorunum:v1-selbar — the selection bar =============================
   The moment anything is ticked, the FILTER ROW is replaced by a tinted bar:
   "16 selected", icon-only actions, and an × that clears. Same box as the row
   it replaces (8px/12px padding around 28px controls = 44px), so the chrome
   does not jump by a pixel when the two swap.

   Where it lives: `.fe-selbar-slot` is an empty div the toolbar inserts into
   `.fe__primary` right after the breadcrumb wrapper — the filter row's place,
   and where the filter row WOULD be in a profile that has none. The toolbar
   teleports the bar into it and marks the slot `.is-active`.
   ---------------------------------------------------------------------- */
.fe-selbar-slot {
  flex: 0 0 auto;
  min-width: 0;
}
/* The swap. A general sibling, because both are children of `.fe__primary`
   and the slot is inserted before the filter row. */
.fe-selbar-slot.is-active ~ .fe-filterbar {
  display: none;
}

.fe-selbar {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  min-width: 0;
  padding: var(--fe-gap-sm) var(--fe-gap);
  /* The "something is active" tint, the same token the set filter pill and the
     active sidebar item wear — the bar is chrome in an active state, not a row. */
  background: var(--fe-primary-soft);
  border-bottom: 1px solid var(--fe-border);
  color: var(--fe-text);
}
/* No slot to land in (a bare <Toolbar>): the bar renders inside the toolbar
   instead of under the breadcrumb, and takes the whole row so it still reads
   as a bar rather than a control wedged between two others. */
.fe-selbar--inline {
  flex: 1 1 100%;
  border-bottom: 0;
  border-radius: var(--fe-radius);
}
.fe-selbar__count {
  flex: 0 0 auto;
  font-size: var(--fe-text-md);
  font-weight: 600;
  color: var(--fe-primary);
  white-space: nowrap;
}
/* The fold's measuring frame: it absorbs the free width, so its `clientWidth`
   IS the room the icons have. `overflow: hidden` keeps a row that does not fit
   from widening the explorer while the arithmetic catches up. */
.fe-selbar__actions {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--fe-gap-xs);
  overflow: hidden;
}
.fe-selbar__btn,
.fe-selbar__close {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--fe-h-sm);
  height: var(--fe-h-sm);
  padding: 0;
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text);
  font: inherit;
  cursor: pointer;
}
.fe-selbar__btn:hover,
.fe-selbar__close:hover {
  background: var(--fe-bg-hover);
}
.fe-selbar__btn:focus-visible,
.fe-selbar__close:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}
.fe-selbar__btn .fe-icon,
.fe-selbar__close .fe-icon {
  display: inline-flex;
  width: 18px;
  height: 18px;
}
.fe-selbar__btn .fe-icon svg,
.fe-selbar__close .fe-icon svg {
  width: 18px;
  height: 18px;
}
/* Destructive is still `--fe-danger`, but as a GLYPH: a filled red button in a
   row of quiet icons reads as the thing you are meant to press. */
.fe-selbar__btn--danger {
  color: var(--fe-danger);
}
.fe-selbar__btn--danger:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-danger-hover);
}
.fe-selbar__btn.is-disabled,
.fe-selbar__btn:disabled {
  opacity: 0.45;
  cursor: default;
}
.fe-selbar__btn.is-disabled:hover,
.fe-selbar__btn:disabled:hover {
  background: transparent;
}
.fe-selbar__close {
  color: var(--fe-text-muted);
}
/* Out of layout, but still LAID OUT: `visibility` (not `display: none`) is what
   leaves the buttons with a real offsetWidth to measure. */
.fe-selbar__measure {
  position: absolute;
  top: 0;
  inset-inline-start: 0;
  display: flex;
  align-items: center;
  gap: var(--fe-gap-xs);
  visibility: hidden;
  pointer-events: none;
}
/* At 390px the count keeps its line and the icons fold into the "⋯" on their
   own — the bar never scrolls sideways, because the × has to stay reachable. */
.fe--narrow .fe-selbar {
  gap: var(--fe-gap-xs);
  padding: var(--fe-gap-sm) var(--fe-gap-sm);
}
.fe--narrow .fe-selbar__count {
  font-size: var(--fe-text-sm);
}
/* === /gorunum:v1-selbar === */

/* === gorunum:v1-list === */
/* The list row, rebuilt to the measured spec. Everything here is an override
   of the pre-gorunum list block further up the file — it lives at the end so
   two agents working on the same wave cannot collide in the middle. */

/* Eight columns: tick · name · type · OWNER · modified · size · star · ⋮.

   ⚠ This block used to say "there is deliberately NO Owner column: a listing
   carries no owner, and an empty column headed Owner is worse than no column at
   all". The first half is what changed — migration 00038 put ownership on the
   row and the listing projection carries it — and the second half is still the
   rule the column has to keep earning. */
.fe-list__head,
.fe-list__row {
  grid-template-columns: 28px minmax(0, 1fr) 84px 120px 170px 84px 24px var(--fe-h-sm);
  gap: var(--fe-gap-sm);
  padding: 0 var(--fe-gap);
}

/* 13px/500 muted, no background — the header is a label for the columns, not
   a band of chrome sitting on top of them. */
.fe-list__head {
  min-height: var(--fe-h-md);
  background: transparent;
  border-bottom: 1px solid var(--fe-border-soft);
  font-size: var(--fe-text-md);
  font-weight: 500;
  text-transform: none;
  letter-spacing: normal;
}
.fe-list__head .fe-list__col {
  display: flex;
  align-items: center;
  min-width: 0;
}

/* ~34px rows, a hairline between them, no zebra. `min-height` rather than
   `height`: a search result carries a parent path and a snippet under the
   name, and a fixed height clips them. */
.fe-list__row {
  min-height: var(--fe-h-md);
  border-bottom: 1px solid var(--fe-border-soft);
}

/* The tick — the one click on an item that selects (issue #26; ListView
   onCheckClick, components/ItemCheck). The plain control look, not an accent
   of its own. */
.fe-list__col--check {
  display: flex;
  align-items: center;
  justify-content: center;
}
.fe-list__check {
  appearance: none;
  -webkit-appearance: none;
  width: 20px;
  height: 20px;
  flex: 0 0 20px;
  margin: 0;
  padding: 0;
  position: relative;
  border: 1.5px solid var(--fe-border-strong);
  border-radius: 5px;
  background: var(--fe-bg);
  cursor: pointer;
}
.fe-list__check.is-on {
  background: var(--fe-primary);
  border-color: var(--fe-primary);
}
/* A drawn tick rather than a glyph: a text check inherits the row's font and
   lands a pixel differently in every family. */
.fe-list__check.is-on::after {
  content: '';
  position: absolute;
  left: 6px; /* rtl-physical: a tick reads the same in every script — it is not an arrow */
  top: 2.5px;
  width: 4px;
  height: 9px;
  border: solid var(--fe-text-on-primary);
  border-width: 0 2px 2px 0; /* rtl-physical: the tick's two strokes (see `left` above) */
  transform: rotate(45deg);
}
.fe-list__check:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}

/* The 24px type tile (lib/fileIcons sizes itself off 1em). */
.fe-list__icon {
  flex: 0 0 auto;
  font-size: 24px;
  line-height: 0;
}

/* The name sits tight against the tile so the kind label reads as its
   annotation rather than as the next column. */
.fe-list__col--name {
  gap: var(--fe-gap-sm);
}
.fe-list__name-wrap {
  flex: 0 1 auto;
}
.fe-list__name {
  font-size: var(--fe-text-sm);
  font-weight: 500;
  line-height: 1.35;
}
.fe-list__kind {
  flex: 0 0 auto;
  font-size: var(--fe-text-xs);
  font-weight: 400;
  color: var(--fe-text-muted);
  white-space: nowrap;
}

.fe-list__col--owner,
.fe-list__col--type,
.fe-list__col--mod,
.fe-list__col--size {
  display: flex;
  align-items: center;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fe-text-md);
  color: var(--fe-text-muted);
}

/* The ⋮ at the trailing edge of every row. Same box and same glyph size as
   the grid card's, because it opens the same menu. */
.fe-list__col--menu {
  display: flex;
  align-items: center;
  justify-content: center;
}
.fe-list__menu {
  flex: 0 0 var(--fe-h-sm);
  width: var(--fe-h-sm);
  height: var(--fe-h-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--fe-radius-md);
  background: transparent;
  color: var(--fe-text-muted);
  font-family: inherit;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
.fe-list__menu:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-list__menu:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}

/* Compact density still means something with a min-height in play. */
.fe--density-compact .fe-list__row {
  min-height: var(--fe-h-sm);
  padding: 0 var(--fe-gap);
}
.fe--density-compact .fe-list__head {
  min-height: var(--fe-h-sm);
  padding: 0 var(--fe-gap);
}

/* The loading ghosts mirror the row grid so the swap to real rows does not
   shift the layout. */
.fe-skel-row {
  grid-template-columns: 28px minmax(0, 1fr) 84px 120px 170px 84px 24px var(--fe-h-sm);
  gap: var(--fe-gap-sm);
  padding: 0 var(--fe-gap);
  min-height: var(--fe-h-md);
  border-bottom-color: var(--fe-border-soft);
}

/* Narrow: the tick, the name, the size and the ⋮ survive; the rest would each
   take a third of a phone's width to say one word. ⚠ This has to restate the
   column template — the pre-gorunum narrow rule further up the file names
   three columns, and seven cells flowing into three make implicit rows.

   ⚠ Twice, on purpose. The media query answers the VIEWPORT; `.fe--narrow` is
   what an embedded explorer gets from its own ResizeObserver (< 560px of
   container, FileExplorer "bag:b4"), and an embed in a narrow column on a wide
   screen only ever matches the second. */
.fe--narrow .fe-list__head,
.fe--narrow .fe-list__row,
.fe--narrow .fe-skel-row {
  grid-template-columns: 28px minmax(0, 1fr) 72px var(--fe-h-sm);
}
.fe--narrow .fe-list__col--type,
.fe--narrow .fe-list__col--owner,
.fe--narrow .fe-list__col--mod,
.fe--narrow .fe-list__col--remaining,
.fe--narrow .fe-list__col--star,
.fe--narrow .fe-list__head .fe-list__col--type,
.fe--narrow .fe-list__head .fe-list__col--owner,
.fe--narrow .fe-list__head .fe-list__col--mod,
.fe--narrow .fe-list__head .fe-list__col--remaining,
.fe--narrow .fe-list__head .fe-list__col--star {
  display: none;
}

@media (max-width: 640px) {
  .fe-list__head,
  .fe-list__row,
  .fe-skel-row {
    grid-template-columns: 28px minmax(0, 1fr) 72px var(--fe-h-sm);
  }
  .fe-list__col--type,
  .fe-list__col--owner,
  .fe-list__col--mod,
  .fe-list__col--remaining,
  .fe-list__col--star,
  /* ⚠ The header's cells need the same two-class weight as the rule that
     lays them out (`.fe-list__head .fe-list__col`), or they stay visible and
     the header wraps to a second line while the rows below it do not —
     measured at 390px: "Name Type Mod" over "Size" against four-cell rows. */
  .fe-list__head .fe-list__col--type,
  .fe-list__head .fe-list__col--owner,
  .fe-list__head .fe-list__col--mod,
  .fe-list__head .fe-list__col--remaining,
  .fe-list__head .fe-list__col--star {
    display: none;
  }
}
/* === /gorunum:v1-list === */

/* === gorunum:v1-preview === */
/* The synthetic preview: the first lines of the real file, drawn inside the
 * card's existing 184×108 box (and inside the gallery tile, same rules).
 *
 * It is deliberately unreadable as prose — 8.5px monospace, muted, clipped —
 * because it is not a reader. Its job is to let the eye tell a config file
 * from a component from a table before the name is read, which is the one
 * thing the coloured type tile cannot do: one glyph covers .ts, .go, .css and
 * .sql. `lib/filePreview.ts` bounds what is fetched to draw it.
 */

.fe-fprev {
  /* Fills the box rather than sitting in its flex centre: a preview that
     starts anywhere but the top-left is not a preview of a file. */
  position: absolute;
  inset: 0;
  padding: 9px 10px 0;
  background: var(--fe-bg-elev);
  font-family: var(--fe-font-mono);
  font-size: 8.5px;
  line-height: 11px;
  color: var(--fe-text-muted);
  text-align: start;
  white-space: pre;
  overflow: hidden;
  /* The box always ends mid-file, so END it. A hard crop through a line of
     text reads as a rendering bug; a fade reads as "there is more below".
     ⚠ The `#000` is not a theme colour and must not become one — a mask uses
     only the alpha channel, so the black here means "fully opaque". */
  -webkit-mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
  /* The card owns every gesture: click, double-click, drag and right-click
     must not land on the preview instead of the thing it is a preview of. */
  pointer-events: none;
  user-select: none;
}

.fe-fprev__line {
  overflow: hidden;
}

/* The tint. Only the first token of a line, only when it really is a keyword
   or a markdown marker (`lib/filePreview.ts` decides), and in the family's own
   accent — the same colour as that file's tile in the footer, so the two read
   as one object. Everything else stays plain: the brief's rule is that plain
   text beats fake colour, and a highlighter that guesses is fake colour. */
.fe-fprev--code .fe-fprev__tint {
  color: var(--fe-icon-code);
}
.fe-fprev--text .fe-fprev__tint {
  color: var(--fe-icon-text);
}

/* Tables are one grid, not a stack of rows: `--fprev-cols` comes from the
   parsed file, and every cell is a direct child, so column two is under column
   two even when a row is short. */
.fe-fprev--table {
  display: grid;
  grid-template-columns: repeat(var(--fprev-cols, 3), minmax(0, 1fr));
  align-content: start;
  gap: 0 6px;
  white-space: nowrap;
}
.fe-fprev__cell {
  padding: 2px 0;
  border-bottom: 1px solid var(--fe-border-soft);
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The first row is a header often enough that treating it as one is right
   more often than not, and when it is not, the file's first row is still the
   row a person looks at first. */
.fe-fprev__cell.is-head {
  color: var(--fe-text);
  font-weight: 600;
}

/* The gallery tile is several times the area of a grid card's box, so the
   same type would be a smudge in a corner. Scale only — the markup, the
   parser and the limits are shared (no surface-specific behaviour). */
.fe-gal__thumb .fe-fprev {
  padding: 12px 14px 0;
  font-size: 10px;
  line-height: 14px;
}
.fe-gal__thumb .fe-fprev--table {
  gap: 0 8px;
}
/* === /gorunum:v1-preview === */

/* === gorunum:v1-viewer ===
 *
 * The viewer stopped being a card. What it is now: an overlay that owns the
 * whole viewport, with one bar across the top (type tile + name + a muted
 * "246.3 KB • Sep 9, 2026 • 1 of 9" line, centred icon actions, close at the
 * trailing edge), a chevron on each screen edge, and a floating zoom pill at
 * the bottom centre.
 *
 * ⚠ `--fullbleed` is NOT `--chromeless`. Chromeless is the standalone
 * /files/edit route, where the browser tab is the container and NO chrome is
 * drawn at all; e2e/tests/83 asserts on exactly that. Fullbleed keeps the
 * modal identity and hands the chrome to PreviewModal.
 */
/* ⚠ `border: 0` is not decoration. The backdrop also carries the `.fe` root
 * class (that is how it reaches the theme variables) and `.fe` draws a 1px
 * border with a radius — so the "full-bleed" card measured 1438×898 at 1,1
 * inside a 1440×900 viewport, with a hairline of the host shell showing
 * around all four edges. */
.fe-modal__backdrop--fullbleed {
  padding: 0;
  border: 0;
  border-radius: 0;
}
/* ⚠ 100%, not 100vh. The backdrop is `position: fixed; inset: 0`, i.e. exactly
 * the viewport (measured 1440×900), while `100vh` came out 898px in the same
 * browser and left the card floating 1px down with 2px of the shell showing
 * under it. A percentage of a parent we can measure beats a viewport unit we
 * cannot — and it is also what survives a mobile browser's dynamic toolbar. */
.fe-modal__card--fullbleed {
  max-width: none;
  width: 100%;
  height: 100%;
  max-height: 100%;
  border-radius: 0;
  box-shadow: none;
}
.fe-modal__card--fullbleed > .fe-modal__body {
  flex: 1 1 auto;
  min-height: 0;
  padding: 0;
  overflow: hidden;
}

.fe-viewer {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  min-height: 0;
  background: var(--fe-bg);
  color: var(--fe-text);
  /* The edge chevrons' size and inset are tokens (variables.css); narrow
     screens set them smaller below. */
}

/* ---- Drafts (issue #71) ----
   The bar a draft's editor carries, under the viewer's own bar (or alone, on
   the standalone tab): a pill that says DRAFT, where it will be saved, and
   the Save that puts it there. Quiet: a tint of the primary colour, never a
   warning's — nothing is wrong with a draft. */
.fe-draftbar {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--fe-gap-sm) var(--fe-gap);
  padding: var(--fe-gap-sm) var(--fe-gap);
  background: color-mix(in srgb, var(--fe-primary) 7%, var(--fe-bg-elev));
  border-bottom: 1px solid var(--fe-border);
  color: var(--fe-text);
  font-size: 13px;
  min-width: 0;
}
.fe-draftbar__pill {
  flex: 0 0 auto;
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--fe-primary);
  color: var(--fe-text-on-primary);
  font-size: 11px;
  font-weight: 600;
  line-height: 18px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.fe-draftbar__text {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 8px;
  min-width: 0;
  overflow-wrap: anywhere;
}
.fe-draftbar__text > bdi {
  font-weight: 600;
}
.fe-draftbar__target {
  color: var(--fe-text-muted);
}
.fe-draftbar__error {
  color: var(--fe-danger);
  overflow-wrap: anywhere;
}
.fe-draftbar__gap {
  flex: 1 1 auto;
}
.fe-draftbar--saved {
  background: color-mix(in srgb, var(--fe-ok) 10%, var(--fe-bg-elev));
}
.fe-draftbar__done {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--fe-text);
}
.fe-draftbar__done .fe-icon {
  color: var(--fe-ok);
}

/* ---- A .csv in ONLYOFFICE (0.51) ----
   One line under the viewer's bar while a CSV is edited in ONLYOFFICE: what a
   save as CSV keeps. A note, not a warning: nothing is wrong yet. */
.fe-officenote {
  flex: 0 0 auto;
  margin: 0;
  padding: var(--fe-gap-sm) var(--fe-gap);
  background: color-mix(in srgb, var(--fe-primary) 7%, var(--fe-bg-elev));
  border-bottom: 1px solid var(--fe-border);
  color: var(--fe-text);
  font-size: 13px;
  overflow-wrap: anywhere;
}

/* The two questions a draft asks (DraftCloseModal, DraftConflictModal). */
.fe-draftq__body {
  margin: 0 0 6px;
  overflow-wrap: anywhere;
}
.fe-draftq__hint {
  margin: 0;
  color: var(--fe-text-muted);
  font-size: 12.5px;
}
.fe-draftq__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--fe-gap-sm);
  width: 100%;
}
.fe-draftq__gap {
  flex: 1 1 auto;
}

/* ---- The document changed outside the editor (#184) ----
   The three answers, each with what it does (OutsideChangeModal), and the
   line under the viewer's bar: what happened (a note that goes away by
   itself), or a question put away that is still open (with the button that
   brings it back). */
.fe-outsideq__list {
  display: grid;
  gap: 4px;
  margin: 0 0 6px;
  padding-inline-start: 1.1em;
  font-size: 13px;
  overflow-wrap: anywhere;
}
.fe-officenote--outside {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--fe-gap-sm);
}
.fe-officenote--outside .fe-btn {
  margin-inline-start: auto;
}
/* A destructive answer that is not the dialog's point: the danger colour on
   the text, not a red slab beside the default. */
.fe-btn--danger-quiet {
  color: var(--fe-danger);
  border-color: color-mix(in srgb, var(--fe-danger) 35%, var(--fe-border));
}
.fe-btn--danger-quiet:hover:not(:disabled) {
  background: color-mix(in srgb, var(--fe-danger) 10%, transparent);
}

/* The Drafts view (DraftsView): THE table, under a line saying what a draft
   is and how many are kept. */
.fe-drafts {
  display: flex;
  flex-direction: column;
  min-height: 0;
  height: 100%;
}
.fe-drafts__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px var(--fe-gap);
  padding: var(--fe-gap-sm) var(--fe-gap) 0;
}
.fe-drafts__lead,
.fe-drafts__count {
  margin: 0;
  color: var(--fe-text-muted);
  font-size: 12.5px;
}
.fe-drafts__name {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.fe-drafts__tile {
  flex: 0 0 auto;
  display: inline-flex;
}
.fe-drafts__open {
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-drafts__open:hover {
  text-decoration: underline;
}
.fe-drafts__open:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 2px;
  border-radius: var(--fe-radius-sm);
}

/* ---- Top bar ---- */
.fe-viewer__bar {
  flex: 0 0 auto;
  display: grid;
  /* Three tracks, not flex: the action cluster is CENTRED on the bar, which
     only holds when the two side tracks are equal. With flex the cluster
     drifts with the length of the filename. */
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: var(--fe-gap);
  height: 56px;
  padding: 0 var(--fe-gap);
  background: var(--fe-bg-elev);
  border-bottom: 1px solid var(--fe-border);
}
.fe-viewer__ident {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  min-width: 0;
}
/* The tile sizes itself from font-size (lib/fileIcons draws a 1em chip). */
.fe-viewer__tile {
  font-size: 24px;
  line-height: 0;
  display: inline-flex;
  flex: 0 0 auto;
}
.fe-viewer__idcol {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 1px;
}
.fe-viewer__name {
  font-size: var(--fe-text-sm);
  font-weight: 500;
  color: var(--fe-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.fe-viewer__meta {
  font-size: var(--fe-text-xs);
  font-weight: 400;
  color: var(--fe-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.fe-viewer__acts {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-xs);
  justify-self: center;
}
.fe-viewer__tail {
  display: flex;
  align-items: center;
  justify-content: flex-end;
}
.fe-viewer__act {
  width: var(--fe-h-md);
  height: var(--fe-h-md);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  border-radius: var(--fe-radius-md);
  color: var(--fe-text-muted);
  cursor: pointer;
  text-decoration: none;
  padding: 0;
}
.fe-viewer__act:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-viewer__act .fe-aicon {
  width: 18px;
  height: 18px;
  color: inherit;
}
/* StarButton brings its own padding/box; inside the bar it is one of the
   same 34px squares as its neighbours. The component itself is untouched —
   it is the SAME button the listing rows use. */
.fe-viewer__act--star.filex-star-btn {
  padding: 0;
  border: 0;
}
.fe-viewer__act--star.filex-star-btn:hover {
  background: var(--fe-bg-hover);
}

/* ---- Stage ---- */
.fe-viewer__stage {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  overflow: hidden;
}
.fe-viewer__stage > .fe-preview {
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
}

/* Media: the file sits in the middle of the ground with room around it.
   ⚠ Centred with `margin: auto`, not `justify-content: center` — a flex item
   centred the other way cannot be scrolled back to on its leading edge, which
   is exactly what a zoomed-in photo needs. */
.fe-viewer:not(.fe-viewer--bare) .fe-preview--center {
  align-items: flex-start;
  justify-content: flex-start;
  overflow: auto;
  padding: var(--fe-gap-lg);
}
/* ⚠ `flex-shrink: 0` is what makes the zoom pill tell the truth. `.fe-preview`
 * is a flex container, so the image is a flex ITEM and shrinks to the stage by
 * default: measured with `width: 1800px; max-width: none` set inline and the
 * element still 1406px wide — the pill said 150% and nothing on screen grew
 * past 125%. max-width was never the constraint. */
.fe-viewer:not(.fe-viewer--bare) .fe-preview--center > * {
  margin: auto;
  flex: 0 0 auto;
}
.fe-viewer:not(.fe-viewer--bare) .fe-preview--center .fe-preview__image,
.fe-viewer:not(.fe-viewer--bare) .fe-preview--center .fe-preview__video,
.fe-viewer:not(.fe-viewer--bare) .fe-preview--center .fe-preview__audio {
  max-height: 100%;
}

/* Editors and document surfaces take every pixel — the old 70vh/78vh heights
   were sized for a card that no longer exists and would leave a dead band
   under the editor. */
.fe-viewer:not(.fe-viewer--bare) .fe-preview--fill {
  align-items: stretch;
  justify-content: stretch;
  background: var(--fe-bg);
}
.fe-viewer:not(.fe-viewer--bare) .fe-preview--fill > * {
  width: 100%;
  height: 100%;
  max-height: none;
}

/* ---- Edge chevrons ---- */
.fe-viewer__chev {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: var(--fe-viewer-chev-size);
  height: var(--fe-viewer-chev-size);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--fe-border);
  background: var(--fe-bg-elev);
  color: var(--fe-text-muted);
  border-radius: 999px;
  box-shadow: var(--fe-shadow-sm);
  cursor: pointer;
  padding: 0;
  z-index: 2;
}
.fe-viewer__chev:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-viewer__chev--prev { inset-inline-start: var(--fe-viewer-chev-inset); }
.fe-viewer__chev--next { inset-inline-end: var(--fe-viewer-chev-inset); }
/* #110: a stage that fills the screen keeps a gutter on each side for the
   chevrons, so they stand beside the surface instead of on it (an app's
   interface, an editor, a document viewer: PreviewModal's FILL_KINDS). The
   same on both sides whichever chevron is drawn, so stepping to the first or
   the last file does not move the surface. */
.fe-viewer__stage--gutter {
  padding-inline: calc(var(--fe-viewer-chev-size) + var(--fe-viewer-chev-inset) * 2);
}

/* ---- Zoom pill ---- */
.fe-viewer__zoom {
  position: absolute;
  left: 50%; /* rtl-physical: centred by translate(-50%) — the same place in both directions */
  bottom: 20px;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: var(--fe-gap-xs);
  height: var(--fe-h-md);
  padding: 0 var(--fe-gap-sm);
  border-radius: 999px;
  border: 1px solid var(--fe-border);
  background: var(--fe-bg-elev);
  box-shadow: var(--fe-shadow);
  z-index: 2;
}
/* The quick-look legend already owns the bottom centre (`.fe.fe-ql-hint`,
   bottom: 14px, teleported to <body>). Stack the pill above it instead of
   printing one on top of the other. */
.fe-modal__backdrop.fe-quicklook .fe-viewer__zoom {
  bottom: 56px;
}
.fe-viewer__zoom-btn,
.fe-viewer__zoom-level {
  border: 0;
  background: transparent;
  color: var(--fe-text-muted);
  cursor: pointer;
  border-radius: var(--fe-radius-sm);
  font-size: var(--fe-text-sm);
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.fe-viewer__zoom-btn {
  width: var(--fe-h-sm);
  height: var(--fe-h-sm);
  padding: 0;
  font-size: var(--fe-text-md);
}
.fe-viewer__zoom-level {
  min-width: 46px;
  height: var(--fe-h-sm);
  padding: 0 var(--fe-gap-xs);
  font-variant-numeric: tabular-nums;
  color: var(--fe-text);
}
.fe-viewer__zoom-btn:hover:not(:disabled),
.fe-viewer__zoom-level:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-viewer__zoom-btn:disabled {
  opacity: 0.4;
  cursor: default;
}
.fe-viewer__zoom-sep {
  width: 1px;
  height: 16px;
  background: var(--fe-border);
}

/* Narrow screens: the name column is the only thing that can give way, and
   the chevrons come off the edges so they stop sitting on the content. */
@media (max-width: 640px) {
  .fe-viewer__bar {
    height: 52px;
    gap: var(--fe-gap-sm);
    padding: 0 var(--fe-gap-sm);
  }
  /* The chevrons come off the edges and get smaller, and the gutter shrinks
     with them: at 375px wide a full-size gutter would take a quarter of the
     screen from the surface. */
  .fe-viewer {
    --fe-viewer-chev-size: var(--fe-h-md);
    --fe-viewer-chev-inset: var(--fe-gap-xs);
  }
}
/* === /gorunum:v1-viewer === */

/* === gorunum:v1-advsearch — the Advanced search dialog =====================
 *
 * Two visually distinct halves, because the dialog makes two different
 * promises and the reader has to be able to tell them apart without reading
 * the code: everything under the first legend is answered by the server,
 * everything under the second narrows the rows that came back. The second
 * legend therefore sits on a rule, not just in a lighter colour — a shade is
 * a hint, a line is a boundary.
 */

/* The filter affordance inside the search field, beside the palette chip. */
.fe-drivesearch__tune {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--fe-h-sm);
  height: var(--fe-h-sm);
  padding: 0;
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text-muted);
  cursor: pointer;
}
.fe-drivesearch__tune:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-drivesearch__tune:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}
.fe-drivesearch__tune .fe-aicon {
  width: 17px;
  height: 17px;
}

.fe-advsearch {
  display: flex;
  flex-direction: column;
  gap: var(--fe-gap);
  font-size: var(--fe-text-md);
  color: var(--fe-text);
}

.fe-advsearch__legend {
  margin: 0;
  font-size: var(--fe-text-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fe-text-muted);
}
.fe-advsearch__legend--client {
  margin-top: var(--fe-gap-xs);
  padding-top: var(--fe-gap);
  border-top: 1px solid var(--fe-border);
}

.fe-advsearch__row {
  display: flex;
  gap: var(--fe-gap);
  align-items: flex-start;
}
.fe-advsearch__field {
  display: flex;
  flex-direction: column;
  gap: var(--fe-gap-xs);
  flex: 1 1 0;
  min-width: 0;
}
.fe-advsearch__field--wide {
  flex: 1 1 100%;
}
.fe-advsearch__label {
  font-size: var(--fe-text-sm);
  font-weight: 500;
  color: var(--fe-text);
}

/* The query box — the same shape as the header field, so the dialog reads as
   that field opened up rather than as a different search. */
.fe-advsearch__box {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  height: var(--fe-h-lg);
  padding: 0 var(--fe-gap);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg);
}
.fe-advsearch__box:focus-within {
  border-color: var(--fe-primary);
}
.fe-advsearch__glass {
  display: inline-flex;
  flex: 0 0 auto;
  color: var(--fe-text-muted);
}
.fe-advsearch__glass .fe-aicon {
  width: 17px;
  height: 17px;
}
.fe-advsearch__input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  padding: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: var(--fe-text-md);
  outline: none;
}
.fe-advsearch__input::placeholder {
  color: var(--fe-text-muted);
}

/* Segmented control — the scope tabs and the ± span picker. */
.fe-advsearch__seg {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg-elev);
  align-self: flex-start;
  max-width: 100%;
  flex-wrap: wrap;
}
.fe-advsearch__seg-btn {
  appearance: none;
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text-muted);
  font: inherit;
  font-size: var(--fe-text-sm);
  font-weight: 500;
  height: var(--fe-h-sm);
  padding: 0 var(--fe-gap);
  cursor: pointer;
  white-space: nowrap;
}
.fe-advsearch__seg-btn:hover {
  color: var(--fe-text);
}
.fe-advsearch__seg-btn.is-active {
  background: var(--fe-primary-soft);
  color: var(--fe-primary-ink);
}
.fe-advsearch__seg-btn:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}

.fe-advsearch__pair {
  display: flex;
  gap: var(--fe-gap-sm);
  align-items: center;
  flex-wrap: wrap;
}
.fe-advsearch__num {
  width: 6.5rem;
  flex: 0 0 auto;
}

.fe-advsearch__hint {
  font-size: var(--fe-text-xs);
  line-height: 1.45;
  color: var(--fe-text-muted);
}
.fe-advsearch__hint--block {
  margin: 0;
}
/* etiket:k2 — the known tags under the Tags box: one-click picks, each with
   its kind's glyph, grouped Personal then Team like the navigation panel. */
.fe-advsearch__tagpicks {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 6px;
  margin-top: 6px;
}
.fe-advsearch__tagkind {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  font-weight: 600;
  color: var(--fe-text-muted);
  margin-inline-end: 2px;
}
.fe-advsearch__tagpick {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border: 1px solid var(--fe-border);
  border-radius: 999px;
  background: transparent;
  color: var(--fe-text);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.fe-advsearch__tagpick:hover,
.fe-advsearch__tagpick:focus-visible {
  border-color: var(--fe-primary);
  color: var(--fe-primary);
}

/* The live count. Boxed, because it is a measurement and not a caption: the
   number and the sentence explaining what it cost belong to each other. */
.fe-advsearch__count {
  display: flex;
  flex-direction: column;
  gap: var(--fe-gap-xs);
  margin-top: var(--fe-gap-xs);
  padding: var(--fe-gap);
  border: 1px solid var(--fe-border-soft);
  border-radius: var(--fe-radius-md);
  background: var(--fe-bg-elev);
}
.fe-advsearch__count-line {
  margin: 0;
  font-size: var(--fe-text-md);
  color: var(--fe-text);
  min-height: 1.2em;
}
.fe-advsearch__wire {
  margin: 0;
  font-size: var(--fe-text-xs);
  color: var(--fe-text-muted);
  overflow-wrap: anywhere;
}
.fe-advsearch__wire code {
  font-family: var(--fe-font-mono);
  font-size: var(--fe-text-xs);
  padding: 1px 5px;
  border-radius: var(--fe-radius-sm);
  border: 1px solid var(--fe-border);
  background: var(--fe-bg);
  color: var(--fe-text);
}

/* Reset sits apart from Cancel/Search: it changes the form, they leave it. */
.fe-advsearch__reset {
  margin-inline-end: auto;
}

/* One column on a narrow viewport — two 1fr fields at 390px leave a date
   input that cannot show a date. */
@media (max-width: 640px) {
  .fe-advsearch__row {
    flex-direction: column;
    gap: var(--fe-gap-sm);
    /* ⚠ The row sets `align-items: flex-start` so two side-by-side fields of
       different heights line up at the top. Turned on its side that same rule
       stops being alignment and becomes WIDTH: measured at 390px, every input
       shrank to about half the dialog. Stretch is the column's answer. */
    align-items: stretch;
  }
}
/* === /gorunum:v1-advsearch === */

/* === gorunum:v2-topbar === =================================================
   The page bar above the explorer is gone and this header is the only one on
   the screen, so it takes the reference shell's shape in EVERY profile: one
   wide field across the row, and a trailing cluster of icon-only buttons.
   The two controls that belong to a listing (view switcher, details) and the
   tab strip moved down onto / under the breadcrumb row.

   Only declared `--fe-*` tokens (web/tests/api/themeTokens.test.ts fails the
   build on a phantom one). Last block in the file — sibling agents append
   theirs after it, nobody edits the middle.
   ------------------------------------------------------------------------ */

/* ── the header ───────────────────────────────────────────────────────── */
/* One set of metrics for both profiles. The classic row used to be 10px tall
   on the outside and the drive row 8px, which is two headers wearing one
   name; with the same contents they now measure the same. */
.fe-toolbar:not(.fe-toolbar--narrow) {
  gap: var(--fe-gap-sm);
  padding: var(--fe-gap-sm) var(--fe-gap);
}
/* The trailing cluster: the explorer's own utilities, then the host's
   account-level doors through the slot. `0 0 auto` so the field beside it is
   the thing that absorbs the width. */
.fe-toolbar__tail {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--fe-gap-xs);
  min-width: 0;
}
/* Every button in the cluster is the same square, whoever rendered it — the
   host's three arrive through a slot and would otherwise come out as ordinary
   labelled buttons sitting beside icon squares. */
.fe-toolbar__tail .fe-btn {
  height: var(--fe-h-md);
  min-width: var(--fe-h-md);
  padding: 0 var(--fe-gap-sm);
  justify-content: center;
  border-radius: var(--fe-radius-md);
  border-color: transparent;
  background: transparent;
  color: var(--fe-text-muted);
}
.fe-toolbar__tail .fe-btn:hover:not(:disabled) {
  background: var(--fe-bg-hover);
  border-color: transparent;
  color: var(--fe-text);
}
/* The narrow classic row draws its buttons inline rather than inside the
   cluster (search collapses behind a magnifier there, so the row is not the
   same shape). They still have to be the SAME BUTTON: measured at 390px the
   six icons came out as six bordered boxes while the identical six at 1440
   were flat, which is one control wearing two costumes by viewport. */
.fe-toolbar--narrow > .fe-btn--icon-only {
  height: var(--fe-h-md);
  min-width: var(--fe-h-md);
  justify-content: center;
  border-radius: var(--fe-radius-md);
  border-color: transparent;
  background: transparent;
  color: var(--fe-text-muted);
}
.fe-toolbar--narrow > .fe-btn--icon-only:hover:not(:disabled) {
  background: var(--fe-bg-hover);
  border-color: transparent;
  color: var(--fe-text);
}
/* …except when it is pressed: the nav toggle and a search with a live query
   both say so with the shared active style, and a transparent border would
   erase it. */
.fe-toolbar--narrow > .fe-btn--icon-only.is-active {
  background: var(--fe-primary-soft);
  border-color: transparent;
  color: var(--fe-primary-ink);
}

/* ── "coming soon" ────────────────────────────────────────────────────── */
/* The AI assistant is drawn and disabled. ⚠ It must not read as BROKEN: the
   glyph keeps a legible weight (0.55, not the 0.5 a plain `:disabled` would
   give a control nobody meant to announce) and carries a small caption, so
   the row says "not yet" rather than "not working". The caption is hidden
   below 900px, where the tooltip and the aria-label still carry it — six
   words in a 390px header would push the search field off the row. */
.fe-toolbar__soon.fe-btn:disabled {
  opacity: 0.55;
  cursor: default;
  gap: var(--fe-gap-xs);
}
.fe-toolbar__soon-tag {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  line-height: 1;
  padding: 2px 5px;
  border-radius: var(--fe-radius-sm);
  background: var(--fe-primary-soft);
  color: var(--fe-primary-ink);
  white-space: nowrap;
}
@media (max-width: 900px) {
  .fe-toolbar__soon-tag {
    display: none;
  }
}
.fe--narrow .fe-toolbar__soon-tag {
  display: none;
}

/* ── the breadcrumb row ───────────────────────────────────────────────── */
/* The row is now the crumbs' row in every profile (it used to be
   `display: contents` outside the drive shell, with the view switcher and the
   details toggle drawn up in the header instead). */
.fe-subhead {
  flex: 0 0 auto;
}
/* The home crumb. A 24px box like the path-editor beside it, for the same
   reason: this row is pinned at 37px to line up with the panel's head and the
   split pane's own crumbs, and a 28px control inside 6px of padding makes
   this row — and only this row — 40px. */
.fe-breadcrumb__home {
  padding: 0;
  gap: var(--fe-gap-xs);
  color: var(--fe-text-muted);
}
.fe-breadcrumb__home .fe-icon,
.fe-breadcrumb__home .fe-aicon {
  width: 16px;
  height: 16px;
}
.fe-breadcrumb__home:hover {
  color: var(--fe-text);
}
.fe-breadcrumb__home.is-last {
  color: var(--fe-text);
}
/* "Subfolders" — the chevron on the last crumb. Same 24px box as the pencil,
   and the menu hangs off it the way the overflow "…" menu already does. */
.fe-breadcrumb__sub-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.fe-breadcrumb__sub {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text-muted);
  cursor: pointer;
}
.fe-breadcrumb__sub:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-breadcrumb__sub .fe-icon,
.fe-breadcrumb__sub .fe-aicon {
  width: 15px;
  height: 15px;
}
/* An empty folder answers in words. Without this the menu opens as a 0px
   sliver, which reads as a broken control rather than as "nothing in here". */
.fe-breadcrumb__menu-empty {
  margin: 0;
  padding: var(--fe-gap-sm) var(--fe-gap);
  font-size: var(--fe-text-xs);
  color: var(--fe-text-muted);
  white-space: nowrap;
}
/* The path editor. ⚠ `margin-left: auto` (line ~381) floated it to the far end
   of the crumb row, where it landed against the view switcher and read as part
   of that cluster rather than as a thing you do to the path. Measured at 1440:
   the crumbs ended at x=381 and the pencil sat at x=1242. It belongs beside the
   chevron, which is where the reference puts it. */
.fe-breadcrumb__edit {
  margin-inline-start: 0;
}
.fe-breadcrumb__edit .fe-icon,
.fe-breadcrumb__edit .fe-aicon {
  width: 15px;
  height: 15px;
}

/* ── the tab strip, under the breadcrumb ──────────────────────────────── */
/* It used to be the explorer's first row, above the header and across the
   sidebar too. Under the crumbs it belongs to the pane it is standing over,
   so it sits on the listing's own ground rather than on the header's — an
   elevated band here would read as a second header. */
.fe__primary > .fe-tabs {
  padding: var(--fe-gap-xs) var(--fe-gap-sm) 0;
  background: var(--fe-bg);
  border-bottom: 1px solid var(--fe-border);
}
/* The two controls that act on the strip rather than on one tab. They are the
   only door to "new tab" and to the split pane, so they keep a visible box
   while the tabs beside them stay flat. */
.fe__primary > .fe-tabs .fe-tabs__new,
.fe__primary > .fe-tabs .fe-tabs__split {
  align-self: center;
  border-radius: var(--fe-radius-sm);
}
/* === /gorunum:v2-topbar === */

/* === gorunum:v2-share — the Share / Permissions dialog ======================
 *
 * Every capability the old three-tab panel had is still here; what changed is
 * the order they are asked in. The dialog now opens on ONE decision — is this
 * item shared by link, yes or no — answers it in a sentence, and hands over
 * the link with its Copy button. PIN, expiry, the download cap, the people
 * with access and the inbound drop link fold into three NAMED sections under
 * it, one click each.
 *
 * ⚠ Named, not "Advanced". A disclosure whose label does not say what is
 * behind it is a place controls go to be lost; each `__sechead` therefore
 * carries a `__secmeta` summary of its own state ("PIN yok · 7 gün"), so the
 * reader can tell from the closed dialog whether the thing they came for is
 * already set.
 *
 * ⚠ `.fx-perm-*` survives on the root and on two inner hooks only because
 * e2e/shots/capture.mjs addresses the dialog by them. They carry no styling.
 */

.fe-share__scrim {
  position: fixed;
  inset: var(--fe-overlay-top, 0px) 0 0; /* the host's own strip stays reachable — see .fe-modal__backdrop */
  z-index: 10000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--fe-gap-lg);
  background: rgba(0, 0, 0, 0.45);
  font-family: var(--fe-font);
}

/* Small and quiet. The old panel was 520px because it had to hold a tab strip
   over a two-column grid of controls; with one decision on top it does not,
   and a narrower box is what makes the single switch read as the subject. */
.fe-share {
  display: flex;
  flex-direction: column;
  width: min(440px, 100%);
  max-height: 86vh;
  background: var(--fe-bg);
  color: var(--fe-text);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-lg);
  box-shadow: var(--fe-shadow);
  font-size: var(--fe-text-md);
  overflow: hidden;
}

/* ---- head ---- */
.fe-share__head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  padding: var(--fe-gap) var(--fe-gap-lg);
  border-bottom: 1px solid var(--fe-border-soft);
}
.fe-share__tile {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
}
.fe-share__headtext {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1 1 auto;
}
.fe-share__title {
  margin: 0;
  font-size: var(--fe-text-md);
  font-weight: 600;
  color: var(--fe-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-share__path {
  font-size: var(--fe-text-xs);
  color: var(--fe-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-share__close {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--fe-h-sm);
  height: var(--fe-h-sm);
  padding: 0;
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text-muted);
  cursor: pointer;
}
.fe-share__close:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}

.fe-share__body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--fe-gap-lg);
}

/* ---- tier 1: the switch, the sentence, the link ---- */
.fe-share__switchrow {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  min-height: var(--fe-h-md);
}
.fe-share__leadicon {
  display: inline-flex;
  color: var(--fe-primary);
}
.fe-share__leadicon .fe-aicon {
  color: inherit;
}
.fe-share__leadlabel {
  flex: 1 1 auto;
  font-weight: 500;
}

/* The one switch. 40×24 with an 18px knob — a track wide enough that the two
   states differ by more than a shade, which is the whole job of a control
   whose meaning is "the public can read this file". */
.fe-share__switch {
  flex: 0 0 auto;
  position: relative;
  width: 40px;
  height: 24px;
  padding: 0;
  border: 1px solid var(--fe-border-strong);
  border-radius: 999px;
  background: var(--fe-bg-hover);
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.fe-share__switch[aria-checked='true'] {
  background: var(--fe-primary);
  border-color: var(--fe-primary);
}
.fe-share__switch:disabled {
  opacity: 0.55;
  cursor: default;
}
.fe-share__switch:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 2px;
}
.fe-share__knob {
  position: absolute;
  top: 2px;
  inset-inline-start: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--fe-text-muted);
  transition: transform 0.15s ease, background 0.15s ease;
}
.fe-share__switch[aria-checked='true'] .fe-share__knob {
  background: var(--fe-text-on-primary);
  /* Toward "on" = toward the inline end, in either direction. */
  transform: translateX(calc(16px * var(--filex-dir-x, 1)));
}

/* The sentence. Full-strength text on purpose: it is the answer to the
   question the dialog was opened to ask, not a caption under a control. */
.fe-share__who {
  margin: var(--fe-gap-xs) 0 0;
  font-size: var(--fe-text-sm);
  color: var(--fe-text);
}
.fe-share__detail {
  margin: var(--fe-gap-xs) 0 0;
  font-size: var(--fe-text-xs);
  color: var(--fe-text-muted);
}

.fe-share__linkrow {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  margin-top: var(--fe-gap);
}
.fe-share__url {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--fe-primary);
  text-decoration: none;
}
.fe-share__url:hover {
  text-decoration: underline;
}
/* Inside the lead row the link sits in a field-shaped box, so "copy this"
   reads as one control pair rather than as a paragraph with a button after
   it. */
.fe-share__linkrow > .fe-share__url {
  flex: 1 1 auto;
  height: var(--fe-h-lg);
  line-height: calc(var(--fe-h-lg) - 2px);
  padding: 0 var(--fe-gap);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg-elev);
}
.fe-share__copy {
  /* ⚠⚠ THE copy button — every "Copy" in this dialog (the link, the PIN, the
     command line, an older link, the upload link) is this one control. There
     were three: a filled primary button, a small text button and the same
     small button beside a revoke cross (QA, 2026-09-21). Same glyph, same
     words, same "Copied" answer; only the row decides the height. */
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--fe-gap-xs);
  height: var(--fe-h-md);
  padding: 0 var(--fe-gap-sm);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg);
  color: var(--fe-text);
  font-family: inherit;
  font-size: var(--fe-text-sm);
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
}
.fe-share__copy .fe-aicon {
  color: inherit;
  width: 14px;
  height: 14px;
}
.fe-share__copy:hover {
  background: var(--fe-bg-hover);
  border-color: var(--fe-border-strong);
}
.fe-share__linkrow > .fe-share__copy {
  height: var(--fe-h-lg);
}

.fe-share__pinrow {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  margin-top: var(--fe-gap-sm);
}
.fe-share__pinlabel {
  font-size: var(--fe-text-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--fe-text-muted);
}
.fe-share__pin {
  font-family: var(--fe-font-mono);
  font-size: var(--fe-text-md);
  background: var(--fe-bg-hover);
  padding: 2px var(--fe-gap-sm);
  border-radius: var(--fe-radius-sm);
}

/* ---- tier 2: the named sections ---- */
.fe-share__sections {
  margin-top: var(--fe-gap-lg);
  border-top: 1px solid var(--fe-border-soft);
}
.fe-share__section + .fe-share__section {
  border-top: 1px solid var(--fe-border-soft);
}
.fe-share__sechead {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  width: 100%;
  min-height: var(--fe-h-lg);
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--fe-text);
  font-family: inherit;
  font-size: var(--fe-text-md);
  font-weight: 500;
  text-align: start;
  cursor: pointer;
}
.fe-share__sechead:hover .fe-share__seclabel {
  color: var(--fe-primary);
}
.fe-share__sechead:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
  border-radius: var(--fe-radius-sm);
}
.fe-share__caret {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  color: var(--fe-text-muted);
  transition: transform 0.15s ease;
}
.fe-share__caret.is-open {
  transform: rotate(90deg);
}
.fe-share__seclabel {
  flex: 0 0 auto;
}
/* The state summary. Trailing edge, muted, small — it is there to be glanced
   at, and it must never compete with the label it follows. */
.fe-share__secmeta {
  flex: 1 1 auto;
  min-width: 0;
  text-align: end;
  font-size: var(--fe-text-xs);
  font-weight: 400;
  color: var(--fe-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Indented to the label, not to the caret: the panel belongs to the heading
   it opened from. */
.fe-share__panel {
  padding-block: 0 var(--fe-gap); padding-inline: calc(var(--fe-gap-lg) + var(--fe-gap-sm)) 0;
}

/* ---- fields ---- */
.fe-share__opts {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--fe-gap-sm) var(--fe-gap);
  align-items: end;
}
.fe-share__opts > .fe-share__togglerow {
  grid-column: 1 / -1;
}
.fe-share__field {
  display: flex;
  flex-direction: column;
  gap: var(--fe-gap-xs);
  min-width: 0;
}
.fe-share__fieldlabel {
  font-size: var(--fe-text-xs);
  font-weight: 500;
  color: var(--fe-text-muted);
}
.fe-share__input {
  width: 100%;
  box-sizing: border-box;
  height: var(--fe-h-lg);
  padding: 0 var(--fe-gap-sm);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg);
  color: var(--fe-text);
  font-family: inherit;
  font-size: var(--fe-text-md);
}
.fe-share__input:focus {
  outline: none;
  border-color: var(--fe-primary);
}
/* Greyed with its reason above it (RBAC off on the storage): it has to LOOK
   unusable, not merely refuse the click. */
.fe-share__input:disabled,
.fe-share__pick .fe-select__trigger:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}
/* The dialog's lists (core ChoiceSelect, #160) wear the dialog's field shape:
   its radius and type, at the height `size` gives them (lg beside the
   40px boxes). */
.fe-share__pick .fe-select__trigger {
  border-radius: var(--fe-radius);
  font-size: var(--fe-text-md);
}
/* The access level (a segmented ChoiceButtons, #160) keeps its own width.
   Its strip is 34px tall, the height of the row it sits in. */
.fe-share__level {
  flex: 0 0 auto;
}
.fe-share__input--sm {
  flex: 0 0 140px;
  width: 140px;
  height: var(--fe-h-md);
}
/* A yes/no option: its words, then the dialog's one switch. The whole row is
   the label, so a click on the words flips it too. */
.fe-share__togglerow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--fe-gap);
  min-height: var(--fe-h-md);
  font-size: var(--fe-text-md);
  color: var(--fe-text);
  cursor: pointer;
}
.fe-share__togglelabel {
  min-width: 0;
}

/* ---- buttons ---- */
.fe-share__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--fe-gap-xs);
  height: var(--fe-h-lg);
  padding: 0 var(--fe-gap);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg);
  color: var(--fe-text);
  font-family: inherit;
  font-size: var(--fe-text-md);
  white-space: nowrap;
  cursor: pointer;
}
.fe-share__btn:hover:not(:disabled) {
  background: var(--fe-bg-hover);
}
.fe-share__btn:disabled {
  opacity: 0.55;
  cursor: default;
}
.fe-share__btn--primary {
  background: var(--fe-primary);
  border-color: var(--fe-primary);
  color: var(--fe-text-on-primary);
  font-weight: 500;
}
.fe-share__btn--primary:hover:not(:disabled) {
  background: var(--fe-primary-hover);
  border-color: var(--fe-primary-hover);
}
.fe-share__btn--wide {
  width: 100%;
  margin-top: var(--fe-gap);
}
.fe-share__btn .fe-aicon {
  color: inherit;
}
/* A verb rendered as text, for the two doors that lead somewhere else
   ("just send a link", "upload limits") rather than acting here. */
.fe-share__linkbtn {
  display: inline-block;
  margin-top: var(--fe-gap-sm);
  padding: 0;
  border: 0;
  background: none;
  color: var(--fe-primary);
  font-family: inherit;
  font-size: var(--fe-text-sm);
  text-align: start;
  cursor: pointer;
}
.fe-share__linkbtn:hover {
  text-decoration: underline;
}
.fe-share__del {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--fe-h-sm);
  height: var(--fe-h-sm);
  padding: 0;
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text-muted);
  cursor: pointer;
}
.fe-share__del:hover:not(:disabled) {
  background: var(--fe-bg-hover);
  color: var(--fe-danger);
}
.fe-share__del .fe-aicon {
  color: inherit;
  width: 14px;
  height: 14px;
}

/* ---- people ---- */
.fe-share__add {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--fe-gap-sm);
}
/* The address takes the whole first line; the level and Add share the
   second. Three levels side by side and a usable address box do not fit one
   440px line (by the type sizes, ~190px of levels in English and ~245px in
   Turkish; to be measured in a browser). */
.fe-share__add > .fe-share__emailwrap {
  flex: 1 1 100%;
}
.fe-share__add > .fe-share__btn {
  margin-inline-start: auto;
}
.fe-share__emailwrap {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
}
.fe-share__suggest {
  position: absolute;
  top: calc(100% + var(--fe-gap-xs));
  inset-inline-start: 0;
  inset-inline-end: 0;
  z-index: 5;
  margin: 0;
  padding: var(--fe-gap-xs);
  list-style: none;
  max-height: 210px;
  overflow: auto;
  background: var(--fe-bg);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  box-shadow: var(--fe-shadow);
}
.fe-share__suggest li {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  padding: var(--fe-gap-xs) var(--fe-gap-sm);
  border-radius: var(--fe-radius-sm);
  cursor: pointer;
}
.fe-share__suggest li:hover {
  background: var(--fe-bg-hover);
}
.fe-share__suggesttxt {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.fe-share__suggestname {
  font-size: var(--fe-text-sm);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-share__suggestmeta {
  font-size: var(--fe-text-xs);
  color: var(--fe-text-muted);
}
.fe-share__av {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--fe-h-sm);
  height: var(--fe-h-sm);
  border-radius: 50%;
  background: var(--fe-primary);
  color: var(--fe-text-on-primary);
  font-size: var(--fe-text-xs);
  font-weight: 600;
}
.fe-share__av--dim {
  background: var(--fe-bg-hover);
  color: var(--fe-text-muted);
}
/* A group: squared, so it reads as "many people" beside the round avatars. */
.fe-share__av--group {
  border-radius: var(--fe-radius-sm, 4px);
}
.fe-share__kind {
  margin-inline-start: var(--fe-gap-sm);
  font-size: var(--fe-text-xs);
  color: var(--fe-text-muted);
}
.fe-share__person {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-share__badge {
  flex: 0 0 auto;
  padding: 2px var(--fe-gap-sm);
  border-radius: 999px;
  background: var(--fe-bg-hover);
  font-size: var(--fe-text-xs);
}
.fe-share__from {
  flex: 0 0 auto;
  font-size: var(--fe-text-xs);
  color: var(--fe-text-muted);
  white-space: nowrap;
}
.fe-share__invite {
  margin-top: var(--fe-gap);
  padding-top: var(--fe-gap);
  border-top: 1px dashed var(--fe-border);
}
.fe-share__inviterow {
  display: flex;
  gap: var(--fe-gap-sm);
  margin-top: var(--fe-gap-sm);
}
.fe-share__inviterow .fe-share__btn {
  flex: 1 1 auto;
}
/* "user / viewer" (a segmented ChoiceButtons) as tall as the button beside it. */
.fe-share__inviterow .fe-choice--segmented .fe-choice__btn {
  min-height: calc(var(--fe-h-lg) - 6px);
}
@media (pointer: coarse) {
  .fe-share__inviterow .fe-choice--segmented .fe-choice__btn {
    min-height: 44px;
  }
}

/* ---- lists ---- */
.fe-share__list {
  margin-top: var(--fe-gap);
}
.fe-share__listhead {
  margin: 0 0 var(--fe-gap-xs);
  font-size: var(--fe-text-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fe-text-muted);
}
.fe-share__row {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  min-height: var(--fe-h-md);
  padding: var(--fe-gap-xs) 0;
}
.fe-share__row + .fe-share__row {
  border-top: 1px solid var(--fe-border-soft);
}
/* A grant's row: avatar, name, level, remove. When the name would get less
   than 6rem beside the level (Turkish labels, a 390px screen) the level and
   the remove button go to a second line rather than squeezing the name to
   nothing. */
.fe-share__row--grant {
  flex-wrap: wrap;
  row-gap: var(--fe-gap-xs);
}
.fe-share__row--grant > .fe-share__person {
  flex: 1 1 6rem;
}
.fe-share__row--grant > .fe-share__del {
  margin-inline-start: auto;
}
.fe-share__row > .fe-share__url {
  flex: 1 1 auto;
}
.fe-share__row--dim {
  color: var(--fe-text-muted);
}
.fe-share__empty {
  margin: 0;
  padding: var(--fe-gap-xs) 0;
  font-size: var(--fe-text-sm);
  color: var(--fe-text-muted);
}

/* ---- the rest of the link panel ---- */
.fe-share__cli {
  margin-top: var(--fe-gap);
}
.fe-share__clirow {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  margin-top: var(--fe-gap-xs);
}
.fe-share__clicmd {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: var(--fe-gap-xs) var(--fe-gap-sm);
  border-radius: var(--fe-radius-sm);
  background: var(--fe-bg-hover);
  color: var(--fe-text);
  font-family: var(--fe-font-mono);
  font-size: var(--fe-text-xs);
}
.fe-share__mailrow {
  display: flex;
  gap: var(--fe-gap-sm);
  margin-top: var(--fe-gap);
}
.fe-share__mailrow .fe-share__input {
  flex: 1 1 auto;
}
.fe-share__adv {
  display: flex;
  flex-direction: column;
  gap: var(--fe-gap-sm);
  margin-top: var(--fe-gap-sm);
  padding-top: var(--fe-gap-sm);
  border-top: 1px dashed var(--fe-border);
}
.fe-share__advrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--fe-gap-sm);
}
.fe-share__hint {
  margin: var(--fe-gap-sm) 0 0;
  font-size: var(--fe-text-sm);
  color: var(--fe-text-muted);
}
.fe-share__notice {
  margin-top: var(--fe-gap-sm);
  font-size: var(--fe-text-sm);
  color: var(--fe-text-muted);
}
/* A warning is a boundary, so it gets a line rather than a tint — and the
   tokenised warning colour, not an rgba() no theme can reach. */
.fe-share__warn {
  margin-top: var(--fe-gap-sm);
  padding: var(--fe-gap-sm);
  border: 1px solid var(--fe-warning);
  border-radius: var(--fe-radius-sm);
  background: var(--fe-bg-elev);
  font-size: var(--fe-text-sm);
  color: var(--fe-text);
}

/* ---- foot ---- */
.fe-share__foot {
  flex: 0 0 auto;
  display: flex;
  justify-content: flex-end;
  padding: var(--fe-gap) var(--fe-gap-lg);
  border-top: 1px solid var(--fe-border-soft);
}

/* One column once two 1fr fields would each be too narrow to show their own
   value, and the add-row stacks for the same reason. */
@media (max-width: 420px) {
  .fe-share__opts {
    grid-template-columns: 1fr;
  }
  .fe-share__add {
    flex-wrap: wrap;
  }
  .fe-share__add .fe-share__level,
  .fe-share__add .fe-share__btn {
    flex: 1 1 auto;
  }
}
/* === /gorunum:v2-share === */

/* === gorunum:v3-shell — the shell is the product ==========================
 * Appended block (agent: app-shell). Three things live here:
 *
 *   1. the top bar's far-left corner (`.fe-toolbar__brand`) — the collapse
 *      control and the host's product mark, above the navigation panel rather
 *      than inside it;
 *   2. the navigation panel's top edge, now that the panel draws no header of
 *      its own above 560px;
 *   3. the Home view (`.fe-home*`).
 *
 * ⚠ Declared `--fe-*` tokens only, no raw colour: a literal here cannot be
 * reached by a theme, by the palette gallery or by a host override
 * (web/tests/api/themeTokens.test.ts fails the build over it).
 * ------------------------------------------------------------------------ */

/* ── 1. the top bar's left corner ─────────────────────────────────────── */
/* Fixed to the panel's own width so the search field starts exactly where the
   content area does, the way the reference draws it. It does NOT follow the
   rail: shrinking this to 56px would slide the whole header sideways every
   time somebody collapsed the panel, which is a 176px jolt for a control that
   did not move. `flex: 0 0 auto` + `min-width` rather than a fixed width so a
   long wordmark pushes rather than clips. */
.fe-toolbar__brand {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--fe-gap-xs);
}
/* ⚠ Measured, not styled by eye. `.fe:not(.fe--narrow) .fe-toolbar__primary`
   (the ui-fix block above) gives that element `flex: 1 1 auto` — it was the
   folding row of action buttons, and both the buttons and the measuring strip
   that sizing was for now live in the selection bar. What is left in it renders
   only when a deployment has NO navigation panel, i.e. nobody by default, so an
   element with nothing in it was GROWING to 268px: measured, the search field
   started at x=535 on a 1440px viewport where the reference starts it at 207.
   `0 1 auto` — it may still shrink when it does hold Upload / New folder on a
   panel-less embed at 600px; it may not claim space it is not using.
   ⚠ Not `:empty`: measured in Chrome, that selector does NOT match here (the
   block still carries text nodes from the template), which is exactly the kind
   of rule that looks right and does nothing. */
.fe:not(.fe--narrow) .fe-toolbar__primary {
  flex: 0 1 auto;
}
/* The gutter that lines the search field up with the content area: the panel's
   own column width, less this row's left padding.
   ⚠ Derived, not typed in: `--fe-sidenav-w` is the same token `.fe-sidenav`
   sizes itself with, so widening the panel moves this with it. A pixel number
   copied from the panel is a number that is correct until somebody changes the
   panel, and then wrong with nothing to say so — there is a browser assertion
   for it in the shell walk, because a comment cannot fail.
   ⚠ Only when there IS a panel. An embed confined with `rootPath` has none
   (`sideNav` defaults off there), and reserving a quarter of its header for a
   column that is not on screen would push its search field into the middle of
   nothing.
   ⚠ It does NOT follow the rail. Shrinking to 56px when somebody collapses the
   panel would slide the whole header 176px sideways, which is a jolt on every
   control in the row for a control that did not move. */
.fe-toolbar__brand--gutter {
  min-width: calc(var(--fe-sidenav-w) - 12px);
  padding-inline-end: var(--fe-gap-sm);
}
/* At 390px there is no room to reserve anything: the row is toggle · field ·
   "⋯" and the brand mark is not rendered at all (Toolbar.vue). */
.fe--narrow .fe-toolbar__brand--gutter {
  min-width: 0;
  padding-inline-end: 0;
}
/* The host's mark. It is a slot, so this styles whatever arrives: enough room
   for a 22px logo beside a word, ellipsised rather than wrapped — a two-line
   wordmark would make the whole header 20px taller for one embed. */
.fe-toolbar__mark {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  min-width: 0;
  font-size: var(--fe-text-md);
  font-weight: 600;
  color: var(--fe-text);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* ── 2. the panel, with no header of its own ──────────────────────────── */
/* The 37px head used to provide this. Without it the "+ New" button would sit
   flush against the top border, 8px higher than the breadcrumb beside it. */
.fe-sidenav__primary {
  padding-top: var(--fe-gap-sm);
}
/* …except in the drawer, where the head is still drawn (it carries the
   dismiss) and would otherwise double the gap. */
.fe-sidenav--drawer .fe-sidenav__primary {
  padding-top: 0;
}

/* ── 3. the Home view ─────────────────────────────────────────────────── */
/* Its own scroller: `.fe__body` is a flex column that the listing fills, and
   Home is taller than the pane as soon as somebody has a dozen recents. */
.fe-home {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: var(--fe-gap-lg);
}
/* The heading on the breadcrumb row, in the crumbs' place. Matched to
   `.fe-breadcrumb`'s own type so the two rows read as one row in two states,
   not as two different bars. It grows for the same reason the breadcrumb does
   (`.fe-subhead > .fe-breadcrumb`): the details toggle belongs on the far
   right of the row whichever of the two is on screen. */
.fe-subhead__title {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  padding-inline-start: var(--fe-gap-xs);
  font-size: var(--fe-text-md);
  font-weight: 600;
  line-height: 1.3;
  color: var(--fe-text);
}
/* ~28px between blocks, none above the first — the rhythm the listing's own
   section headings keep. */
.fe-home__section + .fe-home__section {
  margin-top: 28px;
}
.fe-home__heading {
  margin: 0 2px var(--fe-gap-sm);
  font-size: var(--fe-text-md);
  font-weight: 600;
  line-height: 1.3;
  color: var(--fe-text);
}
/* A fixed 186px track, not a stretching one: the card's width is part of the
   look, and a grid that stretches its last row makes the same drive a
   different size on two screens. */
.fe-home__storages {
  display: grid;
  grid-template-columns: repeat(auto-fill, 186px);
  gap: var(--fe-gap);
  align-content: start;
  /* A read-only card carries one more line (its tag); its row-mates grow
     with it rather than standing short beside it. */
  align-items: stretch;
}
/* The storage card IS the grid's folder card — 186×56, the solid glyph inset
   12px — because in the explorer's own root listing a storage is a folder row.
   The only thing it adds is a caption the listing has no room for. */
.fe-home__storage {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--fe-gap);
  width: 186px;
  min-height: 56px;
  padding: 0 var(--fe-gap);
  text-align: start;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-md);
  background: var(--fe-bg);
  color: var(--fe-text);
  font-family: var(--fe-font);
  font-size: var(--fe-text-md);
  cursor: pointer;
}
.fe-home__storage:hover {
  background: var(--fe-bg-hover);
  border-color: var(--fe-border-strong);
}
.fe-home__storage:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}
/* The tile is `.fe-ftile`, which sizes itself in `em` — a 24px tinted rounded
   square with the drive glyph inside it, the same object the Type column and
   the grid card draw. `font-size` is what scales it; the box is only here so a
   card with no tile (should one ever exist) keeps its columns. */
.fe-home__storage-tile {
  flex: 0 0 24px;
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  line-height: 1;
}
.fe-home__storage-main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.fe-home__storage-label {
  font-size: var(--fe-text-sm);
  font-weight: 500;
  line-height: 1.3;
  color: var(--fe-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-home__storage-meta {
  margin-top: 2px;
  font-size: var(--fe-text-xs);
  font-weight: 400;
  line-height: 1.3;
  color: var(--fe-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-home__loading {
  margin: 0 2px;
  padding: var(--fe-gap) 0;
  font-size: var(--fe-text-sm);
  color: var(--fe-text-muted);
}
/* `.fe-state` is the product's empty state — same title, same one explaining
   line. It is written to fill a pane; here it fills a block. */
.fe-home__state {
  min-height: 0;
  height: auto;
  align-items: flex-start;
  text-align: start;
  padding: var(--fe-gap) 2px var(--fe-gap-lg);
}
/* The grids inside Home are GridView's, and GridView fills its pane. Inside a
   section it has to be as tall as its rows and no taller, or the Starred block
   would start a screen below the Recent one. */
.fe-home .fe-grid {
  flex: 0 0 auto;
  min-height: 0;
  overflow: visible;
  padding: 0 2px;
}
/* === /gorunum:v3-shell === */

/* === gorunum:v4-dialogs — dialogs and panels ===============================
 *
 * The reference shell's dialogs, measured off it at 1280×900 in both themes
 * (owner's call, 2026-09-12: "görüntü birebir yapalım" — make ours look
 * identical to his). Three surfaces live here: the Advanced search dialog,
 * the details panel and the context menu.
 *
 * ⚠ APPEND-ONLY REGION. Three other agents own the rest of this file; this
 * block is last on purpose, so the overrides below beat the earlier rules at
 * the same specificity without anybody editing anybody else's lines. Only
 * declared `--fe-*` tokens — web/tests/api/themeTokens.test.ts fails the
 * build on a phantom one, and a raw hex cannot be reached by a theme.
 * ------------------------------------------------------------------------ */

/* ── Advanced search ──────────────────────────────────────────────────── */

/* The dialog draws its own head (mark + title + sentence + ×) across the full
 * width, so Modal's plain title bar folds away. The <h2> in there stays in the
 * DOM — it is what the dialog's `aria-labelledby` points at — which is why
 * this hides the bar rather than the component dropping the `title` prop. */
.fe-modal__card:has(.fe-advsearch) .fe-modal__head {
  display: none;
}
.fe-modal__card:has(.fe-advsearch) .fe-modal__body {
  padding: var(--fe-gap-lg) 20px;
}
.fe-modal__card:has(.fe-advsearch) .fe-modal__actions {
  padding: var(--fe-gap) 20px var(--fe-gap-lg);
  border-top: 1px solid var(--fe-border);
}

.fe-advsearch {
  gap: var(--fe-gap-lg);
}
.fe-advsearch__head {
  display: flex;
  align-items: flex-start;
  gap: var(--fe-gap);
}
.fe-advsearch__head-icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--fe-radius-md);
  background: var(--fe-primary-soft);
  color: var(--fe-primary-ink);
}
.fe-advsearch__head-icon .fe-aicon {
  width: 18px;
  height: 18px;
}
.fe-advsearch__head-text {
  flex: 1 1 auto;
  min-width: 0;
}
.fe-advsearch__head-title {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--fe-text);
}
.fe-advsearch__head-sub {
  margin: 3px 0 0;
  font-size: var(--fe-text-sm);
  line-height: 1.4;
  color: var(--fe-text-muted);
}
.fe-advsearch__head-close {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--fe-h-sm);
  height: var(--fe-h-sm);
  margin-block: -2px 0; margin-inline: 0 -4px;
  padding: 0;
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text-muted);
  cursor: pointer;
}
.fe-advsearch__head-close:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-advsearch__head-close .fe-aicon {
  width: 16px;
  height: 16px;
}

/* The query field is the widest thing in the dialog: one box, full width. */
.fe-advsearch__box {
  width: 100%;
  height: 44px;
  border-radius: var(--fe-radius-md);
}

/* Scope tabs — the segmented control opened out to the full width, one equal
   share per scope. Same element class as the ± span picker (the mounted test
   counts `.fe-advsearch__seg-btn[role="tab"]`), so the shape is a modifier. */
.fe-advsearch__seg--tabs {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 0;
  width: 100%;
  padding: 0;
  border-radius: var(--fe-radius);
  background: var(--fe-bg-elev);
  overflow: hidden;
}
.fe-advsearch__seg--tabs .fe-advsearch__seg-btn {
  height: 38px;
  gap: var(--fe-gap-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 0;
  border-inline-end: 1px solid var(--fe-border);
  padding: 0 var(--fe-gap-sm);
  min-width: 0;
}
.fe-advsearch__seg--tabs .fe-advsearch__seg-btn:last-child {
  border-inline-end: 0;
}
.fe-advsearch__seg--tabs .fe-advsearch__seg-btn.is-active {
  background: var(--fe-primary-soft);
  color: var(--fe-primary-ink);
}
.fe-advsearch__seg-icon {
  display: inline-flex;
  flex: 0 0 auto;
}
.fe-advsearch__seg-icon .fe-aicon {
  width: 15px;
  height: 15px;
}

/* Two columns, because a single column pushed the count below the fold and a
   dialog whose measurement you have to scroll to is a dialog without one. */
.fe-advsearch__cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--fe-gap-lg) 34px;
  align-items: start;
}
.fe-advsearch__col {
  display: flex;
  flex-direction: column;
  gap: var(--fe-gap-lg);
  min-width: 0;
}
.fe-advsearch__group {
  display: flex;
  flex-direction: column;
  gap: var(--fe-gap-sm);
  min-width: 0;
}
.fe-advsearch__sublabel {
  font-size: var(--fe-text-xs);
  color: var(--fe-text-muted);
}
.fe-advsearch__grouphead {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  margin: 0;
  font-size: var(--fe-text-sm);
  font-weight: 600;
  color: var(--fe-text);
}
.fe-advsearch__grouphead-icon {
  display: inline-flex;
  color: var(--fe-text-muted);
}
.fe-advsearch__grouphead-icon .fe-aicon {
  width: 15px;
  height: 15px;
}

/* Controls take the shell's control height and radius so a field here and a
   field in the filter row are the same object - the lists (core ChoiceSelect)
   included. */
.fe-advsearch .fe-input {
  height: var(--fe-h-md);
  padding: 0 var(--fe-gap-sm);
  border-radius: var(--fe-radius);
  font-size: var(--fe-text-sm);
}
.fe-advsearch .fe-select__trigger {
  border-radius: var(--fe-radius);
  font-size: var(--fe-text-sm);
}
.fe-advsearch .fe-advsearch__num {
  flex: 1 1 0;
  width: auto;
  min-width: 0;
}
.fe-advsearch__dash {
  flex: 0 0 auto;
  color: var(--fe-text-muted);
}
.fe-advsearch__pair {
  gap: var(--fe-gap-xs);
  flex-wrap: nowrap;
}
.fe-advsearch__seg--wide {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  width: 100%;
  align-self: stretch;
}
.fe-advsearch__seg--wide .fe-advsearch__seg-btn {
  padding: 0 var(--fe-gap-sm);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The count is a measurement, not a caption — a rule above it and the
   sentence that says what the server did NOT do directly underneath. */
.fe-advsearch__count {
  margin-top: 0;
  padding: var(--fe-gap) 0 0;
  border: 0;
  border-top: 1px solid var(--fe-border);
  border-radius: 0;
  background: transparent;
  gap: var(--fe-gap-sm);
}
.fe-advsearch__count-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--fe-gap);
}
.fe-advsearch__count-line {
  font-size: var(--fe-text-md);
}
.fe-advsearch__viewall {
  flex: 0 0 auto;
  border: 0;
  padding: 0;
  background: transparent;
  color: var(--fe-primary);
  font: inherit;
  font-size: var(--fe-text-sm);
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
}
.fe-advsearch__viewall::after {
  content: ' \2192';
}
/* ⚠ RTL: "view all" points the way the line reads. */
.fe-advsearch__viewall:dir(rtl)::after {
  content: ' \2190';
}
.fe-advsearch__viewall:hover {
  text-decoration: underline;
}

.fe-advsearch__btn-icon {
  display: inline-flex;
  margin-inline-end: 6px;
  vertical-align: -3px;
}
.fe-advsearch__btn-icon .fe-aicon {
  width: 15px;
  height: 15px;
}

/* One column on a phone. The tabs stay a row — three 1fr cells of an icon and
   a short word fit at 390; stacked they would be three full-width bars and
   the dialog would open on nothing but its own tabs. */
@media (max-width: 720px) {
  .fe-advsearch__cols {
    grid-template-columns: 1fr;
    gap: var(--fe-gap-lg);
  }
}
/* ⚠ Measured at 390: three tabs share 316px, and "Names and paths" plus its
   glyph needs 118 of the 105 it gets — the label came out clipped mid-word.
   The glyph is what goes: it repeats what the word says, and a tab whose
   WORD is cut is a tab nobody can read. The ellipsis is the floor under
   that, not the plan. */
@media (max-width: 560px) {
  .fe-advsearch__seg--tabs .fe-advsearch__seg-btn {
    font-size: var(--fe-text-xs);
    gap: var(--fe-gap-xs);
    padding: 0 var(--fe-gap-xs);
    overflow: hidden;
    text-overflow: ellipsis;
    display: block;
    line-height: 38px;
  }
  .fe-advsearch__seg--tabs .fe-advsearch__seg-icon {
    display: none;
  }
}
@media (max-width: 480px) {
  .fe-advsearch__pair {
    flex-wrap: wrap;
  }
  .fe-advsearch .fe-advsearch__num {
    flex: 1 1 40%;
  }
}

/* The footer takes the shell's large control height — these three are the
   dialog's verbs and they sit on the same line as the reference shell's. */
.fe-modal__card:has(.fe-advsearch) .fe-modal__actions .fe-btn {
  height: var(--fe-h-lg);
  padding: 0 var(--fe-gap-lg);
  border-radius: var(--fe-radius);
  font-size: var(--fe-text-md);
}

/* ── the details panel ────────────────────────────────────────────────── */

/* The head is about the ITEM: glyph, name, one caption naming what it is,
   then the two icon buttons. It used to say "Details" and put the item's name
   a section lower, under a second heading — a label on the frame rather than
   on the thing inside it. */
.fe-inspector__head {
  align-items: flex-start;
  justify-content: flex-start;
  gap: 10px;
  padding: var(--fe-gap) var(--fe-gap);
  border-bottom: 0;
}
.fe-inspector__head-icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  margin-top: 1px;
}
/* `fileIconTile` is em-sized (`.fe-ftile { width: 1em }`) so it scales with
   whatever it sits in — the head sets that em rather than pixel-sizing the
   glyph, which is what the listing does and why the two always match. */
.fe-inspector__head-icon {
  font-size: 28px;
}
.fe-inspector__head-icon .fe-ftile--folder .fe-ficon {
  width: 1em;
  height: 1em;
}
/* The thumbnail, moved out of the head and into the panel proper: wide and
   short, where a picture is legible as a picture. */
.fe-inspector__preview {
  display: block;
  width: 100%;
  max-height: 140px;
  margin: 0 0 var(--fe-gap);
  object-fit: cover;
  border-radius: var(--fe-radius);
  border: 1px solid var(--fe-border);
  background: var(--fe-bg-elev);
}
.fe-inspector__head-text {
  flex: 1 1 auto;
  min-width: 0;
}
.fe-inspector__title {
  font-size: var(--fe-text-md);
  font-weight: 600;
  letter-spacing: 0;
  line-height: 1.25;
  overflow-wrap: anywhere;
}
.fe-inspector__caption {
  margin: 3px 0 0;
  font-size: var(--fe-text-xs);
  line-height: 1.2;
  color: var(--fe-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-inspector__iconbtn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--fe-h-sm);
  height: var(--fe-h-sm);
  margin-top: -2px;
  padding: 0;
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text-muted);
  cursor: pointer;
}
.fe-inspector__iconbtn:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-inspector__iconbtn .fe-aicon {
  width: 15px;
  height: 15px;
}
/* The close button inherits the icon-button shape now; the old rule sized it
   as a text "×" and would otherwise fight the SVG inside it. */
.fe-inspector__close {
  font-size: 0;
  padding: 0;
  margin-inline-end: -4px;
}

/* Tabs: two equal halves under the head, the active one underlined. */
.fe-inspector__tabs {
  gap: 0;
  margin: 0 var(--fe-gap);
  padding: 0;
}
.fe-inspector__tab {
  flex: 1 1 0;
  height: 38px;
  padding: 0;
  font-size: var(--fe-text-sm);
  font-weight: 500;
}
.fe-inspector__tab.is-active {
  font-weight: 600;
}

/* Section headings sit closer to their table, and every section after the
   first is separated by a hairline instead of by air alone. */
.fe-inspector__scroll {
  padding: var(--fe-gap-lg) var(--fe-gap) var(--fe-gap);
}
.fe-inspector__section {
  margin-bottom: 0;
  padding-bottom: var(--fe-gap);
}
.fe-inspector__section + .fe-inspector__section {
  margin-top: var(--fe-gap);
  padding-top: var(--fe-gap-lg);
  border-top: 1px solid var(--fe-border);
}
.fe-inspector__heading {
  margin: 0 0 var(--fe-gap-sm);
  font-size: var(--fe-text-xs);
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  color: var(--fe-text);
}

/* The General table. Fixed label column and a fixed row height: a panel whose
   left edge wanders line by line reads as a list of unrelated facts. */
.fe-inspector__meta {
  margin: 0;
  gap: 0;
}
.fe-inspector__row {
  align-items: center;
  min-height: 25px;
  gap: var(--fe-gap-sm);
  font-size: var(--fe-text-sm);
}
.fe-inspector__row dt {
  flex: 0 0 76px;
}

/* ── the context menu ─────────────────────────────────────────────────── */

/* A menu is not a modal: it takes the small shadow. The large one
   (`--fe-shadow`, 0 20px 60px) is the dialog's, and a right-click menu
   wearing it floats a centimetre off the page. */
.fe-ctx {
  box-shadow: var(--fe-shadow-sm);
  border-radius: var(--fe-radius);
  padding: var(--fe-gap-xs);
}
.fe-ctx__item {
  height: var(--fe-h-sm);
  padding: 0 var(--fe-gap-sm);
  font-size: var(--fe-text-md);
  border-radius: var(--fe-radius-sm);
}
.fe-ctx__key {
  font-size: var(--fe-text-xs);
}

/* The share-link row: the glyph is the shared stroked one now, in a soft
   round well, the way the reference shell draws it. */
.fe-inspector__linkicon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  flex: 0 0 auto;
  border-radius: 999px;
  background: var(--fe-bg-hover);
  color: var(--fe-text-muted);
}
.fe-inspector__linkicon .fe-aicon {
  width: 14px;
  height: 14px;
}
/* The inline copy buttons carry the same stroked glyph the head does, so the
   panel has one copy mark rather than a symbol here and an icon there. */
.fe-inspector__copy {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border-radius: var(--fe-radius-sm);
  font-size: 0;
}
.fe-inspector__copy .fe-aicon {
  width: 13px;
  height: 13px;
}
.fe-inspector__copy:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
/* === /gorunum:v4-dialogs === */

/* === gorunum:v1-preview — the card shows as much of the file as fits ======
 *
 * Appended block. ⚠ Everything above is owned by other work in flight; these
 * rules are here, at the end, because the cascade is what makes them win
 * without editing a line anybody else is holding.
 *
 * The owner, 2026-09-12, on the grid cards (translated from Turkish): "about
 * the previews — I said the first line, I've changed my mind, let's do as
 * much as we can fit. For videos put the video's first frame. For a PDF the
 * part of the first page that fits."
 */

/* A blank line in the file is part of what the file looks like, and it was
 * disappearing. `.fe-fprev__line` has no content on an empty line, so the div
 * collapsed to zero height: the preview drew a markdown document as though it
 * were dense code, and — because the old ten-line budget counted the blanks
 * it then failed to draw — left the bottom of the box empty as well. Measured
 * before this change: README.md filled 75 of a 108px box and 110 of a 220px
 * one. A non-breaking space gives every line a line box at whatever
 * line-height the view is using, so the same rule holds in the grid (11px)
 * and the gallery (14px) without either number appearing here. */
.fe-fprev__line::after {
  content: '\00a0';
}

/* Columns follow the box. `lib/filePreview.ts` now parses up to eight, and a
 * hard minimum track width decides how many of them are actually drawn: the
 * grid card shows about four, the larger gallery tile about five, and the
 * rest is clipped by the `overflow: hidden` the box already had. A table
 * that runs past the edge reads as "there is more to the right", which is
 * true — and is the same thing the bottom fade says about the rows. */
.fe-fprev--table {
  grid-template-columns: repeat(var(--fprev-cols, 3), minmax(40px, 1fr));
  /* ⚠ And the rows have to be told to keep their height, or asking for more
   * of them makes the table WORSE. `.fe-fprev__cell` is `overflow: hidden`,
   * which sets its automatic minimum size to zero, so `auto` rows in a grid
   * with a definite height are free to shrink — and they did: sixteen rows in
   * a 108px box came out 6px tall each, every line of the table clipped
   * through the middle of its own glyphs. It was invisible while the cap was
   * six rows, because six rows fit. `max-content` sizes each row to its line
   * and lets the surplus overflow, which is what the box's `overflow: hidden`
   * and the fade at the bottom are there to finish. */
  grid-auto-rows: max-content;
}

/* A PAGE crops from its TOP.
 *
 * `object-fit: cover` on a portrait page in a landscape box has to discard
 * most of the height, and centred — the default — it discards the letterhead,
 * the title and the date, which is the only part of a document that says
 * which document it is. Measured on a reference deployment carrying exactly
 * this default: five PDFs built from one template produced five cards that
 * could not be told apart, because the sole difference between them was the
 * title that had been cropped away.
 *
 * Photographs keep the centred crop — a photo's subject is usually in the
 * middle, and a portrait photo cropped to its top is a picture of somebody's
 * forehead. `lib/filePreview.ts drawsAsPage()` is the one place that decides
 * which a file is, and both views ask it. */
.fe-grid__thumb img.fe-thumb--page,
.fe-gal__thumb img.fe-thumb--page {
  object-position: 50% 0;
}

/* A still from a moving picture, marked as one.
 *
 * With the first frame now actually landing on the card, a video became
 * indistinguishable from a photograph at a glance — which is the one thing
 * worth knowing before clicking. Drawn from tokens so it survives both
 * themes over any frame: the disc is the elevated surface at near-full
 * opacity, the triangle is body text. */
.fe-thumb__play {
  position: absolute;
  top: 50%;
  left: 50%; /* rtl-physical: centred by the negative margins below — symmetric */
  width: 34px;
  height: 34px;
  margin: -17px 0 0 -17px; /* rtl-physical: half the disc, to centre it */
  border-radius: 50%;
  background: color-mix(in srgb, var(--fe-bg-elev) 88%, transparent);
  box-shadow: 0 1px 4px color-mix(in srgb, var(--fe-text) 22%, transparent);
  /* The card owns every gesture — the badge must not swallow a click, a
     double-click or the start of a drag. */
  pointer-events: none;
}
.fe-thumb__play::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%; /* rtl-physical: ⚠ PLAY points the way media plays, in every language — never mirrored */
  margin: -6px 0 0 -4px; /* rtl-physical: optical centre of the play triangle */
  border-style: solid;
  border-width: 6px 0 6px 10px; /* rtl-physical: the play triangle, pointing right */
  border-color: transparent transparent transparent var(--fe-text); /* rtl-physical: the play triangle */
}
/* The gallery tile is several times the area of a grid card's preview box, so
   the badge scales with it. Scale only: one badge, one rule, two sizes. */
.fe-gal__thumb .fe-thumb__play {
  width: 46px;
  height: 46px;
  margin: -23px 0 0 -23px; /* rtl-physical: half the disc, to centre it */
}
.fe-gal__thumb .fe-thumb__play::after {
  margin: -8px 0 0 -5px; /* rtl-physical: optical centre of the play triangle */
  border-width: 8px 0 8px 13px; /* rtl-physical: the play triangle, pointing right */
}
/* === /gorunum:v1-preview === */

/* The two families that are PLACES rather than file types. Same two rules
 * every other family gets (`.fe-ficon--x` tints the glyph, `.fe-ftile--x`
 * fills the chip behind it) — they are only down here because the block they
 * belong with is in the middle of a file three other pieces of work are
 * holding open. */
.fe-ficon--storage { color: var(--fe-icon-storage); }
.fe-ficon--trash { color: var(--fe-icon-trash); }
.fe-ftile--storage { background: var(--fe-icon-storage); }
.fe-ftile--trash { background: var(--fe-icon-trash); }

/* === surucu:d1-sort — the sort control at the filter row's trailing edge ===
   Two controls, flush against each other: a text button that NAMES the active
   sort and flips its direction, and a 28px chevron whose menu picks the key.
   Both drive `lib/sortOrder`, the same singleton the list view's column
   headers drive — one sort, two handles.
   ---------------------------------------------------------------------- */

/* `display: contents` — the four chips keep being flex items of the row, so
   nothing about the existing layout moves; the wrapper exists only to carry
   the `role="group" aria-label="Filters"` that used to sit on the whole row.
   The sort control is not a filter and must not be announced inside that
   group. */
.fe-filterbar__chips {
  display: contents;
}

.fe-filterbar__sort {
  flex: 0 0 auto;
  /* The trailing edge. ⚠ `.fe-filterbar__count` carried this same `auto`
     while it was the last child; leaving both would split the free space
     between them and park the count in mid-row the moment a filter is set. */
  margin-inline-start: auto;
  display: flex;
  align-items: center;
  /* Flush, deliberately: the chevron belongs TO the button on its left — it
     opens the menu that changes what that button says. A gap would read as
     two unrelated controls. */
  gap: 0;
  padding-inline-start: var(--fe-gap-sm);
}
.fe-filterbar__count {
  margin-inline-start: 0;
}

.fe-filterbar__sortdir {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--fe-gap-xs);
  height: var(--fe-h-sm);
  padding: 0 var(--fe-gap-sm);
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text);
  font: inherit;
  font-size: var(--fe-text-sm);
  cursor: pointer;
  white-space: nowrap;
}
.fe-filterbar__sortdir:hover {
  background: var(--fe-bg-hover);
}
.fe-filterbar__sortdir:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}
.fe-filterbar__sortarrow {
  width: 14px;
  height: 14px;
}

.fe-filterbar__sortpick {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--fe-h-sm);
  height: var(--fe-h-sm);
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  /* Muted against the button beside it: that one states the sort, this one is
     the way to change it. */
  color: var(--fe-text-muted);
  cursor: pointer;
}
.fe-filterbar__sortpick:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-filterbar__sortpick[aria-expanded='true'] {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-filterbar__sortpick:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}

/* The Sort-by menu is the filter popover in a narrower box. It draws NO tick:
   the button it opens from already names the active key in full, an inch to
   its left, and the reference build shows none either. The keyboard still
   lands on the active row (`[data-active]`, set in FilterBar). */
.fe-filterpop--sort {
  min-width: 160px;
}
/* === /surucu:d1-sort === */

/* gorunum:v3-shell — the config-driven mark (ExplorerConfig.brand), drawn as
   the `#brand` slot's fallback for a host that cannot fill a slot at all — our
   own desktop app among them. Sized to the panel rows' own glyph so a
   slot-filled mark and a config-filled one come out the same height. */
.fe-toolbar__markimg {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  object-fit: contain;
}

/* surucu:d1-actions — the "⋮" at the end of the filter row, and its menu.
   The button borrows the sort control's own metrics so the row ends in two
   controls of one height rather than a button and an afterthought. */
.fe-filterbar__actions {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text-muted);
  cursor: pointer;
}
.fe-filterbar__actions:hover,
.fe-filterbar__actions[aria-expanded='true'] {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-filterbar__actions:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}
/* 232px — the reference's width, and wide enough that "Deselect all" and
   "Upload files" never wrap; the filter popovers are narrower because an
   option label is one or two words. */
.fe-filterpop--actions {
  min-width: 232px;
}
.fe-filterpop--actions .fe-filterpop__item[disabled] {
  opacity: 0.45;
  cursor: default;
}
.fe-filterpop__rule {
  height: 0;
  margin: 4px 6px;
  border: 0;
  border-top: 1px solid var(--fe-border-soft);
}

/* ⚠ Pinned to the row's trailing edge while the row scrolls.
   Measured at 390px: the filter row overflows by 260px and scrolls on itself
   (the page does not), which left this button — and with it "Select all", the
   only door that verb has on a phone — 260px off screen behind a scrollbar
   that is deliberately invisible. `sticky` costs nothing at a width where the
   row does not scroll, and the background is what stops the chips sliding
   under a transparent button. */
.fe-filterbar__actions {
  position: sticky;
  inset-inline-end: 0;
  background: var(--fe-bg);
  /* The fade falls on the START side of this end-pinned cell, where the chips
     slide under it. */
  box-shadow: calc(-8px * var(--filex-dir-x, 1)) 0 8px -6px var(--fe-bg);
}
.fe-filterbar__actions:hover,
.fe-filterbar__actions[aria-expanded='true'] {
  background: var(--fe-bg-hover);
}

/* ═══ surucu:d1-sort — the sort control over a RANKED listing ═══════════════
 *
 * A search result is in the server's order, not the active key's (see
 * `lib/sortOrder.ListingOrder`), so the control stops claiming a key: it
 * prints "Relevance", drops its arrow and closes. These rules are what makes
 * "closed" read as a state rather than as a broken button — muted text, the
 * default cursor, and no hover response at all, because a control that still
 * lights up under the pointer is one people keep clicking.
 *
 * ⚠ The reason is NOT drawn here. It lives in the button's `title` and in its
 * accessible name (`sort.relevance_why`), so it reaches a tooltip, a screen
 * reader and a keyboard user alike; a line of helper text in the filter row
 * would appear and disappear on every keystroke of a debounced search and
 * reflow the chips beside it.
 *
 * ⚠ No `opacity`. Fading the whole button fades the WORD "Relevance" with it,
 * and that word is the one thing the control still has to say clearly. The
 * muted token does the same job to the chrome and leaves the text legible in
 * both themes — which is also why it is a token and not a hex.
 */
.fe-filterbar__sortdir:disabled,
.fe-filterbar__sortpick:disabled {
  color: var(--fe-text-muted);
  cursor: default;
}
.fe-filterbar__sortdir:disabled:hover,
.fe-filterbar__sortpick:disabled:hover {
  background: transparent;
  color: var(--fe-text-muted);
}
/* The word carries the state, so it keeps the normal text colour while the
   chrome around it recedes. */
.fe-filterbar__sortdir.is-ranked > span:first-child {
  color: var(--fe-text);
}

/* The list view's column headers close for the same reason and must look the
   same way: the four of them and the filter row's button are one control with
   two handles, and a header that still hovered would be the handle that
   pretends the sort is live. */
.fe-list__sort:disabled {
  cursor: default;
}
.fe-list__sort:disabled:hover {
  color: inherit;
}

/* === gorunum:v5-empty — one empty state, and it is centred ==================
 * Appended block (agent: home-empty). Owner's report, 2026-09-13: "home benzer
 * yakınlıkta ama starred bölgesi yanlış" — Home is close, the Starred block is
 * not.
 *
 * ⚠ APPEND-ONLY REGION, last in the file on purpose: `.fe-home__state` is
 * declared in the `gorunum:v3-shell` block above and another agent owns that
 * region, so this overrides it at equal specificity without anybody editing
 * anybody else's lines. Declared `--fe-*` tokens only — a raw colour or a
 * phantom token fails web/tests/api/themeTokens.test.ts.
 * ------------------------------------------------------------------------ */

/* Measured on both builds at 1440×900, content area x∈[256,1439] (ours) and
 * x∈[192,1440] (reference):
 *
 *   reference · Home → Starred   art centred at x=818 (block centre), both
 *                                lines `text-align: center`, ~32px of air
 *                                above the art and below the last line.
 *   ours (before)                art at x=274 — 2px from the content's left
 *                                edge — and both lines left-aligned, which is
 *                                why it read as a stray icon under a heading
 *                                rather than as an empty state.
 *
 * The left alignment was not an accident, it was this block: `.fe-state` (the
 * product's one empty state, used by the folder, search, trash, not-found and
 * every panel view) centres, and the Home override turned that off to line the
 * art up with the section heading. Lining it up with the heading is exactly
 * what makes it read as a loose glyph: a heading is a label for the block, the
 * empty state IS the block, and the block is the full width of the pane.
 *
 * What stays from the override: `min-height: 0` / `height: auto`. `.fe-state`
 * is written to FILL a pane (`min-height: 240px; height: 100%`); inside a Home
 * section it has to be as tall as its own content or the Starred block starts
 * a screen below the Recent one.
 *
 * 32px rather than `--fe-gap-lg` (16px): measured off the reference, and the
 * air is what separates "this list is empty" from "this list is still
 * loading". Derived from the token so a denser scale takes it with it. */
.fe-home__state {
  align-items: center;
  text-align: center;
  padding: calc(var(--fe-gap-lg) * 2) var(--fe-gap);
}
/* === /gorunum:v5-empty === */

/* === gorunum:v4-hostmenu / v4-panepair ====================================
 *
 * Two decisions from 2026-09-13, in the header. Appended as one marked block
 * because the file above is being edited by other hands tonight.
 *
 * ── the AI mark's words, on hover ──────────────────────────────────────────
 * The mark used to carry a permanent `COMING SOON` pill (`.fe-toolbar__soon-tag`,
 * defined further up and no longer rendered by Toolbar.vue — its rules are
 * dead and should be swept by whoever next owns that region of this file).
 * A caption that is always on screen spends header width on a promise; the
 * words now arrive when the pointer does.
 *
 * ⚠ The tip is drawn by the WRAPPER's `:hover`, not the button's. The button
 * is `disabled`, and a disabled control does not receive mouse events in
 * Chromium — the hit test falls through it, so a rule hung on the button
 * itself is a rule that fires only sometimes. The wrapper always gets it.
 *
 * ⚠ `pointer-events: none` on the tip: it is painted UNDER the pointer's path
 * to the rest of the row, and a tooltip that can itself be hovered is a
 * tooltip that flickers as the cursor crosses its own edge.
 *
 * ⚠ `right: 0`, never `left`: this control sits at the end of the row, and a
 * left-anchored box the width of a sentence hangs off the viewport at 390px.
 */
.fe-toolbar__soon-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
}
/* The same tip is a segmented ChoiceButtons cell's meaning (#160: the
   sharing dialog's access levels): one look for a hover tip in the product.
   Its own show rules sit with ChoiceButtons' (hover AND keyboard focus). */
.fe-toolbar__soon-tip,
.fe-choice__tip {
  position: absolute;
  top: calc(100% + var(--fe-gap-xs));
  inset-inline-end: 0;
  z-index: 6;
  padding: 4px 8px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  background: var(--fe-bg-elev);
  color: var(--fe-text);
  box-shadow: var(--fe-shadow-sm);
  font-size: var(--fe-text-xs);
  line-height: 1.3;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s ease;
}
.fe-toolbar__soon-wrap:hover .fe-toolbar__soon-tip {
  opacity: 1;
}
/* Someone who asked not to be moved at. The words are still in the button's
   accessible name, which is the door this rule cannot take away. */
@media (prefers-reduced-motion: reduce) {
  .fe-toolbar__soon-tip,
  .fe-choice__tip {
    transition: none;
  }
}

/* ── the split toggle, docked beside the details toggle ────────────────────
 * It carries `fe-btn fe-btn--icon-only` when it is docked, which is the
 * details toggle's own class set, so the pair needs no size of its own — the
 * glyph is `.fe-ficon` at 1em in both, and `.fe-btn.is-active` is the pressed
 * style both already had. The one thing worth saying here is that the docked
 * button must NOT inherit the strip's 22px box, which is why it drops
 * `.fe-tabs__split` rather than adding to it. */
.fe-tabs__split--docked {
  flex: 0 0 auto;
}
/* === /gorunum:v4-hostmenu / v4-panepair === */

/* === gorunum:v4-wordmark — the product mark, at the reference's size ========
 *
 * Owner's decision, 2026-09-13, verbatim: *"filex yazısını daha büyük
 * yapalım."*
 *
 * ⚠ Measured off the contributor's reference build rather than chosen:
 * https://filex.blipnova.com, signed in, 1440px — the wordmark there computes
 * to `font-size: 18px; font-weight: 600; letter-spacing: normal;
 * line-height: 18px` (Inter, its own class is literally `text-18 font-semibold
 * leading-none`), with a 28×28 logo 8px to its left. Ours was 13px
 * (`--fe-text-md`) beside a 22px mark — a third smaller in both.
 *
 * ⚠ A raw 18px, and deliberately not derived from `--fe-text-*`: that scale
 * stops at 13px and it is BODY type. A wordmark is not body type — it does not
 * get smaller because a host asked for a denser listing — and a `calc()` off
 * a 13px token would be a number whose meaning changes the day somebody
 * retunes the scale.
 *
 * ⚠ `line-height: 1` (the reference's `leading-none`) is load-bearing, not
 * tidiness: the inherited 1.5 would give an 18px word a 27px line box, and the
 * brand block is one of the things that sets the header's height. Measured
 * both ways — the header stays at 59px with it.
 *
 * ⚠ Nothing here reserves space. An embed that passes no `config.brand` and
 * fills no `#brand` slot renders an EMPTY `.fe-toolbar__mark`, and an empty
 * flex box with no content has no line box and no width — so the toggle still
 * sits at the edge on its own, at 18px exactly as it did at 13px. A `min-width`
 * or a `min-height` here would have been the thing that broke that.
 *
 * ⚠ It must not push the search field. The gutter is
 * `min-width: calc(var(--fe-sidenav-w) - 12px)` and it is the field's left
 * edge; the brand's CONTENT has to stay under it. Measured: content 98px →
 * 114px, against a gutter of 243px today and 180px once the panel is narrowed
 * to 192px — 66px of headroom at the narrower width, and the field does not
 * move at either. There is a browser assertion for both widths.
 */
.fe-toolbar__mark {
  font-size: 18px;
  line-height: 1;
}
/* The `config.brand` mark — the door a `<filex-explorer>` host has, since slots
   do not reach a Vue custom element. Kept the SAME size as the slot-filled one
   above it (`.fx-explore-logo` in the web app), which is the whole point of the
   rule this replaces: a config-filled mark and a slot-filled mark must not come
   out at two different heights. */
.fe-toolbar__markimg {
  width: 28px;
  height: 28px;
}
/* === /gorunum:v4-wordmark === */

/* ======================================================================
 * belge:n1 — the "New document" picker (modals/NewDocumentModal.vue).
 *
 * One screen, three answers: which type, what name, which folder. It is not a
 * wizard on purpose — a three-step flow for three fields makes the person
 * click Next twice to do something they could see all of at once, and the
 * destination is nearly always the folder they are already standing in.
 *
 * ⚠ The type tiles size their chip with `font-size`, not width/height: the
 * chips come from lib/fileIcons `iconTile`, whose `.fe-ftile` box is measured
 * in `em` precisely so one glyph definition serves a 13px listing row and a
 * 28px picker tile. A pixel size here would be a second sizing system for the
 * same element.
 * ==================================================================== */

.fe-newdoc {
  display: flex;
  flex-direction: column;
  gap: var(--fe-gap-lg);
}

/* --- the type grid -------------------------------------------------- */

.fe-newdoc__group + .fe-newdoc__group {
  margin-top: var(--fe-gap);
}
.fe-newdoc__grouplabel {
  margin: 0 0 var(--fe-gap-sm);
  font-size: var(--fe-text-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fe-text-muted);
}
/* auto-fill rather than a fixed column count: the same grid holds six office
   types and twelve text ones, and at 390px it has to hold either without a
   horizontal scrollbar. */
.fe-newdoc__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: var(--fe-gap-sm);
}
.fe-newdoc__type {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--fe-gap-xs);
  padding: var(--fe-gap) var(--fe-gap-sm);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-md);
  background: var(--fe-bg);
  color: var(--fe-text);
  font: inherit;
  text-align: center;
  cursor: pointer;
}
.fe-newdoc__type:hover {
  background: var(--fe-bg-hover);
  border-color: var(--fe-border-strong);
}
.fe-newdoc__type.is-selected {
  background: var(--fe-primary-soft);
  border-color: var(--fe-primary);
  color: var(--fe-primary-ink);
}
.fe-newdoc__type:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 2px;
}
.fe-newdoc__tile {
  display: inline-flex;
  font-size: 28px;
  line-height: 1;
}
.fe-newdoc__ext {
  font-size: var(--fe-text-md);
  font-weight: 600;
}
.fe-newdoc__kind {
  font-size: var(--fe-text-xs);
  color: var(--fe-text-muted);
  /* Some kind names wrap a 96px tile ("Shell script", "Presentation").
     Wrapping is fine; a tile that grows taller than its neighbours is not, so
     the line box is tight rather than the default 1.5. */
  line-height: 1.25;
}
.fe-newdoc__type.is-selected .fe-newdoc__kind {
  color: var(--fe-primary-ink);
}
.fe-newdoc__withheld {
  margin: var(--fe-gap) 0 0;
  font-size: var(--fe-text-xs);
  line-height: 1.45;
  color: var(--fe-text-muted);
}

/* --- name + location ------------------------------------------------ */

.fe-newdoc__field {
  display: flex;
  flex-direction: column;
  gap: var(--fe-gap-sm);
}
.fe-newdoc__label {
  font-size: var(--fe-text-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fe-text-muted);
}
/* #56 — the field holds the WHOLE file name. The extension used to sit beside
   it as a read-only label, which kept a name from losing it by accident and
   also made LICENSE, Makefile and test.conf impossible; the type now only
   prefills it, and focusing the field selects the stem as a rename does. */
.fe-newdoc__name {
  min-width: 0;
  width: 100%;
}
/* "Will be created as report.docx" — a type whose editor needs its extension,
   said before Create. Quiet, not an error: nothing is wrong with the name. */
/* Drafts (issue #71): the draft limit, and the way to Drafts beside it. */
.fe-newdoc__limit {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px var(--fe-gap);
  margin-top: var(--fe-gap-sm);
}
.fe-newdoc__limit .fe-form__error {
  margin: 0;
  flex: 1 1 16rem;
}
.fe-newdoc__hint {
  margin: 0;
  font-size: var(--fe-text-xs);
  line-height: 1.45;
  color: var(--fe-text-muted);
  overflow-wrap: anywhere;
}

.fe-newdoc__dest {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  min-height: var(--fe-h-md);
  padding: 0 var(--fe-gap-sm);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg-elev);
}
.fe-newdoc__destpath {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fe-text-md);
  color: var(--fe-text);
}
.fe-newdoc__changebtn {
  flex: 0 0 auto;
  padding: var(--fe-gap-xs) var(--fe-gap-sm);
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: none;
  color: var(--fe-primary-ink);
  font: inherit;
  font-size: var(--fe-text-sm);
  cursor: pointer;
}
.fe-newdoc__changebtn:hover {
  background: var(--fe-bg-hover);
}

/* --- the folder browser --------------------------------------------- */

.fe-newdoc__browser {
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg);
  overflow: hidden;
}
.fe-newdoc__browserbar {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  padding: var(--fe-gap-xs) var(--fe-gap-sm);
  border-bottom: 1px solid var(--fe-border-soft);
  background: var(--fe-bg-elev);
}
.fe-newdoc__up {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--fe-h-sm);
  height: var(--fe-h-sm);
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: none;
  color: var(--fe-text);
  cursor: pointer;
}
.fe-newdoc__up:hover:not(:disabled) {
  background: var(--fe-bg-hover);
}
.fe-newdoc__up:disabled {
  color: var(--fe-text-muted);
  cursor: default;
}
.fe-newdoc__up .fe-ficon {
  width: 16px;
  height: 16px;
}
.fe-newdoc__browserpath {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fe-text-sm);
  color: var(--fe-text-muted);
}
/* A fixed height, not max-height: this panel opens above the Create button,
   and one that grew with the folder count would push that button off the
   bottom of a phone screen in a busy drive. */
.fe-newdoc__rows {
  height: 168px;
  margin: 0;
  padding: var(--fe-gap-xs);
  list-style: none;
  overflow-y: auto;
}
.fe-newdoc__row {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  width: 100%;
  padding: var(--fe-gap-xs) var(--fe-gap-sm);
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: none;
  color: var(--fe-text);
  font: inherit;
  font-size: var(--fe-text-md);
  text-align: start;
  cursor: pointer;
}
.fe-newdoc__row:hover {
  background: var(--fe-bg-hover);
}
.fe-newdoc__row:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}
.fe-newdoc__rowicon {
  display: inline-flex;
  font-size: 18px;
  line-height: 1;
}
.fe-newdoc__rowname {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* A folder nobody may write to is still listed and still opens: the grant
   that allows writing may live on a subfolder, so hiding the parent would
   hide the only route to it. What it loses is the claim that you can save
   here — the word beside it, and the Create button that stays disabled. */
.fe-newdoc__row.is-locked .fe-newdoc__rowname {
  color: var(--fe-text-muted);
}
.fe-newdoc__rowlock {
  flex: 0 0 auto;
  font-size: var(--fe-text-xs);
  color: var(--fe-text-muted);
}
.fe-newdoc__note {
  padding: var(--fe-gap-sm);
  font-size: var(--fe-text-sm);
  color: var(--fe-text-muted);
}
.fe-newdoc__browseractions {
  display: flex;
  justify-content: flex-end;
  gap: var(--fe-gap-sm);
  padding: var(--fe-gap-sm);
  border-top: 1px solid var(--fe-border-soft);
  background: var(--fe-bg-elev);
}

/* --- nothing to offer ------------------------------------------------ */

.fe-newdoc__empty {
  padding: var(--fe-gap-lg);
  border: 1px dashed var(--fe-border-strong);
  border-radius: var(--fe-radius-md);
  text-align: center;
}
.fe-newdoc__empty-title {
  margin: 0 0 var(--fe-gap-xs);
  font-size: var(--fe-text-md);
  font-weight: 600;
  color: var(--fe-text);
}
.fe-newdoc__empty-body {
  margin: 0;
  font-size: var(--fe-text-sm);
  line-height: 1.5;
  color: var(--fe-text-muted);
}

@media (max-width: 480px) {
  .fe-newdoc__grid {
    grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  }
  .fe-newdoc__rows {
    height: 132px;
  }
}
/* === /belge:n1 === */

/* === tablo:t1 — resizable / hideable columns, the column menu, the trash
       banner. Appended at the END of the file: the list block above is a wave
       several agents were in at once, and a block at the tail cannot collide
       with an edit in the middle of one.

   ⚠ There is deliberately NO `grid-template-columns` here. That answer is
   computed in `ListView` from the container's own width and the person's own
   widths (lib/viewPrefs.fitColumns) and applied inline, because it used to be
   three stylesheet copies — a base rule, a `.fe--narrow` override and a
   `@media` override — and they disagreed: the media query answers the VIEWPORT
   while `.fe--narrow` answers the CONTAINER, so an embed 500px wide on a
   1400px screen got one rule's hidden cells and the other rule's track list.
   The cells that are not drawn are no longer RENDERED, so nothing here has to
   agree with anything about how many tracks exist. === */

/* The header cell has to be a positioning context for its drag handle. */
.fe-list__head .fe-list__col {
  position: relative;
}

.fe-list__head-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The drag handle straddles the divider: 9px of target over a 1px line, which
   is the smallest a pointer reliably hits without the hit area reaching into
   the label beside it. */
.fe-list__resize {
  position: absolute;
  /* Above the pinned ⋮ cell (z-index 1): the LAST column's handle straddles
     into it, and under it the edge could not be grabbed at all — measured in
     LTR and RTL alike (elementFromPoint at the handle = the column-menu
     button). Below the frozen lead (3). */
  z-index: 2;
  top: 0;
  bottom: 0;
  inset-inline-end: calc(var(--fe-gap-sm) / -2 - 1px);
  width: 9px;
  cursor: col-resize;
  /* ⚠ `touch-action: none` or a drag on a touchscreen scrolls the listing
     instead of moving the column — the pointer events never arrive. */
  touch-action: none;
  user-select: none;
}
.fe-list__resize::after {
  content: '';
  position: absolute;
  top: 20%;
  bottom: 20%;
  inset-inline-start: 4px;
  width: 1px;
  background: var(--fe-border);
  opacity: 0;
  transition: opacity 120ms ease;
}
.fe-list__head:hover .fe-list__resize::after,
.fe-list__resize:focus-visible::after {
  opacity: 1;
}
.fe-list__resize:hover::after {
  background: var(--fe-primary);
  opacity: 1;
}
.fe-list__resize:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
  border-radius: var(--fe-radius-sm);
}

/* The header's own ⋮. Same box as the row's, because it is the same gesture in
   the same column — the difference is what it is a menu ABOUT. */
.fe-list__colmenu-btn {
  opacity: 0.7;
}
.fe-list__colmenu-btn:hover,
.fe-list__colmenu-btn[aria-expanded='true'] {
  opacity: 1;
}

/* ── the column menu ───────────────────────────────────────────────── */

.fe-colmenu__backdrop {
  position: fixed;
  inset: var(--fe-overlay-top, 0px) 0 0; /* the host's own strip stays reachable — see .fe-modal__backdrop */
  z-index: 2147483000;
  background: transparent;
}

.fe-colmenu {
  position: fixed;
  min-width: 232px;
  max-width: 320px;
  padding: var(--fe-gap-xs) 0;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-md);
  background: var(--fe-bg-elev);
  box-shadow: var(--fe-shadow);
  color: var(--fe-text);
  font-family: var(--fe-font);
  font-size: var(--fe-text-md);
}
.fe-colmenu:focus {
  outline: none;
}

.fe-colmenu__title {
  margin: 0;
  padding: var(--fe-gap-xs) var(--fe-gap);
  font-size: var(--fe-text-xs);
  font-weight: 600;
  color: var(--fe-text-muted);
}

.fe-colmenu__hint {
  margin: 0;
  padding: 0 var(--fe-gap) var(--fe-gap-xs);
  font-size: var(--fe-text-xs);
  line-height: 1.45;
  color: var(--fe-text-muted);
}

.fe-colmenu__row {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  width: 100%;
  min-height: var(--fe-h-sm);
  padding: var(--fe-gap-xs) var(--fe-gap);
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
}
.fe-colmenu__row:hover {
  background: var(--fe-bg-hover);
}
.fe-colmenu__row:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}

/* The tick reuses the list's own checkbox look, so "shown" means the same
   shape here as a selected row's box does two inches away. */
.fe-colmenu__tick {
  pointer-events: none;
}

.fe-colmenu__label {
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.fe-colmenu__note {
  flex: 0 0 auto;
  font-size: var(--fe-text-xs);
  color: var(--fe-text-muted);
}

.fe-colmenu__sep {
  height: 1px;
  margin: var(--fe-gap-xs) 0;
  background: var(--fe-border-soft);
}

/* ── the trash banner ──────────────────────────────────────────────── */

/* One row above the listing: what the trash IS on the left, the one
   irreversible action on the right. */
.fe-trashbar {
  display: flex;
  align-items: center;
  gap: var(--fe-gap);
  flex-wrap: wrap;
  padding: var(--fe-gap-sm) var(--fe-gap);
  border-bottom: 1px solid var(--fe-border-soft);
  background: var(--fe-bg-elev);
  color: var(--fe-text-muted);
  font-size: var(--fe-text-md);
}
.fe-trashbar__text {
  flex: 1 1 240px;
  margin: 0;
  line-height: 1.45;
}
.fe-trashbar__action {
  flex: 0 0 auto;
}
/* A purge that goes on in the background, counted as it goes. */
.fe-trashbar__progress {
  flex: 0 0 auto;
  margin: 0;
  color: var(--fe-text);
  font-variant-numeric: tabular-nums;
}
/* The server's count and size of the whole trash, and how much of it is on
   screen (0.54, finding D1: the view is paged). */
.fe-trashbar__count {
  flex: 0 1 auto;
  margin: 0;
  color: var(--fe-text);
  font-variant-numeric: tabular-nums;
}
.fe-trashbar__shown {
  color: var(--fe-text-muted);
}
.fe-trashbar__more {
  flex: 0 0 auto;
}

/* tablo:t1 — the row's real thumbnail. Exactly the type tile's box (24px), so
   turning a glyph into a picture does not move a single row. */
.fe-list__icon--img {
  width: 24px;
  height: 24px;
  border-radius: var(--fe-radius-sm);
  object-fit: cover;
  background: var(--fe-bg-elev);
}
/* A page is cropped from its TOP — the half of a document that says what it
   is. Same rule the grid and the gallery already apply to a page thumbnail. */
.fe-list__icon--img.fe-thumb--page {
  object-position: 50% 0;
}

/* 0.50: the type badge on a list thumbnail drawn on paper.
 *
 * Real pages reached the list in 0.50 (0.49 drew a coloured card with the
 * extension on it), and at 24px the first page of a .docx is a white square
 * on a white row: the row looked as if it had lost its icon. So the picture
 * carries a small badge naming its kind (DOCX, PDF, ZIP) in its inline-end
 * corner: lib/filePreview decides which files get one (drawsOnPaper) and
 * ThumbTile draws it only while the picture is there. No frame around the
 * picture (the owner's call).
 *
 * ⚠ The badge sets NO background: it is filled by the `fe-ftile--<family>`
 * class it also carries, the type tile's own colour from `--fe-icon-<family>`
 * (light and dark). A background here, later in the file, would win the tie
 * and paint every badge one colour.
 *
 * Logical insets only: under dir="rtl" the badge moves to the opposite corner
 * by itself. Absolutely placed against `.fe-list__thumb`, which is exactly the
 * tile's box, so the row's height does not change.
 *
 * ⚠ INSIDE the corner (insets 0), never hanging over it: the list cell clips
 * (`:where(.fe-list__cell) { overflow: hidden }`, which the name's ellipsis
 * needs) and the folder peek's tile clips to its rounded corners. The first
 * version hung 3px below the tile, and the v0.50.0 release shots showed every
 * badge in the list with its lower edge and the bottom of its letters cut off
 * flat. */
.fe-list__thumb {
  position: relative;
  display: inline-flex;
  flex: 0 0 auto;
  line-height: 0;
}
.fe-thumb__type {
  position: absolute;
  inset-block-end: 0;
  inset-inline-end: 0;
  /* ⚠ Never wider than the tile. A wider badge (it was capped at 30px on a
     24px tile) grew out of the tile's other side, and the list cell clipped
     that edge: the v0.50.0 release shot drew "BOARD" with its B cut off. A
     name longer than four letters is set smaller (--long) and, past what fits
     even then, ends at the tile's edge. */
  max-width: 100%;
  overflow: hidden;
  padding: 1px 2px;
  border-radius: 2px;
  color: #ffffff;
  font-size: 7px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.02em;
  white-space: nowrap;
  pointer-events: none;
}
.fe-thumb__type--long {
  padding-inline: 1px;
  font-size: 5.5px;
  letter-spacing: 0;
}

/* === gezinti:g3 — the header follows the panel, in BOTH states =============
 *
 * The owner reported that collapsing the navigation panel *"düzgün
 * çalışmıyor"*. The mechanism was fine — the panel goes 192 ↔ 56, the state
 * persists, the toggle's label is right. What was wrong is that the HEADER did
 * not move with it. Measured at 960 before this block:
 *
 *   expanded   nav 1→193 · brand gutter 13→193 · search 210 · content 193
 *   collapsed  nav 1→ 57 · brand gutter 13→193 · search 210 · content  57
 *
 * — the panel and the content column go to 57 and the gutter and the field
 * stay, leaving a ~136px band of empty header standing over the content. The
 * cause is that `.fe-toolbar__brand--gutter`'s `min-width` is
 * `calc(var(--fe-sidenav-w) - 12px)`, a STATIC token: it describes the
 * expanded panel and nothing tells it the panel collapsed.
 *
 * ⚠⚠ This REVERSES a decision documented twice further up this file ("It does
 * NOT follow the rail. Shrinking to 56px when somebody collapses the panel
 * would slide the whole header 176px sideways, which is a jolt on every
 * control in the row for a control that did not move."). Two things overrule
 * it: the owner reported the result as broken, and the contributor's reference
 * build does follow the rail — measured signed-in on filex.blipnova.com,
 * collapsing its panel moves its search field from x=236 to x=104 and drops
 * its logo and wordmark entirely, re-centring the `☰` in the rail.
 * ⚠ Those two comments above are now describing behaviour that no longer
 * exists. They are mid-file and this block may only append; whoever next owns
 * that region should strike them — a comment that confidently states the
 * opposite of what the code does is worse than no comment.
 *
 * ⚠ The jolt the old comment feared is real, and it is answered rather than
 * ignored: the same `0.14s ease` the panel animates its own width with, so the
 * header slides WITH the panel instead of teleporting a frame later.
 *
 * ⚠ 56px is `.fe-sidenav--rail`'s own width and there is no token for it. The
 * literal is not the contract — the browser gate is: the search field's left
 * edge has to land within 0–24px of the panel's right edge in BOTH states, and
 * that assertion fails if either number moves. (It was passing while the
 * layout was visibly wrong, because it only ever measured the expanded state;
 * extended, it produced 8 red checks against the old behaviour.)
 *
 * ⚠ Nothing here touches 390px: the `--rail` class is only applied when the
 * explorer is wide, because below 560px the panel is a drawer, not a rail, and
 * `.fe--narrow .fe-toolbar__brand--gutter` already drops the gutter to 0.
 */
.fe-toolbar__brand--gutter {
  transition: min-width 0.14s ease;
}
.fe-toolbar__brand--rail.fe-toolbar__brand--gutter {
  min-width: calc(56px - 12px);
}
@media (prefers-reduced-motion: reduce) {
  .fe-toolbar__brand--gutter {
    transition: none;
  }
}
/* === /gezinti:g3 === */

/* ══ gorunum:v1-advsearch — the Owner filter's off state ═══════════════════
   (appended block, agent: advanced-search / 2026-09-13)

   The Owner select is disabled under the content scopes, because a content
   hit reaches the client without an owner and every row would read as
   "System" (see `AdvancedSearch.ownerFilterable`). Nothing in the shell
   styled a disabled `.fe-input`, so the browser's default was a control that
   looked exactly like a working one — which is the whole failure this dialog
   exists to avoid, moved from the wire to the paint. */
.fe-advsearch .fe-input:disabled {
  background: var(--fe-bg-elev);
  color: var(--fe-text-muted);
  border-color: var(--fe-border-soft);
  cursor: not-allowed;
}

/* belge:n1 addendum — keep Name and Location on screen.
 *
 * Measured at 390×780 (Turkish, dark): the type grid filled the whole dialog
 * and both fields sat below the fold, so the first thing the person saw after
 * choosing a type was a Create button for a document whose name and
 * destination they had not seen. The grid is the part that can grow without
 * limit — a deployment may offer nineteen types — so the grid is the part that
 * gets a ceiling and its own scrollbar.
 *
 * A share of the WINDOW rather than a media query: a short landscape window on
 * a laptop has the same problem as a phone and no media query for width would
 * catch it.
 *
 * ⚠ No fixed arm. It used to be `min(46vh, 420px)`, written when the grid was
 * ~370px tall and the 420px arm kept the scrollbar off a normal desktop
 * dialog. The grid grew past it (the ODF row, then an Apps group), and from
 * then on the arm was a ceiling of its own: a window of any height scrolled
 * the list, and picking a tile in the lower rows scrolled the rows above it
 * half out of the box - the New document picture of 0.52 and 0.53
 * (apps/app-new-document-1440.png) opened on the bottom edges of the office
 * tiles even after its window was made 1400px tall. Bounded by the window
 * alone, a tall window shows every group and a short one still keeps Name and
 * Location in view.
 */
.fe-newdoc__types {
  max-height: 46vh;
  overflow-y: auto;
}
/* === /belge:n1 addendum === */

/* ⚠⚠ The header cell must NOT clip. `.fe-list__col--type/--owner/--mod/--size`
   set `overflow: hidden` so a long value ellipsises inside its column — right
   for a ROW, fatal for a HEADER, because the drag handle is absolutely
   positioned to straddle the divider and half of it therefore sits outside the
   cell box. Measured 2026-09-13: a drag on the handle changed the column width
   by 0px, and a person would never have seen the handle to try.

   The ellipsis is kept where it belongs — on the label and the sort button
   inside the cell — so nothing about the header's text behaviour changes. */
.fe-list__head .fe-list__col {
  overflow: visible;
}
.fe-list__head .fe-list__sort,
.fe-list__head .fe-list__head-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The header a column is being dragged out of, and the gap it would land in. */
.fe-list__head.is-dragging-col {
  cursor: grabbing;
  user-select: none;
}
.fe-list__head .fe-list__col.is-movable .fe-list__sort,
.fe-list__head .fe-list__col.is-movable .fe-list__head-label {
  cursor: grab;
}
.fe-list__col.is-col-dragging {
  opacity: 0.45;
}
/* The drop indicator: a line in the gap the column would land in, drawn on the
   cell that would come after it — so the mark is always between two columns
   rather than on top of one. */
.fe-list__col.is-drop-before::before,
.fe-list__col.is-drop-after::after {
  content: '';
  position: absolute;
  top: 15%;
  bottom: 15%;
  width: 2px;
  border-radius: 1px;
  background: var(--fe-primary);
}
.fe-list__col.is-drop-before::before {
  inset-inline-start: calc(var(--fe-gap-sm) / -2 - 1px);
}
.fe-list__col.is-drop-after::after {
  inset-inline-end: calc(var(--fe-gap-sm) / -2 - 1px);
}

/* ══ tasi:m1 — the destination picker ("Move to" / "Copy to") ══════════
   (appended block, agent: multi-selection / 2026-09-13)

   The dialog is a one-level-at-a-time folder browser, not a tree: a tree's
   expand arrows are a second navigation model to learn, and on a storage whose
   listing is a network round trip each one is a request whose latency the user
   did not ask for. The rows list, the breadcrumb walks back.

   ⚠ The list has a FIXED height, deliberately. A picker that grows with the
   folder makes the Choose button move between clicks — the same reason
   .fe-newdoc__rows is pinned. Everything below uses declared --fe-* tokens so
   the panel follows the active theme in both light and dark. */
.fe-destpick {
  display: flex;
  flex-direction: column;
  gap: var(--fe-gap-sm);
}

.fe-destpick__bar {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  min-height: var(--fe-h-md);
}

.fe-destpick__up {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: var(--fe-h-md);
  height: var(--fe-h-md);
  padding: 0;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  background: var(--fe-bg);
  color: var(--fe-text);
  cursor: pointer;
}
.fe-destpick__up:hover:not(:disabled) {
  background: var(--fe-bg-hover);
  border-color: var(--fe-border-strong);
}
.fe-destpick__up:disabled {
  color: var(--fe-text-muted);
  cursor: not-allowed;
  opacity: 0.55;
}
.fe-destpick__up .fe-ficon {
  width: 16px;
  height: 16px;
}

/* The breadcrumb scrolls sideways rather than wrapping: a wrapping crumb line
   changes the dialog's height as you walk down a tree, which moves everything
   under it. */
.fe-destpick__crumbs {
  display: flex;
  align-items: center;
  gap: 2px;
  min-width: 0;
  overflow-x: auto;
  white-space: nowrap;
  scrollbar-width: thin;
}

.fe-destpick__crumb {
  flex: 0 0 auto;
  padding: 2px 6px;
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text-muted);
  font-size: var(--fe-text-sm);
  cursor: pointer;
}
.fe-destpick__crumb:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-destpick__crumb.is-current {
  color: var(--fe-text);
  font-weight: 600;
}
/* The crumbs line scrolls (overflow-x), and a scroller clips on BOTH axes: an
   outline drawn outside the button, which is what the browser's default focus
   ring is, loses its left, top and bottom edge against the first crumb. The
   ring is drawn inside the button's own box instead, like the list rows'. */
.fe-destpick__crumb:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}

.fe-destpick__sep {
  flex: 0 0 auto;
  color: var(--fe-text-muted);
  font-size: var(--fe-text-sm);
}

.fe-destpick__rows {
  height: 216px;
  overflow-y: auto;
  margin: 0;
  padding: 4px;
  list-style: none;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg);
}

.fe-destpick__note {
  padding: 10px 8px;
  color: var(--fe-text-muted);
  font-size: var(--fe-text-sm);
}

.fe-destpick__row {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-sm);
  width: 100%;
  padding: 6px 8px;
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text);
  font-size: var(--fe-text-md);
  text-align: start;
  cursor: pointer;
}
.fe-destpick__row:hover:not(:disabled) {
  background: var(--fe-bg-hover);
}
.fe-destpick__row:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}

/* Unwritable is DIMMED, not hidden: the grant that allows writing may live on
   a subfolder, so the row still has to be openable. Blocked (the folder being
   moved, or something inside it) is dimmed AND disabled, because everything
   under it is blocked too and walking in would be a dead end. */
.fe-destpick__row.is-locked .fe-destpick__rowname {
  color: var(--fe-text-muted);
}
.fe-destpick__row.is-blocked {
  color: var(--fe-text-muted);
  cursor: not-allowed;
  opacity: 0.6;
}

.fe-destpick__rowicon {
  display: inline-flex;
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  align-items: center;
  justify-content: center;
}
.fe-destpick__rowicon .fe-ficon {
  width: 18px;
  height: 18px;
}

.fe-destpick__rowname {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.fe-destpick__rowtag {
  flex: 0 0 auto;
  padding: 1px 6px;
  border-radius: var(--fe-radius-sm);
  background: var(--fe-bg-elev);
  color: var(--fe-text-muted);
  font-size: var(--fe-text-xs);
}

.fe-destpick__rowinto {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  color: var(--fe-text-muted);
}
.fe-destpick__row.is-blocked .fe-destpick__rowinto {
  visibility: hidden;
}

/* One line under the list, and it is always one of the two: where the file is
   going, or why it cannot go here. A dialog whose primary button is disabled
   without saying why is a dialog people report as broken. */
.fe-destpick__target,
.fe-destpick__reason {
  margin: 0;
  min-height: 18px;
  font-size: var(--fe-text-sm);
}
.fe-destpick__target {
  color: var(--fe-text-muted);
}
.fe-destpick__reason {
  color: var(--fe-danger);
}
/* …except when the line is a prompt rather than a refusal. "Open a storage to
   choose a folder inside it" is the drives level saying what to do next, and in
   the danger colour it made the dialog's FIRST screen look broken. */
.fe-destpick__reason.is-hint {
  color: var(--fe-text-muted);
}

@media (max-width: 560px) {
  .fe-destpick__rows {
    height: 168px;
  }
}
/* === /tasi:m1 === */

/* === gorunum:v5-panerow — the strip's third control ========================
 * The details toggle's home once the row becomes the WINDOW's row rather than
 * the left pane's (see the handover diff for FileExplorer.vue). Until that
 * diff lands `inspectorEnabled` is passed by nobody and this button is not
 * rendered at all — the ⓘ is still drawn in the breadcrumb row, and two of
 * them on screen is exactly the duplicate this wave removes.
 *
 * ⚠ These metrics are `.fe-tabs__new` / `.fe-tabs__split`'s, restated rather
 * than shared, because that rule is mid-file and this block may only append.
 * The three controls in this row are one control family and want ONE selector;
 * whoever next owns that region should fold this into it. If they drift, the
 * row ends with a 22px box beside a 27px one — which is the mismatch the
 * teleported version was measured and rejected for.
 */
.fe-tabs__inspector {
  flex: 0 0 auto;
  align-self: center;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid transparent;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text-muted);
  cursor: pointer;
  padding: 0;
}
.fe-tabs__inspector .fe-ficon {
  width: 14px;
  height: 14px;
}
.fe-tabs__inspector:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-tabs__inspector.is-active {
  background: var(--fe-bg-selected);
  color: var(--fe-primary);
  border-color: var(--fe-border);
}
/* === /gorunum:v5-panerow === */

/* === gorunum:v5-panestack — the tab strip belongs to the WINDOW ============
 *
 * Owner's decision, 2026-09-13, verbatim: *"tab içinde split yapman lazım,
 * dışında yapıyorsun, bok gibi duruyor o yüzden."* Measured at 1440 with the
 * split on: the strip sits INSIDE the left pane (x=193→816, 623px), so the
 * left half carries breadcrumb + tabs + filter row + listing while the right
 * half gets a bare crumb line and a table. A tab is a location the window is
 * showing; the split happens WITHIN that location, so the strip is above both
 * panes, full width, and each pane carries only its own address and listing.
 *
 * ⚠ These rules are inert until the FileExplorer.vue diff in the handover is
 * applied — nothing renders `.fe__stack` or `.fe__panes` yet. They are here so
 * that diff is a pure template move with no CSS work attached to it, and so
 * the two rules it BREAKS are already replaced:
 *   · `.fe__main--split > .fe__primary` (flex basis) and
 *   · `.fe__main--split > .fe__primary.fe-pane--focus` (the active-pane accent)
 * both use a direct-child selector on `.fe__main`, and after the move
 * `.fe__primary` is a child of `.fe__panes`. ⚠ Losing the second one silently
 * would take away the only thing that says WHICH pane the keyboard is in.
 * The replacements below are more specific, so they win where they match and
 * the originals simply stop matching.
 *
 * ⚠ `.fe__primary > .fe-tabs`'s own padding/ground/border (mid-file) stops
 * matching for the same reason; restated here against the new parent.
 */
.fe__stack {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
}
.fe__panes {
  display: flex;
  flex-direction: row;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
}
.fe__stack > .fe-tabs {
  padding: var(--fe-gap-xs) var(--fe-gap-sm) 0;
  background: var(--fe-bg);
  border-bottom: 1px solid var(--fe-border);
}
.fe__stack > .fe-tabs .fe-tabs__new,
.fe__stack > .fe-tabs .fe-tabs__split,
.fe__stack > .fe-tabs .fe-tabs__inspector {
  align-self: center;
  border-radius: var(--fe-radius-sm);
}
.fe__main--split .fe__panes > .fe__primary {
  flex: 1 1 50%;
}
.fe__main--split .fe__panes > .fe__primary,
.fe__panes > .fe-split {
  box-shadow: inset 0 2px 0 transparent;
}
.fe__main--split .fe__panes > .fe__primary.fe-pane--focus,
.fe__panes > .fe-split.fe-pane--focus {
  box-shadow: inset 0 2px 0 var(--fe-primary);
}
/* === /gorunum:v5-panestack === */

/* tablo:t1 — a column's row in the menu: the tick and the name on the left,
   the two move buttons on the right, all on ONE line. Without this the
   buttons wrap under the checkbox and their arrows read as stray dashes. */
.fe-colmenu__line {
  display: flex;
  align-items: center;
  gap: 2px;
  padding-inline-end: var(--fe-gap-xs);
}
.fe-colmenu__line .fe-colmenu__row {
  flex: 1 1 auto;
  min-width: 0;
}
.fe-colmenu__nudge {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text-muted);
  font-family: inherit;
  font-size: var(--fe-text-md);
  line-height: 1;
  cursor: pointer;
}
.fe-colmenu__nudge:hover:not(:disabled) {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fe-colmenu__nudge:disabled {
  opacity: 0.3;
  cursor: default;
}
.fe-colmenu__nudge:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}

/* gorunum:v5-panerow — the row with no tabs in it (the `simple` profile, where
   tabs are deliberately not offered but the details toggle still needs a home).
   Without this the trailing controls sit hard against the left edge, because
   the scrolling tab area was the only thing pushing them right. */
.fe-tabs:not(:has(.fe-tabs__scroll)) {
  justify-content: flex-end;
}

/* === pane:p1 — ONE PANE, RENDERED TWICE =================================
 *
 * Owner's ruling, 2026-09-13: *"split pane ile gelen yeni pane aslında
 * yandaki pane ile birebir olması lazım — sen yine yapmıyorsun, bunun için
 * bayağı savaş verdik hatırlatayım sana."* The two halves of a split are now
 * one component (`FilePane.vue`), so almost everything that used to be styled
 * twice is styled once, by the rules the left pane already had:
 *
 *   · both panes carry `.fe__primary`, so the flex column, the 50/50 basis
 *     (`gorunum:v5-panestack`, above) and the focus accent all apply to both;
 *   · both draw `.fe-subhead`, `.fe-selbar-slot`, `.fe-filterbar` and
 *     `.fe__body`, so those four rule-sets serve both halves.
 *
 * ⚠ The whole `.fe-split*` family (mid-file, ~3440-3600) is INERT from this
 * commit: nothing renders `.fe-split`, `.fe-split__crumbs`, `.fe-split__row`
 * or their nine siblings any more. Left in place rather than deleted here
 * because this block is append-only while other agents are live in the file;
 * it is ~160 lines of dead stylesheet and deleting it is a follow-up, not a
 * behaviour change.
 *
 * What genuinely differs is what this block is for: which side the pane is on,
 * and that only the second one can be closed.
 */
.fe-pane--split {
  border-inline-start: 1px solid var(--fe-border);
}
/* The pane's × — the one control the main pane does not have. Sized like the
   view switcher beside it so the address row keeps its 37px height, which is
   what lines the crumbs up with the navigation panel's own head. */
.fe-pane__close {
  flex: 0 0 auto;
}
/* The drop highlight, on the pane BODY. It used to be `.fe-split__body`, i.e.
   the right pane only: dragging onto the left pane's empty space showed
   nothing at all even though the drop worked. One rule, both panes. */
.fe__body.is-dropover {
  outline: 2px dashed var(--fe-primary);
  outline-offset: -4px;
}
/* ⚠ The address row must not collapse when a pane is narrow. `.fe-subhead` is
   a flex row and the breadcrumb inside it will happily shrink to nothing,
   which at 960px with the split on left the right pane's crumbs as a bare
   chevron. The actions cluster keeps its intrinsic width; the crumbs take the
   rest and scroll. */
.fe-pane .fe-subhead__actions {
  flex: 0 0 auto;
}
.fe-pane > .fe-subhead > .fe-breadcrumb {
  min-width: 0;
}
/* === /pane:p1 === */

/* pane:p1 — the filter row already says it "scrolls sideways ON ITSELF at 390px
   rather than widening the explorer" (`overflow-x: auto`, mid-file), and it does
   — visually. What it does NOT do is stop its content being counted in an
   ANCESTOR's scrollable overflow: measured 2026-09-13 at 390px with a real
   folder open, the row's own box was 388px wide with 648px of content, and
   `.fe` reported `scrollWidth` 513 against `clientWidth` 388. `overflow: hidden`
   on any ancestor — the pane, the pane row, the stack, `.fe__main` — changed
   nothing; `contain: paint` on the row itself brought `.fe` back to 388/388.
   ⚠ This matters because it is what the release gate asserts
   (`e2e/shots/driveshell.mjs`, "the explorer's own box does not scroll
   horizontally", measured on `.fe`), and a number that says the shell scrolls
   sideways when it visibly does not is a gate nobody will trust for long.
   ⚠ Safe here specifically because the row's popover is teleported to <body>:
   `contain: paint` clips painting to the padding box (which `overflow-x: auto`
   already did) and makes the row a containing block for fixed descendants —
   and it has none. */
.fe-pane > .fe-filterbar {
  contain: paint;
}

/* ══════════════════════════════════════════════════════════════════════════
   inceleme:r1 — the owner's 2026-09-13 review pass.

   ⚠ APPENDED, and appended as one block, because the body of this file was
   being edited by somebody else the same night. Everything below either
   re-sizes a control the review named or dresses markup this pass added; it
   overrides by ORDER, not by piling on specificity, so each rule stays at the
   weight of the one it is answering and the two read together.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── the view switcher, in proportion to the row it now lives on ────────────
   Owner: "ızgara görüntüleme değiştirme butonları çok büyük, onlara bir el
   atarsın."

   ⚠ The control did not change — its ROW did. It used to sit in the 48px
   header beside other 28px icon buttons, which is where the reference build
   still draws it (measured on filex.blipnova.com: cells 28.3×26.7 among
   28×28 neighbours — in proportion). `gorunum:v2-topbar` moved it onto the
   breadcrumb row, whose other two icon buttons — the path editor and the
   Subfolders chevron — are 24×24, and where the row itself is pinned at
   ~37px. At 32×28 in a 2px pill the cluster measured 102×34: 92% of the row's
   height, and half again the height of the buttons it now sits beside.

   So: the CELL matches the pencil (24px tall), and the pill comes to 28 —
   which is what makes it read as one small segmented control instead of a
   second toolbar.

   ⚠⚠ The HIT box does not shrink with it. `::after` carries the target back
   out to 28×32 per cell, which is larger than the 28×28 the cell had before;
   the three are contiguous (no gap between them), so there is no dead strip
   between neighbours either. Visual weight and pointer target are two
   different measurements and this rule changes only the first. */
.fe-subhead .fe-toolbar__view .fe-btn {
  position: relative;
  height: 24px;
  padding: 0 6px;
}
.fe-subhead .fe-toolbar__view .fe-btn::after {
  content: '';
  position: absolute;
  /* Vertically only: widening it sideways would push each cell's target over
     its neighbour's face, and a segmented control whose cells overlap is one
     that hands clicks to the wrong tab. */
  inset: -4px 0;
}

/* ── Home: a block the filter emptied ──────────────────────────────────────
   surucu:d1-scope. ⚠ Not `.fe-state`: that is the product's "there is nothing
   here" and it carries an illustration and two lines. "Your filter matched
   none of them" is a smaller, temporary fact about a block that is not empty,
   and it must not be dressed as the permanent one. Same box as
   `.fe-home__loading`, which is the other transient line in this view. */
.fe-home__nomatch {
  margin: 0 2px;
  padding: var(--fe-gap) 0;
  font-size: var(--fe-text-sm);
  color: var(--fe-text-muted);
}

/* ── "+ New": the caret half ───────────────────────────────────────────────
   inceleme:r1. Owner asked twice; the reference build's own pill was measured
   for the spec (179×34, radius 8, no border, a 1×16px hairline at 25% white,
   a fixed 32px chevron half) and ours was 171×45 with a 999px radius — a
   different control wearing the same words.

   ⚠ Tokens, not the measured literals: `--fe-h-md` IS 34px and `--fe-radius`
   IS 8px, so the two numbers that mattered are already named here. Writing
   `34px` would have pinned this pill to a height nothing else follows the next
   time the scale moves.

   ⚠ The border goes. A 1px `--fe-primary` border around a `--fe-primary` fill
   is invisible until the hover darkens the fill and leaves the old border
   ringing it — which is what "the whole pill darkens together" is about. The
   height then has to be re-stated, because the padding that used to make it
   was doing so through that border box. */
.fe-sidenav__new {
  gap: 0;
  height: var(--fe-h-md);
  padding: 0;
  border: 0;
  border-radius: var(--fe-radius);
  font-size: var(--fe-text-md);
}
/* The label half takes the slack; the chevron half is fixed, so the caret sits
   on the trailing edge at every panel width instead of drifting with the word. */
.fe-sidenav__new-main {
  flex: 1 1 0%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--fe-gap-sm);
  min-width: 0;
  padding: 0 var(--fe-gap-sm);
}
.fe-sidenav__new-rule {
  flex: 0 0 auto;
  width: 1px;
  height: 16px;
  background: var(--fe-text-on-primary);
  opacity: 0.25;
}
.fe-sidenav__new-caret {
  flex: 0 0 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.fe-sidenav__new-caret .fe-ficon {
  width: 16px;
  height: 16px;
}
/* One ring around the whole pill — it is one control, so it gets one focus
   shape. `box-shadow` rather than `outline`, so the ring follows the 8px
   corners instead of boxing them. */
.fe-sidenav__new:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--fe-primary-soft), var(--fe-shadow-sm);
}
/* The rail is a circle with no label, so the two halves are not drawn at all
   (`v-if="showLabels"`) and the plus re-centres itself. */
.fe-sidenav--rail .fe-sidenav__new-main {
  padding: 0;
}

/* ── the details panel's "held subject" line ───────────────────────────────
   pane:p1. Quieter than the caption above it and on its own line: it is not
   another property of the file, it is the panel saying which file this is
   about. */
.fe-inspector__held {
  margin: 2px 0 0;
  font-size: var(--fe-text-xs);
  line-height: 1.3;
  color: var(--fe-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── the drop overlay's mark ───────────────────────────────────────────────
   inceleme:r1. `.fe__dragover-card .fe-icon` sets `font-size: 32px`, which is
   how you size a TEXT glyph; the `.fe-aicon` that replaced it is a box pinned
   at 16px and would have come out half-size on the one screen whose entire
   content is a glyph and a sentence. Sized explicitly rather than by leaving
   the font-size to do nothing. */
.fe__dragover-card .fe-icon > .fe-aicon {
  width: 32px;
  height: 32px;
  margin: 0 auto;
  color: inherit;
}

/* ══ tablo:t2 — the listing is a TABLE, and a table scrolls ════════════════
   (appended block, agent: list-table / 2026-09-13)

   The model this replaces sized the columns to the pane and, when they stopped
   fitting, dropped the least informative one and told the person "no room" in
   the header menu. The owner rejected it, verbatim: "büyütme küçültme düzgün
   çalışmıyor, tablo gibi durmuyor orası. Çok büyütünce yok oluyor, 'no room'
   diyor — no room demesin, kenara devam eden bir scroll getirsin. Sadece
   sondaki actions üç noktaları en sonda kalacak… ayrıca çok küçük tablolar
   sola yatıyor olsun."

   Four rules, and every declaration below serves one of them:
     1. the table's width is the sum of its columns; when that is more than the
        pane, the pane scrolls sideways — nothing is ever dropped;
     2. the ⋮ column stays pinned to the right edge while the rest scrolls
        under it, and the header scrolls sideways WITH the body while staying
        pinned vertically;
     3. Name is an ordinary column with a width of its own;
     4. a table narrower than its pane sits at the LEFT, with the slack after
        it — it does not stretch and it does not centre.

   ⚠ The `tablo:t1` block further up this file still describes that model in
   its own words — "computed in ListView from the container's own width…
   (lib/viewPrefs.fitColumns)". `fitColumns` does not exist any more; the
   function is `tableLayout` and it fits nothing to the container unless nobody
   has ever sized a column. This block is appended rather than written there
   because another agent is editing the same file tonight.

   ⚠⚠ `.fe-list` owns BOTH scroll axes, and it has to. The vertical scroll used
   to belong to `.fe__body`; a header pinned with `position: sticky` sticks to
   the nearest scrollport, so leaving the two axes in two boxes means either the
   header drifts vertically or the body does not scroll sideways. Keeping the
   overflow inside `.fe-list` is also what stops the grid and the gallery — the
   other two views in that same `.fe__body` — ever seeing a horizontal
   scrollbar of the list's making. */

.fe-list {
  display: block;
  height: 100%;
  overflow: auto;
  /* ⚠⚠ Reserve the vertical scrollbar's room even when it is not showing
     (PR #39, Berk Başarır). Every table in the product (DataTable) lays itself
     out against THIS box's width, and the layout decides whether the box
     needs scrollbars — so without the reserve there is a loop waiting to
     close: the table overflows by a pixel, the horizontal bar takes a row's
     height, the vertical bar comes, the box is a scrollbar narrower, the table
     fits, both bars go, the box is wide again — every frame. Reported from a
     Windows PC (a 12-file folder froze the tab); not reproduced on v0.42.2 or
     v0.43.0, whose widths round to the pixel the box snaps to. But a table
     that is told it has 2px more than it does on alternate width bands loops
     exactly like that in a pane its rows just fit — and stops looping with
     this one line (e2e 139 measures both). The reserve is what makes the
     table's width independent of its own vertical scrollbar, so no future
     width rule can close the loop. Overlay scrollbars (macOS by default,
     phones) take no room, so there it reserves nothing. */
  scrollbar-gutter: stable;
}

/* ⚠⚠ FLEX ROWS, not grid rows. A sticky GRID item is confined to its own grid
   area, which has exactly zero room to move in — so `position: sticky` on a ⋮
   cell of a grid row does nothing at all. A flex item's containing block is
   the flex container's content box, i.e. the whole row, which is the range the
   pinned column needs. */
.fe-list__head,
.fe-list__row,
.fe-list__body {
  display: flex;
}
.fe-list__head,
.fe-list__row {
  flex-direction: row;
  align-items: center;
  /* The width is inline, from `tableLayout.total` — one number for the header
     and every row, so they cannot disagree about where a column starts.
     `min-width: 100%` is rule 4's other half: a narrow table's ROWS still span
     the pane, so hover and selection do not stop halfway across, while the
     cells inside them pack left and leave the slack on the right. */
  min-width: 100%;
}
.fe-list__body {
  flex-direction: column;
}

/* Rule 2, vertical half: the header stays put while the rows scroll under it.
   It needs an opaque ground to do that — it was `transparent`, which was
   invisible only because nothing ever scrolled beneath it. */
.fe-list__head {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--fe-bg);
}

/* Every cell is sized by its inline `flex: 0 0 <width>px`.
   ⚠⚠ `min-width: 0` is not decoration. A flex item's automatic minimum size is
   its MIN-CONTENT size, so one unbreakable filename would push its cell wider
   than the track the header allocated and every column after it would sit a
   few pixels off — the drift that makes a header worse than no header.
   ⚠ This also has to out-specify the two `display: none` blocks further up the
   file (`.fe--narrow …` and `@media (max-width: 640px) …`), which hid Type,
   Owner, Modified and the star on a narrow screen. That was the old model's
   shedding wearing a media query; at 390px the honest answer is now the same
   one as everywhere else — the table is wider than the pane and scrolls. */
.fe-list__head .fe-list__col,
.fe-list__row .fe-list__col,
.fe--narrow .fe-list__head .fe-list__col,
.fe--narrow .fe-list__row .fe-list__col {
  display: flex;
  align-items: center;
  min-width: 0;
}
.fe-list__col--check {
  flex: 0 0 28px;
  justify-content: center;
}
.fe-list__col--star {
  justify-content: center;
}
/* issue #26 — the star's click guard (ListView): stops the click without a
   box of its own, so the star sits where it always did. */
.fe-list__star-hit {
  display: contents;
}
/* Location was never in the muted-value rule above it — it is a value like
   Type and Owner and reads like one. So is the Trash's "time left". */
.fe-list__col--location,
.fe-list__col--remaining {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fe-text-md);
  color: var(--fe-text-muted);
}

/* Rule 2, horizontal half: the ⋮ stays at the right edge while everything else
   scrolls under it.

   ⚠⚠ `right: 0`, and the row's trailing padding moves INTO this cell to pay
   for it. Pinning at `right: var(--fe-gap)` reads better on paper — the pinned
   ⋮ keeps the inset an unpinned one has — and it is wrong: it leaves a 12px
   channel between the pinned cell and the edge of the scrollport, and the
   columns sliding underneath show through it. Measured at 390px, where half a
   letter of the Owner column travelled up the right-hand edge of every row.
   The track is the same total width either way (12px of row padding + a 28px
   cell = a 40px cell with 12px of padding), so the layout arithmetic in
   `tableLayout` is untouched.

   Sticky only ever SHIFTS a box that would otherwise be further right than its
   offset, so when the table is narrower than the pane (rule 4) this cell does
   not move at all: it stays where the columns end and the slack is after it. */
.fe-list__head,
.fe-list__row,
.fe--density-compact .fe-list__head,
.fe--density-compact .fe-list__row {
  padding-inline-end: 0;
}
.fe-list__head .fe-list__col--menu,
.fe-list__row .fe-list__col--menu {
  flex: 0 0 calc(var(--fe-h-sm) + var(--fe-gap));
  padding-inline-end: var(--fe-gap);
  justify-content: center;
  background: var(--fe-bg);
}
/* The pinned cell has to carry the row's own ground, or the columns sliding
   beneath it show through. */
.fe-list__row:hover .fe-list__col--menu {
  background: var(--fe-bg-hover);
}
.fe-list__row.is-selected .fe-list__col--menu,
.fe-list__row.is-droptarget .fe-list__col--menu {
  background: var(--fe-bg-selected);
}
/* Frozen to the end edge — but only while it leaves the columns sliding
   under it somewhere to be (DataTable: `pinMenu` / PIN_MENU_ROOM).

   ⚠⚠ The cell carries an opaque ground, as a sticky cell must, so what it
   covers is GONE, not dimmed. At 104px of a 265px inspector panel it covered
   both columns an app's list was drawing and its signer table read as names
   and buttons with a blank gap between them (v0.43.0). Below the threshold
   nothing is pinned and the row scrolls as one piece — the same answer the
   frozen lead gives, for the same reason. */
.fe-list.is-pin-menu .fe-list__head .fe-list__col--menu,
.fe-list.is-pin-menu .fe-list__row .fe-list__col--menu {
  position: sticky;
  inset-inline-end: 0;
  z-index: 1;
}

/* The edge, only while there is something scrolled underneath. A permanent
   divider would claim the column is floating when it is simply the last cell
   of a table that fits. `box-shadow` rather than a border so turning it on
   moves nothing. */
.fe-list.is-pin-menu.is-scrolled-x .fe-list__col--menu {
  /* On the cell's START side — the side the rows scroll under (left in LTR,
     right in RTL, where the ⋮ is pinned to the left edge). */
  box-shadow: calc(-1px * var(--filex-dir-x, 1)) 0 0 0 var(--fe-border);
}

/* ══ THE FROZEN LEAD: the tick and Name ═══════════════════════════════
   (owner, 2026-09-20: "yatay kaydırmada ad solda, işlemler sağda kalsın;
   ikisinin de kendi gölgesi olsun")

   The ⋮ has ridden the right edge since this became a table; the left edge was
   never frozen, so a listing scrolled sideways to reach Size or Modified lost
   the one column that says WHICH FILE each row is, and read as a wall of
   `PDF document · Sep 20, 2026`. Name is the row's handle — the tick, the type
   tile, the click target — so the tick freezes with it rather than sliding out
   from under its own checkboxes.

   The same three parts as the ⋮, mirrored:

   1. The padding moves INTO the cell. `sticky; left: 0` pulls the cell to the
      scrollport's edge, so a 12px inset living on the ROW would be scrolled
      away and the ticks would sit against the glass. The tick's track is
      `28 + 12` with the 12 as its own padding — the same total width, so
      `tableLayout`'s arithmetic is untouched (exactly the trade the ⋮ makes).

   2. Opaque grounds, hover and selection included, or the columns sliding
      underneath show through the frozen cells.

   3. An edge only while something is actually scrolled under it.

   ⚠⚠ GATED ON `.is-pin-lead`, which ListView computes. Name is the person's to
   resize, and a sticky cell WIDER than the pane covers the pane: 900px of Name
   frozen in a 706px list paints over every column it was frozen to keep
   company. The class is set only while the frozen lead leaves at least 160px
   of pane for the columns that slide under it; below that nothing is pinned
   and the table scrolls as one piece, which is the honest answer.

   ⚠ `--fe-list-lead` (Name's offset) is inline on `.fe-list`, because it is
   the tick's track plus the flex gap and only the component knows the first. */
/* ⚠ The loading ghosts are deliberately NOT in this list: they have no cells
   of their own to carry the padding, so taking it off the row would start them
   12px left of the rows they stand in for and the swap to real rows would
   jump. They keep the ordinary inset and line up either way. */
.fe-list.is-pin-lead .fe-list__head,
.fe-list.is-pin-lead .fe-list__row,
.fe--density-compact .fe-list.is-pin-lead .fe-list__head,
.fe--density-compact .fe-list.is-pin-lead .fe-list__row {
  padding-inline-start: 0;
}
.fe-list.is-pin-lead .fe-list__col--check {
  position: sticky;
  inset-inline-start: 0;
  z-index: 2;
  flex: 0 0 calc(var(--fe-h-sm) + var(--fe-gap));
  padding-inline-start: var(--fe-gap);
  background: var(--fe-bg);
}
.fe-list.is-pin-lead .fe-list__col--lead {
  position: sticky;
  inset-inline-start: var(--fe-list-lead, calc(var(--fe-h-sm) + var(--fe-gap) + var(--fe-gap-sm)));
  z-index: 2;
  background: var(--fe-bg);
}
.fe-list.is-pin-lead .fe-list__row:hover .fe-list__col--check,
.fe-list.is-pin-lead .fe-list__row:hover .fe-list__col--lead {
  background: var(--fe-bg-hover);
}
.fe-list.is-pin-lead .fe-list__row.is-selected .fe-list__col--check,
.fe-list.is-pin-lead .fe-list__row.is-selected .fe-list__col--lead,
.fe-list.is-pin-lead .fe-list__row.is-droptarget .fe-list__col--check,
.fe-list.is-pin-lead .fe-list__row.is-droptarget .fe-list__col--lead {
  background: var(--fe-bg-selected);
}
/* Its own edge, mirroring the ⋮'s — drawn on Name's trailing side, and only
   while there is something scrolled underneath it. */
.fe-list.is-pin-lead.is-scrolled-x .fe-list__col--lead {
  /* Name's END side, whichever physical side that is. */
  box-shadow: calc(1px * var(--filex-dir-x, 1)) 0 0 0 var(--fe-border);
}
/* ⚠ The header's frozen cells sit ABOVE the rows' (the head is drawn first and
   a row's sticky cell would otherwise paint over it while the head is the
   thing being scrolled past). Same reason the header's ⋮ outranks a row's. */
.fe-list.is-pin-lead .fe-list__head .fe-list__col--check,
.fe-list.is-pin-lead .fe-list__head .fe-list__col--lead {
  z-index: 3;
}

/* ══ THE ONE TABLE — what DataTable adds over the explorer's list ═════════
   (packages/core/src/components/DataTable.vue)

   The rules above were written for the explorer's listing, and since
   2026-09-21 they are the stylesheet of EVERY table in the product: the
   explorer's list renders through DataTable, and so does every admin page,
   the connection panels and an app's `list` node. The owner: "Artık explore
   tablomuz bizim her yerde kullanacağımız tablo yapısıdır." What follows is
   only what a table that is not a file listing needs on top — the frozen lead
   is `.fe-list__col--lead` (the explorer's Name carries both classes), a table
   with no tick column freezes its lead at the glass, cells that are plain
   text, a card for the admin pages, and the pager.

   ⚠ Every value is a `--fe-*` token. A Tailwind colour cannot follow the
   palette the person picked, and the panel following it was half of the
   complaint that started the admin-table work ("admin panel seçili renk
   paletinden etkilenmiyor, etkilenmeli"). */

/* The explorer's listing is not framed: the wrapper generates no box, so its
   pane lays `.fe-list` out exactly as it did before there was a wrapper. */
.fe-table--bare {
  display: contents;
}
/* An admin page's table is a card: the toolbar, the table and the pager in one
   box. `--tbl-bg` is the ground the frozen cells paint with, so a table on a
   raised panel (the connection panels) can say so once. */
.fe-table--framed {
  display: block;
  min-width: 0;
  background: var(--tbl-bg, var(--fe-bg));
  border: 1px solid var(--fe-border-soft);
  border-radius: var(--fe-radius-lg);
  overflow: hidden;
}
/* ⚠ Not the explorer's `height: 100%`: a card on a page is as tall as its rows,
   and a percentage of an auto-height parent is nothing. It still owns the
   sideways scroll. */
.fe-table--framed .fe-list {
  height: auto;
  overflow-x: auto;
  overflow-y: hidden;
}
.fe-table--framed .fe-list__head,
.fe-table--framed .fe-list__head .fe-list__col--menu,
.fe-table--framed .fe-list__row .fe-list__col--menu,
.fe-table--framed .fe-list.is-pin-lead .fe-list__col--lead,
.fe-table--framed .fe-list.is-pin-lead .fe-list__col--check {
  background: var(--tbl-bg, var(--fe-bg));
}
.fe-table--framed .fe-list__row:hover .fe-list__col--menu,
.fe-table--framed .fe-list.is-pin-lead .fe-list__row:hover .fe-list__col--lead,
.fe-table--framed .fe-list.is-pin-lead .fe-list__row:hover .fe-list__col--check {
  background: var(--fe-bg-hover);
}
.fe-table--framed .fe-list.is-pin-lead .fe-list__row.is-selected .fe-list__col--lead,
.fe-table--framed .fe-list__row.is-selected .fe-list__col--menu {
  background: var(--fe-bg-selected);
}
/* The last row's hairline would double the card's own border. */
.fe-table--framed .fe-list__row:last-child {
  border-bottom: 0;
}
.fe-table--framed .tbl-bar {
  border-bottom: 1px solid var(--fe-border-soft);
}

/* With no tick in front of it, a frozen lead sits at the glass and takes the
   row's leading padding INTO itself (DataTable adds the 12px to its track) —
   the same trade the ⋮ makes on the right, so the table's arithmetic is
   untouched. */
.fe-list.is-pin-lead.fe-list--nocheck .fe-list__col--lead {
  inset-inline-start: 0;
  padding-inline-start: var(--fe-gap);
}

/* A plain cell: one line, cut with an ellipsis, the whole value in its title.
   ⚠ `min-width: 0` on the text too, or one long unbroken value would push its
   cell past the track the header allocated. */
/* ⚠ `:where()`, zero specificity: the explorer's own cells carry this class AND
   `fe-list__col--type` / `--owner` / `--mod` / `--size`, whose muted colour is
   written earlier in this file at one class of weight. A plain `.fe-list__cell`
   here would out-rank them by source order and turn every file's Type and
   Size into body-coloured text. */
:where(.fe-list__cell) {
  font-size: var(--fe-text-md);
  color: var(--fe-text);
  overflow: hidden;
}
.fe-list__cell.is-right {
  justify-content: flex-end;
  text-align: end;
}
.fe-list__cell.is-center {
  justify-content: center;
  text-align: center;
}
.fe-list__cell-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-list__col--lead .fe-list__cell-text {
  font-weight: 500;
}
/* A labelled Actions control is wider than the explorer's ⋮ and reads best
   flush with the row's right edge; the header's column ⋮ lines up with it. */
.fe-list__head .fe-list__col--menu.is-wide,
.fe-list__row .fe-list__col--menu.is-wide {
  justify-content: flex-end;
}
.fe-list__row.is-clickable {
  cursor: pointer;
}
/* A row the page wants to quieten (a notification already read, a key that
   is disabled). Opacity rather than a colour, so it reads the same under
   every palette. */
.fe-list__row.is-muted {
  opacity: 0.65;
}
/* Whatever a page puts in a cell may shrink below its content, or one long
   value would push its cell past the track the header allocated. Zero
   specificity, so a component's own rule still wins. */
:where(.fe-list__cell) > * {
  min-width: 0;
}
/* The admin pages' "do not let free text stretch the table" helper: inside a
   cell of fixed width the cell IS the clamp, so it only has to fill it. */
.fe-list__cell .tbl-clamp {
  max-width: 100%;
}
/* A cell whose slot puts secondary text (`.tbl-sub`) straight under its main
   value: stack them. A cell lays its children out in a ROW, so without this
   the value and the line meant to sit under it were drawn side by side — the
   Panel's Recent activity read "Profil: güncellendiProfil" and Duplicates put
   a file's path beside its name (release-candidate sweep, 2026-09-22). */
.fe-list__cell:has(> .tbl-sub) {
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
}
.fe-list__empty--loading {
  color: var(--fe-text-muted);
}
.tbl-pager {
  display: flex;
  align-items: center;
  gap: var(--fe-gap-xs);
}
.tbl-pager__at {
  padding: 0 var(--fe-gap-xs);
}

/* ==================================================================
   THE ONE TABLE'S CELL VOCABULARY — and what is left of "the admin table".

   Until 2026-09-21 this block was a second table: `web/src/components/ui/
   Table.vue` drew a real <table class="tbl"> with these rules, frozen edges
   of its own (`.tbl-lead`, `.tbl-actions`, `.tbl-scroll`) and a sort button
   of its own. It IMITATED the explorer's list and never got what lives in
   the explorer's code — resizing, the column menu, persistence. The owner:
   "Admin tabloları hâlâ explore tablolarıyla AYNI KODDA DEĞİL … tablo
   sütunları düzenlenebilir değil, büyütme küçültme yok, sıralama yok." Every
   table is DataTable now (components/DataTable.vue, styled by the
   `fe-list` rules above), and those table rules are deleted rather than kept
   "just in case": a stylesheet for a table nobody draws is an invitation to
   draw one.

   What remains is what a CELL or the card around a table uses, whichever
   page draws it:
     · `.tbl-sub` / `.tbl-clamp` / `.tbl-mono` — secondary text, a
       free-text value that must not stretch its column, a technical value;
     · `.tbl-rowactions*` — the row's ONE labelled Actions control
       (components/RowActions.vue), the admin counterpart of the ⋮;
     · `.tbl-bar` / `.tbl-foot` / `.tbl-page` — the toolbar above a framed
       table and the pager below it (DataTable draws both).

   ⚠ EVERY COLOUR IS A `--fe-*` TOKEN: "admin panel seçili renk paletinden
   etkilenmiyor, etkilenmeli" was half of the complaint that started this.
   ================================================================== */

/* The raised box a connection panel draws is not the page's ground, and the
   table's frozen cells paint with `--tbl-bg` — so the token moves, not the
   rules. */
.fe-s3keys .fe-table--framed {
  --tbl-bg: var(--fe-bg-elev);
}

/* Secondary text inside a cell — a path under a name, a timestamp under a
   badge. One class rather than a `text-xs text-zinc-500` in twelve files. */
.tbl-sub {
  display: block;
  margin-top: 2px;
  font-size: var(--fe-text-xs);
  color: var(--fe-text-muted);
}
/* A cell that must not be allowed to stretch the table: `min-width:
   max-content` means a free-text column otherwise sets the table's width and
   pushes every other column off the right-hand edge. */
.tbl-clamp {
  display: block;
  max-width: 28rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tbl-mono {
  font-family: var(--fe-font-mono);
  font-size: var(--fe-text-xs);
}

/* The row's ONE control, and the label the owner asked for. The explorer's
   handle is the ⋮ glyph; the admin panel's says what it is, because an
   admin row's verbs (revoke a token, reset a password, purge a version) are
   not guessable from a glyph. Sized to the row so a table of them does not
   grow taller than one without. */
.tbl-rowactions {
  display: inline-flex;
  justify-content: flex-end;
  width: 100%;
}
.tbl-rowactions__btn {
  display: inline-flex;
  align-items: center;
  gap: var(--fe-gap-xs);
  height: var(--fe-h-sm);
  padding: 0 var(--fe-gap-sm);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text);
  font: inherit;
  font-size: var(--fe-text-md);
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
}
.tbl-rowactions__btn:hover:not(:disabled) {
  background: var(--fe-bg-hover);
  border-color: var(--fe-border-strong);
}
.tbl-rowactions__btn:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}
.tbl-rowactions__btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.tbl-rowactions__caret {
  font-size: 10px;
  color: var(--fe-text-muted);
}


/* The bar above the table (search, filters) and the one below it (the pager).
   Part of the same card, so they carry the card's ground and its hairlines. */
.tbl-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--fe-gap-sm);
  padding: var(--fe-gap-sm) var(--fe-gap);
  border-bottom: 1px solid var(--fe-border-soft);
}
.tbl-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--fe-gap-sm);
  padding: var(--fe-gap-sm) var(--fe-gap);
  border-top: 1px solid var(--fe-border-soft);
  font-size: var(--fe-text-md);
  color: var(--fe-text-muted);
}
.tbl-page {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--fe-h-sm);
  height: var(--fe-h-sm);
  padding: 0 var(--fe-gap-sm);
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.tbl-page:hover:not(:disabled) {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.tbl-page:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}
.tbl-page:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* The date headings travel with the horizontal scroll rather than sliding off
   the left edge — they are `max-content` wide so sticky has somewhere to move
   them to (a full-width block has none). */
.fe-list__group {
  position: sticky;
  inset-inline-start: 0;
  width: max-content;
  max-width: 100%;
}

/* Name's handle sits on its trailing edge like every other column's. The cell
   is `display: flex` with the sort button as its first child, so without this
   the absolutely positioned handle would have no positioned ancestor inside
   the header row. */
.fe-list__head .fe-list__col--name {
  position: relative;
  overflow: visible;
}
/* === /tablo:t2 === */


/* === ikon:emoji =======================================================
 * The boxes that used to hold an emoji and now hold a drawing.
 *
 * Sizing only — every mark here comes from `lib/actionIcons.ts` or
 * `lib/fileIcons.ts` and paints on `currentColor`, so the surface it sits in
 * keeps deciding its colour. Two shapes need a box: `.fe-aicon` is a fixed
 * 16px (right for a menu row, far too small for a 48px fallback screen), and
 * `.fe-ftile` is `1em` (so its box is the `font-size` the slot already had —
 * which is why most of the rules below only have to switch a text span into
 * a flex one).
 * ==================================================================== */

/* --- the command palette's 20px column ------------------------------- */
.fe-cmdp__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* The em the file tiles size themselves from. Slightly over the 16px an
     action icon draws at, because a tile is the full box and a stroked glyph
     is not — at a shared 20 the tiles shouted over the commands. */
  font-size: 18px;
}
.fe-cmdp__icon .fe-aicon {
  width: 18px;
  height: 18px;
}
.fe-cmdp__del .fe-aicon {
  width: 15px;
  height: 15px;
}

/* --- the viewer / preview fallback screen ---------------------------- */
/* Both slots were `font-size: 48px` text boxes. A tile obeys that already; an
   action icon has to be told, and both need centring once they are elements
   rather than glyphs on a text baseline. */
.fe-preview__fallback-icon,
.filex-viewer-fallback__icon {
  display: flex;
  align-items: center;
  justify-content: center;
}
.fe-preview__fallback-icon .fe-aicon,
.filex-viewer-fallback__icon .fe-aicon {
  width: 44px;
  height: 44px;
}
/* "Still loading" cannot be said by a still picture: the open ring turns.
   `fe-spin` is the keyframe the initial-load spinner at the top of this file
   already uses, so there is one rotation in the package. */
.fe-preview__fallback-icon--spin .fe-aicon,
.filex-viewer-fallback__icon--spin .fe-aicon {
  animation: fe-spin 0.9s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .fe-preview__fallback-icon--spin .fe-aicon,
  .filex-viewer-fallback__icon--spin .fe-aicon {
    animation-duration: 2.4s;
  }
}
/* The one state that is an ERROR rather than a kind of file keeps saying so
   in colour, which the emoji it replaces could not do (a colour emoji paints
   its own palette and ignores the row it is in). */
.fe-preview__fallback-icon--alert .fe-aicon {
  color: var(--fe-danger);
}
/* Inside the archive listing the tile sits on a text baseline in a 13px
   table, so it needs the alignment a glyph got for free. */
.filex-viewer-archive__icon {
  display: inline-flex;
  align-items: center;
  vertical-align: -0.18em;
}

/* --- panel closers, shortcut rows, the encrypted-folder link --------- */
/* `.fe-conn__btn` is the connections panel's own ✕; the other two closers
   are styled in the SFCs that own them, because their rules live there and a
   half of a button's box in each file is how a rule ends up losing to the one
   beside it. */
.fe-conn__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* The "create an encrypted folder" link is a bare text button — it has no
   `.fe-btn` box, so it gets the icon gap here. */
.fe-e2e-optlink {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* The unlocked-folder strip's padlock. The rule is here rather than in the
   hunk that swaps the glyph, so that hunk stays one line. */
.fe-e2e-strip__icon {
  display: inline-flex;
  align-items: center;
}
.fe-e2e-strip__icon .fe-aicon {
  width: 14px;
  height: 14px;
  color: currentColor;
}
.fe-e2e-optlink .fe-aicon {
  width: 14px;
  height: 14px;
  color: currentColor;
}
.fe-shortset__btn .fe-aicon,
.fe-shortset__reset-all .fe-aicon {
  color: currentColor;
}
/* === /ikon:emoji === */

/* === tur:sweep — list cells ELLIPSISE instead of being guillotined ======
   `.fe-list__col--type/--owner/--mod/--size` ask for `text-overflow: ellipsis`
   but also set `display: flex`. On a flex container that property does
   NOTHING: the cell's text is a bare text node, so it becomes an ANONYMOUS
   flex item, and `text-overflow` only ever applies to a block container's own
   inline content. The value was therefore sliced mid-glyph with no ellipsis —
   measured 2026-09-13 at 960px, where "Hesap tablosu" painted as "Hesap
   tablos" + half a "u" (scrollWidth 82 / clientWidth 79) and "Tasarım
   dosyası" lost its last four characters, in both languages.

   `.fe-list__col--location` is the control: same markup, same ellipsis
   request, no `display: flex` — and it truncates correctly. So these four
   simply stop being flex containers. Vertical centring is untouched: the row
   is `display: grid` with `align-items: center`, which centres the cell box
   itself, and every one of these cells holds text and nothing else
   (ListView.vue renders `<div :class="colClass(id)">{{ cellText(id, n) }}`).

   ⚠ Scoped to `.fe-list__row` on purpose. The HEADER reuses the same class
   names, but its cells contain the sort button and the absolutely-positioned
   resize handle, and it already has its own `overflow: visible` carve-out.

   ⚠⚠ The doubled class (`.fe-list__col.fe-list__col--type`) is load-bearing,
   not style. The rule that gives every cell `display: flex` is written
   `.fe--narrow .fe-list__row .fe-list__col` — weight 0,3,0 — so a plain
   `.fe-list__row .fe-list__col--type` (0,2,0) loses to it and the fix silently
   did nothing below 640px, where the table scrolls sideways and long values
   are MORE likely to be cut. Measured at 390px: "Tasarım dosyası" still
   painted as "Tasarım do" + half an "s". Matching 0,3,0 and sitting later in
   the file wins in both modes. */
.fe-list__row .fe-list__col.fe-list__col--type,
.fe-list__row .fe-list__col.fe-list__col--owner,
.fe-list__row .fe-list__col.fe-list__col--mod,
.fe-list__row .fe-list__col.fe-list__col--size {
  display: block;
}
/* === /tur:sweep === */

/* === agent-fix2 — release screenshot pass fixes, 2026-09-14 ===
 *
 * 1. Rendered markdown keeps its list markers.
 *    `.fe-preview__md ul/ol` set an indent and nothing else, and the host
 *    page's CSS reset decides the rest: under the admin SPA's Tailwind
 *    preflight (`ol, ul { list-style: none }`) a bulleted list rendered as
 *    indented loose lines and a NUMBERED list lost its numbers — measured in
 *    the markdown viewer, `list-style-type: none` on every <li>. The shadow-
 *    DOM embed has no preflight, so the same file read correctly there. The
 *    type is stated here so the viewer does not depend on who hosts it. The
 *    notebook viewer's markdown cells are the same markdown and get the same.
 */
.fe-preview__md ul,
.filex-viewer-ipynb__md ul {
  list-style: disc outside;
}
.fe-preview__md ul ul,
.filex-viewer-ipynb__md ul ul {
  list-style-type: circle;
}
.fe-preview__md ol,
.filex-viewer-ipynb__md ol {
  list-style: decimal outside;
}
.filex-viewer-ipynb__md ul,
.filex-viewer-ipynb__md ol {
  padding-inline-start: 2em;
}

/* 2. A form field with a visible name (EncryptedFolderModal, NewFolderModal).
 *    A placeholder disappears as soon as the field has a value, and on a
 *    password field the value is dots — so the name goes above the box. */
.fe-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.fe-field__label {
  font-size: var(--fe-text-sm);
  font-weight: 500;
  color: var(--fe-text-muted);
}
.fe-checkbox {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
}
.fe-checkbox input[type='checkbox'] {
  flex: 0 0 auto;
  margin: 0;
}
/* === /agent-fix2 === */

/* === App plugins — a plugin's declarative screen (components/plugin/) ===
 * Drawn from data with our own elements; the classes here are the whole
 * vocabulary a surface can reach, which is the point. */
.fe-surface {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.fe-surface__text {
  margin: 0;
  font-size: var(--fe-text-sm);
  line-height: 1.5;
  white-space: pre-wrap;
}
.fe-surface__text--heading {
  font-size: var(--fe-text-md, 15px);
  font-weight: 600;
}
.fe-surface__text--muted {
  color: var(--fe-text-muted);
}
.fe-surface__text--danger {
  color: var(--fe-danger);
}
.fe-surface__text--info {
  color: var(--fe-primary);
}
.fe-surface__divider {
  border: 0;
  border-top: 1px solid var(--fe-border);
  margin: 4px 0;
}
.fe-surface__row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: flex-start;
}
.fe-surface__row > .fe-surface {
  flex-direction: row;
  flex-wrap: wrap;
}
.fe-surface__unknown {
  margin: 0;
  padding: 8px 10px;
  border: 1px dashed var(--fe-border-strong);
  border-radius: var(--fe-radius-sm);
  color: var(--fe-text-muted);
  font-size: 12px;
}
.fe-surface__error {
  color: var(--fe-danger);
  font-size: 12px;
  margin: 0;
}
.fe-plugin-view {
  min-width: 0;
}
/* === /App plugins === */

/* === App plugins — M2 surface components (components/plugin/nodes/*) === */
.fe-surface__form {
  min-width: 0;
}
.fe-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
}
.fe-steps__item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fe-text-sm);
  color: var(--fe-text-muted);
}
.fe-steps__item.is-active {
  color: var(--fe-text);
  font-weight: 600;
}
.fe-steps__item.is-done {
  color: var(--fe-text);
}
.fe-steps__marker {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 1px solid var(--fe-border-strong);
  font-size: 12px;
  font-weight: 600;
  flex: 0 0 auto;
}
.fe-steps__marker .fe-ficon {
  width: 14px;
  height: 14px;
}
.fe-steps__item.is-active .fe-steps__marker {
  border-color: var(--fe-primary);
  background: var(--fe-primary);
  /* The palette's ink for its primary, as on every primary button. White
     measured 3.16:1 on the stock dark primary and 2.3:1 on an operator's pale
     pink one (#57), where the share dialog beside it used this token. */
  color: var(--fe-text-on-primary);
}
.fe-steps__item.is-done .fe-steps__marker {
  border-color: var(--fe-primary);
  color: var(--fe-primary);
}
/* The plugin surfaces' `list` node draws `.tbl` — the one admin table — so
   everything `.fe-slist__table` used to say (12px/600 headers, 6px 8px cells,
   `--fe-border`, a `text-align: right` actions cell that was never actually
   pinned) is deleted rather than tuned. What is left is the node's own box:
   it must be allowed to be narrower than its content so the table inside can
   scroll instead of squeezing a surface into a column of single words. */
.fe-slist {
  min-width: 0;
}

.fe-sprogress {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.fe-sprogress__head {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  font-size: var(--fe-text-sm);
}
.fe-sprogress__pct {
  color: var(--fe-text-muted);
  font-variant-numeric: tabular-nums;
}
.fe-sprogress__track {
  position: relative;
  height: 6px;
  border-radius: 999px;
  background: var(--fe-border);
  overflow: hidden;
}
.fe-sprogress__bar {
  height: 100%;
  background: var(--fe-primary);
  border-radius: 999px;
  transition: width 0.2s ease;
}
.fe-sprogress__track.is-indeterminate .fe-sprogress__bar {
  position: absolute;
  width: 35%;
  animation: fe-sprogress-slide 1.2s ease-in-out infinite;
}
@keyframes fe-sprogress-slide {
  0% {
    inset-inline-start: -35%;
  }
  100% {
    inset-inline-start: 100%;
  }
}
.fe-speople {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.fe-speople__chips {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.fe-speople__chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding-block: 3px; padding-inline: 3px 6px;
  border-radius: 999px;
  border: 1px solid var(--fe-border-strong);
  background: var(--fe-bg-elev);
  font-size: 12.5px;
  max-width: 100%;
}
.fe-speople__av {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--fe-primary);
  color: var(--fe-text-on-primary);
  font-size: 11px;
  font-weight: 600;
  flex: 0 0 auto;
}
.fe-speople__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-speople__remove {
  border: 0;
  background: none;
  color: var(--fe-text-muted);
  cursor: pointer;
  padding: 0;
  display: inline-flex;
  align-items: center;
}
.fe-speople__remove .fe-ficon {
  width: 14px;
  height: 14px;
}
.fe-speople__remove:hover {
  color: var(--fe-danger);
}
.fe-speople__addwrap {
  position: relative;
  display: flex;
  gap: 6px;
  align-items: stretch;
}
.fe-speople__input {
  flex: 1 1 auto;
}
.fe-speople__suggest {
  top: 100%;
  inset-inline-start: 0;
  inset-inline-end: 0;
}
.fe-speople.is-invalid .fe-speople__input {
  border-color: var(--fe-danger);
}
.fe-spin {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.fe-spin__input {
  width: auto;
  max-width: calc(var(--spin-len, 6) * 1.6ch + 24px);
  letter-spacing: 0.5ch;
  font-size: 18px;
  text-align: center;
}
.fe-spin__input.is-invalid {
  border-color: var(--fe-danger);
}
.fe-sfile {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  min-width: 0;
}
.fe-sfile__value {
  flex: 1 1 160px;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fe-text-sm);
  padding: 6px 10px;
  border: 1px solid var(--fe-border-strong);
  border-radius: var(--fe-radius);
  background: var(--fe-bg);
}
.fe-sfile__value--empty {
  color: var(--fe-text-muted);
}
.fe-sfile.is-invalid .fe-sfile__value {
  border-color: var(--fe-danger);
}
.fe-destpick__row.is-file .fe-destpick__rowname {
  color: var(--fe-text);
}
.fe-destpick__row.is-picked {
  background: var(--fe-primary-soft, rgba(37, 99, 235, 0.12));
  outline: 1px solid var(--fe-primary);
}
.fe-spreview {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.fe-spreview__media {
  max-width: 100%;
  max-height: 320px;
  border-radius: var(--fe-radius);
  object-fit: contain;
  background: var(--fe-bg-elev);
}
.fe-spreview__audio {
  width: 100%;
}
.fe-spreview__tile {
  display: inline-flex;
  width: 48px;
  height: 48px;
}
.fe-spreview__tile > * {
  width: 100%;
  height: 100%;
}
.fe-spreview__name {
  font-size: 12px;
  color: var(--fe-text-muted);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* inspector placement */
.fe-pinsp__heading {
  margin: 0;
}
.fe-pinsp__toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-align: start;
}
.fe-pinsp__chev {
  width: 14px;
  height: 14px;
  transition: transform 0.15s ease;
}
.fe-pinsp__chev.is-open {
  transform: rotate(90deg);
}
.fe-pinsp__body {
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.fe-pinsp__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
/* home placement — the "Apps" rows */
.fe-sidenav__appicon {
  display: inline-flex;
  flex: 0 0 auto;
}
.fe-sidenav__appicon .fe-aicon,
.fe-sidenav__appicon svg {
  width: 18px;
  height: 18px;
}
/* === /App plugins M2 === */

/* === App plugins — M3 sign track (signature-pad, pdf-fields) + the public page === */
.fe-ssig {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.fe-ssig__modes {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.fe-ssig__padwrap,
.fe-ssig__typewrap,
.fe-ssig__uploadwrap {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}
.fe-ssig__canvas,
.fe-ssig__preview {
  display: block;
  width: 100%;
  border: 1px dashed var(--fe-border-strong);
  border-radius: var(--fe-radius);
  /* Paper, on both palettes: the ink is black and lands on a document. */
  background: #fff;
  touch-action: none;
  cursor: crosshair;
}
.fe-ssig__preview {
  cursor: default;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  min-height: 48px;
}
.fe-ssig__img {
  max-width: 100%;
  max-height: 200px;
  display: block;
}
.fe-ssig.is-invalid .fe-ssig__canvas,
.fe-ssig.is-invalid .fe-ssig__preview {
  border-color: var(--fe-danger);
}
.fe-ssig.is-disabled .fe-ssig__canvas {
  cursor: not-allowed;
  opacity: 0.6;
}
.fe-ssig__hint {
  margin: 0;
  font-size: 12px;
  color: var(--fe-text-muted);
}
.fe-ssig__pick {
  position: relative;
  overflow: hidden;
  align-self: flex-start;
}
.fe-ssig__file {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}
.fe-ssig__foot {
  display: flex;
  align-items: center;
  gap: 10px;
}
.fe-ssig__signed {
  font-size: 12px;
  color: var(--fe-ok);
}

.fe-spdf {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.fe-spdf__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
}
.fe-spdf__palette,
.fe-spdf__zoom,
.fe-spdf__props {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.fe-spdf__zoomval {
  min-width: 52px;
  font-variant-numeric: tabular-nums;
}
.fe-spdf__hint {
  margin: 0;
}
/* ⚠⚠ The `place` step's one strip (component: `fe-spdf__strip`). Its height
 * is CONSTANT whatever it says — a hint, the selected box's controls, or an
 * idle line — because in a `page` layout the document is fitted to the pane
 * below it, and a strip that came and went re-fitted and redrew the whole
 * page on every tap (measured 2026-09-21: 383px → 349px wide mid-gesture).
 * One line of controls, scrolled sideways on a phone rather than wrapped to
 * a second line, for the same reason. */
.fe-spdf__strip {
  min-height: 44px;
  display: flex;
  align-items: center;
  min-width: 0;
}
.fe-spdf__strip > .fe-spdf__props {
  flex: 1 1 auto;
  flex-wrap: nowrap;
  overflow-x: auto;
  white-space: nowrap;
  min-width: 0;
}
.fe-spdf__strip > .fe-spdf__hint {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 18px;
}
.fe-spdf__props {
  padding: 6px 8px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg-elev);
  font-size: var(--fe-text-sm);
}
.fe-spdf__propname {
  font-weight: 600;
}
.fe-spdf__prop {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.fe-spdf__select {
  width: auto;
  padding: 2px 6px;
  height: 26px;
}
.fe-spdf__num {
  width: 72px;
  padding: 2px 6px;
  height: 26px;
}
/* The field's own name (v3 §3.1). Wide enough for "Yetkili imzası" and no
   wider: the property row is a strip over the document, not a form. */
.fe-spdf__name {
  width: 160px;
  padding: 2px 6px;
  height: 26px;
}
/* The choice rows in the property strip are small — they sit beside the
   document rather than on a form of their own. */
.fe-spdf__prop .fe-choice {
  gap: 4px;
}
.fe-spdf__prop .fe-choice__btn {
  padding: 3px 8px;
  font-size: var(--fe-text-xs);
}
.fe-spdf__prop .fe-choice__label {
  font-weight: 500;
}
/* What a text box will accept, or what is wrong with what is in it. ⚠ Drawn
   OUTSIDE the box: a field is a few millimetres of a page, and a hint inside
   it would cover the value it is about. */
.fe-spdf__rule {
  position: absolute;
  top: 100%;
  inset-inline-start: 0;
  margin-top: 2px;
  padding: 1px 4px;
  max-width: 240px;
  border-radius: var(--fe-radius-sm);
  background: var(--fe-bg-elev);
  border: 1px solid var(--fe-border);
  color: var(--fe-text-muted);
  font-size: 10px;
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
  z-index: 2;
}
.fe-spdf__rule.is-bad {
  color: var(--fe-danger);
  border-color: var(--fe-danger);
}
.fe-spdf__scroll {
  overflow: auto;
  max-height: 70vh;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg-hover);
  padding: 12px;
  min-width: 0;
}

/* ⚠⚠ v3 §3.2 — `page` placement: the node owns the screen.
 *
 * The complaint that started this round was a signature page you had to
 * scroll in two directions to read: the document was fitted to the column's
 * WIDTH, so a portrait page in a landscape window ran off the bottom, and
 * the boxes were found by hunting. Here the node is a column of its own
 * height (`--spdf-h`, handed down by the frame), the toolbar and the
 * property strip keep their natural size, and the document takes the rest —
 * with the drawn width fitted to that remainder (component: `fitWidth`).
 *
 * `min-height: 0` is not decoration: without it a flex child refuses to
 * shrink below its content and the scroll area grows past the box instead
 * of scrolling inside it. */
/* ── define: the boxes as cards, no document ──────────────────────────
 *
 * The step that asks WHAT is being asked of whom. Each card carries the
 * signer's colour down its edge, so the same box is recognisable on the
 * next step when it is dropped on the page. */
.fe-spdf.is-define {
  gap: 12px;
}
.fe-spdf__define {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.fe-spdf__card {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding-block: 10px; padding-inline: 14px 12px;
  border: 1px solid var(--fe-border);
  border-inline-start: 3px solid var(--spdf-color, var(--fe-primary));
  border-radius: var(--fe-radius-md);
  background: var(--fe-bg-elev);
}
.fe-spdf__card .fe-spdf__props {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  /* Row by row rather than one long line: a card is read, not scanned. */
  row-gap: 8px;
  column-gap: 10px;
}
.fe-spdf__card .fe-spdf__propname {
  flex: 0 0 auto;
  min-width: 84px;
  font-weight: 600;
}
.fe-spdf__card .fe-spdf__name {
  min-width: 180px;
}
/* The face row starts a line of its own — five buttons in the middle of a
   sentence of controls read as noise. */
.fe-spdf__card .fe-signfonts {
  flex-basis: 100%;
}
/* What is printed under a signature: the choice, and the plugin's own words
   for it underneath — a card previews the paper, it does not describe it. */
/* The date a box will carry, under the two answers that make it. Plain
   text, never a control: it is the result, not a third question. */
.fe-spdf__dateex {
  margin: 0;
  display: flex;
  align-items: baseline;
  gap: 6px;
  flex-wrap: wrap;
  font-size: var(--fe-text-sm);
}
.fe-spdf__dateex-title {
  color: var(--fe-text-muted);
}
.fe-spdf__dateex-value {
  font-family: var(--fe-font-mono);
  font-weight: 600;
}
.fe-spdf__lines {
  flex-basis: 100%;
  flex-wrap: wrap;
  align-items: flex-start;
  row-gap: 6px;
}
.fe-spdf__linepreview {
  flex-basis: 100%;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 6px 10px;
  border: 1px dashed var(--fe-border-strong, var(--fe-border));
  border-radius: var(--fe-radius);
  background: var(--fe-bg);
  font-size: var(--fe-text-xs, 11px);
  line-height: 1.35;
  color: var(--fe-text);
}
.fe-spdf__linepreview-title {
  color: var(--fe-text-muted);
  font-weight: 600;
  margin-bottom: 2px;
}
.fe-spdf__linepreview-line.is-none {
  color: var(--fe-text-muted);
  font-style: italic;
}
.fe-spdf__propwho {
  font-size: var(--fe-text-sm);
  color: var(--fe-text-muted);
}
.fe-spdf__cardno {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: var(--fe-text-xs, 11px);
  font-weight: 600;
  /* ⚠ The ink is the COLOUR's, not the theme's: the ground is a signer's
     identity colour (lib/pdfFields SIGNER_PALETTE), the same in every theme,
     so `--fe-text-on-primary` — dark in dark mode — measured 3.5:1 on the
     first signer's blue. SurfacePdfFields sets --spdf-ink = inkOn(colour). */
  color: var(--spdf-ink, var(--fe-text-on-primary));
  background: var(--spdf-color, var(--fe-primary));
}
.fe-spdf__add {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  padding-top: 2px;
}
.fe-spdf__addlbl {
  font-size: var(--fe-text-sm);
  color: var(--fe-text-muted);
  margin-inline-end: 4px;
}
.fe-spdf__empty {
  padding: 14px;
  border: 1px dashed var(--fe-border);
  border-radius: var(--fe-radius-md);
  color: var(--fe-text-muted);
  font-size: var(--fe-text-sm);
  line-height: 1.5;
}
.fe-spdf__done {
  font-size: var(--fe-text-sm);
  color: var(--fe-text-muted);
  align-self: center;
}
/* A waiting box wears its signer's colour, so the toolbar and the page
   agree about which box is which. */
.fe-spdf__palette .fe-btn[style*='--spdf-color'] {
  border-inline-start: 3px solid var(--spdf-color, var(--fe-primary));
}

.fe-spdf--page {
  display: flex;
  flex-direction: column;
  height: var(--spdf-h, 100%);
  min-height: 0;
}
.fe-spdf--page .fe-spdf__scroll {
  flex: 1 1 auto;
  max-height: none;
  min-height: 0;
}
/* Centred, because a fitted page is narrower than the box it is fitted in
   and a document pinned to the left edge of a wide screen reads as broken. */
.fe-spdf--page .fe-spdf__pages {
  align-items: center;
}
.fe-spdf__pages {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
}
/* ⚠⚠ DOCUMENT SPACE from here in. A page is the PDF's own coordinate system:
   its boxes are placed with PHYSICAL inline `left`/`top` in % of the page
   (SurfacePdfFields, lib/pdfFieldsGeom → the signer stamps at those
   fractions), so a box at the left of the page is at the left of the page in
   Arabic too. Nothing inside a page may use logical properties or read
   `--filex-dir-x` — the RTL guard (web/tests/quality/rtlLogical.test.ts)
   allow-lists exactly these rules. */
.fe-spdf__page {
  position: relative;
  background: #fff;
  box-shadow: var(--fe-shadow-sm);
  flex: 0 0 auto;
  touch-action: pan-y;
  user-select: none;
}
.fe-spdf.is-placing .fe-spdf__page {
  cursor: crosshair;
}
.fe-spdf__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
.fe-spdf__field {
  position: absolute;
  box-sizing: border-box;
  border: 1.5px solid var(--spdf-color, var(--fe-primary));
  background: color-mix(in srgb, var(--spdf-color, var(--fe-primary)) 12%, transparent);
  border-radius: 2px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  font-size: 11px;
  line-height: 1.15;
  color: var(--spdf-color, var(--fe-primary));
  cursor: pointer;
}
.fe-spdf.is-edit .fe-spdf__field {
  cursor: move;
  /* ⚠ A finger that drags a box must move the BOX. The page allows vertical
     panning (`pan-y`) so a signer can scroll a document with a finger; on a
     box that same pan was taken by the browser, which then cancelled the
     pointer — the box stopped half way and the pane scrolled instead. */
  touch-action: none;
}
.fe-spdf.is-edit.is-placing .fe-spdf__page {
  touch-action: none;
}
.fe-spdf__field.is-selected {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
  z-index: 2;
}
.fe-spdf__field.is-foreign {
  border-style: dashed;
  border-color: var(--fe-border-strong);
  background: rgba(128, 128, 128, 0.12);
  color: var(--fe-text-muted);
  cursor: default;
  pointer-events: none;
}
.fe-spdf__field.is-required::after {
  content: '*';
  position: absolute;
  top: 0;
  right: 3px; /* rtl-physical: ⚠⚠ DOCUMENT SPACE — drawn on the page, which never mirrors */
  font-weight: 700;
}
.fe-spdf__field--ghost {
  pointer-events: none;
  border-style: dotted;
  z-index: 3;
}
.fe-spdf__label {
  text-align: center;
  padding: 0 4px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.fe-spdf__who {
  font-size: 9px;
  opacity: 0.8;
}
.fe-spdf__sig {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  pointer-events: none;
}
.fe-spdf__text {
  width: 100%;
  height: 100%;
  border: 0;
  background: transparent;
  color: #111;
  font: inherit;
  font-size: 12px;
  padding: 0 4px;
  outline: none;
  min-width: 0;
}
.fe-spdf__check .fe-aicon {
  width: 90%;
  height: 90%;
  color: #111;
}
.fe-spdf__val {
  color: #111;
  font-variant-numeric: tabular-nums;
}
.fe-spdf__handle {
  position: absolute;
  /* ⚠⚠ DOCUMENT SPACE. The box sits at its PDF coordinates (inline `left`/`top`
     in % of the page, SurfacePdfFields) and grows right and down from there in
     every UI language — the resize maths reads the pointer against the page's
     own left edge. A handle mirrored to the box's left corner would drag the
     RIGHT edge while the pointer sits on the left one. */
  right: -5px; /* rtl-physical: document space (above) */
  bottom: -5px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--spdf-color, var(--fe-primary));
  border: 2px solid #fff;
  cursor: nwse-resize;
  z-index: 3;
}
.fe-spdf__pad {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg-elev);
}
.fe-spdf__pad > .fe-btn {
  align-self: flex-end;
}
.fe-spreview__embed {
  width: 100%;
  height: 60vh;
  max-height: none;
}

/* ── The outward-facing page ─────────────────────────────────────────────
 *
 * A share, a file request and an app plugin's public page are ONE page with
 * three bodies (v3 §1), and this is what that page LOOKS like. The shape is
 * the one the Go-rendered PIN gate had until v0.42.2 — a calm centred card
 * on a soft ground, the instance's mark above it, a tinted round badge, a
 * heading, a quiet line under it, a full-width field and a full-width accent
 * button — restored here so that unifying the pages levelled them UP.
 *
 * ⚠⚠ It was levelled DOWN in v0.43.0 (owner, 2026-09-23: "iki sayfa aynı
 * olsun dediğim için ikisini de kötü hale çevirmişsin"). The measurements
 * that had been lost, and are back: the card is 400px and CENTRED in the
 * viewport rather than 720px glued to the top; it is LIGHTER than the ground
 * (v0.43.0 drew `--fe-bg-elev`, the app's *sunken* tone, on `--fe-bg` — a
 * card you cannot see); 16px radius and 32/28 padding, not 12 and 20; a
 * 64px badge; a 20px/700 heading; and the primary button and the PIN box
 * span the card instead of hugging its left edge.
 *
 * Three widths, one family — exactly the old stylesheet's three cards:
 *   · `--gate` 400px, centred text: the PIN gate, "this link is not
 *     available", an error, an acceptance, and a single document.
 *   · `--form` 520px, text from the start edge: a file request.
 *   · `--wide` 880px: a folder's listing, and an app's screen.
 * The mode is the SHELL's (`layout`, forced to `gate` for every state that
 * is not `ready`), so no body carries a layout of its own.
 *
 * ⚠ The ground and the card are the public page's own tokens rather than
 * `--fe-bg`/`--fe-bg-elev`: in the app those two mean "surface" and "sunken
 * strip", and a card has to sit ABOVE its ground, not below it.
 * ⚠ Logical properties only (`padding-inline`, `margin-inline`): the page is
 * opened in Arabic too, and web/tests/quality/rtlLogical.test.ts says so. */
.fe.fe-ppage {
  /* The ground, the card and the three widths are declared in
     styles/variables.css (light + dark) — see the note there. */
  min-height: 100vh;
  height: auto;
  border: 0;
  border-radius: 0;
  overflow: visible;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* ⚠ `safe center` and not plain `center`: a tall body (an app's wizard with
     a document in it) is taller than the window, and a plainly centred flex
     column overflows past the TOP of the scrollport, where nothing can scroll
     it back. `safe` falls back to start alignment exactly then. The plain
     value stays as the fallback for anything that does not know the keyword. */
  justify-content: center;
  justify-content: safe center;
  gap: 18px;
  padding: 40px 16px;
  background: linear-gradient(160deg, var(--fe-ppage-ground-1), var(--fe-ppage-ground-2));
  color: var(--fe-text);
}
/* The instance's mark, ABOVE the card and centred on it — the first thing a
 * stranger reads, and (v3 §1.2) theirs, not ours. */
.fe-ppage__brand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  max-width: 100%;
  min-width: 0;
}
.fe-ppage__logo {
  height: 34px;
  width: auto;
  max-width: 200px;
  object-fit: contain;
  display: block;
  flex: none;
}
.fe-ppage__brandname {
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--fe-text);
  overflow-wrap: anywhere;
}

.fe-ppage__card {
  width: 100%;
  max-width: var(--fe-ppage-max);
  background: var(--fe-ppage-card);
  border: 1px solid var(--fe-border);
  border-radius: 16px;
  box-shadow: var(--fe-ppage-shadow);
  padding: 28px 24px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
}
.fe-ppage--gate .fe-ppage__card {
  padding: 32px 28px;
  text-align: center;
  gap: 12px;
}

/* The round, accent-tinted badge the card opens with. One element with three
 * tones, so "protected", "gone" and "done" are the same shape in a different
 * colour rather than three drawings. */
.fe-ppage__badge {
  width: 64px;
  height: 64px;
  margin-block-end: 2px;
  margin-inline: auto;
  border-radius: 50%;
  display: grid;
  place-items: center;
  flex: none;
  background: var(--fe-primary-soft);
  color: var(--fe-primary-ink);
}
.fe-ppage__badge svg {
  width: 30px;
  height: 30px;
}
.fe-ppage__badge--ok {
  background: color-mix(in srgb, var(--fe-ppage-ok) 14%, transparent);
  color: var(--fe-ppage-ok);
}
.fe-ppage__badge--err {
  background: color-mix(in srgb, var(--fe-danger) 12%, transparent);
  color: var(--fe-danger);
}
.fe-ppage__spinner {
  width: 26px;
  height: 26px;
  border: 2.5px solid currentColor;
  border-inline-end-color: transparent;
  border-radius: 50%;
  animation: fe-ppage-spin 0.8s linear infinite;
  opacity: 0.6;
}
@keyframes fe-ppage-spin {
  to {
    transform: rotate(360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  .fe-ppage__spinner {
    animation-duration: 2.4s;
  }
}

.fe-ppage__head {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.fe-ppage__title {
  margin: 0;
  font-size: 20px;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}
.fe-ppage__subject {
  margin: 0;
  color: var(--fe-text-muted);
  font-size: 14px;
  line-height: 1.5;
  overflow-wrap: anywhere;
}
.fe-ppage__state {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.fe-ppage__state-title {
  margin: 0;
  font-size: 20px;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.01em;
}
.fe-ppage__state--ok .fe-ppage__state-title {
  color: var(--fe-ppage-ok);
}
/* The shell's OWN paragraphs at the reading size the old page used (0.9rem)
 * rather than the app's 13px table type.
 * ⚠ Scoped to the card's own children and to the shell's states — NOT to
 * every `.fe-surface__text` under `.fe-ppage`. An app plugin's screen is the
 * same `SurfaceConversation` a signed-in person sees, and resizing its text
 * only on a public link is exactly the surface-specific divergence the one
 * shell exists to prevent. */
.fe-ppage__card > .fe-surface__text,
.fe-ppage__state > .fe-surface__text,
.fe-ppage__pin > .fe-surface__text,
.fe-ppage__pin .fe-ppage__state > .fe-surface__text,
.fe-pdrop > .fe-surface__text,
.fe-pdrop__zone > .fe-surface__text,
.fe-ppage__file-one > .fe-surface__text {
  font-size: 14px;
  line-height: 1.5;
}

/* ⚠ On the gate the primary act spans the card: a 4-digit box and a
 * "Continue" hugging the left edge of a 400px card is what v0.43.0 shipped,
 * and it reads as a form fragment rather than the one thing to do. */
.fe-ppage--gate .fe-btn {
  justify-content: center;
}
.fe-ppage--gate .fe-btn--primary {
  width: 100%;
  padding-block: 11px;
  font-size: 15px;
  font-weight: 600;
  border-radius: 10px;
}
.fe-ppage__pin {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
/* The code box: the whole width, big, spaced and centred, as it was. A PIN is
 * read off a message and typed once — it is not a text field among others. */
.fe-ppage__pinput.fe-ppage__pinput {
  box-sizing: border-box;
  width: 100%;
  max-width: none;
  height: auto;
  padding: 12px;
  font-size: 22px;
  font-weight: 600;
  letter-spacing: 0.32em;
  text-indent: 0.32em;
  text-align: center;
  border: 1.5px solid var(--fe-border-strong);
  border-radius: 10px;
  background: transparent;
  color: inherit;
  font-family: inherit;
}
.fe-ppage__pinput:focus {
  outline: none;
  border-color: var(--fe-primary);
  box-shadow: 0 0 0 3px var(--fe-primary-soft);
}
.fe-ppage__pinput.is-invalid {
  border-color: var(--fe-danger);
}
/* Named for a screen reader, not for the eye: the heading above it already
 * says what the box is, and a "PIN" label over a PIN box is furniture. */
.fe-ppage__srlabel {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.fe-ppage__error {
  margin: 0;
  padding: 10px 12px;
  border-radius: 9px;
  background: color-mix(in srgb, var(--fe-danger) 10%, transparent);
  color: var(--fe-danger);
  font-size: 13.5px;
}

/* A card's actions: a full-width row under a rule. ⚠ `width: 100%` is load
   bearing — inside a body that packs its children to the start
   (`__file-one`) the row shrink-wrapped and its rule stopped halfway across
   the card, which is what a folder share shipped as. */
.fe-ppage__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
  width: 100%;
  box-sizing: border-box;
  padding-top: 12px;
  border-top: 1px solid var(--fe-border-soft);
}
.fe-ppage--gate .fe-ppage__file-one {
  align-items: center;
  gap: 14px;
}
.fe-ppage--gate .fe-ppage__actions {
  justify-content: center;
  border-top: 0;
  padding-top: 0;
}
.fe-ppage__files {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--fe-border);
  border-radius: 12px;
  overflow: hidden;
}
.fe-ppage__file {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  font-size: 14px;
}
.fe-ppage__file + .fe-ppage__file {
  border-top: 1px solid var(--fe-border);
}
.fe-ppage__file:hover {
  background: var(--fe-primary-soft);
}
.fe-ppage__file a {
  color: var(--fe-primary-ink);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-ppage__file a:hover {
  text-decoration: underline;
}
.fe-ppage__filesize {
  color: var(--fe-text-muted);
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
}

/* The phone. 390px is the measurement that matters: the card gives up its
 * generous side padding rather than its rhythm, and keeps a 12px gutter each
 * side. (A body too tall to centre starts at the top by itself — that is
 * `justify-content: safe center` above, not a rule here.) */
@media (max-width: 560px) {
  .fe.fe-ppage {
    padding: 24px 12px 32px;
  }
  .fe-ppage__card {
    padding: 24px 18px;
  }
  .fe-ppage--gate .fe-ppage__card {
    padding: 28px 20px;
  }
  .fe-ppage__logo {
    height: 28px;
  }
  .fe-ppage__title,
  .fe-ppage__state-title {
    font-size: 18px;
  }
  .fe-ppage__actions .fe-btn {
    flex: 1 1 auto;
    justify-content: center;
  }
}
/* === /App plugins M3 === */

/* === App plugins v3 — the shared choice control and the ONE public
 * shell (docs/APP-PLUGINS-V3.md §1–§2).
 *
 * ⚠⚠ These rules live HERE and not in the components, for a reason the
 * build makes and not a taste: the web component ships only the styles of
 * the modules its entry imports, while `packages/core` ships all of them,
 * so a `<style>` block in a component one distribution does not mount makes
 * the two published stylesheets differ. `web/tests/deploy/packageLook` is
 * the gate that says so. === */

/* ChoiceButtons.vue */
.fe-choice {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.fe-choice__btn {
  flex: 0 1 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  min-width: 0;
  font: inherit;
  font-size: 13.5px;
  text-align: start;
  padding: 8px 12px;
  border-radius: var(--fe-radius);
  border: 1px solid var(--fe-border-strong);
  background: var(--fe-bg);
  color: var(--fe-text);
  cursor: pointer;
}
.fe-choice__btn:hover:not(:disabled) {
  background: var(--fe-bg-hover);
}
.fe-choice__btn:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}
/* ⚠ The chosen one is marked by BORDER and BACKGROUND, not by colour alone:
   a palette where the primary is close to the surface still shows which is
   on, and so does a screen nobody has calibrated. */
.fe-choice__btn.is-on {
  border-color: var(--fe-primary);
  background: var(--fe-bg-selected);
  color: var(--fe-text);
  box-shadow: inset 0 0 0 1px var(--fe-primary);
}
.fe-choice__btn:disabled {
  opacity: 0.55;
  cursor: default;
}
.fe-choice.is-invalid .fe-choice__btn {
  border-color: var(--fe-danger);
}
.fe-choice__label {
  font-weight: 600;
}
.fe-choice__help {
  font-size: 12px;
  color: var(--fe-text-muted);
  line-height: 1.4;
}
.fe-choice__icon {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
}
/* The strip and the cells wrap the buttons for the segmented look (a cell
   holds its button's tip). In the default look they are not boxes at all:
   the buttons lay out as the group's own children, as they always have
   (and a host's `display: grid` on the group still reaches them). */
.fe-choice__strip,
.fe-choice__cell {
  display: contents;
}

/* ChoiceButtons.vue, `segmented` — the same radio group drawn as ONE strip:
   two to four short answers that sit inline beside other fields (a unit
   after a number, "user" or "viewer" before a button, an access level).
   Ported from the Apps store's FaSegmented (2026-10-04), which was written
   to come here. The group is a column: the strip, then (on a screen that
   cannot hover) the chosen cell's meaning under it. */
.fe-choice--segmented {
  display: inline-flex;
  flex: 0 0 auto;
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: flex-start;
  gap: 2px;
  min-width: 0;
  max-width: 100%;
}
.fe-choice--segmented .fe-choice__strip {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px;
  max-width: 100%;
  padding: 2px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg-elev);
}
/* A cell is the box its tip hangs from. */
.fe-choice--segmented .fe-choice__cell {
  position: relative;
  display: inline-flex;
}
.fe-choice--segmented .fe-choice__btn {
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: var(--fe-h-sm);
  padding: 2px 10px;
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  box-shadow: none;
  color: var(--fe-text-muted);
  font-size: var(--fe-text-sm);
  font-weight: 500;
  white-space: nowrap;
  transition: background-color 0.16s ease, color 0.16s ease, box-shadow 0.16s ease;
}
.fe-choice--segmented .fe-choice__btn:hover:not(:disabled) {
  color: var(--fe-text);
  background: var(--fe-bg-hover);
}
/* ⚠ The chosen cell is marked by its ground AND a ring, not by colour alone
   (the rule the default look follows too). */
.fe-choice--segmented .fe-choice__btn.is-on {
  color: var(--fe-primary-ink);
  background: var(--fe-primary-soft);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--fe-primary) 40%, transparent);
}
.fe-choice--segmented .fe-choice__label {
  font-weight: inherit;
}
.fe-choice--segmented.is-invalid .fe-choice__strip {
  border-color: var(--fe-danger);
}
/* Icons only: a pill of round cells. */
.fe-choice--icons .fe-choice__strip {
  flex-wrap: nowrap;
  border-radius: 999px;
}
.fe-choice--icons .fe-choice__btn {
  width: var(--fe-h-sm);
  height: var(--fe-h-sm);
  padding: 0;
  border-radius: 999px;
}
@media (pointer: coarse) {
  /* A finger needs a bigger target than a pointer (44px, WCAG 2.5.5). */
  .fe-choice--segmented .fe-choice__btn {
    min-height: 44px;
  }
  .fe-choice--icons .fe-choice__btn {
    width: 44px;
    height: 44px;
  }
}
/* A cell's tip shows on a resting pointer and on keyboard focus (its look is
   the header's hover tip, `.fe-toolbar__soon-tip`, further down: one rule
   for both). `:focus-visible`, not `:focus`: a click focuses the button too,
   and a tip that stays up after the click is one nobody asked for. */
.fe-choice__cell:hover .fe-choice__tip,
.fe-choice__btn:focus-visible + .fe-choice__tip {
  opacity: 1;
}
/* The chosen cell's meaning under the strip: one small line, only where a
   tip cannot be hovered (a phone, a tablet). There the tips stay out: a tap
   leaves `:hover` stuck on the tapped cell, and the line already says it. */
.fe-choice__note {
  display: none;
  max-width: 100%;
  margin: 0;
  overflow: hidden;
  color: var(--fe-text-muted);
  font-size: var(--fe-text-xs);
  line-height: 1.3;
  text-overflow: ellipsis;
  white-space: nowrap;
}
@media (hover: none) {
  .fe-choice__note {
    display: block;
  }
  .fe-choice__tip {
    display: none;
  }
}

/* ChoiceSelect.vue — one answer from a list, without the native dropdown.
   The combobox looks like a field; the listbox is teleported to <body> (or
   the open <dialog>) and placed by the component, along the inline axis, on
   the layer lib/popupLayer measures. It carries the field's computed tokens
   (see the component), so the rules below read the same `--fe-*` names in
   either place. Ported from the Apps store's FaSelect (2026-10-04). */
.fe-select {
  position: relative;
  display: flex;
  min-width: 0;
  max-width: 100%;
}
.fe-select__trigger {
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  min-width: 0;
  min-height: var(--fe-h-md);
  padding-block: 6px;
  padding-inline: 10px 8px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  background: var(--fe-bg);
  color: var(--fe-text);
  font: inherit;
  text-align: start;
  cursor: pointer;
  transition: border-color 0.16s ease, box-shadow 0.16s ease;
}
.fe-select--sm .fe-select__trigger {
  min-height: var(--fe-h-sm);
  padding-block: 2px;
}
.fe-select--lg .fe-select__trigger {
  min-height: var(--fe-h-lg);
}
.fe-select__trigger:hover {
  border-color: var(--fe-border-strong);
}
.fe-select__trigger:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}
.fe-select.is-open .fe-select__trigger {
  border-color: var(--fe-primary);
  box-shadow: 0 0 0 3px var(--fe-primary-soft);
}
.fe-select.is-invalid .fe-select__trigger {
  border-color: var(--fe-danger);
}
.fe-select__trigger:disabled {
  cursor: not-allowed;
  color: var(--fe-text-muted);
  background: var(--fe-bg-elev);
}
.fe-select__value {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-select__value.is-placeholder {
  color: var(--fe-text-muted);
}
.fe-select__chev {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  color: var(--fe-text-muted);
  transition: transform 0.16s ease;
}
.fe-select.is-open .fe-select__chev {
  transform: rotate(180deg);
}
/* The hidden input that carries `required` and `name` for a form. */
.fe-select__native {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
}
.fe-select__panel {
  position: fixed;
  z-index: 80;
  box-sizing: border-box;
  margin: 0;
  padding: 4px;
  overflow-y: auto;
  overscroll-behavior: contain;
  list-style: none;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg);
  color: var(--fe-text);
  box-shadow: var(--fe-shadow);
}
.fe-select__option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 36px;
  padding: 6px 10px;
  border-radius: var(--fe-radius-sm);
  cursor: pointer;
}
.fe-select__option.is-active {
  background: var(--fe-bg-hover);
}
.fe-select__option.is-selected {
  color: var(--fe-primary-ink);
  font-weight: 600;
}
.fe-select__option[aria-disabled='true'] {
  color: var(--fe-text-muted);
  cursor: not-allowed;
}
.fe-select__label {
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow-wrap: anywhere;
}
.fe-select__help {
  color: var(--fe-text-muted);
  font-size: var(--fe-text-xs);
  font-weight: 400;
}
.fe-select__check {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  color: var(--fe-primary);
}
@media (pointer: coarse) {
  /* A finger needs a bigger target (44px, WCAG 2.5.5), whatever the size. */
  .fe-select__trigger,
  .fe-select--sm .fe-select__trigger,
  .fe-select__option {
    min-height: 44px;
  }
}

/* SurfaceFooterButtons.vue */
/* Back sits at the far end of the row, away from the way forward: a step's
   two directions must not be two neighbouring buttons. `margin-right: auto`
   works in every frame that lays the row out as a flex line, and is inert in
   one that does not. */
.fe-surface__back {
  margin-inline-end: auto;
}

/* PublicShell.vue — the meta line and the foot.
   The expiry and the visit counter are a quiet line under the heading, and
   the language picker + attribution sit OUTSIDE the card, centred under it,
   the way the old page's brand footer did. */
.fe-ppage__meta {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  font-size: 13px;
  color: var(--fe-text-muted);
}
.fe-ppage--gate .fe-ppage__meta {
  justify-content: center;
}
.fe-ppage__foot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: 100%;
}
.fe-ppage__footmark {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12.5px;
  color: var(--fe-text-muted);
}
.fe-ppage__footmark img,
.fe-ppage__footmark svg {
  width: 16px;
  height: 16px;
  flex: none;
  border-radius: 3px;
  object-fit: contain;
}
.fe-ppage__footline {
  margin: 0;
  font-size: 12.5px;
  color: var(--fe-text-muted);
  text-align: center;
  max-width: 480px;
  overflow-wrap: anywhere;
}

/* PublicShareBody.vue */
.fe-ppage__crumbs {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--fe-text-muted);
}
.fe-ppage__crumb {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-ppage__entry {
  font: inherit;
  font-size: 13.5px;
  color: var(--fe-primary);
  background: none;
  border: 0;
  padding: 0;
  text-align: start;
  cursor: pointer;
  text-decoration: none;
}
.fe-ppage__entry:hover {
  text-decoration: underline;
}
.fe-ppage__file-one {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
}
/* An app page's exposed files, drawn as a shared file is, under a rule that
   separates them from the app's own screen above. */
.fe-ppage__appfiles {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--fe-border);
}
.fe-ppage__shot {
  max-width: 100%;
  max-height: 46vh;
  border-radius: var(--fe-radius);
  border: 1px solid var(--fe-border);
  object-fit: contain;
}

/* PublicRequestBody.vue */
.fe-pdrop {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
/* The uploader's name — above the drop area, because dropping sends. */
.fe-pdrop__who {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.fe-pdrop__wholabel {
  font-size: var(--fe-text-sm);
  font-weight: 500;
  color: var(--fe-text);
}
.fe-pdrop__whoinput {
  box-sizing: border-box;
  width: 100%;
  height: var(--fe-h-lg);
  padding: 0 var(--fe-gap-sm);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg);
  color: var(--fe-text);
  font-family: inherit;
  font-size: var(--fe-text-md);
}
.fe-pdrop__whoinput:focus {
  outline: none;
  border-color: var(--fe-primary);
}
.fe-pdrop__zone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 28px 16px;
  border: 2px dashed var(--fe-border-strong);
  border-radius: var(--fe-radius-lg);
  background: var(--fe-bg-elev);
  cursor: pointer;
  text-align: center;
}
.fe-pdrop__zone:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 2px;
}
.fe-pdrop__zone.is-over {
  border-color: var(--fe-primary);
  background: var(--fe-bg-selected);
}
.fe-pdrop__zone.is-disabled {
  cursor: default;
  opacity: 0.6;
}
.fe-pdrop__icon {
  width: 38px;
  height: 38px;
  margin-block-end: 6px;
  color: inherit;
}
.fe-pdrop__lead {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  color: var(--fe-text);
}
/* The zone answers the pointer the way the old one did: the accent's tint,
   its border and its ink, all three at once. */
.fe-pdrop__zone:hover:not(.is-disabled),
.fe-pdrop__zone.is-over {
  border-color: var(--fe-primary);
  background: var(--fe-primary-soft);
  color: var(--fe-primary-ink);
}
.fe-pdrop__zone:hover:not(.is-disabled) .fe-pdrop__lead,
.fe-pdrop__zone.is-over .fe-pdrop__lead {
  color: inherit;
}
.fe-pdrop__input {
  display: none;
}
.fe-pdrop__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.fe-pdrop__item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--fe-text);
}
.fe-pdrop__name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-pdrop__bar {
  flex: 0 0 120px;
  height: 6px;
}
.fe-pdrop__ok {
  color: var(--fe-ok);
}
.fe-pdrop__saving {
  color: var(--fe-text-muted);
}
/* The ink of text, not the amber accent (2.1:1 on the white card): this is
   the one line that tells the visitor not to send the files again. The
   warning is the rule beside it. */
.fe-pdrop__unconfirmed {
  color: var(--fe-text);
  border-inline-start: 3px solid var(--fe-warning);
  padding-inline-start: 8px;
}

/* PublicLanguagePicker.vue */
.fe-plang {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.fe-plang__btn {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  font: inherit;
  font-size: 12.5px;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid var(--fe-border);
  background: transparent;
  color: var(--fe-text-muted);
  cursor: pointer;
}
.fe-plang__btn:hover {
  color: var(--fe-text);
  background: var(--fe-bg-hover);
}
.fe-plang__btn.is-on {
  color: var(--fe-text);
  border-color: var(--fe-primary);
  background: var(--fe-bg-selected);
}
.fe-plang__btn:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}
.fe-plang__src {
  font-size: 10.5px;
  opacity: 0.75;
}


/* === App plugins v2 ===================================================
 * A `page` view (`{base}apps/{plugin}/{view}`), the lock badge an app's
 * hold puts on a row, and the face picker a typed signature carries. */

/* The page view is a whole document, not a panel: the `.fe` root's explorer
 * chrome (border, radius, fixed height, hidden overflow) is undone here for
 * the same reason `.fe-ppage` undoes it. ⚠ Without this the wizard is a
 * 420px-tall bordered box floating in an empty tab. */
.fe.fe-apppage {
  min-height: 100vh;
  height: auto;
  border: 0;
  border-radius: 0;
  overflow: visible;
  padding: 0 0 40px;
  gap: 0;
}
.fe-apppage__bar {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  padding: 16px;
  border-bottom: 1px solid var(--fe-border);
  background: var(--fe-bg-elev);
}
.fe-apppage__title {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
  line-height: 1.25;
}
.fe-apppage__path {
  margin: 0;
  flex: 1 1 200px;
  min-width: 0;
  color: var(--fe-text-muted);
  font-size: var(--fe-text-sm);
  font-family: var(--fe-font-mono);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fe-apppage__back {
  flex: 0 0 auto;
  text-decoration: none;
}
.fe-apppage__body {
  width: 100%;
  max-width: 960px;
  margin: 0 auto;
  padding: 20px 16px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
}

/* v4 — an app's OWN interface on a page: the frame takes the width and the
 * height the page leaves, like a document step does (the interface draws its
 * own layout; a 960px column would squeeze an editor). */
.fe-apppage__body--ui {
  max-width: none;
  flex: 1 1 auto;
  min-height: calc(100vh - 80px);
  padding: 0;
}

/* ⚠⚠ v3 §3.2 — the step that shows a DOCUMENT is shaped by the window,
 * not by the text column.
 *
 * A form step is a readable 960px column that the page scrolls; a step
 * carrying a `pdf-fields` node is not a form, it is a document. Measured
 * before this rule, on the request wizard's "place the fields" step at
 * 1440x900: the page box was 916x1296 inside a 916x618 viewport, i.e. the
 * document ran off the bottom AND sideways — which is the complaint that
 * started this round, reproduced exactly.
 *
 * `min-height: 0` on the body is not decoration: without it a flex child
 * refuses to shrink below its content, and the "remainder" the document is
 * fitted into grows to the document's own height, which is the bug again
 * wearing a fix's clothes. */
.fe.fe-apppage.is-doc {
  height: 100vh;
  min-height: 0;
  overflow: hidden;
  padding-bottom: 0;
}
.fe-apppage.is-doc .fe-apppage__body {
  flex: 1 1 auto;
  min-height: 0;
  max-width: none;
  overflow: hidden;
}
/* ⚠⚠ ...and one step further down, or none of the above does anything.
 * `.fe-surface` sits between the body and the document node and grows to its
 * content by default, so `.fe-spdf--page { height: 100% }` resolved against
 * an auto-height parent and came back as "as tall as the document" —
 * measured at 1440x900: a 775px body holding a 1466px document, i.e. the
 * page running off the bottom exactly as before, with every rule above this
 * one in place and looking correct. */
.fe-apppage.is-doc .fe-surface {
  flex: 1 1 auto;
  min-height: 0;
}
.fe-apppage__state {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 0;
}
.fe-apppage__state-title {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
}
.fe-apppage__state-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  padding-top: 4px;
}
.fe-apppage__state-actions .fe-btn {
  text-decoration: none;
}
.fe-apppage__foot {
  position: sticky;
  bottom: 0;
  width: 100%;
  max-width: 960px;
  margin: 0 auto;
  padding: 12px 16px;
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  flex-wrap: wrap;
  border-top: 1px solid var(--fe-border);
  background: var(--fe-bg);
}

/* The SAME screen inside a HOST's page (`PluginPageView frame="embedded"`) —
 * the admin panel's Apps section, an embed that already has a header.
 *
 * Exactly three things the tab frame provides have to go, and they are the
 * three the host has already decided: the viewport height (the panel's page
 * is as tall as its content), the 960px reading column (the host fixed the
 * width, and an app's home screen is a TABLE, which a text column cramps) and
 * the tail padding a scrolling tab needs.
 *
 * ⚠ The footer stops being sticky too. In a tab it hovers over the document
 * it belongs to; inside a card `bottom: 0` would pin it to the WINDOW's edge,
 * a screenful away from the form it submits. */
.fe.fe-apppage--embedded {
  min-height: 0;
  padding: 0;
}
.fe-apppage--embedded .fe-apppage__body {
  max-width: none;
  padding: var(--fe-gap);
}
.fe-apppage--embedded .fe-apppage__foot {
  position: static;
  max-width: none;
  padding: var(--fe-gap);
  background: transparent;
}

/* A home page's section menu (`SurfaceSections`, `surface.sections`). The
 * product's own underlined tab strip — the look of the Connections and
 * Plugins screens — and ⚠ one line on a phone, scrolled sideways: a strip
 * that wrapped moved the table under it every time a count changed. */
.fe-sections {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--fe-border);
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: thin;
  flex: 0 0 auto;
  min-width: 0;
}
.fe-sections__tab {
  font: inherit;
  font-size: 13.5px;
  border: 0;
  background: none;
  color: var(--fe-text-muted);
  padding: 8px 12px;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.fe-sections__tab:hover:not(:disabled) {
  color: var(--fe-text);
}
.fe-sections__tab.is-active {
  color: var(--fe-primary);
  border-bottom-color: var(--fe-primary);
  font-weight: 600;
}
.fe-sections__tab:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: -2px;
}
.fe-sections__count {
  min-width: 18px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--fe-bg-hover);
  color: var(--fe-text-muted);
  font-size: 11px;
  font-weight: 600;
  line-height: 18px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.fe-sections__tab.is-active .fe-sections__count {
  background: color-mix(in srgb, var(--fe-primary) 14%, transparent);
  color: var(--fe-primary);
}

/* The lock badge. ⚠ Drawn OUTSIDE `.fe-list__name`: the shot scripts read
 * that element's textContent as the filename, and a row whose name reads
 * "nda.pdf 🔒" breaks them silently (lesson #29). */
.fe-applock {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0 6px;
  height: 18px;
  border-radius: 9px;
  border: 1px solid var(--fe-border);
  background: var(--fe-bg-elev);
  color: var(--fe-text-muted);
  font-size: 11px;
  line-height: 1;
  white-space: nowrap;
  cursor: help;
}
.fe-applock__glyph {
  font-size: 11px;
}
.fe-applock--inline {
  vertical-align: middle;
}

/* issue #34 — the badge on a symlink the server will not follow. Same chip as
 * the lock badge above, deliberately: a row that is not ordinary is marked one
 * way in this product, and a second vocabulary is how two marks end up meaning
 * the same thing on neighbouring rows.
 * ⚠ Drawn OUTSIDE `.fe-list__name` for the same reason (lesson #29). */
.fe-symlink {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0 6px;
  height: 18px;
  border-radius: 9px;
  border: 1px solid var(--fe-border);
  background: var(--fe-bg-elev);
  color: var(--fe-text-muted);
  font-size: 11px;
  line-height: 1;
  white-space: nowrap;
  cursor: help;
}
.fe-symlink__glyph {
  font-size: 11px;
}
/* A broken link is the one state nothing can be configured back into working,
 * so it is the one state that gets the amber. The other three are boundaries
 * the operator chose, not faults. */
.fe-symlink--broken {
  border-color: var(--fe-warning);
  color: var(--fe-warning);
}

/* issue #104 - an entry the storage could not answer for: "!" on the row. The
 * same chip as the lock and link badges, in the warning colour: nothing about
 * this entry can be trusted, or done, until the storage answers again
 * (lib/unavailable). */
.fe-unavailable {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 9px;
  border: 1px solid var(--fe-warning);
  background: var(--fe-bg-elev);
  color: var(--fe-warning);
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  cursor: help;
}

/* One notice box, two names: an app's hold on a file and a link that cannot be
 * opened are the same shape of fact — the thing above the panel's facts that
 * explains why the facts alone mislead. */
.fe-inspector__lock,
.fe-inspector__linknote,
.fe-inspector__unavailable {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 8px 10px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg-elev);
  color: var(--fe-text);
  font-size: var(--fe-text-sm);
  overflow-wrap: anywhere;
}

/* The face picker a typed signature and a pdf text field share. */
.fe-signfonts {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.fe-signfonts__btn {
  padding: 4px 10px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius);
  background: var(--fe-bg-elev);
  color: var(--fe-text);
  font-size: 15px;
  line-height: 1.4;
  cursor: pointer;
}
.fe-signfonts__btn:hover:not(:disabled) {
  border-color: var(--fe-primary);
}
.fe-signfonts__btn.is-on {
  border-color: var(--fe-primary);
  box-shadow: inset 0 0 0 1px var(--fe-primary);
}
.fe-signfonts__btn:disabled {
  opacity: 0.5;
  cursor: default;
}
/* === /App plugins v2 === */

/* ── StorageTags: a storage's driver, "Read-only", "Disabled" ───────────
   ONE vocabulary for every list of storages (components/StorageTags.vue,
   QA #34: "RO" on one admin list, "SALT OKUNUR" on the other). Global, like
   every core rule — scoped styles do not survive the web-component build. */
.fe-stags {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  min-width: 0;
}
.fe-stag {
  font-size: 11px;
  line-height: 16px;
  border: 1px solid var(--fe-border-strong, currentColor);
  border-radius: 999px;
  padding: 0 8px;
  color: var(--fe-text-muted, inherit);
  white-space: nowrap;
}
.fe-stag--warn {
  color: var(--fe-warning, var(--fe-danger));
  border-color: var(--fe-warning, var(--fe-danger));
}

/* ── PublicLinkPreview: the public page inside the admin's Corporate identity
   page. The page is a whole document (`min-height: 100vh`); in the admin card
   it is a panel. Only the height is undone — padding, width, colours and
   alignment stay the page's own, so what the operator sees is what a visitor
   gets. It is looked at, not used. */
.fe-ppreview {
  pointer-events: none;
  user-select: none;
  border-radius: var(--fe-radius-lg, 12px);
  overflow: hidden;
}
.fe-ppreview > .fe.fe-ppage {
  min-height: 0;
}

/* A listing read again (opening a folder, Refresh, a search) keeps its old
   rows until the answer comes: a thin bar runs along the top and the rows
   fade, after a moment so a quick answer does not flicker. The list carried
   an `is-loading` class that nothing drew.
   ⚠ The bar is the SIBLING of the list / the grid (DataTable, GridView), not
   their child: a `role="grid"` owns rows and a `role="listbox"` options.
   Sticky at the top of the box that scrolls — the pane for the grid, the
   page for a framed admin table — and taking no room (net zero height), so
   it stays in view however far the listing is scrolled and cannot change
   what scrolls. (Inside the grid it was absolutely placed at the grid's top
   and scrolled away with the first row.) */
.fe-gridbox {
  display: contents;
}
.fe-list__refreshing,
.fe-grid__refreshing {
  position: sticky;
  top: 0;
  height: 2px;
  margin-bottom: -2px;
  z-index: 4;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  animation: fe-refreshing-in 0s linear 300ms forwards;
}
/* The product's one indeterminate slide (the share panel's progress track):
   it runs along `inset-inline-start`, so it goes toward the end of the line in
   Arabic and Hebrew as well, with no keyframe of its own. */
.fe-list__refreshing::before,
.fe-grid__refreshing::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 35%;
  background: var(--fe-primary);
  animation: fe-sprogress-slide 1.1s ease-in-out infinite;
}
.fe-list.is-loading .fe-list__row,
.fe-grid.is-loading .fe-grid__card {
  opacity: 0.55;
  transition: opacity 0.2s ease 300ms;
}
@keyframes fe-refreshing-in {
  to {
    opacity: 1;
  }
}
@media (prefers-reduced-motion: reduce) {
  .fe-list__refreshing::before,
  .fe-grid__refreshing::before {
    animation: none;
    width: 100%;
    opacity: 0.6;
  }
}

/* === 0.50: the thumbnail note (ThumbTile, lib/thumbNote) ===================
 * A file whose thumbnail will not come because of the file itself (damaged,
 * encrypted, too large) keeps its type tile, and a small round marker with
 * an icon sits in the tile's block-end, inline-end corner; resting the
 * pointer on it, or tapping it, shows the sentence. INSIDE the corner, like
 * the list's type badge: the list cell and the tiles clip. The marker takes
 * its own clicks (ThumbTile stops them), so a tap never opens the file. */
.fe-thumb__note {
  position: absolute;
  inset-block-end: 6px;
  inset-inline-end: 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border: 1px solid currentColor;
  border-radius: 50%;
  background: var(--fe-bg-elev);
  color: var(--fe-text-muted);
  box-shadow: var(--fe-shadow-sm);
  cursor: help;
  line-height: 0;
}
.fe-thumb__note svg {
  width: 13px;
  height: 13px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.fe-thumb__note--corrupt {
  color: var(--fe-danger);
}
.fe-thumb__note--encrypted {
  color: var(--fe-primary);
}
.fe-thumb__note--too_large {
  color: var(--fe-warning);
}
/* The list's tile is 24 pixels: a small marker in its very corner. */
.fe-list__thumb .fe-thumb__note {
  inset-block-end: 0;
  inset-inline-end: 0;
  width: 12px;
  height: 12px;
  box-shadow: none;
}
.fe-list__thumb .fe-thumb__note svg {
  width: 8px;
  height: 8px;
  stroke-width: 2.6;
}
/* The gallery's tile is several times a grid card's: the marker scales, and
 * it stays above the size-and-date line a resting pointer slides up from the
 * tile's bottom edge (.fe-gal__meta), which used to cover it. */
.fe-gal__thumb .fe-thumb__note {
  inset-block-end: 8px;
  inset-inline-end: 8px;
  z-index: 1;
  width: 28px;
  height: 28px;
}
.fe-gal__thumb .fe-thumb__note svg {
  width: 16px;
  height: 16px;
}
/* The sentence: teleported to <body> and placed by ThumbTile along the
 * inline axis (insetInlineStart, top). It takes no clicks. */
.fe-thumb__note-tip {
  position: fixed;
  max-width: min(260px, calc(100vw - 16px));
  padding: 6px 8px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  background: var(--fe-bg);
  color: var(--fe-text);
  box-shadow: var(--fe-shadow-sm);
  font-family: var(--fe-font);
  font-size: var(--fe-text-sm);
  line-height: 1.4;
  pointer-events: none;
}

/* === #82 (0.51) - the mega menu (components/MegaMenu.vue) ===============
   The admin panel's navigation: top entries, each a panel of sections, and
   the same entries as a headed list in a phone's drawer. Here, not in the
   component, so the web-component sheet carries it too (packageLook).
   ⚠ Declared `--fe-*` tokens only, so an operator's theme paints the menu
   like the rest of the admin chrome (#74).
   ⚠ Logical properties only (web/tests/quality/rtlLogical.test.ts): the
   panel hangs from the bar's inline START, which is its right in Arabic. */
.fx-mega {
  position: relative;
  min-width: 0;
}
.fx-mega--bar {
  /* The full height of the bar it sits in, so the panel's `top: 100%` is the
     bar's bottom edge rather than a few pixels inside it. */
  align-self: stretch;
  display: flex;
  align-items: center;
}
.fx-mega__bar,
.fx-mega__items {
  margin: 0;
  padding: 0;
  list-style: none;
}
.fx-mega__bar {
  display: flex;
  align-items: center;
  gap: 2px;
}
.fx-mega__toplink,
.fx-mega__topbtn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  /* ⚠ Said outright: a host's `button { height }` rule reaches this control
     (core lesson #639), and a bar whose buttons and link differ in height
     reads as broken. */
  height: var(--fe-h-md);
  padding-block: 0;
  padding-inline: 10px;
  border: 0;
  border-radius: var(--fe-radius-md);
  background: transparent;
  color: var(--fe-text-muted);
  font: inherit;
  font-size: var(--fe-text-md);
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
}
.fx-mega__toplink:hover,
.fx-mega__topbtn:hover,
.fx-mega__topbtn.is-open {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
/* ⚠ `--fe-primary-ink`, not `--fe-primary`: the primary on its own soft tint
   measures 3.97:1, under the 4.5 bar (the old sidebar's `.nav-link-active`
   learned it); the ink token is the one every palette makes clear it. */
.fx-mega__toplink.is-current,
.fx-mega__topbtn.is-current {
  color: var(--fe-primary-ink);
}
.fx-mega__toplink:focus-visible,
.fx-mega__topbtn:focus-visible,
.fx-mega__item:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}
.fx-mega__chev {
  width: 14px;
  height: 14px;
  opacity: 0.7;
  transition: transform 120ms ease;
}
.fx-mega__topbtn.is-open .fx-mega__chev {
  transform: rotate(180deg);
}

.fx-mega__panel {
  position: absolute;
  top: 100%;
  inset-inline-start: 0;
  z-index: 40;
  display: grid;
  grid-auto-flow: column;
  /* A fixed column: the panel is shrink-to-fit (absolutely placed), so a
     `minmax()` track settles at its minimum and a short line such as "Sunucu
     geneli ayarlar ve e-posta" broke at its hyphen (measured at 200px). Three
     columns still end inside a 1024px window (e2e 198). */
  grid-auto-columns: 232px;
  gap: 20px;
  margin-block-start: 2px;
  padding: 14px;
  max-width: calc(100vw - 32px);
  max-height: calc(100vh - 80px);
  overflow: auto;
  background: var(--fe-bg);
  color: var(--fe-text);
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-lg);
  box-shadow: var(--fe-shadow);
}
.fx-mega__section {
  min-width: 0;
}
.fx-mega__heading,
.fx-mega__grouphead {
  margin: 0;
  padding-block: 4px;
  padding-inline: 10px;
  color: var(--fe-text-muted);
  font-size: var(--fe-text-xs);
  font-weight: 600;
  letter-spacing: 0.02em;
}
.fx-mega__item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  height: auto;
  padding-block: 6px;
  padding-inline: 10px;
  border-radius: var(--fe-radius-md);
  color: var(--fe-text);
  text-decoration: none;
}
.fx-mega__item:hover {
  background: var(--fe-bg-hover);
}
.fx-mega__item.is-current {
  background: var(--fe-primary-soft);
  color: var(--fe-primary-ink);
}
.fx-mega__icon {
  flex: 0 0 16px;
  width: 16px;
  height: 16px;
  margin-block-start: 1px;
  color: var(--fe-text-muted);
}
.fx-mega__item.is-current .fx-mega__icon {
  color: var(--fe-primary-ink);
}
.fx-mega__glyph,
.fx-mega__glyph svg {
  display: block;
  width: 16px;
  height: 16px;
}
.fx-mega__text {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.fx-mega__label {
  font-size: var(--fe-text-md);
  font-weight: 500;
  line-height: 1.3;
}
.fx-mega__hint {
  color: var(--fe-text-muted);
  font-size: var(--fe-text-xs);
  line-height: 1.35;
}
.fx-mega__item.is-current .fx-mega__hint {
  color: var(--fe-text-muted);
}

/* ── list (a phone's drawer) ── */
.fx-mega--list {
  display: block;
}
.fx-mega__list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.fx-mega__group {
  margin-block-start: 10px;
  padding-block-start: 10px;
  border-block-start: 1px solid var(--fe-border-soft);
}
.fx-mega__grouphead {
  color: var(--fe-text);
  font-size: var(--fe-text-sm);
}
.fx-mega__list .fx-mega__section + .fx-mega__section {
  margin-block-start: 6px;
}
.fx-mega__list .fx-mega__item {
  align-items: center;
}
.fx-mega__list .fx-mega__icon {
  margin-block-start: 0;
}
/* === /#82 === */

/* === #168 - the admin panel's search (components/PanelSearch.vue) ===
   ⚠ Here and not in the component, for MegaMenu's reason: a single-file
   component's styles reach only a bundle that imports it. */
.fx-psearch {
  position: relative;
  display: inline-flex;
  min-width: 0;
}
.fx-psearch__trigger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* ⚠ Said outright, like the mega menu's buttons: a host's `button` rule
     reaches this control (core lesson #639). */
  height: var(--fe-h-md);
  padding-block: 0;
  padding-inline: 10px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-md);
  background: var(--fe-bg-elev);
  color: var(--fe-text-muted);
  font: inherit;
  font-size: var(--fe-text-md);
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
}
.fx-psearch__trigger:hover,
.fx-psearch__trigger[aria-expanded="true"] {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fx-psearch__trigger:focus-visible,
.fx-psearch__iconbtn:focus-visible,
.fx-psearch__chip:focus-visible,
.fx-psearch__clearall:focus-visible {
  outline: 2px solid var(--fe-primary);
  outline-offset: 1px;
}
.fx-psearch--compact .fx-psearch__trigger {
  /* A phone: the glyph alone, the size of the bar's other buttons. */
  border-color: transparent;
  background: transparent;
  padding-inline: 8px;
}
/* The box's word and key cap come in where the bar has room for them: below
   1536px the mega menu, the quota, the bell and the account's name (from
   1280px) leave room for the glyph alone, as they did for the old search
   button (measured at 1024px by e2e 198, in English and Turkish). */
.fx-psearch__trigger-text,
.fx-psearch__kbd {
  display: none;
}
@media (min-width: 1536px) {
  .fx-psearch--box .fx-psearch__trigger {
    min-width: 12rem;
  }
  .fx-psearch--box .fx-psearch__trigger-text {
    display: inline;
    flex: 1 1 auto;
    text-align: start;
  }
  .fx-psearch--box .fx-psearch__kbd {
    display: inline-block;
  }
}
.fx-psearch__kbd {
  padding-block: 2px;
  padding-inline: 5px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  background: var(--fe-bg);
  color: var(--fe-text-muted);
  font-family: var(--fe-font-mono);
  font-size: var(--fe-text-xs);
  line-height: 1.2;
}
.fx-psearch__glyph,
.fx-psearch__glyph svg,
.fx-psearch__svg,
.fx-psearch__svg > svg {
  display: block;
  width: 16px;
  height: 16px;
}
.fx-psearch__glyph {
  flex: 0 0 16px;
}

/* ── the panel: under the box, or over the whole window ── */
.fx-psearch__layer {
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--fe-bg);
  color: var(--fe-text);
  font-family: var(--fe-font);
  font-size: var(--fe-text-md);
}
.fx-psearch__layer--drop {
  /* top / right (left in RTL) / width / max-height come from the box's place
     (lib/anchoredPanel), set on the element. */
  position: fixed;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-lg);
  box-shadow: var(--fe-shadow);
  overflow: hidden;
}
.fx-psearch__layer--full {
  /* A phone: the whole window below the host's own strip (--fe-overlay-top,
     web/tests/api/overlayTop.test.ts). */
  position: fixed;
  inset: var(--fe-overlay-top, 0px) 0 0;
}
.fx-psearch__field {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  padding-block: 8px;
  padding-inline: 12px;
  border-block-end: 1px solid var(--fe-border-soft);
}
.fx-psearch__glyph--field {
  color: var(--fe-text-muted);
}
.fx-psearch__input {
  flex: 1 1 auto;
  min-width: 0;
  height: var(--fe-h-md);
  padding: 0;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--fe-text);
  font: inherit;
  /* 16px: a smaller field makes Safari on a phone zoom the page in. */
  font-size: 16px;
}
.fx-psearch__input::placeholder {
  color: var(--fe-text-muted);
}
.fx-psearch__iconbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: var(--fe-h-md);
  height: var(--fe-h-md);
  padding: 0;
  border: 0;
  border-radius: var(--fe-radius-md);
  background: transparent;
  color: var(--fe-text-muted);
  cursor: pointer;
}
.fx-psearch__iconbtn:hover {
  background: var(--fe-bg-hover);
  color: var(--fe-text);
}
.fx-psearch__list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-block: 6px;
  padding-inline: 6px;
}
.fx-psearch__layer--drop .fx-psearch__list {
  max-height: 460px;
}
.fx-psearch__group + .fx-psearch__group {
  margin-block-start: 6px;
}
.fx-psearch__heading {
  padding-block: 6px 4px;
  padding-inline: 10px;
  color: var(--fe-text-muted);
  font-size: var(--fe-text-xs);
  font-weight: 600;
  letter-spacing: 0.02em;
}
.fx-psearch__row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 40px;
  padding-block: 6px;
  padding-inline: 10px;
  border-radius: var(--fe-radius-md);
  cursor: pointer;
}
.fx-psearch__row.is-active {
  background: var(--fe-primary-soft);
  color: var(--fe-primary-ink);
}
.fx-psearch__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 20px;
  width: 20px;
  /* The em a file tile sizes itself from (the command palette's 18px). */
  font-size: 18px;
  color: var(--fe-text-muted);
}
.fx-psearch__row.is-active .fx-psearch__icon {
  color: var(--fe-primary-ink);
}
.fx-psearch__text {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.fx-psearch__label,
.fx-psearch__detail {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fx-psearch__label {
  font-weight: 500;
  line-height: 1.3;
}
.fx-psearch__detail {
  color: var(--fe-text-muted);
  font-size: var(--fe-text-xs);
  line-height: 1.35;
}
.fx-psearch__row.is-active .fx-psearch__detail {
  color: var(--fe-primary-ink);
}
.fx-psearch__remove {
  width: 28px;
  height: 28px;
}
.fx-psearch__note {
  margin: 0;
  padding-block: 14px;
  padding-inline: 12px;
  color: var(--fe-text-muted);
  font-size: var(--fe-text-sm);
  text-align: center;
}
.fx-psearch__note--warn {
  color: var(--fe-warning);
}
.fx-psearch__foot {
  display: flex;
  flex: 0 0 auto;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding-block: 8px;
  padding-inline: 12px;
  border-block-start: 1px solid var(--fe-border-soft);
  color: var(--fe-text-muted);
  font-size: var(--fe-text-xs);
}
.fx-psearch__foot-label {
  margin-inline-end: 2px;
}
.fx-psearch__chip {
  height: auto;
  padding-block: 2px;
  padding-inline: 7px;
  border: 1px solid var(--fe-border);
  border-radius: var(--fe-radius-sm);
  background: var(--fe-bg-elev);
  color: var(--fe-text);
  font-family: var(--fe-font-mono);
  font-size: var(--fe-text-xs);
  line-height: 1.4;
  cursor: pointer;
}
.fx-psearch__chip:hover {
  background: var(--fe-bg-hover);
}
.fx-psearch__chip.is-on {
  border-color: var(--fe-primary);
  background: var(--fe-primary-soft);
  color: var(--fe-primary-ink);
}
.fx-psearch__clearall {
  height: auto;
  margin-inline-start: auto;
  padding-block: 2px;
  padding-inline: 6px;
  border: 0;
  border-radius: var(--fe-radius-sm);
  background: transparent;
  color: var(--fe-text-muted);
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}
.fx-psearch__clearall:hover {
  color: var(--fe-text);
}
/* === /#168 === */
