/*==============================================================================
  sidemenu.css  --  NasaOS / eDesk left side menu

  Visual direction
  ----------------
  A single rounded card floating on the wallpaper, not a flush chrome rail.
  The wallpaper stays part of the product's identity, so the panel sits on it
  as translucent glass rather than covering it.

  Everything is quiet at rest. The one accent in the list is the left bar on
  rows whose app is currently open - it encodes real state (it mirrors the
  taskbar), which is why it is allowed to be the loudest thing here.

  Tokens live on .sideMenu-host so a host app can retheme without touching
  selectors below.
==============================================================================*/

.sideMenu-host {
  /* Version marker, readable from the console:
     getComputedStyle(document.querySelector('.sideMenu-host'))
       .getPropertyValue('--sm-version')                                  */
  --sm-version: '2026-08-12.5';

  /* geometry */
  --sm-width:      222px;   /* was 268 - tuned so a single-column tile has
                                just enough gutter to breathe, not spare */
  --sm-width-min:   64px;    /* collapsed */
  --sm-inset:       14px;
  --sm-radius:      18px;
  --sm-row-h:       80px;
  /* THE control for icon size - verified end to end (computed box tracks
     this token, background-size:contain scales the artwork to it).
     Note ~10% of the box is padding baked into the SVG canvases, so the
     visible glyph lands near 0.9 x this value: 48px -> ~43px of ink.      */
  --sm-ico:         72px;

  /* LIGHT theme (default). The veil has to be opaque enough to carry black
     text over a mid-blue wallpaper - a thin white wash reads as mid-dark
     and nothing legible sits on it. Surface and ink move TOGETHER; changing
     one without the other is what made the labels unreadable before.      */
  /* Same white veil as dark mode, only marginally stronger. 0.55 was too
     opaque: over a near-white wallpaper it computed to (247,250,253) -
     indistinguishable from solid white, so the clouds behind it vanished
     and the panel stopped reading as glass. At 0.22 the wallpaper shows
     through and the border + shadow carry the panel edge instead.       */
  --sm-surface:     rgba(255,255,255,0.22);
  --sm-border:      rgba(255,255,255,0.55);
  --sm-shadow:      0 14px 44px rgba(6,12,22,0.28);
  --sm-hover:       rgba(22,35,47,0.07);
  --sm-fav:         #E09B12;

  --sm-ink:         #16232F;
  --sm-muted:       #5A6B7E;
  --sm-accent:      #2F6FED;
  --sm-accent-soft: rgba(47,111,237,0.14);
  --sm-rule:        rgba(22,35,47,0.13);

  /* Absolute placement inside pnlDesktop. UniGUI writes inline left/top/
     width/height on the panel element, so these have to win.              */
  position: absolute !important;
  left:   var(--sm-inset) !important;
  right:  auto !important;
  top:    var(--sm-inset) !important;
  bottom: var(--sm-inset) !important;
  width:  var(--sm-width) !important;
  height: auto !important;

  /* Sits in the desktop's own stacking level, BELOW UniGUI windows
     (~19000) so a maximised form covers it completely. Raising this above
     windows also makes backdrop-filter sample the window painted behind
     the card, which is what made the panel change colour.                 */
  z-index: 1 !important;

  background: transparent !important;
  border: 0 !important;
  overflow: visible !important;
  transition: width .18s ease;
}

/*------------------------------------------------------------- Ext panel --
  .sideMenu-host is not a plain div - it's a real Ext panel
  (x-panel x-panel-default), and Ext's default skin paints its OWN opaque
  body background/border on every panel (#f2f1f0, #e0dbd6) regardless of
  theme. That paint sits BEHIND .sideMenu (the inner flex container that
  carries --sm-surface), so however correct the glass token is, it was
  rendering on top of an opaque near-white panel underneath - which is what
  read as "solid white" in both themes, since Ext's rule never checked
  body.dark-theme at all.

  Neutralise Ext's own paint on the outer shell; ours is the only surface.  */

