/* ============================================================================
   Sky Employee Hub - "Console" design system
   v1 - 2026-09-16

   One source of truth for the whole hub. Change a colour here, it changes on
   every page. Page-specific layout still lives in each page's <style> block,
   but it consumes these tokens instead of inventing its own.

   GOLD RULE, from SKY DOCUMENT STYLE.pdf: gold is never body type. It is only
   ever a fill (with dark type on it), a hairline border, or a tracked label.
   SKY blue carries all ordinary emphasis so gold never has to do normal work.
   ========================================================================= */

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');

:root{
  /* ground + surfaces */
  --bg:#0b0d10;
  --surface:#141c25;
  --surface-2:#18222d;
  --surface-3:#1a2431;
  --line:#1f2b38;
  --line-soft:#1b242f;

  /* type */
  --text:#eef3f8;
  --text-strong:#ffffff;
  --text-2:#8195a8;
  /* Sept 17 2026 - raised after an audit of every page measured the real contrast on
     this ground. --text-3 and --text-4 were being used for things people actually have
     to READ - a shift date, "New hire paperwork", "1 of 15" - at 2.0 to 2.3:1, which is
     under even the loosest legibility bar and hopeless on a phone in a bright shop.
     These two are now legible in their own right; anything dimmer than this is
     decoration and must not carry a word that matters. */
  --text-3:#7c8fa3;
  --text-4:#64768a;

  /* brand */
  --gold:#f8d74c;
  --gold-dim:rgba(248,215,76,.10);
  --gold-line:rgba(248,215,76,.45);
  --sky:#2ea2cd;
  --blue:#1c72b9;

  /* status - readable on the dark ground, not the print palette */
  --ok:#3fbf7f;
  --ok-bg:rgba(63,191,127,.10);
  --warn:#e8b23a;
  --warn-bg:rgba(232,178,58,.10);
  --bad:#e5564b;
  --bad-bg:rgba(229,86,75,.10);

  /* ── COMPLETING THE SET ─ Sept 21 2026 ──────────────────────────────────────
     Karam: "we need to make sure everything is to standard... every single thing
     on the hub."

     An audit of every rendered element across the whole Hub found twelve colours
     in use that had no token. They were not sloppiness - there was no token for
     "text on a gold fill", or "--bad quiet enough to read as body copy", so each
     page invented one. Pages cannot use a vocabulary that does not exist. Every
     value below was ALREADY on screen somewhere; naming it is what stops the
     next page inventing a thirteenth. */
  --on-sky:#06222e;       /* type sitting on a --sky fill */
  --on-gold:#04141c;      /* type sitting on a --gold fill */
  --text-5:#5c7085;       /* one step quieter than --text-4, for dense lists */
  --ok-soft:#8ee5ba;      /* --ok used as text on the dark ground */
  --bad-soft:#ffb3ad;     /* --bad used as text on the dark ground */
  --bad-dim:#d4948e;      /* --bad, muted again - a warning you are not acting on */
  --gold-soft:#f2d193;    /* --gold at body weight, where full gold would shout */
  --sky-hover:#2790b6;
  --ok-line:rgba(63,191,127,.45);
  --warn-line:rgba(232,178,58,.45);
  --bad-line:rgba(229,86,75,.45);

  --r-card:13px;
  --r-ctl:10px;
  --font:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;

  /* ── TYPE SCALE ─────────────────────────────────────────────────────────────
     Added Sept 17 2026. Karam: "the propotions dont make sense, the CALL STORE OR
     CANT MAKE IT are hhuge, the actual hours are small and tiny."

     He was describing a symptom. The cause was that every element got its own
     font-size when it was written, measured against nothing, so a button added
     late ended up louder than the fact it acts on. These six sizes are the whole
     vocabulary. Nothing in the hub sets a font-size that is not one of them.

     THE RULE, and it is not negotiable:
       On any card or row, the thing that card exists to tell you is --fs-hero or
       --fs-title, and it is the LARGEST text in it. Labels, dates, store names and
       counts are --fs-meta or --fs-label. An action is NEVER larger or heavier
       than the fact it acts on - a button that shouts louder than the shift time
       is the bug, whatever its font-size happens to be.

     Applies to all three sign-ins. Employee, store and admin are one product. */
  --fs-hero:23px;    /* the answer the screen exists to give: a shift time */
  --fs-title:19px;   /* page and dialog titles */
  --fs-lg:16px;      /* a name, a store, a section heading inside a card */
  --fs-body:15px;    /* sentences */
  --fs-meta:12.5px;  /* dates, counts, store names, secondary detail */
  --fs-label:10px;   /* ALL-CAPS tracked labels only */

  --fw-hero:700;
  --fw-strong:600;
}

/* ── Sept 21 2026 · unclaimed browser defaults ──────────────────────────────
   The audit found colours on screen that no Hub rule had ever set: <hr> was
   painting itself grey (#808080) and bare form controls were taking the
   browser's own dark-mode fill (#3b3b3b on a #858585 border). Nobody wrote
   those - they are what you get when a rule is simply absent. Declared here,
   immediately after the tokens, so every specific rule further down still wins. */
hr{ border:0; border-top:1px solid var(--line); color:var(--line); background:none; }
input, select, textarea{
  background:var(--surface-2); color:var(--text);
  border:1px solid var(--line); border-radius:var(--r-ctl);
  font:inherit;
}
input::placeholder, textarea::placeholder{ color:var(--text-4); }


*{box-sizing:border-box;margin:0;padding:0}

html{-webkit-text-size-adjust:100%}

body{
  font-family:var(--font);
  background:var(--bg);
  color:var(--text);
  -webkit-font-smoothing:antialiased;
  min-height:100vh;
  font-size:15px;
  line-height:1.45;
}

a{color:var(--sky);text-decoration:none}

.wrap{max-width:560px;margin:0 auto;
  padding-bottom:calc(40px + env(safe-area-inset-bottom,0px))}

/* ---- page header ------------------------------------------------------- */
.hubtop{display:flex;align-items:center;gap:13px;
  padding:calc(18px + env(safe-area-inset-top,0px)) 16px 16px;
  border-bottom:1px solid var(--line)}
.hubtop img{width:36px;height:36px;flex:0 0 auto;display:block}
.hubtop .hid{flex:1;min-width:0}
.hubtop .eyebrow{color:var(--gold);font-size:10px;font-weight:700;
  letter-spacing:.2em;text-transform:uppercase;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.hubtop h1{color:var(--text-strong);font-size:17px;font-weight:700;
  letter-spacing:-.02em;margin-top:3px}
.hubtop .back,.hubtop .out{color:var(--text-3);font-size:12.5px;flex:0 0 auto}

/* ---- the one header's icon buttons: back / refresh / sign out -----------
   Sept 21 2026. .back and .out above are the OLD text links, kept only so
   nothing breaks mid-sweep - new markup uses .ic. Karam asked for a refresh
   control on every page: the Hub installs to the home screen, where there is
   no browser reload button at all. */
.hubtop .acts{display:flex;align-items:center;gap:8px;flex:0 0 auto;margin-left:auto}
/* Sept 21 2026. These were 34x34 and came out as 34-wide, 44-TALL OVALS, because
   hub.css puts a 44px minimum height on every button for the touch target - correct
   rule, wrong result on a round icon. The button keeps the full 44px hit area and the
   circle is drawn at 36px by ::before, so it is a circle AND a legal touch target. */
.hubtop .ic{width:44px;height:44px;min-height:44px;flex:0 0 auto;position:relative;
  display:inline-flex;align-items:center;justify-content:center;
  padding:0;border:0;background:none;color:var(--text-3);cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:color .12s ease}
.hubtop .ic::before{content:'';position:absolute;top:4px;right:4px;bottom:4px;left:4px;
  border-radius:50%;background:var(--surface);border:1px solid var(--line);
  transition:background .12s ease,border-color .12s ease}
.hubtop .ic svg{position:relative;z-index:1}
.hubtop .ic svg{width:17px;height:17px;display:block}
.hubtop .ic:hover{color:var(--text-strong)}
.hubtop .ic:active::before{background:var(--surface-2)}
.hubtop .ic:active{color:var(--text-strong)}
.hubtop .ic:focus-visible{outline:2px solid var(--gold-line);outline-offset:2px}
.hubtop .ic[data-skyrefresh].spin{color:var(--gold)}
.hubtop .ic[data-skyrefresh].spin::before{border-color:var(--gold-line)}
.hubtop .ic[data-skyrefresh].spin svg{animation:skyspin .7s linear infinite}
@keyframes skyspin{from{transform:rotate(0)}to{transform:rotate(360deg)}}
@media (max-width:400px){
  .hubtop{gap:10px;padding-left:14px;padding-right:14px}
  .hubtop .ic{width:40px;height:40px;min-height:40px}
  .hubtop .ic svg{width:16px;height:16px}
}


/* ---- section label: gold, tracked, hairline ---------------------------- */
.sec{display:flex;align-items:center;gap:10px;margin:0 2px 9px}
.sec span,.sec b{color:var(--gold);font-size:9.5px;font-weight:700;
  letter-spacing:.22em;text-transform:uppercase;flex:0 0 auto}
.sec hr{flex:1;border:0;border-top:1px solid var(--line)}

/* ---- cards and rows ---------------------------------------------------- */
.card{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-card);overflow:hidden}
.card+.card{margin-top:10px}

