/* ============================================================
   style.css — Design tokens, base, and shell layout
   Taqwa Automobile Service Center
   ============================================================ */

/* ---------- tokens ---------- */
:root {
  /* palette — industrial workshop */
  --ink-900: #16202c;        /* sidebar / darkest */
  --ink-800: #1b2735;
  --ink-700: #24344a;
  --paper:   #f2f4f7;        /* workspace background */
  --surface: #ffffff;        /* cards */
  --line:    #dde3ea;
  --text:    #1c2733;
  --text-2:  #5b6b7c;
  --text-3:  #8a97a6;

  --amber:      #e59a17;     /* accent — workshop amber */
  --amber-deep: #c47f06;
  --amber-soft: #fdf3df;

  --good:      #1f9d61;  --good-soft: #e3f5ec;
  --info:      #2b7de0;  --info-soft: #e7f0fc;
  --warn:      #d97a08;  --warn-soft: #fdf1e0;
  --bad:       #d8434e;  --bad-soft:  #fceaec;

  --sidebar-w: 248px;
  --topbar-h: 64px;
  --radius: 10px;
  --radius-sm: 7px;
  --shadow: 0 1px 2px rgba(22, 32, 44, .06), 0 4px 14px rgba(22, 32, 44, .05);
  --shadow-lift: 0 8px 28px rgba(22, 32, 44, .16);

  --font: 'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;

  /* Modern Light additions. New names only, so nothing that already reads a
     token changes meaning. */
  --line-strong: #d3dae3;    /* input and secondary-button borders */
  --amber-text:  #9a5a00;    /* amber used AS text on light surfaces (AA) */
  --focus-ring: 0 0 0 3px color-mix(in srgb, var(--amber) 28%, transparent);
}

/* Modern Light values for the screen. Inside @media screen on purpose: the
   printed job card, invoice, receipt and report keep the values above
   byte-for-byte (the report prints the on-screen markup). The theme blocks
   below are more specific than :root, so dark and system-dark still win. */
@media screen {
  :root {
    --paper:  #f5f7fa;
    --line:   #e5e9f0;
    --text:   #172131;
    --text-2: #566476;
    --text-3: #6b7889;       /* was #8a97a6: small muted text now reads at AA */

    --radius: 12px;
    --radius-sm: 8px;
    --shadow: 0 1px 2px rgba(16, 24, 40, .05), 0 1px 3px rgba(16, 24, 40, .04);
    --shadow-lift: 0 12px 32px rgba(16, 24, 40, .14), 0 2px 6px rgba(16, 24, 40, .06);
  }
}

/* ---------- dark theme ---------- */
:root[data-theme="dark"] {
  --paper:   #131b24;
  --surface: #1b2735;
  --line:    #2b3a4d;
  --text:    #e8edf2;
  --text-2:  #a4b1bf;
  --text-3:  #6d7c8c;

  --amber-soft: #3a2f14;
  --good-soft: #15321f;
  --info-soft: #17293f;
  --warn-soft: #382a10;
  --bad-soft:  #3b1c1f;

  --shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 4px 14px rgba(0, 0, 0, .25);
  --shadow-lift: 0 8px 28px rgba(0, 0, 0, .5);

  --line-strong: #3a4b60;
  --amber-text:  #f2b545;
}

/* "System" before app.js has resolved it. The pre-paint script in each
   page's <head> copies the saved preference as-is, so a saved "system"
   arrives as data-theme="system" -- which matched nothing above, and a
   dark-OS user got the light palette for every page load until app.js
   resolved it after the data loaded: a white screen between two dark
   pages. Same values as the dark block above. (The <head> script itself
   is pinned by a CSP hash in _headers, so this is fixed here instead.) */
@media (prefers-color-scheme: dark) {
  :root[data-theme="system"] {
    --paper:   #131b24;
    --surface: #1b2735;
    --line:    #2b3a4d;
    --text:    #e8edf2;
    --text-2:  #a4b1bf;
    --text-3:  #6d7c8c;

    --amber-soft: #3a2f14;
    --good-soft: #15321f;
    --info-soft: #17293f;
    --warn-soft: #382a10;
    --bad-soft:  #3b1c1f;

    --shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 4px 14px rgba(0, 0, 0, .25);
    --shadow-lift: 0 8px 28px rgba(0, 0, 0, .5);

    --line-strong: #3a4b60;
    --amber-text:  #f2b545;
  }
}

/* ---------- base ---------- */
* { box-sizing: border-box; }