.sideMenu-host.x-panel-default,
.sideMenu-host .x-panel-body-default {
  background: transparent !important;
  border: 0 !important;
}

/* UniGUI injects an inner body element; let it fill the host. */
.sideMenu-host > .x-panel-body,
.sideMenu-host .x-panel-body {
  position: absolute !important;
  inset: 0 !important;
  width: auto !important;
  height: auto !important;
  background: transparent !important;
  border: 0 !important;
  overflow: visible !important;
}

/*---------------------------------------------------------------- the card */

.sideMenu {
  display: flex;
  flex-direction: column;
  height: 100%;
  box-sizing: border-box;
  padding: 10px 8px 8px;
  border-radius: var(--sm-radius);
  background: var(--sm-surface);
  border: 1px solid var(--sm-border);
  box-shadow: var(--sm-shadow);
  backdrop-filter: blur(24px) saturate(165%);
  -webkit-backdrop-filter: blur(24px) saturate(165%);
  color: var(--sm-ink);
  font: 400 13px/1.35 -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  overflow: hidden;
}

/*-------------------------------------------------------------------- head */

.sideMenu__head {
  /* Plain block. An earlier attempt made this a grid with named areas, but
     the JS wraps avatar+name in .sideMenu__headRow, so the grid only ever
     saw the WRAPPER as one child - the areas never applied and the name
     dropped below the avatar. Matching the markup is simpler than fighting
     it: headRow is the flex row, badgeRow sits under it, logout is pinned.  */
  position: relative;
  padding: 2px 34px 10px 6px;   /* right gutter reserves room for logout */
  border-bottom: 1px solid var(--sm-rule);
}

.sideMenu__headRow {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
}

/* Badge row: only rendered when a badge exists, so it costs nothing in
   production where it is normally absent.                                */
.sideMenu__badgeRow {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 5px;
  margin-top: 6px;
}

.sideMenu__avatar {
  width: 30px; height: 30px;
  border-radius: 50%;
  object-fit: cover;
  flex: 0 0 auto;
  background: var(--sm-hover);
}

.sideMenu__avatar--initials {
  display: grid;
  place-items: center;
  background: var(--sm-accent-soft);
  color: var(--sm-accent);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .02em;
}

.sideMenu__user {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sideMenu__icobtn {
  /* Pinned into .sideMenu__head's top-right corner (see position:relative
     there) rather than sitting inline, now that a badge row can push a
     second line of content below the name.                               */
  position: absolute;
  top: 2px; right: 2px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 28px; height: 28px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--sm-muted);
  cursor: pointer;
  transition: background .14s ease, color .14s ease;
}
.sideMenu__icobtn:hover { background: var(--sm-hover); color: var(--sm-ink); }

/*------------------------------------------------------------------ search */

.sideMenu__badge {
  flex: 0 0 auto;
  padding: 2px 6px;
  border-radius: 5px;
  background: var(--sm-accent-soft);
  color: var(--sm-accent);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  white-space: nowrap;
}
.sideMenu__badge + .sideMenu__badge { margin-left: -4px; }

/*------------------------------------------------------- identity block ---
  Office / Branch / Reg. code. A two-column definition list: a fixed muted
  key column so the eye can jump straight down to the values, which is what
  people actually read. Labels are spelled out rather than implied by
  hierarchy - this data is checked rarely, so it has to be unambiguous when
  it is.
---------------------------------------------------------------------------*/

.sideMenu__org {
  margin: 8px 6px 0;
  padding: 9px 8px 10px;
  border-radius: 10px;
  background: rgba(255,255,255,0.07);
  flex: 0 0 auto;
  text-align: center;      /* centred in BOTH directions, as requested */
}

/* Stacked, not two columns: at 222px a fixed 62px key column left too
   little for the value and forced ellipsis on the office name.          */
