{
  "title": "Your Account — The Swiss Laundry",
  "description": "",
  "keywords": "",
  "robots": "noindex,nofollow,noarchive,nosnippet,noimageindex",
  "canonical": "",
  "hreflangs": [],
  "og": {},
  "geo": {},
  "jsonLd": [
    {
      "@context": "https://schema.org",
      "@type": "WebPage",
      "name": "Your Account — The Swiss Laundry"
    }
  ],
  "styleCss": "\n*{margin:0;padding:0;box-sizing:border-box}\n:root{--dark:#0c0b0a;--dark2:#141312;--gold:#C9A84C;--gold-l:#E2C97E;--pink:#C0415A;\n  --serif:'Cormorant Garamond',Georgia,serif;--sans:'Jost','Helvetica Neue',sans-serif;\n  --line:rgba(255,255,255,.1);--muted:rgba(255,255,255,.55);--faint:rgba(255,255,255,.4)}\nhtml{-webkit-text-size-adjust:100%}\nbody{background:var(--dark);color:#fff;font-family:var(--sans);font-weight:300;line-height:1.6;min-height:100vh;display:flex;flex-direction:column}\na{color:inherit}\n\n/* ── Header ── */\n.dh{position:sticky;top:0;z-index:50;background:rgba(12,11,10,.92);backdrop-filter:blur(10px);border-bottom:1px solid var(--line)}\n.dh-in{max-width:920px;margin:0 auto;padding:16px 22px;display:flex;align-items:center;justify-content:space-between;gap:16px}\n.dh-logo{display:inline-flex;align-items:center;text-decoration:none}.dh-logo img{height:42px;width:auto;display:block}\n\n.dh-out{font-family:var(--sans);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);text-decoration:none;border:1px solid var(--line);padding:9px 16px;border-radius:6px;transition:all .2s}\n.dh-out:hover{border-color:var(--gold);color:var(--gold)}\n\n/* ── Layout ── */\n.wrap{max-width:920px;margin:0 auto;padding:40px 22px 80px;width:100%;flex:1}\n.loading{text-align:center;padding:90px 20px;color:var(--muted)}\n\n/* ── Account header ── */\n.acct{display:flex;justify-content:space-between;align-items:flex-start;gap:20px;flex-wrap:wrap;margin-bottom:30px;padding-bottom:26px;border-bottom:1px solid var(--line)}\n.acct-eyebrow{font-size:11px;letter-spacing:.4em;text-transform:uppercase;color:var(--gold);margin-bottom:12px}\n.acct-name{display:flex;align-items:center;gap:14px;flex-wrap:wrap}\n.acct-name h1{font-family:var(--serif);font-weight:300;font-size:clamp(28px,5vw,40px);line-height:1.05}\n.acct-sub{font-size:14px;color:var(--muted);margin-top:8px}\n.acct-actions{display:flex;gap:10px;flex-wrap:wrap}\n.btn-act{font-family:var(--sans);font-size:11px;letter-spacing:.08em;text-transform:uppercase;padding:9px 16px;border-radius:6px;border:1px solid var(--line);color:rgba(255,255,255,.8);background:none;cursor:pointer;text-decoration:none;transition:all .2s;white-space:nowrap}\n.btn-act:hover{border-color:var(--gold);color:var(--gold)}\n.btn-act.danger:hover{border-color:#e0697a;color:#e0697a}\n\n/* ── Tier badge ── */\n.badge{display:inline-flex;align-items:center;gap:7px;font-size:11px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;padding:6px 14px 6px 10px;border-radius:30px;border:1px solid}\n.badge svg{width:18px;height:18px}\n.badge.gold{color:#E2C97E;border-color:rgba(226,201,126,.5);background:rgba(226,201,126,.1)}\n.badge.silver{color:#cfd4d8;border-color:rgba(207,212,216,.5);background:rgba(207,212,216,.1)}\n.badge.copper{color:#d98a5c;border-color:rgba(217,138,92,.5);background:rgba(217,138,92,.1)}\n.badge.bronze{color:#c08457;border-color:rgba(192,132,87,.45);background:rgba(192,132,87,.1)}\n.badge.promising{color:#9ab3d4;border-color:rgba(154,179,212,.5);background:rgba(154,179,212,.1)}\n.badge.new{color:#8fd3a8;border-color:rgba(143,211,168,.5);background:rgba(143,211,168,.1)}\n\n/* ── Summary ── */\n.summary{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:1px;background:var(--line);border:1px solid var(--line);border-radius:14px;overflow:hidden;margin-bottom:30px}\n.sum-cell{background:var(--dark2);padding:20px 22px}\n.sum-cell b{display:block;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint);margin-bottom:8px;font-weight:400}\n.sum-cell .v{font-family:var(--serif);font-size:26px;font-weight:300}\n.sum-cell .v.due{color:#e0697a}.sum-cell .v.paid{color:#8fd3a8}\n\n/* ── Controls ── */\n.controls{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:16px}\n.search{flex:1;min-width:200px;position:relative}\n.search input{width:100%;background:rgba(255,255,255,.05);border:1px solid var(--line);border-radius:8px;padding:11px 14px 11px 38px;color:#fff;font-family:var(--sans);font-size:13.5px}\n.search input::placeholder{color:var(--faint)}\n.search input:focus{outline:none;border-color:var(--gold)}\n.search svg{position:absolute;left:13px;top:50%;transform:translateY(-50%);width:16px;height:16px;color:var(--faint)}\n.sel{background:rgba(255,255,255,.05);border:1px solid var(--line);border-radius:8px;padding:11px 14px;color:rgba(255,255,255,.85);font-family:var(--sans);font-size:12.5px;cursor:pointer}\n.sel:focus{outline:none;border-color:var(--gold)}\n.count{font-size:12px;color:var(--faint);margin-bottom:14px}\n\n/* ── Order row ── */\n.row{background:rgba(255,255,255,.035);border:1px solid var(--line);border-radius:12px;margin-bottom:10px;overflow:hidden;transition:border-color .2s}\n.row:hover{border-color:rgba(255,255,255,.2)}\n.row.open{border-color:rgba(201,168,76,.4)}\n.row-head{display:grid;grid-template-columns:auto 1fr auto auto auto;gap:16px;align-items:center;padding:16px 20px;cursor:pointer}\n.rh-chev{color:var(--faint);transition:transform .25s;font-size:11px}\n.row.open .rh-chev{transform:rotate(90deg)}\n.rh-no{font-size:14px;font-weight:500;letter-spacing:.03em}\n.rh-no .inv{color:var(--faint);font-weight:300;font-size:12px}\n.rh-date{font-size:12px;color:var(--muted)}\n.rh-amt{font-size:14px;text-align:right;white-space:nowrap}\n.rh-amt .due{color:#e0697a;font-size:11px;display:block;margin-top:2px}\n.pill{font-size:10px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;padding:5px 11px;border-radius:30px;white-space:nowrap}\n.pill.live{background:rgba(201,168,76,.16);color:var(--gold)}\n.pill.ready{background:rgba(76,175,110,.16);color:#7ed99a}\n.pill.done{background:rgba(143,211,168,.14);color:#8fd3a8}\n@media(max-width:640px){.row-head{grid-template-columns:auto 1fr auto;row-gap:8px}.rh-date{grid-column:2/4}.rh-amt{grid-column:2/4;text-align:left}}\n\n/* ── Detail ── */\n.detail{display:none;padding:4px 20px 22px;border-top:1px solid var(--line)}\n.row.open .detail{display:block}\n.dates{display:flex;gap:26px;flex-wrap:wrap;margin:16px 0 20px}\n.dates div{font-size:13px;color:rgba(255,255,255,.7)}\n.dates b{display:block;font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);margin-bottom:3px;font-weight:400}\n\n/* ── 8-stage vertical timeline ── */\n.track{margin:6px 0 20px}\n.tstep{display:flex;gap:14px;position:relative;padding-bottom:16px}\n.tstep:last-child{padding-bottom:0}\n.tstep::before{content:'';position:absolute;left:6px;top:16px;bottom:-2px;width:2px;background:rgba(255,255,255,.12)}\n.tstep:last-child::before{display:none}\n.tstep.on::before{background:var(--gold)}\n.tdot{width:14px;height:14px;border-radius:50%;background:rgba(255,255,255,.15);flex-shrink:0;margin-top:2px;z-index:2}\n.tstep.on .tdot{background:var(--gold)}\n.tstep.cur .tdot{box-shadow:0 0 0 5px rgba(201,168,76,.2)}\n.tlabel{font-size:13px;color:rgba(255,255,255,.45);line-height:1.3}\n.tstep.on .tlabel{color:#fff}\n.tstep.cur .tlabel{color:var(--gold)}\n.tmeta{font-size:11px;color:var(--faint);margin-top:2px}\n\n/* ── Invoice ── */\n.inv-line{display:flex;justify-content:space-between;font-size:13.5px;color:rgba(255,255,255,.72);padding:7px 0}\n.inv-line.total{border-top:1px solid rgba(255,255,255,.12);margin-top:6px;padding-top:12px;color:#fff;font-weight:500;font-size:15px}\n.inv-line.due{color:#e0697a;font-weight:500}.inv-line.paid{color:#8fd3a8}\n.inv-line .lbl{color:var(--muted)}\n.foot{display:flex;gap:10px;flex-wrap:wrap;margin-top:18px}\n.btn{font-family:var(--sans);font-size:11px;letter-spacing:.1em;text-transform:uppercase;padding:11px 22px;border-radius:6px;text-decoration:none;cursor:pointer;border:none;transition:all .2s;display:inline-block}\n.btn.pay{background:var(--gold);color:#0c0b0a;font-weight:600}.btn.pay:hover{background:var(--gold-l)}\n.btn.ghost{background:none;border:1px solid rgba(255,255,255,.25);color:rgba(255,255,255,.8)}.btn.ghost:hover{border-color:#fff;color:#fff}\n.btn.feedback{background:rgba(201,168,76,.16);color:var(--gold);border:1px solid rgba(201,168,76,.4)}.btn.feedback:hover{background:rgba(201,168,76,.26)}\n\n.empty,.noresults{text-align:center;padding:50px 20px;color:var(--muted)}\n.empty .btn{margin-top:18px}\n\n/* ── Modal ── */\n.modal{display:none;position:fixed;inset:0;z-index:100;background:rgba(12,11,10,.9);align-items:center;justify-content:center;padding:20px}\n.modal.show{display:flex}\n.modal-box{background:#1a1816;border:1px solid rgba(255,255,255,.12);border-radius:16px;max-width:440px;width:100%;padding:34px}\n.modal-box h3{font-family:var(--serif);font-weight:300;font-size:26px;margin-bottom:10px}\n.modal-box p{font-size:14px;color:var(--muted);line-height:1.6;margin-bottom:22px}\n.field{margin-bottom:14px}\n.field label{display:block;font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);margin-bottom:6px}\n.field input,.field textarea{width:100%;background:rgba(255,255,255,.05);border:1px solid var(--line);border-radius:8px;padding:11px 14px;color:#fff;font-family:var(--sans);font-size:14px}\n.field input:focus{outline:none;border-color:var(--gold)}\n.modal-foot{display:flex;gap:10px;margin-top:8px}\n\n/* ── Footer ── */\n.df{border-top:1px solid var(--line);padding:30px 22px;text-align:center}\n.df p{font-size:12px;color:var(--faint);line-height:1.7}\n.df a{color:var(--muted);text-decoration:none}\n.df a:hover{color:var(--gold)}\n@media(max-width:600px){.acct{flex-direction:column}.dates{gap:16px}}\n\n\n\n\n\n\n/* ═══ Universal dark-default content readability v3 ═══ */\n.faq-a{color:rgba(255,255,255,.78)!important}\n.faq-q{color:rgba(255,255,255,.92)!important}\n.faq-item.open .faq-q,.faq-q:hover{color:var(--gold,#C9A84C)!important}\n\n.hub-faq,.hub-faq-inner{background:#1a1918!important;color:#fff!important}\n.hub-faq-h2{color:#fff!important}\n.hub-faq-h2 em{color:var(--gold,#C9A84C)!important}\n.hub-faq-ey{color:var(--gold,#C9A84C)!important}\n\n/* cream hub sections */\n.hub-grid-sec.cream .hub-sec-h2{color:var(--ink,#1a1918)!important}\n.hub-grid-sec.cream .hub-sec-h2 em{color:var(--gold,#C9A84C)!important}\n.hub-grid-sec.cream .hub-sec-sub{color:rgba(26,25,24,.65)!important}\n.hub-grid-sec.cream .hub-sec-ey{color:var(--gold,#C9A84C)!important}\n/* dark/off hub sections */\n.hub-grid-sec.off .hub-sec-h2,.hub-grid-sec.off .hub-sec-h2,\n.off .hub-sec-h2{color:#fff!important}\n.hub-grid-sec.off .hub-sec-h2 em{color:var(--gold,#C9A84C)!important}\n.hub-grid-sec.off .hub-sec-sub,.off .hub-sec-sub{color:rgba(255,255,255,.65)!important}\n.hub-grid-sec.off .hub-sec-ey{color:var(--gold,#C9A84C)!important}\n\n/* Cream FAQ bands */\n.aeo-faq .faq-q,.bsc-faq .faq-q,.dcm-faq .faq-q,.dpc-faq .faq-q,.g-faq .faq-q,\n.sp-faq .faq-q,.sscg-faq .faq-q,.wcg-faq .faq-q,.wcs-faq .faq-q,\n.guide-faq .faq-q,.csr-faq .faq-q,.ex-faq .faq-q{\n  color:rgba(26,25,24,.92)!important;\n}\n.aeo-faq .faq-a,.bsc-faq .faq-a,.dcm-faq .faq-a,.dpc-faq .faq-a,.g-faq .faq-a,\n.sp-faq .faq-a,.sscg-faq .faq-a,.wcg-faq .faq-a,.wcs-faq .faq-a,\n.guide-faq .faq-a,.csr-faq .faq-a,.ex-faq .faq-a{\n  color:rgba(26,25,24,.7)!important;\n}\n.aeo-faq .faq-item.open .faq-q,.aeo-faq .faq-q:hover,\n.bsc-faq .faq-item.open .faq-q,.bsc-faq .faq-q:hover,\n.g-faq .faq-item.open .faq-q,.g-faq .faq-q:hover,\n.dcm-faq .faq-item.open .faq-q,.dpc-faq .faq-item.open .faq-q,\n.sp-faq .faq-item.open .faq-q,.sscg-faq .faq-item.open .faq-q,\n.wcg-faq .faq-item.open .faq-q,.wcs-faq .faq-item.open .faq-q,\n.guide-faq .faq-item.open .faq-q,.csr-faq .faq-item.open .faq-q,\n.ex-faq .faq-item.open .faq-q{color:var(--gold,#C9A84C)!important}\n\n/* Dark FAQ shells */\n.dvw-faq .faq-q,.lk-faq .faq-q,.to-faq .faq-q,.hub-faq .faq-q{color:rgba(255,255,255,.92)!important}\n.dvw-faq .faq-a,.lk-faq .faq-a,.to-faq .faq-a,.hub-faq .faq-a{color:rgba(255,255,255,.78)!important}\n.dvw-faq .faq-item.open .faq-q,.lk-faq .faq-item.open .faq-q,.to-faq .faq-item.open .faq-q,\n.hub-faq .faq-item.open .faq-q,.hub-faq .faq-q:hover{color:var(--gold,#C9A84C)!important}\n\n/* AEO */\n.aeo-h2,.aeo-h3,.aeo-title{color:#fff!important}\n.aeo-p,.aeo-lead,.aeo-li,.aeo-body,.aeo-text{color:rgba(255,255,255,.78)!important}\n.aeo-section td,.aeo-section th,.aeo-body td,.aeo-body th,\n.care-cell,.yes-cell,.exp-cell,.warn-cell{\n  color:rgba(255,255,255,.8)!important;border-color:rgba(255,255,255,.12)!important;\n}\n\n/* Guide cards on dark */\n.guide-card,.gc-card{\n  background:rgba(255,255,255,.05)!important;\n  border-color:rgba(255,255,255,.12)!important;\n  color:rgba(255,255,255,.85)!important;\n}\n.gc-title,.gc-tag,.gc-meta,.gc-desc{color:rgba(255,255,255,.8)!important}\n.gc-title{color:#fff!important}\n.gc-tag{color:var(--gold,#C9A84C)!important}\n\n/* Guides index grid */\n.gd-grid{background:transparent!important;border-color:rgba(255,255,255,.1)!important}\n.gd-grid a,.gd-grid a:visited{\n  background:rgba(255,255,255,.05)!important;\n  color:rgba(255,255,255,.85)!important;\n}\n.gd-grid a:hover{background:rgba(255,255,255,.08)!important}\n.gd-grid .t{color:#fff!important}\n.gd-grid .d{color:rgba(255,255,255,.6)!important}\n.gd-label{color:var(--gold,#C9A84C)!important}\n\n/* Callouts + inline links on dark */\n.box-title,.garment-fabric,.sym-pro,.lw-method-note,.lw-fab-note,\n.garment-note-text{color:rgba(255,255,255,.85)!important}\n.box-title a,.sym-pro a,.garment-note-text a,.warn-box a,.fact-box a,\n.clause-body a,.aeo-p a,.aeo-li a{\n  color:var(--gold,#C9A84C)!important;\n}\n\n/* Site map */\n.sm-grid a,.sm-grid a:visited{color:rgba(255,255,255,.85)!important}\n.sm-grid a:hover{color:var(--gold,#C9A84C)!important}\n.sm-sec h2,.sm-hero h1{color:#fff!important}\n.sm-sec .note,.sm-sub,.sm-sec h2 .cnt{color:rgba(255,255,255,.55)!important}\n\n.to-section-h2,.lk-h2{color:#fff!important}\n\n\n/* cream hub guide cards = dark text on light cards */\n.hub-grid-sec.cream .guide-card,\n.cream .guide-card{\n  background:#fff!important;\n  border-color:rgba(26,25,24,.08)!important;\n  color:rgba(26,25,24,.85)!important;\n}\n.hub-grid-sec.cream .gc-title,\n.cream .gc-title{color:#1a1918!important}\n.hub-grid-sec.cream .gc-desc,\n.cream .gc-desc{color:rgba(26,25,24,.55)!important}\n.hub-grid-sec.cream .gc-tag,\n.cream .gc-tag{color:var(--gold,#C9A84C)!important}\n.hub-grid-sec.cream .gc-meta,\n.cream .gc-meta{color:rgba(26,25,24,.45)!important}\n/* off/dark hub guide cards stay light-on-dark */\n.hub-grid-sec.off .guide-card,\n.off .guide-card{\n  background:rgba(255,255,255,.05)!important;\n  color:rgba(255,255,255,.85)!important;\n}\n.hub-grid-sec.off .gc-title,.off .gc-title{color:#fff!important}\n.hub-grid-sec.off .gc-desc,.off .gc-desc{color:rgba(255,255,255,.7)!important}\n/* pink CTA links on dark — brighten */\n.pro-trigger-text a,.sym-pro a,.garment-note-text a{\n  color:#E8A0AD!important;\n  text-decoration:underline;\n}\n\n/* order-dashboard invoice contrast */\n.inv-line .lbl{color:rgba(255,255,255,.72)!important}\n.inv-line{color:rgba(255,255,255,.88)!important}\n.inv-line.total .lbl,.inv-line.due .lbl,.inv-line.paid .lbl{color:inherit!important}\n",
  "scriptJs": "\n(function(){\n  'use strict';\n  var TIERS={gold:{l:'Gold',s:'<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M5 16l-2-9 5 4 4-7 4 7 5-4-2 9z\"/><rect x=\"5\" y=\"17\" width=\"14\" height=\"2.5\" rx=\"1\"/></svg>'},silver:{l:'Silver',s:'<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M5 16l-2-9 5 4 4-7 4 7 5-4-2 9z\"/><rect x=\"5\" y=\"17\" width=\"14\" height=\"2.5\" rx=\"1\"/></svg>'},copper:{l:'Copper',s:'<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"9\" r=\"6\"/><path d=\"M9 14l-2 7 5-3 5 3-2-7\"/></svg>'},bronze:{l:'Bronze',s:'<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"9\" r=\"6\"/><path d=\"M9 14l-2 7 5-3 5 3-2-7\"/></svg>'},promising:{l:'Promising',s:'<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2l2.4 7.4H22l-6 4.6 2.3 7.4-6.3-4.6L5.7 21.4 8 14 2 9.4h7.6z\"/></svg>'},new:{l:'New',s:'<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"M12 8v8M8 12h8\"/></svg>'}};\n  var STAGES=[\n    {key:'booked',label:'Pickup booked'},\n    {key:'pickedup',label:'Picked up',meta:'by'},\n    {key:'invoiced',label:'Invoice created',meta:'no'},\n    {key:'cleaning',label:'Cleaning in process'},\n    {key:'ready',label:'Ready for delivery'},\n    {key:'outfordelivery',label:'Out for delivery',meta:'by'},\n    {key:'delivered',label:'Delivered'},\n    {key:'paid',label:'Payment received'}\n  ];\n  function inr(n){return '₹'+Number(n||0).toLocaleString('en-IN');}\n  function g(id){return document.getElementById(id);}\n  function fmt(d){ if(!d) return '—'; var x=new Date(d); if(isNaN(x)) return d; return x.toLocaleDateString('en-IN',{day:'numeric',month:'short',year:'numeric'}); }\n  function statusOf(o){ if(o.stage>=8) return {cls:'done',txt:'Completed'}; if(o.stage>=7) return {cls:'done',txt:'Delivered'}; if(o.stage>=5) return {cls:'ready',txt:'Ready'}; return {cls:'live',txt:'In progress'}; }\n  function feedbackLive(o){ if(!o.deliveredDate) return false; var days=(Date.now()-new Date(o.deliveredDate).getTime())/86400000; return days>=0&&days<=7; }\n\n  var DEMO_ACCOUNT={name:'Anand Bhatt',tier:'gold',phone:'+91 98XXXXXX13',email:'a***@theswisslaundry.com'};\n  var DEMO_ORDERS=[\n    {orderNo:'TSL-8F3A2',invoiceNo:'INV-24817',pickupDate:'2026-06-10',pickupBy:'Rahul',expectedDate:'2026-06-13',deliveredDate:null,deliveredBy:null,pieces:5,value:1200,gst:216,discount:120,valueAfterDiscount:1296,advancePaid:500,pending:796,stage:4},\n    {orderNo:'TSL-7B1C9',invoiceNo:'INV-24790',pickupDate:'2026-06-06',pickupBy:'Sourav',expectedDate:'2026-06-09',deliveredDate:null,deliveredBy:null,pieces:1,value:3500,gst:630,discount:350,valueAfterDiscount:3780,advancePaid:3780,pending:0,stage:5},\n    {orderNo:'TSL-6A0D4',invoiceNo:'INV-24612',pickupDate:'2026-05-28',pickupBy:'Rahul',expectedDate:'2026-05-31',deliveredDate:'2026-06-08',deliveredBy:'Imran',pieces:2,value:900,gst:162,discount:0,valueAfterDiscount:1062,advancePaid:1062,pending:0,stage:8}\n  ];\n  var ALL=[];\n  var ACC=null;\n\n  function loadAccount(){\n    var token=sessionStorage.getItem('tsl_token');\n    if(!token){ window.location.replace('/track-order'); return; }\n    Promise.all([\n      fetch('/api/account',{headers:{'Authorization':'Bearer '+token}}),\n      fetch('/api/account/orders',{headers:{'Authorization':'Bearer '+token}})\n    ]).then(function(res){\n      if(res[0].status===401||res[1].status===401){\n        sessionStorage.removeItem('tsl_token');sessionStorage.removeItem('tsl_verified');\n        window.location.replace('/track-order');\n        return;\n      }\n      if(!res[0].ok||!res[1].ok){ throw 0; }\n      return Promise.all([res[0].json(),res[1].json()]).then(function(data){ init(data[0], data[1].orders||data[1]); });\n    }).catch(function(){\n      g('odLoading').innerHTML='Couldn\\'t load your account right now. <a href=\"#\" onclick=\"location.reload();return false;\" style=\"color:var(--gold);text-decoration:underline\">Try again</a>, or <a href=\"https://wa.me/919836233354\" target=\"_blank\" rel=\"noopener\" style=\"color:var(--gold);text-decoration:underline\">WhatsApp us</a> if this keeps happening.';\n    });\n  }\n\n  function init(acc, orders){\n    ACC=acc;\n    g('odLoading').style.display='none';\n    g('odContent').style.display='block';\n    g('odName').textContent=acc.name||'Welcome';\n    var t=TIERS[acc.tier]||TIERS.new;\n    var b=g('odBadge'); b.className='badge '+(acc.tier||'new'); b.innerHTML=t.s+t.l;\n    g('odSub').textContent=(acc.phone||'')+(acc.email?' · '+acc.email:'');\n    ALL=orders.slice();\n    var due=orders.reduce(function(s,o){return s+(o.pending||0);},0);\n    var active=orders.filter(function(o){return o.stage<7;}).length;\n    g('odSummary').innerHTML=\n      '<div class=\"sum-cell\"><b>Active orders</b><span class=\"v\">'+active+'</span></div>'+\n      '<div class=\"sum-cell\"><b>Total orders</b><span class=\"v\">'+orders.length+'</span></div>'+\n      '<div class=\"sum-cell\"><b>Amount due</b><span class=\"v '+(due>0?'due':'paid')+'\">'+inr(due)+'</span></div>';\n    odApply();\n  }\n\n  window.odApply=function(){\n    var q=(g('odSearch').value||'').toLowerCase().trim();\n    var sort=g('odSort').value, filt=g('odFilter').value;\n    var rows=ALL.filter(function(o){\n      if(filt==='live'&&o.stage>=5) return false;\n      if(filt==='ready'&&!(o.stage>=5&&o.stage<7)) return false;\n      if(filt==='done'&&o.stage<7) return false;\n      if(filt==='due'&&!(o.pending>0)) return false;\n      if(q){ var hay=(o.orderNo+' '+o.invoiceNo+' '+statusOf(o).txt+' '+fmt(o.pickupDate)+' '+fmt(o.deliveredDate||o.expectedDate)+' '+o.valueAfterDiscount).toLowerCase(); if(hay.indexOf(q)<0) return false; }\n      return true;\n    });\n    rows.sort(function(a,b){\n      switch(sort){\n        case 'pickup_asc': return new Date(a.pickupDate)-new Date(b.pickupDate);\n        case 'pickup_desc': return new Date(b.pickupDate)-new Date(a.pickupDate);\n        case 'delivery_desc': return new Date(b.deliveredDate||b.expectedDate)-new Date(a.deliveredDate||a.expectedDate);\n        case 'amount_desc': return b.valueAfterDiscount-a.valueAfterDiscount;\n        case 'amount_asc': return a.valueAfterDiscount-b.valueAfterDiscount;\n        case 'status': return a.stage-b.stage;\n        case 'invoice': return a.invoiceNo>b.invoiceNo?1:-1;\n        default: return 0;\n      }\n    });\n    g('odCount').textContent=rows.length+' of '+ALL.length+' orders';\n    if(!ALL.length){ g('odList').innerHTML='<div class=\"empty\">You have no orders yet.<br><a class=\"btn pay\" href=\"/pickup\">Book a pickup</a></div>'; return; }\n    if(!rows.length){ g('odList').innerHTML='<div class=\"noresults\">No orders match your search.</div>'; return; }\n    g('odList').innerHTML=rows.map(rowHTML).join('');\n  };\n\n  function rowHTML(o){\n    var st=statusOf(o);\n    var due=o.pending>0?'<span class=\"due\">'+inr(o.pending)+' due</span>':'';\n    var dLabel=o.deliveredDate?'Delivered on':'Expected delivery';\n    var dVal=fmt(o.deliveredDate||o.expectedDate);\n    var tl=STAGES.map(function(stp,i){\n      var on=i<o.stage?' on':''; var cur=i===o.stage-1?' cur':''; var meta='';\n      if(stp.meta==='by'&&i<o.stage){ meta=(stp.key==='pickedup'&&o.pickupBy)?('by '+o.pickupBy):(stp.key==='outfordelivery'&&o.deliveredBy)?('by '+o.deliveredBy):''; }\n      if(stp.meta==='no'&&i<o.stage&&o.invoiceNo){ meta=o.invoiceNo; }\n      if(stp.key==='delivered'&&o.deliveredDate&&i<o.stage){ meta=fmt(o.deliveredDate); }\n      return '<div class=\"tstep'+on+cur+'\"><div class=\"tdot\"></div><div><div class=\"tlabel\">'+stp.label+'</div>'+(meta?'<div class=\"tmeta\">'+meta+'</div>':'')+'</div></div>';\n    }).join('');\n    var pay=o.pending>0?'<button class=\"btn pay\" onclick=\"event.stopPropagation();odPay(\\''+o.orderNo+'\\')\">Pay '+inr(o.pending)+'</button>':'';\n    var fb=feedbackLive(o)?'<a class=\"btn feedback\" href=\"/rate-us\" onclick=\"event.stopPropagation()\">Share your feedback</a>':'';\n    return '<div class=\"row\" id=\"row-'+o.orderNo+'\">'+\n      '<div class=\"row-head\" onclick=\"odToggle(\\''+o.orderNo+'\\')\">'+\n        '<span class=\"rh-chev\">▶</span>'+\n        '<span class=\"rh-no\">'+o.orderNo+' <span class=\"inv\">· '+o.invoiceNo+'</span></span>'+\n        '<span class=\"rh-date\">'+fmt(o.pickupDate)+'</span>'+\n        '<span class=\"rh-amt\">'+inr(o.valueAfterDiscount)+due+'</span>'+\n        '<span class=\"pill '+st.cls+'\">'+st.txt+'</span>'+\n      '</div>'+\n      '<div class=\"detail\">'+\n        '<div class=\"dates\"><div><b>Pickup date</b>'+fmt(o.pickupDate)+'</div><div><b>'+dLabel+'</b>'+dVal+'</div><div><b>Pieces</b>'+o.pieces+'</div><div><b>Invoice</b>'+o.invoiceNo+'</div></div>'+\n        '<div class=\"track\">'+tl+'</div>'+\n        '<div class=\"inv-line\"><span class=\"lbl\">Invoice value</span><span>'+inr(o.value)+'</span></div>'+\n        '<div class=\"inv-line\"><span class=\"lbl\">GST</span><span>'+inr(o.gst)+'</span></div>'+\n        '<div class=\"inv-line\"><span class=\"lbl\">Discount applied</span><span>'+(o.discount>0?'– '+inr(o.discount):'—')+'</span></div>'+\n        '<div class=\"inv-line total\"><span class=\"lbl\">Invoice total</span><span>'+inr(o.valueAfterDiscount)+'</span></div>'+\n        '<div class=\"inv-line paid\"><span class=\"lbl\">Advance paid</span><span>'+inr(o.advancePaid)+'</span></div>'+\n        (o.pending>0?'<div class=\"inv-line due\"><span class=\"lbl\">Pending amount</span><span>'+inr(o.pending)+'</span></div>':'')+\n        '<div class=\"foot\">'+pay+fb+'<button class=\"btn ghost\" onclick=\"event.stopPropagation();odInvoice(\\''+o.orderNo+'\\')\">See full invoice</button></div>'+\n      '</div>'+\n    '</div>';\n  }\n\n  window.odToggle=function(no){ var r=g('row-'+no); if(r) r.classList.toggle('open'); };\n  window.odPay=function(no){\n    var o=ALL.find(function(x){return x.orderNo===no;});\n    if(!o){ alert('Order not found.'); return; }\n    if(!(o.pending>0)){ alert('Nothing is due on this order.'); return; }\n\n    var token=sessionStorage.getItem('tsl_token');\n    if(!token){ window.location.replace('/track-order'); return; }\n\n    var btn=document.querySelector('#row-'+no+' .btn.pay');\n    var prev=btn?btn.textContent:'';\n    if(btn){ btn.disabled=true; btn.textContent='Opening Razorpay…'; }\n\n    var cancelPosted=false;\n    var rzpOrderId='';\n\n    function restoreBtn(){ if(btn){ btn.disabled=false; btn.textContent=prev; } }\n\n    function postPaymentResponse(status, extra){\n      var payload={\n        status: status,\n        orderNo: no,\n        amount: o.pending,\n        order: {\n          orderNo: o.orderNo,\n          invoiceNo: o.invoiceNo,\n          invoiceUrl: o.invoiceUrl||'',\n          payUrl: o.payUrl||'',\n          pickupDate: o.pickupDate,\n          expectedDate: o.expectedDate,\n          deliveredDate: o.deliveredDate,\n          pieces: o.pieces,\n          value: o.value,\n          gst: o.gst,\n          discount: o.discount,\n          valueAfterDiscount: o.valueAfterDiscount,\n          advancePaid: o.advancePaid,\n          pending: o.pending,\n          stage: o.stage\n        }\n      };\n      if(extra){\n        if(extra.razorpay_order_id) payload.razorpay_order_id=extra.razorpay_order_id;\n        if(extra.razorpay_payment_id) payload.razorpay_payment_id=extra.razorpay_payment_id;\n        if(extra.razorpay_signature) payload.razorpay_signature=extra.razorpay_signature;\n        if(extra.error) payload.error=extra.error;\n      }\n      if(!payload.razorpay_order_id && rzpOrderId) payload.razorpay_order_id=rzpOrderId;\n      return fetch('/api/payments/response',{\n        method:'POST',\n        headers:{'Content-Type':'application/json','Authorization':'Bearer '+token},\n        body:JSON.stringify(payload)\n      }).then(function(r){\n        return r.json().then(function(data){ return {ok:r.ok, data:data}; });\n      }).catch(function(err){\n        return {ok:false, data:{error:(err&&err.message)||'Network error'}};\n      });\n    }\n\n    function loadCheckout(){\n      return new Promise(function(resolve, reject){\n        if(window.Razorpay){ resolve(); return; }\n        var s=document.createElement('script');\n        s.src='https://checkout.razorpay.com/v1/checkout.js';\n        s.async=true;\n        s.onload=function(){ resolve(); };\n        s.onerror=function(){ reject(new Error('Could not load Razorpay Checkout. Disable any ad blocker for localhost, then hard-refresh and try again.')); };\n        document.body.appendChild(s);\n      });\n    }\n\n    loadCheckout()\n      .then(function(){\n        return fetch('/api/payments/create-order',{\n          method:'POST',\n          headers:{'Content-Type':'application/json','Authorization':'Bearer '+token},\n          body:JSON.stringify({orderNo:no})\n        }).then(function(r){\n          return r.json().then(function(data){ return {ok:r.ok, data:data}; });\n        });\n      })\n      .then(function(res){\n        if(!res.ok || !res.data || !res.data.orderId){\n          throw new Error((res.data&&res.data.error)||'Could not start payment');\n        }\n        var data=res.data;\n        rzpOrderId=data.orderId||'';\n        var rzp=new window.Razorpay({\n          key: data.keyId,\n          amount: data.amount,\n          currency: data.currency||'INR',\n          name: 'The Swiss Laundry',\n          description: 'Payment for order '+no,\n          order_id: data.orderId,\n          prefill: data.prefill||{},\n          notes: { orderNo: no },\n          theme: { color: '#C9A84C' },\n          modal: {\n            ondismiss: function(){\n              if(cancelPosted){ restoreBtn(); return; }\n              cancelPosted=true;\n              postPaymentResponse('cancelled', { razorpay_order_id: rzpOrderId })\n                .then(function(){ restoreBtn(); })\n                .catch(function(){ restoreBtn(); });\n            }\n          },\n          handler: function(response){\n            cancelPosted=true;\n            postPaymentResponse('success', {\n              razorpay_order_id: response.razorpay_order_id,\n              razorpay_payment_id: response.razorpay_payment_id,\n              razorpay_signature: response.razorpay_signature\n            }).then(function(v){\n              restoreBtn();\n              if(v.ok && v.data && v.data.success){\n                alert(v.data.message||('Payment successful. Ref: '+response.razorpay_payment_id));\n                location.reload();\n              } else {\n                alert((v.data&&(v.data.error||v.data.message))||('Payment received but could not verify. Please WhatsApp us with your payment id: '+response.razorpay_payment_id));\n              }\n            });\n          }\n        });\n        rzp.on('payment.failed', function(resp){\n          cancelPosted=true;\n          var err=(resp&&resp.error)||{};\n          postPaymentResponse('failed', {\n            razorpay_order_id: (resp&&resp.error&&resp.error.metadata&&resp.error.metadata.order_id)||rzpOrderId,\n            razorpay_payment_id: (resp&&resp.error&&resp.error.metadata&&resp.error.metadata.payment_id)||'',\n            error: {\n              code: err.code||'',\n              description: err.description||'',\n              source: err.source||'',\n              step: err.step||'',\n              reason: err.reason||''\n            }\n          }).then(function(){\n            restoreBtn();\n            alert(err.description||'Payment failed. Please try again.');\n          }).catch(function(){\n            restoreBtn();\n            alert(err.description||'Payment failed. Please try again.');\n          });\n        });\n        rzp.open();\n      })\n      .catch(function(err){\n        restoreBtn();\n        alert((err&&err.message)||'Could not open Razorpay. Please try again or WhatsApp us.');\n      });\n  };\n  window.odInvoice=function(no){\n    var o=ALL.find(function(x){return x.orderNo===no;});\n    if(!o){ alert('Order not found.'); return; }\n    var url=String(o.invoiceUrl||'').trim();\n    if(/^https?:\\/\\//i.test(url)){ window.open(url,'_blank','noopener,noreferrer'); return; }\n    alert('Invoice link is not available for this order yet.');\n  };\n  window.odOpenEdit=function(){\n    if(ACC){\n      g('edName').value=ACC.name||'';\n      g('edEmail').value=ACC.email||'';\n      var addr=[ACC.addr1,ACC.area,ACC.city,ACC.pincode].filter(Boolean).join(', ');\n      g('edAddr').value=addr||ACC.addr1||'';\n    }\n    g('odEditModal').classList.add('show');\n    setTimeout(function(){ g('edName').focus(); }, 50);\n  };\n  window.odOpenDelete=function(){ g('odDeleteModal').classList.add('show'); };\n  window.odCloseModal=function(){ g('odEditModal').classList.remove('show'); g('odDeleteModal').classList.remove('show'); };\n  window.odSubmitEdit=function(){\n  var token=sessionStorage.getItem('tsl_token');\n  var btn=document.querySelector('#odEditModal .btn.pay');\n  var payload={name:(g('edName').value||'').trim(),email:(g('edEmail').value||'').trim(),address:(g('edAddr').value||'').trim()};\n  if(!payload.name){ g('edName').focus(); return; }\n  if(!payload.email){ g('edEmail').focus(); return; }\n  if(btn){btn.disabled=true;btn.textContent='Submitting…';}\n  fetch('/api/account/edit-request',{method:'POST',headers:{'Content-Type':'application/json','Authorization':'Bearer '+token},body:JSON.stringify(payload)})\n    .then(function(r){return r.json().then(function(data){return {ok:r.ok,data:data};});})\n    .then(function(res){\n      if(btn){btn.disabled=false;btn.textContent='Submit for approval';}\n      var data=res.data||{};\n      if(data.success){\n        if(ACC){ ACC.name=payload.name; ACC.email=payload.email; ACC.addr1=payload.address; }\n        if(g('odName')) g('odName').textContent=payload.name||'Welcome';\n        if(g('odSub')) g('odSub').textContent=((ACC&&ACC.phone)||'')+(payload.email?' · '+payload.email:'');\n        alert(data.message||'Your changes have been submitted and are pending approval.');\n        odCloseModal();\n      } else {\n        alert(data.error||data.message||'Could not submit changes. Please try again or contact us.');\n      }\n    })\n    .catch(function(){\n      if(btn){btn.disabled=false;btn.textContent='Submit for approval';}\n      alert('Could not submit changes. Please try again or contact us.');\n    });\n};\n  window.odSubmitDelete=function(){ alert('Deletion request submitted. Processed after approval.'); odCloseModal(); };\n  window.odSignOut=function(e){ if(e)e.preventDefault(); sessionStorage.removeItem('tsl_token'); sessionStorage.removeItem('tsl_verified'); sessionStorage.removeItem('tsl_phone'); try{fetch('/api/logout',{method:'POST'})}catch(err){} try{window.dispatchEvent(new Event('tsl-auth-change'))}catch(err){} window.location.replace('/'); };\n\n  loadAccount();\n})();\n",
  "bodyHtml": "<main class=\"wrap\">\n  <div class=\"loading\" id=\"odLoading\">Loading your account…</div>\n  <div id=\"odContent\" style=\"display:none\">\n    <div class=\"acct\">\n      <div>\n        <div class=\"acct-eyebrow\">Your Account</div>\n        <div class=\"acct-name\"><h1 id=\"odName\">—</h1><span class=\"badge new\" id=\"odBadge\"></span></div>\n        <div class=\"acct-sub\" id=\"odSub\">—</div>\n      </div>\n      <div class=\"acct-actions\">\n        <button class=\"btn-act\" onclick=\"odOpenEdit()\">Edit my details</button>\n        <button class=\"btn-act danger\" onclick=\"odOpenDelete()\">Request account deletion</button>\n      </div>\n    </div>\n\n    <div class=\"summary\" id=\"odSummary\"></div>\n\n    <div class=\"controls\">\n      <div class=\"search\">\n        <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"11\" cy=\"11\" r=\"7\"></circle><path d=\"M21 21l-4.3-4.3\"></path></svg>\n        <input type=\"text\" id=\"odSearch\" placeholder=\"Search by invoice, date, status or amount…\" oninput=\"odApply()\">\n      </div>\n      <select class=\"sel\" id=\"odSort\" onchange=\"odApply()\">\n        <option value=\"pickup_desc\">Pickup date (newest)</option>\n        <option value=\"pickup_asc\">Pickup date (oldest)</option>\n        <option value=\"delivery_desc\">Delivery date (newest)</option>\n        <option value=\"amount_desc\">Amount (high to low)</option>\n        <option value=\"amount_asc\">Amount (low to high)</option>\n        <option value=\"status\">Status</option>\n        <option value=\"invoice\">Invoice number</option>\n      </select>\n      <select class=\"sel\" id=\"odFilter\" onchange=\"odApply()\">\n        <option value=\"all\">All orders</option>\n        <option value=\"live\">In progress</option>\n        <option value=\"ready\">Ready</option>\n        <option value=\"done\">Delivered</option>\n        <option value=\"due\">Has balance due</option>\n      </select>\n    </div>\n    <div class=\"count\" id=\"odCount\"></div>\n    <div id=\"odList\"></div>\n  </div>\n</main>\n\n<div class=\"modal\" id=\"odEditModal\"><div class=\"modal-box\">\n  <h3>Edit your details</h3>\n  <p>Changes are reviewed and approved by our team before they take effect. We'll confirm by WhatsApp.</p>\n  <div class=\"field\"><label>Full name</label><input id=\"edName\" type=\"text\"></div>\n  <div class=\"field\"><label>Email</label><input id=\"edEmail\" type=\"email\"></div>\n  <div class=\"field\"><label>Address</label><textarea id=\"edAddr\" rows=\"3\"></textarea></div>\n  <div class=\"modal-foot\"><button class=\"btn pay\" onclick=\"odSubmitEdit()\">Submit for approval</button><button class=\"btn ghost\" onclick=\"odCloseModal()\">Cancel</button></div>\n</div></div>\n\n<div class=\"modal\" id=\"odDeleteModal\"><div class=\"modal-box\">\n  <h3>Request account deletion</h3>\n  <p>This sends a deletion request to our team. Your account and data are removed once we approve it. Pending orders or unpaid bills must be settled first.</p>\n  <div class=\"modal-foot\"><button class=\"btn ghost\" style=\"border-color:#e0697a;color:#e0697a\" onclick=\"odSubmitDelete()\">Yes, request deletion</button><button class=\"btn ghost\" onclick=\"odCloseModal()\">Keep my account</button></div>\n</div></div>\n\n<footer class=\"df\">\n  <p>The Swiss Laundry · Kasba, Kolkata · Mon–Sat 10am–5pm<br>\n  Need help? <a href=\"https://wa.me/919836233354\" target=\"_blank\" rel=\"noopener\">WhatsApp us</a> · <a href=\"tel:+919836233354\">98362 33354</a></p>\n</footer>"
}
