book
import { useState, useMemo } from "react";
// ─── Coûts de production (€) ────────────────────────────────────────────────
// Clic : une grande feuille (SRA3) = 1 clic, au même tarif qu'une A4.
// On imprime l'intérieur en SRA3 recto-verso → 2 clics par feuille.
const C = {
click_nb: 0.0042, // €/clic (1 face) – N&B (Ricoh Pro 8300S)
click_col: 0.038, // €/clic (1 face) – couleur (C7200 / C7500)
cover_paper: 0.072, // Couverture Diva Art 250g 1 face / SRA3 (72,18 €/mille, 1 SRA3 par 32×45)
binding_setup: 0.80, // Mise en route Morgana Pure 200 (DCC) / job
binding_unit: [ // €/livre DCC selon palier qtité
{ min: 1, cost: 0.38 },
{ min: 50, cost: 0.26 },
{ min: 100, cost: 0.19 },
{ min: 200, cost: 0.14 },
{ min: 500, cost: 0.09 },
],
staple_setup: 0.30, // Mise en route piqûre à cheval / job
staple_each: 0.03, // €/agrafe (×2 agrafes par livret)
staple_count: 2,
overhead: 0.18, // Frais généraux 18 %
};
// ─── Papiers intérieur ───────────────────────────────────────────────────────
// perSRA3 = coût d'une feuille SRA3 (320×450) selon le format parent acheté.
// main = rapport volume/épaisseur, pour le calcul du dos.
// usage = "nb" (N&B), "col" (couleur), "both" (les deux).
const PAPERS = {
munken: { label: "Munken Print crème 90 g (bouffant)", grammage: 90, main: 1.8, perSRA3: 0.031, usage: "nb",
note: "Volume « édition », toucher doux — déconseillé en couleur (absorbe l'encre)" },
offset: { label: "Coral Book offset 80 g", grammage: 80, main: 1.2, perSRA3: 0.0153, usage: "both",
note: "Standard polyvalent, économique — texte N&B ou couleur légère" },
couche: { label: "Couché 135 g", grammage: 135, main: 0.85, perSRA3: 0.0338, usage: "col",
note: "Couleur éclatante, idéal illustrations et photos" },
};
// Quels papiers proposer selon le mode d'impression (menu adaptatif).
const PAPER_BY_COLOR = {
nb: ["munken", "offset"],
col: ["offset", "couche", "munken"], // munken en dernier = « déconseillé couleur »
};
// ─── Couverture ──────────────────────────────────────────────────────────────
// Diva Art 250 g une face (recto couché, verso brut). Pelliculage conseillé
// par défaut pour protéger la fibre au rainage du dos.
const COVER = { label: "Diva Art 250 g une face", paper: 0.072 };
// ─── Finitions couverture (toutes en interne) ───────────────────────────────
// setup = calage/préparation par job (amorti) ; cover = €/couverture.
// ⚠️ Estimations à confirmer avec vos coûts réels (film, vernis, temps machine).
const FINISH = {
mat: { label: "Pelliculage mat", setup: 10, cover: 0.12, note: "Conseillé — protège la fibre, anti-reflet" },
brillant: { label: "Pelliculage brillant", setup: 10, cover: 0.12, note: "Couleurs éclatantes" },
soft: { label: "Soft touch (peau de pêche)", setup: 10, cover: 0.25, note: "Toucher velours premium" },
mat3d: { label: "Mat + vernis 3D", setup: 25, cover: 0.47, note: "Relief brillant sur fond mat (MGI)" },
soft3d: { label: "Soft touch + vernis 3D", setup: 25, cover: 0.60, note: "Combo premium (MGI)" },
brut: { label: "Sans finition (déconseillé)",setup: 0, cover: 0, note: "Risque de casse de la fibre au pli" },
};
// ─── Reliure ─────────────────────────────────────────────────────────────────
// DCC dès 48 pages (alerte si dos < 3 mm) ; agrafé (piqûre à cheval) 8–64 pages.
const SPINE_MIN_MM = 3; // sous ce dos, on conseille l'agrafé
const STAPLE_MAX = 64; // agrafé possible jusqu'à 64 pages (multiple de 4)
const DCC_MIN = 48; // DCC possible dès 48 pages
// ─── Main-d'œuvre directe (collage + coupe) ──────────────────────────────────
const LABOR = {
rate: 35, // €/h chargé — main-d'œuvre directe
setupMin: 45, // min / commande (fixe) — contrôle fichier, calages, réglages
finishMin: 1, // min / livre (variable) — cycle reliure + coupe + contrôle
};
// ─── Marge dégressive par quantité ───────────────────────────────────────────
// Petit tirage = premium (service) ; gros tirage = serré (compétitivité).
const MARGINS = { 10: 50, 25: 50, 50: 45, 100: 42, 200: 40, 300: 38, 500: 35 };
// Épaisseur du dos (mm) selon le papier choisi.
// Épaisseur d'1 feuille = grammage × main / 1000 ; 2 pages/feuille → /2000.
const spineMm = (pages, paperKey) => {
const p = PAPERS[paperKey] || PAPERS.munken;
return pages * p.grammage * p.main / 2000;
};
// poses = nombre de pages par FACE de SRA3 (recto-verso → ×2 pages par feuille)
const FORMATS = {
poche: { label: "Poche 10,5×17,7 cm", poses: 6, note: "Roman de poche — 6 poses SRA3" },
a5: { label: "A5 148×210 mm", poses: 4, note: "Standard roman, essai — 4 poses SRA3" },
a4: { label: "A4 210×297 mm", poses: 2, note: "Catalogue, beaux-arts — 2 poses SRA3" },
};
const PAGES = [48, 64, 80, 96, 128, 160, 200, 256, 300];
const QTYS = [10, 25, 50, 100, 200, 300, 500];
const MARKETS = [
{ icon: "✍️", label: "Auto-édition", desc: "Auteurs locaux, romans, témoignages, poésie. Tirage 10–100 ex.", segment: "B2C", marge: "55–65 %" },
{ icon: "🏫", label: "Écoles & Lycées", desc: "Livres de fin d'année, annuaires, portfolios élèves.", segment: "B2B", marge: "40–50 %" },
{ icon: "🏛️", label: "Collectivités", desc: "Livres patrimoniaux, rapports, plaquettes institutionnelles.", segment: "B2B", marge: "35–45 %" },
{ icon: "🤝", label: "Associations", desc: "Livres anniversaire, compte-rendus, livrets de fête.", segment: "B2B", marge: "40–50 %" },
{ icon: "🏢", label: "Entreprises", desc: "Livres blancs, rapports annuels, supports commerciaux.", segment: "B2B", marge: "45–55 %" },
{ icon: "🎨", label: "Artistes", desc: "Catalogues d'expo, portfolios, livres d'artiste.", segment: "B2C/B2B", marge: "50–60 %" },
];
function bindingUnitDCC(qty) {
let cost = C.binding_unit[0].cost;
for (const tier of C.binding_unit) {
if (qty >= tier.min) cost = tier.cost;
}
return cost;
}
function calcBook(format, pages, color, qty, laborRate, finish, paperKey, reliure) {
const fmt = FORMATS[format];
const pagesPerSheet = fmt.poses * 2; // recto-verso
const sheets = Math.ceil(pages / pagesPerSheet); // feuilles SRA3 intérieur
const paper = PAPERS[paperKey] || PAPERS.offset;
const clickRate = color === "nb" ? C.click_nb : C.click_col;
const clickInt = sheets * 2 * clickRate; // 2 faces par feuille SRA3
const paperInt = sheets * paper.perSRA3; // coût SRA3 du papier choisi
const coverMat = COVER.paper + C.click_col; // couverture + 1 clic couleur recto
const fin = FINISH[finish] || FINISH.mat;
const finishUnit= fin.cover + fin.setup / qty; // finition : coût/couverture + calage amorti
// Reliure : DCC (Morgana) ou agrafé (piqûre à cheval)
let bindUnit;
if (reliure === "agrafe") {
bindUnit = C.staple_each * C.staple_count + C.staple_setup / qty;
} else {
bindUnit = bindingUnitDCC(qty) + C.binding_setup / qty;
}
const laborUnit = (LABOR.setupMin / qty + LABOR.finishMin) * (laborRate / 60);
const direct = clickInt + paperInt + coverMat + finishUnit + bindUnit + laborUnit;
const withOH = direct * (1 + C.overhead);
const margin = MARGINS[qty] != null ? MARGINS[qty] : 45;
const sell = withOH / (1 - margin / 100);
const sellRound = Math.ceil(sell * 20) / 20; // arrondi au 0.05 supérieur
return {
sheets, margin,
spine: +spineMm(pages, paperKey).toFixed(1),
clickInt: +clickInt.toFixed(4),
paperInt: +paperInt.toFixed(4),
coverMat: +coverMat.toFixed(4),
finishUnit:+finishUnit.toFixed(4),
bindUnit: +bindUnit.toFixed(4),
laborUnit:+laborUnit.toFixed(4),
direct: +direct.toFixed(4),
withOH: +withOH.toFixed(4),
sell: +sellRound.toFixed(2),
totalSell:+(sellRound * qty).toFixed(2),
totalCost:+(withOH * qty).toFixed(2),
};
}
const fmt2 = n => n.toFixed(2).replace(".", ",") + " €";
const fmtN = n => n.toFixed(4).replace(".", ",") + " €";
// ─── Composant principal ─────────────────────────────────────────────────────
export default function DCC() {
const [tab, setTab] = useState("grille"); // grille | marche | projet
const [format, setFormat] = useState("a5");
const [pages, setPages] = useState(128);
const [color, setColor] = useState("nb");
const [laborRate, setLaborRate] = useState(LABOR.rate);
const [finish, setFinish] = useState("mat");
const [paper, setPaper] = useState("munken");
const [reliure, setReliure] = useState("dcc");
const [selQty, setSelQty] = useState(null); // qty sélectionnée pour détail
// Papiers disponibles selon le mode N&B / couleur (menu adaptatif)
const paperChoices = PAPER_BY_COLOR[color];
// Si le papier courant n'est plus valide pour le mode, on bascule sur le 1er dispo
const effectivePaper = paperChoices.includes(paper) ? paper : paperChoices[0];
const rows = useMemo(
() => QTYS.map(qty => ({ qty, ...calcBook(format, pages, color, qty, laborRate, finish, effectivePaper, reliure) })),
[format, pages, color, laborRate, finish, effectivePaper, reliure]
);
const detail = selQty !== null ? rows.find(r => r.qty === selQty) : null;
const curSpine = spineMm(pages, effectivePaper);
const spineThin = reliure === "dcc" && curSpine < SPINE_MIN_MM;
const stapleOK = pages <= STAPLE_MAX;
return (
{/* ── En-tête ── */}
📚
Les Bateliers Imprimeurs · Bischheim – Strasbourg
Livres — Dos Carré Collé
Morgana Pure 200
{/* ═══════════════ ONGLET GRILLE ═══════════════ */}
{tab === "grille" && (
{/* Configurateur */}
⚙️ Configuration
{/* Format */}
{Object.entries(FORMATS).map(([k,v])=>(
))}
{FORMATS[format].note}
{/* Nb pages */}
Dos ≈ {curSpine.toFixed(1).replace(".",",")} mm · {PAPERS[effectivePaper].label.split(" ")[0]}
{/* Couleur */}
{color==="nb"?"Ricoh Pro 8300S":"Ricoh C7200 / C7500"}
{/* Papier intérieur (adaptatif N&B/couleur) */}
{PAPERS[effectivePaper].note}
{/* Reliure */}
{reliure==="agrafe"
? (stapleOK ? "Piqûre à cheval · livrets fins (≤ 64 p.)" : "Agrafé impossible au-delà de 64 p.")
: "Morgana Pure 200 · dès 48 pages"}
{/* Main-d'œuvre */}
{/* Finition couverture */}
{FINISH[finish].note}
{/* Alerte dos fin */}
{spineThin && (
⚠️ Dos de seulement {curSpine.toFixed(1).replace(".",",")} mm — trop fin pour un dos carré collé propre.
Passez à l'agrafé{stapleOK?"":" (ou ajoutez des pages / un papier plus épais comme le Munken)"} pour cette pagination.
)}
{/* Marge dégressive (note) */}
10–25 ex : 50 %100 ex : 42 %500 ex : 35 %
Premium en petit tirage (service), serré en gros volume (compétitivité). Réglable dans MARGINS.
{/* Tableau */}
📊 Grille tarifaire — {PAPERS[effectivePaper].label.split(" ").slice(0,3).join(" ")} · couverture {COVER.label}
{rows.map(r => {
const marginReal = ((r.sell - r.withOH) / r.sell * 100).toFixed(0);
const isSelected = selQty === r.qty;
return (
= 100 ? s.trGood : {})}>
);
})}
| Quantité | Prix de revient | dont main-d'œuvre | Prix unitaire HT | Total HT | Marge | Détail |
|---|
| {r.qty} ex. | {fmt2(r.withOH)} | {fmt2(r.laborUnit)} | {fmt2(r.sell)} | {fmt2(r.totalSell)} | {marginReal} % | |
{/* Détail coûts */}
{detail && (
🔍 Décomposition coût — {detail.qty} exemplaires (marge {detail.margin} %)
{[
["🖨️ Impression intérieur", detail.clickInt],
["📄 Papier intérieur", detail.paperInt],
["🎨 Couverture (papier+click)", detail.coverMat],
["✨ Finition couverture", detail.finishUnit],
[reliure==="agrafe"?"📎 Reliure (agrafé)":"📌 Reliure (Morgana)", detail.bindUnit],
["👷 Main-d'œuvre (collage+coupe)", detail.laborUnit],
].map(([l,v])=>(
{l}
{fmtN(v)} ({Math.round(v/detail.direct*100)} %)
))}
Coût direct (matière + main-d'œuvre) : {fmt2(detail.direct)}
+ Frais généraux 18 % : {fmt2(detail.withOH - detail.direct)}
Prix de revient : {fmt2(detail.withOH)}
Prix de vente HT : {fmt2(detail.sell)}
)}
{/* Note couverture */}
📌 Couverture : {COVER.label} (recto couché, verso brut), impression couleur recto sur SRA3 — {(""+COVER.paper.toFixed(3)).replace(".",",")} €/couverture.
Pelliculage conseillé par défaut : sur un 250 g il renforce la couverture et protège le pli du dos contre la casse de la fibre au rainage. Le dos est calculé sur l'épaisseur réelle du livre.
{/* Note main-d'œuvre */}
👷 Main-d'œuvre ({laborRate} €/h) : {LABOR.setupMin} min fixes/commande (contrôle fichier, calages, réglages) + {LABOR.finishMin} min/livre (reliure, coupe, contrôle).
L'impression n'est pas comptée en main-d'œuvre : temps masqué (plusieurs machines en parallèle), déjà couvert par le clic et les frais généraux. Réglages dans LABOR.
{/* Note papier */}
📖 Papiers intérieur (achat 45×64, 72×102 ou 32×45 → coupe SRA3) : Munken Print crème bouffant 90 g (main 1,8 · {(""+PAPERS.munken.perSRA3).replace(".",",")} €/SRA3) pour le N&B « édition » ; Coral Book offset 80 g (main 1,2 · {(""+PAPERS.offset.perSRA3).replace(".",",")} €/SRA3) en polyvalent ; Couché 135 g (main 0,85 · {(""+PAPERS.couche.perSRA3).replace(".",",")} €/SRA3) pour la couleur.
Le menu s'adapte au mode : en couleur, le Munken est signalé « déconseillé » (le bouffant absorbe l'encre). Intérieur imprimé en SRA3 recto-verso (poche 6 poses/face, A5 4, A4 2) ; grande feuille = 1 clic au tarif A4. Réglages dans PAPERS et C.
)}
{/* ═══════════════ ONGLET MARCHÉS ═══════════════ */}
{tab === "marche" && (
🎯 Segments de marché — Livres DCC
{MARKETS.map(m=>(
{m.icon}
{m.label}
{m.desc}
{m.segment}
Marge : {m.marge}
))}
🌐 Concurrents en ligne (référence tarifaire)
{[
["TheBookEdition","~5,50 €/u","~9,80 €/u","7–10 j","Auto-édition, ISBN"],
["Lulu.com","~4,80 €/u","~8,50 €/u","10–15 j","Distribution mondiale"],
["BoD","~6,20 €/u","~11,00 €/u","8–12 j","Référencement libraires"],
["Imprimeur local (vous)","3,20–4,50 €/u","6,50–9,00 €/u","2–5 j","Réactivité, personnalisation"],
].map(r=>(
{r.map((c,i)=>(
))}
))}
| Plateforme | A5 128p N&B 50ex | A5 128p Couleur 50ex | Délai | Points forts |
|---|
| {c}
|
💡 Avantage concurrentiel clé : délai 2–5 jours vs 7–15 jours en ligne, personnalisation (tirage à partir de 10 ex.), interlocuteur local Grand Est.
)}
{/* ═══════════════ ONGLET PROJET CLAUDE ═══════════════ */}
{tab === "projet" && (
📋 Instructions pour le Projet Claude "Book DCC"
Copiez-collez ce texte dans le champ "Qu'essayez-vous de faire ?" lors de la création du projet.
{`Tu es l'assistant commercial et technique de Les Bateliers Imprimeurs,
imprimeur numérique à Bischheim – Strasbourg (Alsace, 67800), spécialisé
en livres dos carré collé (DCC) et agrafés.
ÉQUIPEMENT :
• Intérieur N&B : Ricoh Pro 8300S
• Intérieur couleur : Ricoh Pro C7200 & C7500
• Reliure : Morgana Pure 200 (dos carré collé) + piqûre à cheval (agrafé)
• Finitions internes : pelliculage mat/brillant/soft touch + vernis 3D (MGI)
FORMATS MAÎTRISÉS : Poche 10,5×17,7, A5, A4
TIRAGES : 10 à 500 exemplaires
DÉLAI : 2–5 j sans finition / ~15 j avec finition (regroupement)
TES MISSIONS :
1. Qualifier les prospects (format, pages, couleur, papier, finition, quantité, délai)
2. Générer des devis rapides avec décomposition de coût
3. Rédiger des emails commerciaux adaptés au segment client
4. Conseiller sur le choix format / papier / reliure / finition
5. Prospecter : auto-édition, écoles, associations, collectivités Grand Est, entreprises
6. Analyser les marges et identifier les commandes rentables
COÛTS DE RÉFÉRENCE (à ajuster selon les tarifs fournisseurs actuels) :
• Impression : 1 grande feuille (SRA3) = 1 clic, au tarif A4
- Clic N&B : 0,0042 €/face (Ricoh Pro 8300S)
- Clic couleur : 0,038 €/face (C7200 / C7500)
- Intérieur imprimé en SRA3 recto-verso = 2 clics/feuille
• Poses par face SRA3 : Poche 6, A5 4, A4 2 (recto-verso = ×2 pages/feuille)
• Papiers intérieur (coupés en SRA3) :
- Munken Print crème 90 g bouffant (main 1,8) : 0,031 €/SRA3 — N&B « édition »
- Coral Book offset 80 g (main 1,2) : 0,0153 €/SRA3 — polyvalent N&B/couleur
- Couché 135 g (main 0,85) : 0,0338 €/SRA3 — couleur, illustrations
- le bouffant est déconseillé en couleur (absorbe l'encre) ; dos = pages × grammage × main / 2000
• Couverture : Diva Art 250 g une face : 0,072 €/SRA3 + 1 clic couleur (0,038 €) recto
- pelliculage conseillé par défaut (protège la fibre au pli du dos)
• Finitions couverture (toutes en interne, calage amorti sur la quantité) :
- Pelliculage mat ou brillant : ~10 €/job + ~0,12 €/couverture
- Soft touch « peau de pêche » : ~10 €/job + ~0,25 €/couverture
- Vernis 3D sélectif (MGI numérique, sur mat) : ~25 €/job + ~0,47 €/couverture
- Soft touch + vernis 3D (premium) : ~25 €/job + ~0,60 €/couverture
→ argument fort : vernis 3D introuvable chez Lulu / BoD / MonBouquin
• Reliure : DCC dès 48 p. (Morgana, 0,09–0,38 €/livre + 0,80 €/job) ;
agrafé/piqûre à cheval 8–64 p. (2 agrafes à 0,03 € + 0,30 €/job)
• Main-d'œuvre (collage + coupe) : 35 €/h — 45 min fixes/commande + 1 min/livre
(l'impression n'est pas comptée : temps masqué, plusieurs machines en parallèle)
• Frais généraux : 18 %
• Marge cible : 40–55 % selon segment
RÈGLES COMMERCIALES :
• Minimum de commande : 10 exemplaires
• Les prix s'entendent HT, TVA 20 % en sus
• Fichiers acceptés : PDF HD, profil CMJN, fonds perdus 3 mm
• BAT numérique inclus, BAT papier sur demande (+délai)
Réponds toujours en français. Sois précis, concis et orienté résultat.`}
🚀 Checklist lancement produit DCC en ligne
{[
["1","Créer une page produit dédiée sur votre site","Formats, pages min/max, délais, grille tarifaire simplifiée"],
["2","Mettre en ligne un configurateur de devis","Formulaire : format / pages / couleur / quantité → email automatique"],
["3","Produire 3 échantillons de référence","A5 N&B, A5 couleur, A4 couleur — à montrer aux prospects"],
["4","Cibler les auteurs locaux","Groupes Facebook Alsace auto-édition, Salon du livre de Strasbourg"],
["5","Prospection B2B écoles","Rentrée scolaire : contacter les proviseurs en juin–août"],
["6","Google Ads Grand Est","Mots-clés : impression livre à la demande, livre personnalisé Alsace"],
].map(([n,t,d])=>(
))}
)}
);
}
// ─── Styles ─────────────────────────────────────────────────────────────────
const s = {
wrap: { fontFamily:"'Georgia', serif", background:"#f5f3ee", minHeight:"100vh", color:"#1c1a17" },
header: { background:"#1c1a17", color:"#f5f3ee", padding:"0" },
headerInner: { display:"flex", alignItems:"center", gap:14, padding:"18px 20px 12px" },
logo: { fontSize:32 },
brand: { fontSize:"0.75em", opacity:0.6, fontFamily:"sans-serif", letterSpacing:2, textTransform:"uppercase" },
title: { fontSize:"1.25em", fontWeight:700 },
badge: { marginLeft:"auto", background:"#c47d00", color:"#fff", padding:"4px 10px", borderRadius:20, fontSize:"0.72em", fontFamily:"sans-serif", whiteSpace:"nowrap" },
nav: { display:"flex", gap:0, borderTop:"1px solid rgba(255,255,255,0.1)" },
navBtn: { flex:1, padding:"10px 8px", background:"transparent", color:"rgba(245,243,238,0.65)", border:"none", cursor:"pointer", fontFamily:"sans-serif", fontSize:"0.78em", letterSpacing:0.5 },
navActive: { background:"rgba(255,255,255,0.1)", color:"#f5f3ee", borderBottom:"2px solid #c47d00" },
body: { padding:"16px", display:"flex", flexDirection:"column", gap:14 },
card: { background:"#fff", borderRadius:10, padding:"16px", boxShadow:"0 1px 4px rgba(0,0,0,0.07)" },
cardTitle: { fontFamily:"sans-serif", fontWeight:700, fontSize:"0.82em", letterSpacing:1, textTransform:"uppercase", color:"#555", marginBottom:12, paddingBottom:8, borderBottom:"1px solid #eee" },
grid3: { display:"grid", gridTemplateColumns:"repeat(auto-fit, minmax(160px,1fr))", gap:12 },
field: { display:"flex", flexDirection:"column", gap:4 },
label: { fontFamily:"sans-serif", fontSize:"0.75em", fontWeight:600, color:"#666", textTransform:"uppercase", letterSpacing:0.5 },
btnGroup: { display:"flex", gap:4 },
seg: { flex:1, padding:"6px 4px", border:"1.5px solid #ddd", borderRadius:6, background:"#fafafa", cursor:"pointer", fontFamily:"sans-serif", fontSize:"0.82em", color:"#444" },
segOn: { background:"#1c1a17", color:"#fff", border:"1.5px solid #1c1a17" },
select: { padding:"7px 8px", border:"1.5px solid #ddd", borderRadius:6, fontFamily:"sans-serif", fontSize:"0.84em", background:"#fafafa" },
rateBox: { display:"flex", alignItems:"center", border:"1.5px solid #ddd", borderRadius:6, background:"#fafafa", overflow:"hidden" },
rateInput: { flex:1, padding:"7px 8px", border:"none", background:"transparent", fontFamily:"sans-serif", fontSize:"0.84em", width:"100%", outline:"none" },
rateUnit: { padding:"0 10px", fontFamily:"sans-serif", fontSize:"0.8em", color:"#888", borderLeft:"1px solid #e0ddd6" },
hint: { fontFamily:"sans-serif", fontSize:"0.7em", color:"#999", marginTop:2 },
marginRow: { marginTop:14, borderTop:"1px solid #eee", paddingTop:12 },
warnRow: { marginTop:12, background:"#fdf2ee", border:"1px solid #e8c4b6", borderRadius:8, padding:"10px 14px", fontFamily:"sans-serif", fontSize:"0.8em", color:"#a23", lineHeight:1.5 },
range: { width:"100%", marginTop:6, accentColor:"#1a6b3a" },
rangeLabels: { display:"flex", justifyContent:"space-between", fontFamily:"sans-serif", fontSize:"0.68em", color:"#999", marginTop:3 },
table: { width:"100%", borderCollapse:"collapse", fontFamily:"sans-serif", fontSize:"0.82em" },
th: { padding:"8px 10px", background:"#f5f3ee", textAlign:"left", fontWeight:700, fontSize:"0.78em", textTransform:"uppercase", letterSpacing:0.5, color:"#555", borderBottom:"2px solid #e0ddd6" },
td: { padding:"9px 10px", borderBottom:"1px solid #f0ede8", verticalAlign:"middle" },
tdBold: { fontWeight:700 },
tdPrice: { fontWeight:800, fontSize:"1.05em", color:"#c47d00" },
trSel: { background:"#f0faf4" },
trGood: { background:"#fffef8" },
detBtn: { background:"none", border:"1px solid #ddd", borderRadius:6, padding:"3px 8px", cursor:"pointer", fontSize:"0.9em" },
costGrid: { display:"flex", flexDirection:"column", gap:10 },
costItem: { display:"flex", alignItems:"center", gap:10, flexWrap:"wrap" },
costLabel: { width:220, fontSize:"0.82em", color:"#444" },
costBar: { flex:1, minWidth:80, height:10, background:"#eee", borderRadius:5, overflow:"hidden" },
costFill: { height:"100%", background:"#1a6b3a", borderRadius:5, transition:"width 0.3s" },
costVal: { fontSize:"0.8em", color:"#555", whiteSpace:"nowrap" },
costSummary: { marginTop:8, padding:"10px 14px", background:"#f5f3ee", borderRadius:8, fontFamily:"sans-serif", fontSize:"0.85em", lineHeight:1.7 },
infoBox: { background:"#fffbf0", border:"1px solid #f0d888", borderRadius:8, padding:"10px 14px", fontFamily:"sans-serif", fontSize:"0.78em", color:"#6b5500", lineHeight:1.6 },
infoBoxLabor: { background:"#f6f3fc", border:"1px solid #d8c8f0", borderRadius:8, padding:"10px 14px", fontFamily:"sans-serif", fontSize:"0.78em", color:"#4a3a6b", lineHeight:1.6 },
infoBoxPaper: { background:"#f0faf4", border:"1px solid #b8e0c8", borderRadius:8, padding:"10px 14px", fontFamily:"sans-serif", fontSize:"0.78em", color:"#1a5b3a", lineHeight:1.6 },
marketGrid: { display:"grid", gridTemplateColumns:"repeat(auto-fit, minmax(180px,1fr))", gap:10, marginBottom:14 },
marketCard: { border:"1.5px solid #e8e4dc", borderRadius:10, padding:"12px", background:"#fafaf8" },
marketIcon: { fontSize:24, marginBottom:4 },
marketLabel: { fontWeight:700, fontSize:"0.9em", marginBottom:4 },
marketDesc: { fontFamily:"sans-serif", fontSize:"0.72em", color:"#666", lineHeight:1.5, marginBottom:8 },
marketMeta: { display:"flex", justifyContent:"space-between", alignItems:"center", flexWrap:"wrap", gap:4 },
marketBadge: { background:"#1c1a17", color:"#fff", padding:"2px 7px", borderRadius:12, fontFamily:"sans-serif", fontSize:"0.65em" },
marketMarge: { fontFamily:"sans-serif", fontSize:"0.7em", color:"#1a6b3a", fontWeight:700 },
codeBlock: { background:"#1c1a17", color:"#b8f0c8", borderRadius:8, padding:"16px", fontFamily:"'Courier New', monospace", fontSize:"0.75em", lineHeight:1.7, whiteSpace:"pre-wrap", overflowX:"auto" },
checkItem: { display:"flex", gap:12, alignItems:"flex-start", padding:"10px 0", borderBottom:"1px solid #f0ede8" },
checkNum: { background:"#1c1a17", color:"#fff", borderRadius:"50%", width:28, height:28, display:"flex", alignItems:"center", justifyContent:"center", fontFamily:"sans-serif", fontWeight:700, fontSize:"0.85em", flexShrink:0 },
checkTitle: { fontWeight:700, fontSize:"0.9em", marginBottom:2 },
checkDesc: { fontFamily:"sans-serif", fontSize:"0.75em", color:"#777" },
};