html { font-size: 16px; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: .9rem;
  line-height: 1.55;
  color: var(--text);
  /* The topbar strip, painted in the page background itself. Every page
     is a full document load, and app.js draws the shell only once the data
     has arrived; until then this keeps the app's frame on screen instead of
     a blank page. Once the shell is drawn, the topbar covers it exactly. */
  background:
    linear-gradient(var(--line), var(--line)) 0 calc(var(--topbar-h) - 1px) / 100% 1px no-repeat,
    linear-gradient(var(--surface), var(--surface)) 0 0 / 100% var(--topbar-h) no-repeat,
    var(--paper);
  -webkit-font-smoothing: antialiased;
}
/* ...and the sidebar column with its logo, at the exact spot .brand-logo
   occupies, where the sidebar is docked (it becomes a drawer at 900px and
   below, see responsive.css). */
@media (min-width: 901px) {
  body {
    background:
      url("../assets/logo/logo-white.png") 18px 20px / 132px auto no-repeat,
      linear-gradient(var(--ink-900), var(--ink-900)) 0 0 / var(--sidebar-w) 100% repeat-y,
      linear-gradient(var(--line), var(--line)) 0 calc(var(--topbar-h) - 1px) / 100% 1px no-repeat,
      linear-gradient(var(--surface), var(--surface)) 0 0 / 100% var(--topbar-h) no-repeat,
      var(--paper);
  }
}

/* Page content waits, unpainted, until app.js has moved it into the
   shell -- otherwise its bare toolbar and empty tables flash for a frame. */
body > #page-content { visibility: hidden; }

body.no-scroll { overflow: hidden; }

h1, h2, h3, h4 { margin: 0; line-height: 1.25; font-weight: 600; }
p { margin: 0 0 .75em; }
a { color: var(--info); text-decoration: none; }
a:hover { text-decoration: underline; }

:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
  border-radius: 4px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ---------- shell layout ---------- */
.shell { min-height: 100vh; }

.sidebar {
  position: fixed;
  inset: 0 auto 0 0;
  width: var(--sidebar-w);
  background: var(--ink-900);
  color: #c6d0dc;
  display: flex;
  flex-direction: column;
  z-index: 40;
}

.sidebar__brand {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 20px 18px 16px;
  border-bottom: 1px solid rgba(255, 255, 255, .07);
}

.brand-logo {
  width: 132px;
  height: auto;
  display: block;
}

.brand-tag {
  font-size: .68rem;
  color: #8b99a9;
  letter-spacing: .05em;
}

