/* ===== BrewOS ЦКТ — data ===== */ const STATES = { free: { key:'free', label:'Free', ru:'Свободно', cls:'free', color:'#6b645c' }, fill: { key:'fill', label:'Залив', ru:'Идёт залив', cls:'fill', color:'#38bdf8' }, ferment: { key:'ferment', label:'Fermenting', ru:'Брожение', cls:'ferment', color:'#f59e0b' }, mature: { key:'mature', label:'Дозревание', ru:'Дозревание', cls:'mature', color:'#eab308' }, cold: { key:'cold', label:'Cold crash', ru:'Хол. стабилизация', cls:'cold', color:'#22d3ee' }, ready: { key:'ready', label:'Готов', ru:'Готов к розливу',cls:'ready', color:'#4ade80' }, cip: { key:'cip', label:'CIP', ru:'Мойка / CIP', cls:'cip', color:'#a78bfa' }, }; const STATE_ORDER = ['free','fill','ferment','mature','cold','ready','cip']; // liquid color per style const STYLES = { lager: { color:'#e6b13a', foam:'#fbf2d2' }, wheat: { color:'#e7c357', foam:'#fdf6da' }, ipa: { color:'#d98a2b', foam:'#f7e2bd' }, apa: { color:'#cf8a33', foam:'#f3ddb6' }, pale: { color:'#dcc56a', foam:'#fbf4d8' }, stout: { color:'#241814', foam:'#b48a5a' }, porter: { color:'#33201a', foam:'#c79a6a' }, cherry: { color:'#7e1f2c', foam:'#e6a6ac' }, pear: { color:'#cdd06a', foam:'#f3f2cf' }, belgian: { color:'#c8721f', foam:'#f3d6a8' }, tea: { color:'#d8c489', foam:'#f4ebcf' }, saison: { color:'#dba93f', foam:'#f7e6bd' }, }; // helper to build a batch let _bid = 240; function batch(name, styleKey, liters, st, opts={}){ const s = STYLES[styleKey] || STYLES.lager; const total = opts.total ?? 21; // total days in tank plan const day = opts.day ?? 0; // current day in process return { id:'B-'+(_bid++), name, style:styleKey, color:s.color, foam:s.foam, liters, state:st, brewDate: opts.brewDate || null, totalDays: total, day, daysLeft: Math.max(0, total - day), temp: opts.temp ?? null, platoNow: opts.platoNow ?? null, platoTarget: opts.platoTarget ?? null, og: opts.og ?? null, pricePerL: opts.pricePerL ?? null, fill: opts.fill ?? null, // 0..1 visual override }; } // state -> default visual fill level (0..1) of the cylinder part function fillFor(state, b){ if (b && b.fill != null) return b.fill; switch(state){ case 'free': return 0; case 'cip': return 0; case 'fill': return 0.42; case 'ferment': return 0.9; case 'mature': return 0.86; case 'cold': return 0.84; case 'ready': return 0.8; default: return 0; } } // ----- build 26 tanks ----- function mk(num, cap, state, b){ return { id:'T'+num, num, cap, state, batch:b }; } const TANKS = [ // 1000 L — 11 tanks (№1..11) mk(1, 1000, 'ferment', batch('Kuts Соль','pale',1000,'ferment',{brewDate:'2026-05-13',total:24,day:7,temp:19.5,platoNow:7.4,platoTarget:2.6,og:11.8})), mk(2, 1000, 'free', null), mk(3, 1000, 'cip', null), mk(4, 1000, 'ferment', batch('Kuts кл','pale',1000,'ferment',{brewDate:'2026-05-13',total:24,day:7,temp:19.0,platoNow:7.9,platoTarget:2.6,og:11.6})), mk(5, 1000, 'ready', batch('Чай белый','tea',500,'ready',{brewDate:'2026-05-08',total:18,day:18,temp:2.0,pricePerL:80})), mk(6, 1000, 'free', null), mk(7, 1000, 'free', null), mk(8, 1000, 'mature', batch('Montmorency','cherry',1000,'mature',{brewDate:'2026-05-08',total:30,day:21,temp:8.0,platoNow:3.1,platoTarget:2.8,og:13.0})), mk(9, 1000, 'ready', batch('Шорле груша','pear',500,'ready',{brewDate:'2026-05-18',total:16,day:16,temp:1.5,pricePerL:80})), mk(10,1000, 'fill', batch('Гёзе абрикос','saison',1000,'fill',{brewDate:'2026-05-29',total:22,day:0,temp:20.0,fill:0.38})), mk(11,1000, 'free', null), // 2000 L — 15 tanks (№12..26) mk(12,2000, 'ferment', batch('Светлый лагер','lager',2000,'ferment',{brewDate:'2026-05-22',total:28,day:7,temp:11.0,platoNow:8.2,platoTarget:2.4,og:11.2})), mk(13,2000, 'ferment', batch('APA Сосна','apa',2000,'ferment',{brewDate:'2026-05-24',total:21,day:5,temp:20.5,platoNow:9.0,platoTarget:3.0,og:12.4})), mk(14,2000, 'free', null), mk(15,2000, 'cold', batch('Пшеничное','wheat',2000,'cold',{brewDate:'2026-05-15',total:20,day:17,temp:1.0,platoNow:2.9,platoTarget:2.8,og:12.0})), mk(16,2000, 'free', null), mk(17,2000, 'mature', batch('Портер овсяный','porter',2000,'mature',{brewDate:'2026-05-10',total:32,day:19,temp:9.0,platoNow:3.4,platoTarget:3.0,og:15.5})), mk(18,2000, 'free', null), mk(19,2000, 'ferment', batch('IPA Цитра','ipa',2000,'ferment',{brewDate:'2026-05-25',total:23,day:4,temp:21.0,platoNow:10.1,platoTarget:2.8,og:14.0})), mk(20,2000, 'ready', batch('Стаут имперский','stout',2000,'ready',{brewDate:'2026-05-01',total:35,day:35,temp:3.0,pricePerL:110})), mk(21,2000, 'free', null), mk(22,2000, 'cip', null), mk(23,2000, 'free', null), mk(24,2000, 'mature', batch('Бельгийский дюбель','belgian',2000,'mature',{brewDate:'2026-05-12',total:30,day:17,temp:14.0,platoNow:4.2,platoTarget:3.4,og:16.2})), mk(25,2000, 'free', null), mk(26,2000, 'ferment', batch('Вишнёвый сауэр','cherry',1500,'ferment',{brewDate:'2026-05-26',total:26,day:3,temp:22.0,platoNow:11.0,platoTarget:4.0,og:13.5})), ]; // groups const GROUPS = [ { cap:1000, label:'1000 Л', tanks:TANKS.filter(t=>t.cap===1000) }, { cap:2000, label:'2000 Л', tanks:TANKS.filter(t=>t.cap===2000) }, ]; Object.assign(window, { STATES, STATE_ORDER, STYLES, TANKS, GROUPS, fillFor });