.row{display:flex;align-items:center;gap:13px;padding:13px 14px;
  border-top:1px solid var(--line-soft)}
.row:first-child{border-top:0}
a.row:active{background:var(--surface-2)}

/* the one highlighted thing on a screen - gold hairline, never gold text */
.hero{background:var(--surface);border:1px solid var(--gold);
  border-radius:var(--r-card);
  box-shadow:0 0 0 3px rgba(248,215,76,.07)}

/* ---- controls ---------------------------------------------------------- */
input,select,textarea,/* Karam, Sept 17 2026: "the show me how box looks different than turn them on box.
   that applies to many other items everywhere."

   Both carried the SAME class. One was an <a>, one a <button>. Nothing anywhere in
   the Hub set `appearance`, so every <button> in the app was still painting the
   browser's own native button underneath whatever class it was given - its own
   gradient, its own text rendering, its own inner metrics - while the identical-
   looking link beside it was not. On a phone the two sat visibly apart.

   Reset once, here, so a class paints the same whichever tag it lands on. Element
   selectors, so every class rule in every page still wins on specificity. */
button,
input[type=submit],
input[type=button],
input[type=reset]{
  -webkit-appearance:none;appearance:none;
  font-family:inherit;font-size:16px;
  letter-spacing:inherit;background-image:none;
  text-align:center}

/* A button carrying no class of its own used to fall back to the operating system's
   button. Stripping appearance would have left it as bare text, so give it the Hub's
   own neutral control instead. :not([class]) can never collide with a styled one. */
button:not([class]):not([style]),
input[type=submit]:not([class]):not([style]){
  color:var(--text);background:var(--surface-2);
  border:1px solid var(--line);border-radius:var(--r-ctl);
  padding:10px 14px;cursor:pointer}

/* An <a> styled as a button has to be LAID OUT as one too, or its label sits left
   while the <button> next to it centres. */
a.btn,a.btn-primary,a.btn-ghost,a.btn-small,a.pushbtn,a.door,a.back-link{
  display:inline-flex;align-items:center;justify-content:center;
  text-decoration:none}

input[type=text],input[type=email],input[type=tel],input[type=date],
input[type=time],input[type=number],input[type=password],select,textarea{
  width:100%;padding:12px 13px;background:var(--surface-2);
  border:1px solid var(--line);border-radius:var(--r-ctl);
  color:var(--text);outline:none;transition:border-color .12s,box-shadow .12s}
input:focus,select:focus,textarea:focus{
  border-color:var(--sky);box-shadow:0 0 0 3px rgba(46,162,205,.16)}
input::placeholder,textarea::placeholder{color:var(--text-4)}
label{display:block;font-size:11px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--text-3);margin-bottom:7px}

/* Sept 16 2026. The rule above is the small tracked CAPTION that sits above a field.
   It was matching EVERY label, including the ones that WRAP a control - a checkbox row,
   a radio option. Those came out as wide tracked capitals, which is how the store picker
   on the signup form ended up too wide for a phone and clipped off the right side of the
   screen. A label that contains its own control is a row, not a caption, so it opts out
   here rather than every such component having to remember to undo it. */
label:has(input:not([type="hidden"])),
label:has(select),
label:has(textarea){
  display:flex;align-items:center;gap:9px;
  font-size:14px;font-weight:500;letter-spacing:normal;text-transform:none;
  color:var(--text);margin-bottom:0;min-width:0}

/* primary action is SKY blue. Gold is a highlight, not a button. */
.btn,.btn-primary,.btn-ghost{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  padding:13px 18px;border:1px solid transparent;border-radius:var(--r-ctl);
  font-size:15px;font-weight:600;cursor:pointer;text-align:center;
  transition:background .12s,border-color .12s,opacity .12s}
.btn-primary{width:100%;background:var(--sky);color:var(--on-sky);border-color:var(--sky)}
.btn-primary:active{background:var(--sky-hover)}
.btn-primary:disabled{opacity:.5;cursor:not-allowed}
.btn-ghost,.btn-back{background:transparent;color:var(--text-2);
  border:1px solid var(--line)}
.btn-ghost:active,.btn-back:active{background:var(--surface-2);color:var(--text)}

/* ---- messages ---------------------------------------------------------- */
.msg,.note,.error-msg,.success-msg,.info-msg,.warn{
  padding:12px 14px;border-radius:var(--r-ctl);font-size:13.5px;
  border:1px solid var(--line);background:var(--surface-2);color:var(--text-2)}
.error-msg,.bad{border-color:rgba(229,86,75,.4);background:var(--bad-bg);color:var(--bad-soft)}
.success-msg,.ok{border-color:rgba(63,191,127,.4);background:var(--ok-bg);color:var(--ok-soft)}
.warn{border-color:rgba(232,178,58,.4);background:var(--warn-bg);color:var(--gold-soft)}

.empty{padding:26px 16px;text-align:center;color:var(--text-3);font-size:13.5px}

/* ---- small parts ------------------------------------------------------- */
.tag{display:inline-block;width:auto;padding:3px 8px;border-radius:6px;
  background:var(--surface-3);border:1px solid var(--line);
  color:var(--text-2);font-size:10.5px;font-weight:700;
  letter-spacing:.08em;text-transform:uppercase}
.hint{color:var(--text-3);font-size:12px}
.hubfoot{text-align:center;color:var(--text-4);font-size:11px;padding:24px 16px 0}

/* iOS zooms any input under 16px on focus. Never go below it. */
@media screen and (max-width:768px){
  input,select,textarea{font-size:16px !important}
}

/* ---- installed-app navigation (assets/hub-nav.js) ---------------------- */
/* No browser chrome when it runs from the home screen, so the pull gesture and
   the back chevron below are the only way out of a page. */
body{overscroll-behavior-y:contain}

.ptr{position:fixed;top:calc(6px + env(safe-area-inset-top,0px));left:50%;
  margin-left:-15px;width:30px;height:30px;border-radius:50%;
  background:var(--surface-3);border:1px solid var(--line);
  display:grid;place-items:center;opacity:0;pointer-events:none;z-index:120;
  transition:opacity .12s}
.ptr span{width:13px;height:13px;border-radius:50%;
  border:2px solid var(--text-4);border-top-color:var(--sky);display:block}
.ptr.ready{border-color:var(--sky)}
.ptr.ready span{border-top-color:var(--gold);border-color:var(--sky)}
.ptr.spin span{animation:ptrspin .6s linear infinite}
@keyframes ptrspin{to{transform:rotate(360deg)}}

/* The back chevron. hub-nav.js only injects one where a page has none. */
a.back{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  width:34px;height:34px;margin-left:-6px;border-radius:9px;color:var(--text-2)}
a.back:active{background:var(--surface-2);color:var(--text)}

/* ═══════════════════════════════════════════════════════════════════════════════
   NATIVE FEEL - added 2026-09-16

   The Hub is installed to the Home Screen and used one-handed on a shop floor, so
   the things that give away a web page matter more than usual. Each rule below
   removes one of those tells. All of it is additive; nothing here changes layout.
   ═══════════════════════════════════════════════════════════════════════════════ */

/* Tells the OS this is a dark app. Without it iOS and Android render form controls,
   scrollbars, spinners and the on-screen keyboard in their light theme - white
   furniture inside a black app, which is the single most obvious tell. */
:root{color-scheme:dark}

/* iOS paints a grey box over anything tappable for ~300ms after a tap. Nothing
   native does that. Our own :active states already give the feedback. */
html{-webkit-tap-highlight-color:transparent}

/* Stops iOS inflating body text when the phone is turned sideways. */
html{-webkit-text-size-adjust:100%;text-size-adjust:100%}

/* Kills the double-tap-to-zoom delay on controls, so a tap registers instantly. */
a,button,summary,[role="button"],input[type="submit"],input[type="checkbox"],
input[type="radio"],label,.tile,.row,.hero,.door{touch-action:manipulation}

/* A long-press on a button should do nothing, not raise a text-selection loupe.
   Inputs and real content are deliberately left selectable. */
button,summary,.btn,.btn-small,.pushbtn,.door,.again{-webkit-user-select:none;user-select:none}

/* Stops a scroll at the end of a list from dragging the whole page behind it. */
body{overscroll-behavior-y:contain}