.sideMenu__orgRow {
  display: block;
  min-width: 0;
}
.sideMenu__orgRow + .sideMenu__orgRow { margin-top: 4px; }

.sideMenu__orgKey {
  display: block;
  margin: 0;
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--sm-muted);
}
.sideMenu__orgKey:empty { display: none; }

.sideMenu__orgVal {
  display: block;
  margin: 0;
  min-width: 0;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--sm-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sideMenu__searchWrap {
  position: relative;
  margin: 10px 4px 8px;
  flex: 0 0 auto;
}

.sideMenu__searchIco {
  position: absolute;
  left: 10px; top: 50%;
  transform: translateY(-50%);
  color: var(--sm-muted);
  pointer-events: none;
}

.sideMenu__search {
  width: 100%;
  height: 34px;
  box-sizing: border-box;
  padding: 0 10px 0 31px;
  border: 1px solid var(--sm-rule);
  border-radius: 10px;
  background: rgba(255,255,255,0.55);
  color: var(--sm-ink);
  font: inherit;
  outline: none;
  transition: border-color .14s ease, box-shadow .14s ease;
}
.sideMenu__search::placeholder { color: var(--sm-muted); }
.sideMenu__search:focus {
  border-color: var(--sm-accent);
  box-shadow: 0 0 0 3px var(--sm-accent-soft);
}

/*-------------------------------------------------------------- scroll box */

.sideMenu__scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 0 2px 4px;
  scrollbar-width: thin;
  scrollbar-color: var(--sm-rule) transparent;
  overscroll-behavior: contain;
}
.sideMenu__scroll::-webkit-scrollbar        { width: 8px; }
.sideMenu__scroll::-webkit-scrollbar-thumb  {
  background: var(--sm-rule);
  border-radius: 8px;
  border: 2px solid transparent;
  background-clip: content-box;
}
.sideMenu__scroll::-webkit-scrollbar-track  { background: transparent; }

/* Section label. Uppercase + tracking so it reads as structure, not content. */
.sideMenu__eyebrow {
  margin: 12px 8px 5px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .10em;
  text-transform: uppercase;
  color: var(--sm-muted);
}
.sideMenu__eyebrow:first-child { margin-top: 4px; }

.sideMenu__empty {
  margin: 18px 10px;
  color: var(--sm-muted);
  font-size: 12.5px;
}

/*------------------------------------------------------------------- tiles */
/* At 72px the icon stopped being a bullet point and became the content, so
   the caption moves UNDER it (launcher-style) instead of ellipsising beside
   it. Two columns: caption width doubles (~112px vs ~136 shared with a
   72px icon) and the list is half as tall as one big-icon column.         */

.sideMenu__scroll {
  display: grid;
  /* Single column: one tile per row, full card width. Reads as a vertical
     launcher rail - the caption gets the whole width, so even the longest
     names fit on ONE line in practice.                                    */
  grid-template-columns: 1fr;
  gap: 6px;
  align-content: start;
}
/* Headings + empty-state span the full card width inside the grid. */
.sideMenu__scroll .sideMenu__eyebrow,
.sideMenu__scroll .sideMenu__empty { grid-column: 1 / -1; }

.sideMenu__item {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
  min-height: calc(var(--sm-ico) + 42px);
  padding: 10px 4px 8px;   /* less side padding - width dropped, icon/caption
                              sizes did not, so the tile needs the room     */
  border-radius: 12px;
  text-align: center;
  cursor: pointer;
  user-select: none;
  transition: background .14s ease;
}
.sideMenu__item:hover      { background: var(--sm-hover); }
.sideMenu__item:focus-visible {
  outline: 2px solid var(--sm-accent);
  outline-offset: -2px;
}

/* Running-app indicator: the signature element. Hidden until it means
   something, so the resting list stays completely calm.                   */