.nav {
  flex: 1;
  overflow-y: auto;
  padding: 12px 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.nav__link {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 12px;
  border-radius: 8px;
  color: #c6d0dc;
  font-size: .88rem;
  border-left: 3px solid transparent;
}
.nav__link svg { flex: none; opacity: .8; }
.nav__link:hover { background: rgba(255, 255, 255, .06); color: #fff; text-decoration: none; }
.nav__link.is-active {
  background: rgba(229, 154, 23, .13);
  border-left-color: var(--amber);
  color: #fff;
  font-weight: 600;
}
.nav__link.is-active svg { color: var(--amber); opacity: 1; }

/* Social Inbox: a native <details>, so it opens with the keyboard and needs
   no script beyond remembering its state. */
.nav__summary {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 12px;
  border-radius: 8px;
  color: #c6d0dc;
  font-size: .88rem;
  border-left: 3px solid transparent;
  cursor: pointer;
}
.nav__summary::-webkit-details-marker { display: none; }
.nav__summary svg { flex: none; opacity: .8; }
.nav__summary:hover { background: rgba(255, 255, 255, .06); color: #fff; }
.nav__chevron { margin-left: auto; transition: transform .15s ease; }
.nav__inbox[open] .nav__chevron,
.nav__group[open] .nav__chevron { transform: rotate(180deg); }
/* Customer CRM: the group is marked while one of its pages is open, and that
   page's entry carries the same amber active state as a top-level link. */
.nav__group.has-active > .nav__summary { color: #fff; font-weight: 600; }
.nav__group.has-active > .nav__summary > svg:first-child { color: var(--amber); opacity: 1; }
a.nav__sub.is-active { background: rgba(229, 154, 23, .13); color: #fff; font-weight: 600; }
a.nav__sub.is-active svg { color: var(--amber); opacity: 1; }
.nav__subs {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin: 2px 0 4px 23px;
  padding-left: 8px;
  border-left: 1px solid rgba(255, 255, 255, .1);
}
.nav__sub {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 10px;
  border-radius: 7px;
  color: #c6d0dc;
  font-size: .82rem;
}
.nav__sub svg { flex: none; width: 16px; height: 16px; opacity: .8; }
/* Label over a small status line: the 248px column has no room for both side by side. */
.nav__sub-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
.nav__sub small { font-size: .68rem; color: #8b99a9; }
a.nav__sub:hover { background: rgba(255, 255, 255, .06); color: #fff; text-decoration: none; }
/* Not built yet: plain text, not a link, and says so. */
.nav__sub--off { color: #8592a3; cursor: default; }
.nav__sub--off svg { opacity: .55; }

/* Notifications Center: a top-level link with an amber count badge on the right. The count is a
   fixed design placeholder. */
.nav__notify > span:not(.nav__count) { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.nav__count {
  margin-left: auto;
  flex: none;
  min-width: 22px;
  height: 22px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--amber);
  color: var(--ink-900);
  font-size: .75rem;
  font-weight: 700;
  line-height: 22px;
  text-align: center;
}

/* Staff Management, to the approved reference: a rounded panel, amber outline group icon and bold
   title, roomier rows with outline icons and no guide line. Scoped to #navStaff, so every other
   link and group keeps the shared styles above; the sidebar is dark in both themes. */
#navStaff { border-radius: 14px; background: var(--ink-900); }
#navStaff > .nav__summary {
  gap: 11px; min-height: 48px; padding: 0 14px 0 17px; border-radius: 12px;
  color: #fff; font-size: .97rem; font-weight: 600;
}
#navStaff > .nav__summary > span,
#navStaff .nav__sub > span { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
#navStaff > .nav__summary > svg:first-child { color: var(--amber); opacity: 1; }
#navStaff .nav__chevron { color: #c6d0dc; opacity: .9; }
#navStaff > .nav__subs { gap: 1px; margin: 3px 0 6px; padding-left: 0; border-left: 0; }
#navStaff .nav__sub {
  gap: 13px; min-height: 44px; padding: 0 12px 0 34px; border-radius: 10px;
  color: #d5dce5; font-size: .94rem;
}
#navStaff .nav__sub svg { color: #aab6c5; opacity: 1; }
#navStaff a.nav__sub:hover { color: #fff; }
#navStaff a.nav__sub:hover svg { color: #d5dce5; }
#navStaff a.nav__sub.is-active { color: #fff; }
#navStaff a.nav__sub.is-active svg { color: var(--amber); }

.sidebar__foot {
  padding: 14px 18px;
  border-top: 1px solid rgba(255, 255, 255, .07);
  font-size: .76rem;
  color: #8b99a9;
  display: flex;
  align-items: center;
  gap: 8px;
}
.sidebar__foot-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--good);
  box-shadow: 0 0 0 3px rgba(31, 157, 97, .25);
}
/* Shown only while hydration is in flight; removed before the shell draws.
   Transparent, so the frame painted on <body> stays visible behind it, and
   centred in the content area. The text fades in only if the wait passes
   half a second, so an ordinary page change never flashes it. */
.app-loading {
  position: fixed; inset: var(--topbar-h) 0 0 0;
  display: flex; align-items: center; justify-content: center; gap: 10px;
  background: transparent;
  color: var(--text-2);
  font-size: .9rem;
  /* Under the sidebar drawer and topbar, and never in the way of a click:
     the shell above it is live while the data loads. */
  z-index: 20;
  pointer-events: none;
  animation: app-fade-in .2s ease-out .5s backwards;
}
@media (min-width: 901px) {
  .app-loading { left: var(--sidebar-w); }
}

/* ---------- page change ----------
   With a backend, app.js draws the shell before the data arrives (see
   drawShellEarly), so the sidebar and header stay put from page to page and
   only the content area waits. The content is hidden until its module has
   rendered it -- never shown as empty tables -- and then fades in: subtle,
   no movement. Browsers that support cross-page view transitions also fade
   the outgoing page instead of cutting it. Both are off under
   prefers-reduced-motion: the global rule above stops the animation, and
   the view transition is only declared without it. */
.shell.is-pending #page-content { visibility: hidden; }
.shell.is-revealed #page-content { animation: content-in .16s ease-out; }
@keyframes app-fade-in { from { opacity: 0; } }
/* Content and its placeholder fade in from part-way, never from nothing:
   a frame at opacity 0 is a frame of blank page background. */
@keyframes content-in { from { opacity: .35; } }

/* What the content area shows meanwhile: neutral placeholder shapes in the
   theme's own surface and line colours, laid out like a module page (a
   toolbar of fields over a card of rows), so the area is never a blank
   sheet of page background. Every load with a backend takes at least two
   round trips, so it shows at once with a short fade; app.js removes it the
   moment the real content is shown. While it is up, the loader's text is
   kept for screen readers only. */
.content-skeleton { display: none; }
.shell.is-pending .content-skeleton {
  display: block;
  animation: content-in .12s ease-out;
}
.content-skeleton__toolbar { display: flex; gap: 10px; margin-bottom: 18px; }
.content-skeleton__toolbar span {
  display: block;
  height: 38px;
  border-radius: var(--radius-sm);
  background: var(--surface);
  border: 1px solid var(--line);
}
.content-skeleton__card span {
  display: block;
  border-radius: var(--radius-sm);
  background: var(--line);
  animation: skeleton-pulse 1.4s ease-in-out infinite;
}
.content-skeleton__toolbar span:nth-child(1) { width: min(240px, 40%); }
.content-skeleton__toolbar span:nth-child(2),
.content-skeleton__toolbar span:nth-child(3) { width: min(140px, 25%); }
.content-skeleton__card {
  display: grid;
  gap: 14px;
  padding: 18px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.content-skeleton__card span { height: 16px; }
.content-skeleton__card span:nth-child(3n+2) { width: 82%; }
.content-skeleton__card span:nth-child(3n) { width: 64%; }
@keyframes skeleton-pulse { 50% { opacity: .55; } }
.shell.is-pending ~ .app-loading {
  clip-path: inset(50%);
  width: 1px; height: 1px;
  overflow: hidden;
}

@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  ::view-transition-old(root),
  ::view-transition-new(root) { animation-duration: .16s; }
}
.app-loading__dot {
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--amber);
  animation: app-loading-pulse 1s ease-in-out infinite;
}
@keyframes app-loading-pulse { 0%, 100% { opacity: .3; } 50% { opacity: 1; } }

/* The sign-in screen: shown when the Worker is up but this browser has no
   session. It covers the page because nothing behind it has been read. */
.app-gate {
  position: fixed; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--ink-900);
  padding: 24px;
  z-index: 90;
}
.app-gate__card {
  width: 100%; max-width: 360px;
  padding: 28px 24px;
  display: flex; flex-direction: column; gap: 14px;
}
.app-gate__logo { width: 150px; align-self: center; margin-bottom: 4px; }
.app-gate__title {
  font-size: 1.02rem; font-weight: 600; text-align: center;
  margin: 0 0 4px; color: var(--text-1);
}
.app-gate__submit { width: 100%; justify-content: center; }
/* Offered only where staff accounts can sign in (User Accounts, 0009). */
.app-gate__switch { align-self: center; margin-top: 2px; }

/* No server answering: the records on screen live in this browser only. */
.sidebar__foot-dot--local {
  background: var(--warn);
  box-shadow: 0 0 0 3px rgba(217, 122, 8, .25);
}
/* Shell drawn early, source not known yet ("Connecting…"). */
.sidebar__foot-dot--pending {
  background: #8b99a9;
  box-shadow: 0 0 0 3px rgba(139, 153, 169, .25);
}

.sidebar-scrim {
  position: fixed;
  inset: 0;
  background: rgba(10, 16, 22, .55);
  z-index: 35;
}

/* ---------- main column ---------- */
.main {
  margin-left: var(--sidebar-w);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.topbar {
  height: var(--topbar-h);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 0 24px;
  position: sticky;
  top: 0;
  z-index: 30;
}

.topbar__menu.icon-btn { display: none; }

.topbar__title h1 { font-size: 1.05rem; }
.topbar__date { font-size: .74rem; color: var(--text-3); }

.topbar__actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 14px;
}

.topbar__user { display: flex; align-items: center; gap: 10px; }
.avatar {
  width: 36px; height: 36px;
  border-radius: 50%;
  background: var(--amber-soft);
  color: var(--amber-deep);
  display: grid; place-items: center;
  font-weight: 600;
  font-size: .8rem;
  /* The avatar is a photo. `cover` fills the circle without stretching the
     face, and `flex: none` keeps it a circle rather than letting the topbar's
     flex row squash it -- which matters below 900px, where responsive.css
     hides .topbar__user-text and this becomes the only profile element.
     The amber background stays as the fallback if the image cannot load. */
  object-fit: cover;
  object-position: center;
  flex: none;
}
.topbar__user-text { line-height: 1.2; }
.topbar__user-text strong { display: block; font-size: .82rem; }
.topbar__user-text span { font-size: .7rem; color: var(--text-3); }

.icon-btn {
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--text-2);
  border-radius: 9px;
  cursor: pointer;
}
.icon-btn:hover { color: var(--text); border-color: var(--text-3); }

/* theme toggle icons */
.ico-moon { display: none; }
:root[data-theme="dark"] .ico-moon { display: block; }
:root[data-theme="dark"] .ico-sun { display: none; }

.content {
  flex: 1;
  padding: 24px;
  max-width: 1440px;
  width: 100%;
}
