The following is a universal prompt to re-create the IBCS 09C chart-type within Plotly Studio.
Create an IBCS-style portfolio scatter chart (IBCS example type 09C): Net sales vs. margin with exactly 3 iso-gross-profit curves and a shaded top gross-profit segment. The chart must keep a clean IBCS look: consistent typography, minimal decoration, semantic colors, axes starting at zero, no gridlines, and a clear message tied to the shaded segment.
Year of data collected = 2025
Current year = 2026A) DATA INGESTION & NORMALIZATION (MUST NOT FAIL)
Auto-detect delimiter (semicolon/comma/tab/pipe) and decimal style (comma vs dot).
Identify columns by fuzzy matching (case-insensitive):
Product label: contains product|item|name|sku|id|code
Product line/category: contains product line|line|category|segment|group|family|division
Margin (%): contains margin|gm%|gross margin|profit%|contribution%
Net sales: contains net sales|sales|revenue|turnoverCoerce numeric:
Margin: if values mostly in 0–1, multiply by 100.
Sales: keep raw unit but infer a display unit label:
If column name includes mUSD|million|mn → label mUSD and don’t rescale.
Else if typical sales values > 1,000,000 → assume currency, display in m (millions) of that currency.
Else keep as-is and label generically (e.g., “Net sales”).Clean rows:
Remove rows with missing/invalid margin or sales.
Remove rows where margin ≤ 0 or sales ≤ 0.Compute:
Gross Profit (GP) = Net Sales × Margin / 100 (same unit as Net Sales)B) DETERMINE “NICE” GP THRESHOLD AND CURVE LEVELS (UNIVERSAL, DETERMINISTIC)
B1) Choose a “focus threshold” T (the top-segment cutoff)
This must be stable for messy datasets and not driven by max outliers.
Compute:
GP70 = 70th percentile of GP
GP90 = 90th percentile of GPDefine the “nice number” ladder (scale-independent):
Base nice numbers: {0.1, 0.2, 0.3, 0.5, 0.8, 1, 2, 3, 4, 5, 8}
Extended by powers of 10: multiply by 10^k as needed (…, 0.01, 0.02, … 10, 20, 30, 50, 80, 100, …)Set T as:
T = nearest nice number to GP70Tie-breaker: choose the smaller nice number (more conservative, more stable)
Guardrails to prevent absurd outcomes:
If T <= 0, set T to the smallest positive nice number present in the ladder for the data scale.
If T > GP90, step down one nice number.
If T < 0.2 * median(GP) and GP spread is normal, step up one nice number.Result:
T is a readable, “round” threshold that fits the dataset scale (e.g., 0.5, 3, 30, 300).B2) Set EXACTLY 3 curve levels based on T
Use three iso-profit curves at:
L1 = nice(T/3)
L2 = nice(2T/3)
L3 = TWhere nice(x) snaps x to the nearest nice number on the ladder (same tie-breaker: smaller).
Hard constraints:
Ensure L1 < L2 < L3 (if snapping creates duplicates, move the duplicate one step down/up on the ladder).
Always produce exactly 3 curves.Important: This means if the dataset’s natural threshold is 3, then curves become 1, 2, 3 (matching the reference).
If a tiny dataset has max GP around 0.5, then curves become something like 0.2, 0.3, 0.5 (still correct and readable).C) BUILD ISO-PROFIT CURVES (SMOOTH HYPERBOLAS)
For each level L in {L1, L2, L3}:
Compute final axis limits first (before generating curves):
X_MAX = nice_round_up(max_margin × 1.1) (this is the actual x-axis upper bound)
Y_MAX = nice_round_up(max_sales × 1.1) (this is the actual y-axis upper bound)Generate curve x-values to EXACTLY the right-most axis edge:
Create 400 margin values from X_MIN = max(0.5%, 0.7×min_margin) to X_MAX
Compute curve y:
Net Sales = (L × 100) / MarginFilter points to y ≤ (1.05×y_axis_max) (natural filtering only)
Curve styling:
Line color #BFBFBF
Width 1.5 px
Solid
Behind pointsLabel each curve at right end with text “L” (format with 0–1 decimals depending on magnitude).
Add a right-side annotation label:
“Gross profit\nin [unit]” (vertical)Mandatory Curve Labels (Non-Negotiable)
For each iso-profit curve level L in {L1, L2, L3}:
After computing the curve coordinates, take the last visible point of the curve.
Add a separate text-only scatter trace (NOT an annotation) for the label:
mode=“text”
text = formatted value of L
textposition = “middle right”
x = last_margin_value
y = last_sales_value
textfont.size = 10–11
textfont.color = #7F7F7F
showlegend = FalseDo NOT use arrows.
Do NOT use layout annotations.Each iso-profit curve MUST have a visible numeric label at its right end.
Curves without labels are invalid.D) SHADED ZONE (TOP SEGMENT)
Shade area above the highest curve (L3) across full width:
Fill #EFEFEF
Opacity 40%
Behind curves and pointsE) SCATTER POINTS & SEMANTIC COLORS
Scatter:
X = Margin (%)
Y = Net Sales (display unit)Marker:
filled circle
size ~11
opacity 85%
no outlineColors:
If product line/category has 3 dominant values, map consistently:
Most frequent → dark gray #4D4D4D
Second → amber #F2C14E
Third → orange #F05A28If more than 3 categories, color only top 3; group remainder as “Other” in mid gray.
Legend:
Title: “Product lines” (or the detected category name)
Right side, top-alignedF) AXES, GRID, AND IBCS CLEAN LOOK
Axes must start at 0 (both).
X max:
round up to a “nice” tick (5/10/20 steps) beyond max marginY max:
round up to a “nice” tick beyond max salesGrid:
Light gray gridlines #E6E6E6, thin
Slightly stronger zero-linesTypography:
Use one sans-serif font family throughout (Arial/Helvetica equivalent)
Limit font sizes (header bigger, body/legend smaller)Gridlines:
There must be NO gridlines.
Remove all:
• Major gridlines
• Minor gridlines
• Background gridsKeep only clean axes lines.
No:
Gradients
shadows
3D
heavy borders
decorative backgroundsG) LAYOUT: RESERVE SPACE OUTSIDE THE PLOT (MANDATORY)
The plot (axes + points + curves + shading) must occupy only the inner plotting area.
Reserve dedicated outside space so nothing overlaps the plot:
Top margin ≥ 120 px (for title + insight)
Right margin ≥ 160 px (for legend + “Gross profit …” label)
Bottom margin ≥ 60 px (for footer)Ensure legend and all text blocks are outside the plotting area.
Implementation requirement (critical):
All header/footer blocks MUST be implemented as layout annotations positioned in paper coordinates, not as chart title and not as data-coordinate annotations.
Use xref=“paper” and yref=“paper” for all header/footer text.
Use y > 1.0 for header content (in the top margin), and y < 0 for footer content (in the bottom margin).
H) HEADER TITLE BLOCK (TOP-LEFT, OUTSIDE PLOT)
Create three separate text lines as paper-anchored annotations placed above the plotting area:
Line 1 (bold): Alpha Corp., Paper division
Line 2 (regular): Net sales in [unit], margin in %
Line 3 (regular): [Year]Absolute positioning rules (paper coords):
x = 0.00 (left edge of plotting paper)
y = 1.18 for Line 1
y = 1.12 for Line 2
y = 1.06 for Line 3left aligned:
xanchor=“left”
align=“left”Font family:
one sans-serif everywhere (Arial/Helvetica)Sizes:
14 (bold)
12
12Color:
#333333No arrows:
showarrow=FalseNon-negotiable:
Do NOT uselayout.titlefor this.
Use annotations only.I) INSIGHT STATEMENT (OUTSIDE PLOT, UNDER TITLE)
Compute:
COUNT = number of products with GP >= L3
DOMINANT_LINE = most frequent product line among thoseIf tie:
omit DOMINANT_LINE clauseCreate the insight sentence (exact wording):
In [Year] we had [COUNT] products of the product line [DOMINANT_LINE] in the gross profit segment of [L3] [unit] and above
Place it as a paper-anchored annotation:
x = 0.32 (to appear centered-ish like the reference header row)
y = 1.12
xref=“paper”
yref=“paper”
xanchor=“left”
align=“left”Font:
12
bold
color #666666
showarrow=FalseNon-negotiable:
It must be outside the plot area (y > 1.0), never inside.J) LEGEND + RIGHT-SIDE “GROSS PROFIT” LABEL (OUTSIDE PLOT)
Legend on the right, top-aligned, outside plot.
Add a vertical text block on the far right (paper-anchored), outside plot:
Annotation text:
Gross profit
in [unit]Position:
x = 1.08
y = 0.50
xref=“paper”
yref=“paper”Rotate -90
Font 11
color #666666
showarrow=FalseK) FOOTER (OUTSIDE PLOT)
Two paper-anchored annotations below the plot (y < 0):
Bottom-left date:
x=0.00
y=-0.18
xanchor=“left”Bottom-right:
“© [Current year] IBCS Institute | ``www.ibcs.com``”
x=1.00
y=-0.18
xanchor=“right”Font 9
color #9A9A9A
showarrow=FalseFix marker labels so there are NO ARROWS / NO LEADER LINES
L) PRODUCT LABELS MUST BE TRACE TEXT, NOT ANNOTATIONS
- Label 3–5 highest GP products.
- Label at least 1 product near the L3 threshold.
- Label highest margin product.
- Label highest sales product.
- Ensure each dominant product line appears at least once.
- Cap total labelled products at 12.
- Ensure 8-12 products are labelled.
For these selected labelled products, display labels as marker text (trace text), not layout annotations.
Rules:
Use scatter trace with mode=“markers+text”
Set:
textposition=“top right”
textfont.size=10
textfont.family same as chart
textfont.color = category colorDisable arrows completely:
Do NOT create any annotation with showarrow=True
Do NOT use “callouts”
Do NOT draw leader linesIf overlap occurs:
Prefer hiding some labels (keep the top GP ones) rather than adding arrows/lines.Non-negotiable:
Labels must be plain text near the points only.IMPORTANT:
Every instruction above is mandatory and non-optional.
Data visual created in Plotly Studio with above prompt:
Original data visual:

