/* Shared customer order-view styles — account.html and track.html.
   The visual half of shared/order-view.js: both pages answer "where is my
   laundry?", so the status card, the six steps and the bag row are defined
   once. Tokens and layout (.wrap, .card, .btn, .sec, .topbar, .pfoot) come from
   assets/page.css, which must be linked first.

   THE DESIGN, IN ONE PARAGRAPH
   ---------------------------
   Structure borrowed from iOS: inset grouped lists with hairline separators
   that start at the label rather than the card edge, layered low-opacity
   elevation instead of borders, one accent per surface, and radii that grow
   with the size of the thing. Warmth borrowed from the product: the status art
   sits in a PORTHOLE — a circular well with a soft bloom behind it, the way
   light sits behind a washing-machine door. That porthole is the one piece of
   decoration on the page and it is also the one piece of motion, because the
   drum inside it turns only while we actually have the laundry.

   The rule that keeps it calm: colour is spent once per card. A card either
   has a tinted surface or a coloured button, never both. */

:root{
  /* Elevation, layered and low-opacity — two shadows per level, a tight
     contact shadow under a wide ambient one. A single big blurry shadow is
     what makes a card look like it was drawn rather than placed. */
  --e1:0 1px 1px rgba(30,27,46,.04), 0 2px 6px -1px rgba(46,39,90,.07);
  --e2:0 1px 2px rgba(30,27,46,.04), 0 10px 26px -10px rgba(46,39,90,.20);
  --e3:0 2px 4px rgba(30,27,46,.04), 0 26px 56px -22px rgba(46,39,90,.32);
  /* Radii grow with the element. A 12px radius on a 300px card reads as a
     rounded box; a 26px radius reads as a considered shape. */
  --r1:12px; --r2:18px; --r3:24px; --r4:30px;
  --hair:rgba(30,27,46,.09);
}

/* ---- forms ---------------------------------------------------------------
   16px on inputs is not a style choice: anything smaller makes iOS Safari zoom
   the page when the field takes focus. */
.fld{margin-bottom:14px}
.fld label{display:block;font-weight:700;font-size:13px;color:var(--muted);
  margin-bottom:7px;letter-spacing:.005em}
.fld input,.fld select,.fld textarea{width:100%;font-family:var(--sans);font-size:16px;
  color:var(--ink);background:var(--white);border:1px solid var(--line-2);
  border-radius:var(--r1);padding:13px 15px;outline:none;
  transition:border-color .16s ease,box-shadow .16s ease}