.sideMenu__bar {
  position: absolute;
  top: 0; left: 50%;
  height: 3px; width: 0;
  border-radius: 0 0 3px 3px;
  background: var(--sm-accent);
  transform: translateX(-50%);
  transition: width .2s cubic-bezier(.2,.8,.3,1);
}
.sideMenu__item.is-open .sideMenu__bar { width: 34px; }
.sideMenu__item.is-open                { background: var(--sm-accent-soft); }
.sideMenu__item.is-open .sideMenu__label { font-weight: 700; }

.sideMenu__ico {
  /* Rendered as a background-image on a span (see rowHtml in sidemenu.js).
     display:block is load-bearing: a span is inline by default, and inline
     elements IGNORE width/height entirely. Inside our flex row the span is
     blockified automatically, but any global stylesheet that changes the
     row's display (Ext resets, third-party CSS) would silently shrink the
     icon back to its content size - so the box must not depend on context.
     !important throughout for the same reason: this box is not negotiable. */
  display: block !important;
  box-sizing: border-box !important;
  flex: 0 0 var(--sm-ico) !important;
  width: var(--sm-ico) !important;
  height: var(--sm-ico) !important;
  min-width: var(--sm-ico) !important;
  min-height: var(--sm-ico) !important;
  padding: 0 !important;
  background-repeat: no-repeat !important;
  background-position: center center !important;
  background-size: contain !important;
  pointer-events: none;
  transition: transform .16s ease;
}
.sideMenu__item:hover .sideMenu__ico { transform: scale(1.07); }

.sideMenu__label {
  width: 100%;
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: -.005em;
  line-height: 1.25;
  /* Full caption on up to two lines - that fits every current shortcut
     ("Wholesale management", "B.Client Accounting") without truncation.
     Clamp exists only as a guard for future absurdly long names.          */
  white-space: normal;
  overflow-wrap: break-word;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Star stays out of the way until the row is engaged, so 27 rows do not
   turn into 27 competing controls.                                        */
.sideMenu__star {
  position: absolute;
  top: 4px; right: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px; height: 24px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--sm-muted);
  cursor: pointer;
  opacity: 0;
  transition: opacity .12s ease, color .12s ease;
}
.sideMenu__item:hover .sideMenu__star,
.sideMenu__item:focus-within .sideMenu__star,
.sideMenu__item.is-fav .sideMenu__star { opacity: 1; }
.sideMenu__item.is-fav .sideMenu__star { color: var(--sm-fav); }
.sideMenu__star:hover { background: var(--sm-hover); color: var(--sm-fav); }

/*--------------------------------------------------------------- collapsed */

.sideMenu-host.is-collapsed { width: var(--sm-width-min) !important; }
.sideMenu-host.is-collapsed .sideMenu__user,
.sideMenu-host.is-collapsed .sideMenu__label,
.sideMenu-host.is-collapsed .sideMenu__scroll { grid-template-columns: 1fr; }
.sideMenu-host.is-collapsed .sideMenu__item { min-height: calc(var(--sm-ico) + 20px); padding: 10px 4px; }
.sideMenu-host.is-collapsed .sideMenu__star,
.sideMenu-host.is-collapsed .sideMenu__eyebrow,
.sideMenu-host.is-collapsed .sideMenu__searchWrap,
.sideMenu-host.is-collapsed .sideMenu__icobtn,
.sideMenu-host.is-collapsed .sideMenu__org,
.sideMenu-host.is-collapsed .sideMenu__badge { display: none; }
.sideMenu-host.is-collapsed .sideMenu__item   { justify-content: center; padding: 0; }
.sideMenu-host.is-collapsed .sideMenu__head   { justify-content: center; }

/*--------------------------------------------------------------------- RTL */
/* Hebrew and Arabic installs: mirror the card to the right edge. */
.sideMenu-host.is-rtl {
  left: auto !important;
  right: var(--sm-inset) !important;
}
.sideMenu-host.is-rtl .sideMenu       { direction: rtl; }
.sideMenu-host.is-rtl .sideMenu__star { right: auto; left: 4px; }
.sideMenu-host.is-rtl .sideMenu__bar  { left: auto; right: 2px; }
.sideMenu-host.is-rtl .sideMenu__search    { padding: 0 31px 0 10px; }
.sideMenu-host.is-rtl .sideMenu__searchIco { left: auto; right: 10px; }
.sideMenu-host.is-rtl .sideMenu__org       { direction: rtl; }

/* Header mirrors by direction alone: the flex row reverses, and the pinned
   logout button swaps corners. No margin arithmetic to keep in sync.     */
.sideMenu-host.is-rtl .sideMenu__head    { padding: 2px 6px 10px 34px; direction: rtl; }
.sideMenu-host.is-rtl .sideMenu__icobtn  { right: auto; left: 2px; }

/*-------------------------------------------------------------- dark theme */
/* Surface AND ink flip together. Anything that reads body.dark-theme depends
   on applyTheme() keeping that class stable - it sets it through
   Ext.getBody().addCls, because Ext rewrites body.className wholesale and
   silently drops classes added with plain classList.                       */

.dark-theme .sideMenu-host {
  /* The ORIGINAL glass: a white veil at 0.14, measured back off the
     small-icon screenshot (panel (50,83,135) over wallpaper (31,55,93)).
     Reads as lifted rather than sunken. Safe with white ink now - it was
     only unusable back when the ink was still dark.                      */
  --sm-surface:     rgba(255,255,255,0.14);
  --sm-border:      rgba(255,255,255,0.30);
  --sm-shadow:      0 14px 44px rgba(6,12,22,0.34);
  --sm-hover:       rgba(255,255,255,0.13);
  --sm-fav:         #FFC24D;

  --sm-ink:         #FFFFFF;
  --sm-muted:       rgba(255,255,255,0.66);
  --sm-accent:      #8FB4FF;
  --sm-accent-soft: rgba(143,180,255,0.22);
  --sm-rule:        rgba(255,255,255,0.15);
}
.dark-theme .sideMenu__search { background: rgba(255,255,255,0.10); }

/*---------------------------------------------------------------- narrow  */
/* Under 900px the card becomes an overlay drawer instead of stealing width. */
@media (max-width: 900px) {
  .sideMenu-host { --sm-width: 240px; }
}

/*----------------------------------------------------- reduced motion ---- */
@media (prefers-reduced-motion: reduce) {
  .sideMenu-host,
  .sideMenu__item,
  .sideMenu__ico,
  .sideMenu__bar,
  .sideMenu__star,
  .sideMenu__search,
  .sideMenu__icobtn { transition: none !important; }
}

/*==============================================================================
  LIST LAYOUT  --  .sideMenu-host.is-list
  ---------------------------------------------------------------------------
  The earlier design: small icon, caption beside it, one row per app.

  Both layouts are kept because they are good at different things. Tiles give
  a big target and a strong icon read, which suits a short favourites list.
  List fits far more rows on screen (27 apps is ~1.3 screens instead of ~4)
  and puts every caption on one scannable left edge, which is what helps when
  hunting for "Wholesale management".

  Pure class swap - the markup is identical in both modes, so switching never
  re-renders or re-fetches anything.
==============================================================================*/

.sideMenu-host.is-list {
  --sm-width: 252px;
  --sm-ico:    30px;
  --sm-row-h:  44px;
}

.sideMenu-host.is-list .sideMenu__scroll { gap: 2px; }

.sideMenu-host.is-list .sideMenu__item {
  flex-direction: row;
  align-items: center;
  justify-content: flex-start;
  text-align: left;
  gap: 10px;
  min-height: var(--sm-row-h);
  padding: 0 6px 0 10px;
  border-radius: 10px;
}