/* Keyboard focus was invisible. Anyone on a tablet with a keyboard, or using
   VoiceOver's focus ring, had no idea where they were. :focus-visible means mouse
   and touch users never see it - only people actually navigating by key. */
:focus-visible{outline:2px solid var(--sky,#2ea2cd);outline-offset:2px;border-radius:6px}
input:focus-visible,select:focus-visible,textarea:focus-visible{outline-offset:0}

/* Respect the system setting. Someone who gets motion sick from animation has
   asked the OS to stop it; honour that rather than overriding it. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important;
    transition-duration:.001ms !important;scroll-behavior:auto !important}
}

/* Every control clears Apple's 44px minimum target. Below that, thumbs miss. */
button,.btn,.btn-small,.pushbtn,.door,.back-link,.back,.out,.nhlink,.quietlink,
input[type=submit],input[type=button]{min-height:44px}
/* .back is injected by hub-nav.js and was the one control in the header still under
   the target. Sept 17 2026. */

/* Momentum scrolling inside any pane that scrolls on its own. */
.scroll,.tablewrap,.weekgrid{-webkit-overflow-scrolling:touch}

/* ── Narrow phones ───────────────────────────────────────────────────────────
   Measured, not guessed. The longest shift a store can post is a 12-hour span with
   minutes on both ends - "10:30 AM - 11:30 PM" - which sets 230px wide at 23px.
   On a 390px phone the time column has 236px to play with, so it fits with 6px to
   spare; on a 360px phone (older Androids, and plenty of them behind a counter) it
   has 206px and would be clipped, because the time is deliberately nowrap.

   One token, one step down, and every screen that uses it follows. Do not fix this
   by letting the time wrap: a shift broken across two lines is harder to read at a
   glance than a slightly smaller one, and glanceable is the entire point. */
@media (max-width:389px){
  :root{ --fs-hero:19px; --fs-title:17px; }
}

/* ══ THE SHELL ══════════════════════════════════════════════════════════════
   Karam, Sept 17 2026, looking at the Hub on his monitor: "right now on my
   monitor it looks goofy honestly, i want it to look like a high end app that
   works with everything."

   Nothing was broken. Measured 320px to 2560px on every page: zero horizontal
   overflow anywhere. The problem was the opposite - the column capped at
   520-560px and STAYED there at 1920 and at 2560, so a laptop got a phone app
   floating in a sea of black.

   One variable, four sizes. Every page's container reads --shell, so they grow
   together and no page is left behind - which is exactly how it got this way,
   one page at a time. week.php keeps its own wider caps: a seven-day grid needs
   more room than a list of tiles. */
:root{ --shell:560px; }
@media (min-width:640px) { :root{ --shell:660px;  } }
@media (min-width:1024px){ :root{ --shell:1040px; } }
@media (min-width:1600px){
  :root{
    --shell:1280px;
    /* Past this it is a big monitor or the back-office TV, and the type has to
       carry further than a phone held at arm's length. */
    --fs-hero:26px;   --fs-title:21px;  --fs-lg:18px;
    --fs-body:16.5px; --fs-meta:13.5px; --fs-label:11px;
  }
}

/* From a laptop up, a stack of full-width rows wastes the screen. The rows
   become tiles in their own right and flow into however many columns fit.
   auto-fit, not a fixed two, so a section holding ONE row still fills the width
   instead of sitting there at half. */
@media (min-width:1024px){
  .grp .card{
    display:grid;
    grid-template-columns:repeat(auto-fit,minmax(330px,1fr));
    gap:12px;
    background:transparent; border:0; border-radius:0; overflow:visible;
  }
  .grp .card > .row,
  .grp .card > .alert{
    background:var(--surface);
    border:1px solid var(--line);
    border-radius:13px;
  }
  .grp .card > .row:first-child,
  .grp .card > .alert:first-child{ border-top:1px solid var(--line); }
}

/* The setup group holds two independent rows - Home Screen, and alerts. They are
   separate wins, so they get a hairline between them rather than being crammed
   into one sentence. Each disappears on its own when it is done. */
.pushwrap .pushrow + .pushrow{
  margin-top:13px; padding-top:13px; border-top:1px solid var(--line-soft);
}
.pushbtn[disabled]{ opacity:.45; cursor:default; }

/* Hit targets. A radio ships at about 13px, which is fine with a mouse and mean on a
   phone. The whole row is clickable either way; this makes the control itself match. */
input[type="radio"], input[type="checkbox"]{ width:18px; height:18px; accent-color:var(--sky); }