.fld input::placeholder,.fld textarea::placeholder{color:#B6B1C2}
.fld input:focus,.fld select:focus,.fld textarea:focus{border-color:var(--purple-300);
  box-shadow:0 0 0 4px rgba(123,79,199,.13)}
.fld textarea{resize:vertical;min-height:84px;line-height:1.55}
.fld select{appearance:none;-webkit-appearance:none;cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%238A8694' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 14px center;padding-right:42px}
.err{display:block;font-size:12.5px;color:#C4463A;font-weight:700;margin-top:7px}
.hint{font-size:13px;color:var(--muted);margin:12px 0 0;line-height:1.55}

/* The reveal button sits inside the input's own padding, so the field keeps
   its full width and nothing shifts when the label flips Show/Hide. The
   wrapper is tight around the input on purpose — positioned against the whole
   .fld it would land halfway down a label and a strength bar. */
.pw-wrap{position:relative}
.pw-wrap input{padding-right:74px}
/* The toggle appears only once there is something to reveal. An empty field
   with a "Show" button sitting in it reads as broken — there is nothing to
   show, and the control is the only thing in the box. */
.pw-toggle{position:absolute;right:7px;top:50%;transform:translateY(-50%);
  border:0;background:transparent;color:var(--purple);font-family:var(--sans);
  font-weight:800;font-size:13px;padding:8px 11px;border-radius:9px;cursor:pointer;
  opacity:0;pointer-events:none;transition:opacity .15s ease,background .15s}
.pw-wrap.has-text .pw-toggle{opacity:1;pointer-events:auto}
.pw-toggle:hover{background:var(--lav-soft)}

/* Strength bar. Guides, never blocks — the only hard rule is the length. */
.pwbar{display:flex;gap:5px;margin-top:10px}
.pwbar i{flex:1;height:4px;border-radius:999px;background:var(--line);
  transition:background .25s ease}
.pwbar[data-score="1"] i:nth-child(-n+1){background:#D9705F}
.pwbar[data-score="2"] i:nth-child(-n+2){background:var(--orange)}
.pwbar[data-score="3"] i:nth-child(-n+3){background:#6FAE4A}
.pwbar[data-score="4"] i{background:var(--green)}
.pwnote{font-size:12.5px;font-weight:700;color:var(--muted-2);margin-top:8px}

/* ---- the status card -----------------------------------------------------
   A tinted surface carrying the one sentence somebody opened the page to read.
   No left colour bar, no border: the tint IS the state, and the porthole
   repeats it. */
.statcard{display:flex;gap:20px;align-items:center;
  background:var(--lav-soft);border-radius:var(--r4);padding:26px 28px;
  position:relative;overflow:hidden}
.statcard-done{background:var(--green-soft)}
.statcard-warn{background:var(--orange-soft)}

/* The porthole. A circular well, a soft bloom behind the glass, and a hairline
   ring — the one ornamental moment on the page, and the only one that earns it
   by also carrying state. */
.statcard-art{flex:none;width:86px;height:86px;border-radius:50%;
  background:
    radial-gradient(circle at 34% 28%, rgba(255,255,255,.95), rgba(255,255,255,.35) 46%, transparent 68%),
    var(--white);
  box-shadow:inset 0 0 0 1px rgba(78,42,142,.10), var(--e1);
  display:grid;place-items:center;padding:13px}
.statcard-done .statcard-art{box-shadow:inset 0 0 0 1px rgba(31,164,99,.16), var(--e1)}
.statcard-warn .statcard-art{box-shadow:inset 0 0 0 1px rgba(244,113,20,.18), var(--e1)}

.statcard-copy{min-width:0;flex:1}
.statcard-chip{display:inline-block;font-size:10.5px;font-weight:800;letter-spacing:.1em;
  text-transform:uppercase;color:var(--purple);background:rgba(255,255,255,.72);
  padding:5px 11px;border-radius:999px}
.statcard-done .statcard-chip{color:#0E6B40}
.statcard-warn .statcard-chip{color:#8A4A10}
.statcard h2{font-family:var(--round);font-weight:800;
  font-size:clamp(21px,4.2vw,27px);line-height:1.14;color:var(--ink);
  margin:11px 0 5px;letter-spacing:-.022em;text-wrap:balance}
.statcard p{font-size:14.5px;color:var(--muted);margin:0;line-height:1.5;max-width:44ch}

.drum{width:100%;height:100%;display:block}
.drum-body{fill:none;stroke:var(--purple-300);stroke-width:2.2;opacity:.5}
.drum-door{fill:none;stroke:var(--purple-300);stroke-width:2.4}
.drum-win{fill:none;stroke:var(--purple-300);stroke-width:1.8;opacity:.6}
.drum-hole{fill:var(--purple-300)}
.drum-knob{fill:var(--orange)}
.statcard-done .drum-body,.statcard-done .drum-door,
.statcard-done .drum-win{stroke:var(--green)}
.statcard-done .drum-hole{fill:var(--green)}
.statcard-warn .drum-body,.statcard-warn .drum-door,
.statcard-warn .drum-win{stroke:var(--orange)}
.statcard-warn .drum-hole{fill:var(--orange)}

.drum-spin{transform-origin:32px 35px}
.statcard.is-spinning .drum-spin{animation:drum-turn 4.2s linear infinite}
@keyframes drum-turn{to{transform:rotate(360deg)}}

/* ---- the bag summary -----------------------------------------------------
   One count, one phrase. Bags go in the same machine, so they get one row —
   and the count is the biggest numeral on the page because it is the fact
   somebody is checking. */
.bagrow{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-top:18px;
  padding:16px 20px;background:var(--white);border-radius:var(--r3);
  box-shadow:var(--e1)}
.bagrow-icon{flex:none;width:42px;height:42px;border-radius:13px;
  background:var(--lav-soft);color:var(--purple);
  display:grid;place-items:center}
.bagrow-icon svg{width:22px;height:22px}
.bagrow-main{display:flex;flex-direction:column;min-width:0;flex:1;gap:1px}
.bagrow-main b{font-family:var(--round);font-weight:800;font-size:19px;color:var(--ink);
  letter-spacing:-.015em;font-variant-numeric:tabular-nums;white-space:nowrap}
.bagrow-sep{display:none}
.bagrow-state{font-size:13.5px;font-weight:700;color:var(--purple)}

/* ---- the six steps -------------------------------------------------------
   Each step carries the icon of the thing it actually is: a calendar for the
   booking, a motorbike for each leg a rider rides, a machine for the wash, a
   door for the delivery. Two of them move while they are current — the bike
   travels, the drum tumbles — because those are the two steps where something
   is physically happening, and a customer refreshing the page wants to see
   that it is. */
.tl{list-style:none;padding:0;margin:24px 2px 0}
.tl li{position:relative;padding-left:50px;padding-bottom:24px;
  font-size:14.5px;color:var(--muted-2)}
.tl li:last-child{padding-bottom:0}

.tl-node{position:absolute;left:0;top:-3px;width:34px;height:34px;border-radius:50%;
  display:grid;place-items:center;z-index:1;
  background:var(--white);box-shadow:inset 0 0 0 1.5px var(--line-2);
  color:#B6B1C2;transition:background .25s,color .25s,box-shadow .25s}
.tl-node svg{width:19px;height:19px;display:block}

/* The rail runs between the nodes, not behind them. */
.tl li:not(:last-child)::after{content:"";position:absolute;left:16px;top:35px;bottom:2px;
  width:2px;border-radius:2px;background:var(--line)}

.tl-name{display:block;font-weight:600;padding-top:5px}
.tl li.done{color:var(--ink)}
.tl li.done .tl-name{font-weight:700}
.tl li.done .tl-node{background:var(--green-soft);color:#0E6B40;
  box-shadow:inset 0 0 0 1.5px rgba(31,164,99,.28)}
.tl li.done:not(:last-child)::after{background:var(--green);opacity:.4}

.tl li.now{color:var(--purple)}
.tl li.now .tl-name{font-weight:800}
.tl li.now .tl-node{background:var(--purple);color:#fff;
  box-shadow:inset 0 0 0 1.5px var(--purple), 0 0 0 5px var(--lav-soft)}

.tl time{display:block;font-size:12px;font-weight:600;color:var(--muted-2);
  margin-top:3px;font-variant-numeric:tabular-nums}

/* Motion, only on the step that is happening now. */
.tl li.now .mc-ride{transform-box:fill-box;transform-origin:center;
  animation:mc-ride 1.9s ease-in-out infinite}
@keyframes mc-ride{
  0%,100%{transform:translateX(-.6px) translateY(0)}
  45%{transform:translateX(.9px) translateY(-.5px)}
  70%{transform:translateX(.3px) translateY(0)}
}
.tl li.now .wm-drum{animation:wm-tumble 2.8s linear infinite}
@keyframes wm-tumble{to{transform:rotate(360deg)}}

/* The driver's photo, on the two steps where one is taken. Small on purpose:
   it is reassurance that somebody stood at the door, not a gallery. */
.tl-shot{display:inline-flex;align-items:center;gap:10px;margin-top:11px;
  padding:7px 14px 7px 7px;background:var(--white);border-radius:14px;
  text-decoration:none;box-shadow:var(--e1);transition:box-shadow .18s,transform .18s}
.tl-shot img{width:42px;height:42px;border-radius:10px;object-fit:cover;display:block;
  background:var(--cream-2)}
.tl-shot span{font-size:13px;font-weight:700;color:var(--purple)}
.tl-shot:hover{box-shadow:var(--e2);transform:translateY(-1px)}

/* ---- order details (no money, by design) ---------------------------------
   An inset grouped list. The separator starts at the label rather than the
   card edge, which is the detail that makes a list of facts read as one
   object instead of a stack of rows. */
.details{margin-top:22px;background:var(--white);border-radius:var(--r3);
  padding:4px 20px;box-shadow:var(--e1)}
.row{display:flex;justify-content:space-between;align-items:baseline;gap:18px;
  padding:14px 0;font-size:14.5px;border-bottom:1px solid var(--hair)}
.row:last-child{border-bottom:0}
.row span{color:var(--muted);flex:none}
.row b{text-align:right;color:var(--ink);font-weight:700;min-width:0;
  overflow-wrap:anywhere;font-variant-numeric:tabular-nums}

/* A caption above a grouped list — the iOS pattern that says what the group is
   without spending a heading on it. */
.grouplab{font-size:11px;font-weight:800;letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted-2);margin:26px 0 9px;padding-left:20px}
.grouplab + .details{margin-top:0}

/* ---- the repeat offer ----------------------------------------------------
   The full card: a labelled button rather than an arrow, and one line for the
   window it usually goes out in — the second thing somebody checks before
   tapping. An arrow assumes you have met the pattern; a button says what
   happens.

   The address is the WHOLE address, unit first, clipped with an ellipsis after
   one line. That is the compromise the original got wrong in both directions:
   it set the address in bold and let it wrap to three lines, and the fix after
   that threw most of it away. A clipped full address reads completely when it
   is short, reads as far as it fits when it is long, and the card is the same
   height either way.

   Colour is spent once: a white surface, so the button gets the orange. */
.repeat{background:var(--white);border-radius:var(--r4);padding:17px 18px 16px;
  box-shadow:var(--e2)}
.repeat-top{display:flex;align-items:center;gap:13px}
.repeat-ico{flex:none;width:44px;height:44px;border-radius:14px;
  background:var(--orange-soft);color:var(--orange);display:grid;place-items:center}
.repeat-ico svg{width:22px;height:22px}
.repeat-copy{flex:1;min-width:0}
.repeat-eyebrow{display:block;font-size:10.5px;font-weight:800;letter-spacing:.1em;
  text-transform:uppercase;color:var(--orange)}
.repeat-bags{display:block;font-family:var(--round);font-weight:800;font-size:20px;
  color:var(--ink);line-height:1.2;letter-spacing:-.02em;margin-top:3px;
  font-variant-numeric:tabular-nums}
/* One line, always — clipped, never wrapped. */
.repeat-place{display:block;font-size:13.5px;color:var(--muted);margin-top:2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

.repeat-meta{display:flex;align-items:center;gap:7px;margin-top:13px;padding-top:13px;
  border-top:1px solid var(--hair);font-size:12.5px;color:var(--muted);font-weight:600}
.repeat-meta svg{width:15px;height:15px;color:var(--muted-2);flex:none}

.repeat-cta{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;
  margin-top:13px;border:0;border-radius:999px;cursor:pointer;
  background:linear-gradient(135deg,var(--orange),var(--orange-light));color:#fff;
  font-family:var(--sans);font-weight:800;font-size:15.5px;padding:14px 20px;
  text-decoration:none;box-shadow:0 10px 24px -12px rgba(244,113,20,.8);
  transition:transform .15s,box-shadow .15s}
.repeat-cta:hover{transform:translateY(-1px);box-shadow:0 14px 28px -12px rgba(244,113,20,.85)}
.repeat-cta svg{width:17px;height:17px}

.repeat-not{display:block;margin:10px auto 0;border:0;background:transparent;
  color:var(--muted-2);font-family:var(--sans);font-weight:700;font-size:13px;
  cursor:pointer;padding:8px 12px;border-radius:9px;transition:color .15s,background .15s}
.repeat-not:hover{color:var(--ink);background:var(--cream-2)}

/* A caption above a grouped list — the iOS pattern that says what the group is
   without spending a heading on it. */
.grouplab{font-size:11px;font-weight:800;letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted-2);margin:26px 0 9px;padding-left:20px}
.grouplab + .details{margin-top:0}

/* ---- perks list ----------------------------------------------------------
   A short label in the register of the nav ("Track my order"), and one line
   under it saying what that actually does. The label alone was too thin —
   "Address saved" does not tell you it is what makes checkout shorter — and a
   paragraph was too much. One line each.

   The row is top-aligned rather than centred: with two lines of text, centring
   drops the icon to the middle of the block and it stops reading as the label's
   mark. */
.perks{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:2px}
.perks li{display:flex;align-items:flex-start;gap:14px;padding:13px 0;
  border-bottom:1px solid var(--hair);color:var(--ink)}
.perks li:last-child{border-bottom:0}
.perk-ico{flex:none;width:38px;height:38px;border-radius:12px;display:grid;
  place-items:center;background:var(--lav-soft);color:var(--purple)}
.perk-ico svg{width:20px;height:20px}
/* min-width:0 so a long line wraps inside the row instead of widening it. */
.perk-txt{min-width:0;display:flex;flex-direction:column;gap:2px;padding-top:3px}
.perk-txt b{font-size:15px;font-weight:700;line-height:1.3}
.perk-txt span{font-size:13.5px;line-height:1.45;color:var(--muted)}
.perk-ico.tint-orange{background:var(--orange-soft);color:var(--orange)}
.perk-ico.tint-green{background:var(--green-soft);color:#0E6B40}

/* ---- banners ---- */
.banner{display:flex;gap:13px;align-items:flex-start;padding:15px 17px;
  border-radius:var(--r2);font-size:14px;line-height:1.52;margin-top:16px}
.banner b{display:block;margin-bottom:2px;font-weight:800}
/* A bold run INSIDE a banner sentence — an address, a date — rather than the
   heading line the rule above styles. */
.banner b.inl{display:inline;margin:0;font-weight:700;overflow-wrap:anywhere}
.banner-warn{background:var(--orange-soft);color:#8A4A10}
.banner-info{background:var(--lav-soft);color:var(--purple)}
.banner-ok{background:var(--green-soft);color:#0E6B40}

/* ---- misc ---- */
.muted{color:var(--muted)}
.sec-actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:20px}

/* ---- skeletons: a shape that settles, not a spinner that spins ---- */
.skel{background:linear-gradient(90deg,var(--line) 25%,#F5F3F8 37%,var(--line) 63%);
  background-size:400% 100%;animation:shimmer 1.4s ease-in-out infinite;
  border-radius:var(--r2)}
@keyframes shimmer{0%{background-position:100% 0}100%{background-position:-100% 0}}
.skel-card{height:136px;border-radius:var(--r4)}

.hidden{display:none !important}
.toast{position:fixed;left:50%;bottom:26px;transform:translateX(-50%);z-index:200;
  background:var(--ink);color:#fff;font-size:14px;font-weight:600;padding:14px 22px;
  border-radius:999px;box-shadow:var(--e3);max-width:calc(100% - 36px)}
.toast-ok{background:var(--green)}
.toast-err{background:#C4463A}

@media (max-width:560px){
  .statcard{gap:15px;padding:20px 20px;border-radius:var(--r3)}
  .statcard-art{width:66px;height:66px;padding:10px}
  .bagrow{padding:14px 16px;gap:13px}
  .details{padding:2px 16px}
}

/* The drum stops for anyone who asked motion to stop. The card still says
   which state it is in, in words and in colour, so nothing is lost with it. */
@media (prefers-reduced-motion: reduce){
  .skel{animation:none}
  .statcard.is-spinning .drum-spin{animation:none}
  /* The bike and the drum stop too. The step still says which one it is, in
     its icon and its colour, so nothing is lost with the motion. */
  .tl li.now .mc-ride,
  .tl li.now .wm-drum{animation:none}
  .live-dot{animation:none}
  .btn,.tl-shot{transition:none}
  .btn:hover,.tl-shot:hover{transform:none}
}