/* Caption returns to a single line beside the icon. */
.sideMenu-host.is-list .sideMenu__label {
  width: auto;
  flex: 1 1 auto;
  min-width: 0;
  display: block;
  -webkit-line-clamp: unset;
  -webkit-box-orient: horizontal;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 13px;
  line-height: 1.3;
}

/* Star sits inline at the end of the row instead of in the tile corner. */
.sideMenu-host.is-list .sideMenu__star {
  position: static;
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
}

/* Open-app marker returns to a left bar. */
.sideMenu-host.is-list .sideMenu__bar {
  top: 50%; left: 2px;
  width: 3px; height: 0;
  border-radius: 3px;
  transform: translateY(-50%);
  transition: height .2s cubic-bezier(.2,.8,.3,1);
}
.sideMenu-host.is-list .sideMenu__item.is-open .sideMenu__bar { width: 3px; height: 20px; }

/* RTL: mirror the row padding, the bar and the text alignment. */
.sideMenu-host.is-list.is-rtl .sideMenu__item  { padding: 0 10px 0 6px; }
.sideMenu-host.is-list.is-rtl .sideMenu__bar   { left: auto; right: 2px; }
.sideMenu-host.is-list.is-rtl .sideMenu__label { text-align: right; }

/*----------------------------------------------------- favourites in list --
  Favourites must read at a glance in BOTH layouts.

  Two belts here on purpose. .is-fav is a class the JS puts on the row, and
  aria-pressed="true" is set on the star button itself - keying off both
  means the marker survives if either signal is ever changed independently.

  The star is also FILLED when favourited rather than only recoloured: at
  22px an outline in gold and an outline in grey are easy to confuse,
  whereas filled versus hollow is unambiguous at any size, and it still
  reads for anyone who cannot separate the two hues.
---------------------------------------------------------------------------*/

.sideMenu__item.is-fav .sideMenu__star,
.sideMenu__star[aria-pressed="true"] {
  opacity: 1 !important;
  color: var(--sm-fav) !important;
}

/* Fill the glyph. The inline SVG is authored fill="none" stroke="currentColor",
   so the attribute has to be overridden, not just the colour.             */
.sideMenu__item.is-fav .sideMenu__star svg,
.sideMenu__star[aria-pressed="true"] svg {
  fill: currentColor !important;
  stroke: currentColor !important;
}

/* Non-favourites keep the hollow outline and stay hidden until hover. */
.sideMenu__star[aria-pressed="false"] svg { fill: none !important; }

/* In list mode the star sits at the row end - give it a little breathing
   room from the caption so a long label never crowds it.                 */
.sideMenu-host.is-list .sideMenu__star { margin-left: 4px; }
.sideMenu-host.is-list.is-rtl .sideMenu__star { margin-left: 0; margin-right: 4px; }

/*--------------------------------------------------- grid overflow guard --
  Measured cause of the missing star in list mode: the row was 271px wide
  inside a ~252px card, so the star (x 257-279) fell outside .sideMenu's
  overflow:hidden and was clipped. It was rendering correctly the whole
  time - opacity 1, 22x22, gold.

  Why: a grid item's default min-width is AUTO, so a "1fr" track does not
  cap at the container - it GROWS to the item's min-content width. In list
  mode the label is white-space:nowrap, which makes min-content = icon +
  full untruncated caption + star. "User management" therefore stretched
  the track past the card.

  minmax(0, 1fr) lets the track shrink below min-content, and min-width:0
  on the row does the same one level down, so the label's own
  text-overflow:ellipsis can finally take effect.

  Tiles mode never hit this because its captions wrap.
---------------------------------------------------------------------------*/

.sideMenu__scroll { grid-template-columns: minmax(0, 1fr); }
.sideMenu__item   { min-width: 0; }
.sideMenu-host.is-list .sideMenu__scroll { grid-template-columns: minmax(0, 1fr); }
.sideMenu-host.is-list .sideMenu__item   { min-width: 0; overflow: hidden; }
